diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ceac9bc19..c7eb10c10a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -179,13 +179,16 @@ jobs: - name: Build crate run: cargo $CMD build $OPTIONS + - name: Test winit core + run: cargo test -p winit-core + # Test only on Linux x86_64, so we avoid spending unnecessary CI hours. - name: Test dpi crate if: > contains(matrix.platform.name, 'Linux 64bit') && matrix.toolchain != '1.80' run: cargo test -p dpi - + - name: Check dpi crate (no_std) if: > contains(matrix.platform.name, 'Linux 64bit') && diff --git a/Cargo.toml b/Cargo.toml index 7385cf7e46..d46ee22615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,14 @@ android-game-activity = ["android-activity/game-activity"] android-native-activity = ["android-activity/native-activity"] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] mint = ["dpi/mint"] -serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"] +serde = [ + "dep:serde", + "cursor-icon/serde", + "smol_str/serde", + "dpi/serde", + "bitflags/serde", + "winit-core/serde", +] wayland = [ "wayland-client", "wayland-backend", @@ -83,6 +90,7 @@ rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] } serde = { workspace = true, optional = true } smol_str = "0.3" tracing = { version = "0.1.40", default-features = false } +winit-core = { version = "0.0.0", path = "winit-core" } [dev-dependencies] image = { version = "0.25.0", default-features = false, features = ["png"] } @@ -389,7 +397,7 @@ name = "window" name = "child_window" [workspace] -members = ["dpi"] +members = ["dpi", "winit-core"] resolver = "2" [workspace.package] diff --git a/deny.toml b/deny.toml index c789555924..337413f3af 100644 --- a/deny.toml +++ b/deny.toml @@ -51,6 +51,10 @@ allow = [ ] crate = "android-activity" +[[bans.build.bypass]] +allow-globs = ["ci/*", "githooks/*"] +crate = "zerocopy" + [[bans.build.bypass]] allow-globs = ["freetype2/*"] crate = "freetype-sys" diff --git a/examples/application.rs b/examples/application.rs index 931c83665f..aa869384a5 100644 --- a/examples/application.rs +++ b/examples/application.rs @@ -16,29 +16,26 @@ use rwh_06::{DisplayHandle, HasDisplayHandle}; #[cfg(not(android_platform))] use softbuffer::{Context, Surface}; use winit::application::ApplicationHandler; +use winit::cursor::{Cursor, CustomCursor, CustomCursorSource}; use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize}; use winit::error::RequestError; use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent}; use winit::event_loop::{ActiveEventLoop, EventLoop}; -use winit::icon::RgbaIcon; +use winit::icon::{Icon, RgbaIcon}; use winit::keyboard::{Key, ModifiersState}; use winit::monitor::Fullscreen; #[cfg(macos_platform)] -use winit::platform::macos::{ - ApplicationHandlerExtMacOS, OptionAsAlt, WindowAttributesExtMacOS, WindowExtMacOS, -}; +use winit::platform::macos::{OptionAsAlt, WindowAttributesMacOS, WindowExtMacOS}; #[cfg(any(x11_platform, wayland_platform))] -use winit::platform::startup_notify::{ - self, EventLoopExtStartupNotify, WindowAttributesExtStartupNotify, WindowExtStartupNotify, -}; +use winit::platform::startup_notify::{self, EventLoopExtStartupNotify, WindowExtStartupNotify}; +#[cfg(wayland_platform)] +use winit::platform::wayland::{ActiveEventLoopExtWayland, WindowAttributesWayland}; #[cfg(web_platform)] -use winit::platform::web::{ActiveEventLoopExtWeb, WindowAttributesExtWeb}; +use winit::platform::web::{ActiveEventLoopExtWeb, WindowAttributesWeb}; #[cfg(x11_platform)] -use winit::platform::x11::WindowAttributesExtX11; -use winit::window::{ - Cursor, CursorGrabMode, CustomCursor, CustomCursorSource, Icon, ResizeDirection, Theme, Window, - WindowAttributes, WindowId, -}; +use winit::platform::x11::{ActiveEventLoopExtX11, WindowAttributesX11}; +use winit::window::{CursorGrabMode, ResizeDirection, Theme, Window, WindowAttributes, WindowId}; +use winit_core::application::macos::ApplicationHandlerExtMacOS; #[path = "util/tracing.rs"] mod tracing; @@ -149,43 +146,29 @@ impl Application { .with_transparent(true) .with_window_icon(Some(self.icon.clone())); - #[cfg(any(x11_platform, wayland_platform))] - if let Some(token) = event_loop.read_token_from_env() { - startup_notify::reset_activation_token_env(); - info!("Using token {:?} to activate a window", token); - window_attributes = window_attributes.with_activation_token(token); - } - #[cfg(x11_platform)] - match std::env::var("X11_VISUAL_ID") { - Ok(visual_id_str) => { - info!("Using X11 visual id {visual_id_str}"); - let visual_id = visual_id_str.parse()?; - window_attributes = window_attributes.with_x11_visual(visual_id); - }, - Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"), + if event_loop.is_x11() { + window_attributes = window_attributes + .with_platform_attributes(Box::new(window_attributes_x11(event_loop)?)); } - #[cfg(x11_platform)] - match std::env::var("X11_SCREEN_ID") { - Ok(screen_id_str) => { - info!("Placing the window on X11 screen {screen_id_str}"); - let screen_id = screen_id_str.parse()?; - window_attributes = window_attributes.with_x11_screen(screen_id); - }, - Err(_) => info!( - "Set the X11_SCREEN_ID env variable to place the window on non-default screen" - ), + #[cfg(wayland_platform)] + if event_loop.is_wayland() { + window_attributes = window_attributes + .with_platform_attributes(Box::new(window_attributes_wayland(event_loop))); } #[cfg(macos_platform)] if let Some(tab_id) = _tab_id { - window_attributes = window_attributes.with_tabbing_identifier(&tab_id); + let window_attributes_macos = + Box::new(WindowAttributesMacOS::default().with_tabbing_identifier(&tab_id)); + window_attributes = window_attributes.with_platform_attributes(window_attributes_macos); } #[cfg(web_platform)] { - window_attributes = window_attributes.with_append(true); + window_attributes = + window_attributes.with_platform_attributes(Box::new(window_attributes_web())); } let window = event_loop.create_window(window_attributes)?; @@ -593,7 +576,6 @@ impl ApplicationHandler for Application { } } - #[cfg(target_os = "macos")] fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { Some(self) } @@ -605,7 +587,6 @@ impl Drop for Application { } } -#[cfg(target_os = "macos")] impl ApplicationHandlerExtMacOS for Application { fn standard_key_binding( &mut self, @@ -1198,6 +1179,60 @@ fn mouse_button_to_string(button: MouseButton) -> &'static str { } } +#[cfg(web_platform)] +fn window_attributes_web() -> WindowAttributesWeb { + WindowAttributesWeb::default().with_append(true) +} + +#[cfg(wayland_platform)] +fn window_attributes_wayland(event_loop: &dyn ActiveEventLoop) -> WindowAttributesWayland { + let mut window_attributes = WindowAttributesWayland::default(); + + if let Some(token) = event_loop.read_token_from_env() { + startup_notify::reset_activation_token_env(); + info!("Using token {:?} to activate a window", token); + window_attributes = window_attributes.with_activation_token(token); + } + + window_attributes +} + +#[cfg(x11_platform)] +fn window_attributes_x11( + event_loop: &dyn ActiveEventLoop, +) -> Result> { + let mut window_attributes = WindowAttributesX11::default(); + + #[cfg(any(x11_platform, wayland_platform))] + if let Some(token) = event_loop.read_token_from_env() { + startup_notify::reset_activation_token_env(); + info!("Using token {:?} to activate a window", token); + window_attributes = window_attributes.with_activation_token(token); + } + + match std::env::var("X11_VISUAL_ID") { + Ok(visual_id_str) => { + info!("Using X11 visual id {visual_id_str}"); + let visual_id = visual_id_str.parse()?; + window_attributes = window_attributes.with_x11_visual(visual_id); + }, + Err(_) => info!("Set the X11_VISUAL_ID env variable to request specific X11 visual"), + } + + match std::env::var("X11_SCREEN_ID") { + Ok(screen_id_str) => { + info!("Placing the window on X11 screen {screen_id_str}"); + let screen_id = screen_id_str.parse()?; + window_attributes = window_attributes.with_x11_screen(screen_id); + }, + Err(_) => { + info!("Set the X11_SCREEN_ID env variable to place the window on non-default screen") + }, + } + + Ok(window_attributes) +} + /// Cursor list to cycle through. const CURSORS: &[CursorIcon] = &[ CursorIcon::Default, diff --git a/examples/window.rs b/examples/window.rs index b6077270b1..76c60c39e8 100644 --- a/examples/window.rs +++ b/examples/window.rs @@ -6,7 +6,7 @@ use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; #[cfg(web_platform)] -use winit::platform::web::WindowAttributesExtWeb; +use winit::platform::web::WindowAttributesWeb; use winit::window::{Window, WindowAttributes, WindowId}; #[path = "util/fill.rs"] @@ -24,7 +24,8 @@ impl ApplicationHandler for App { #[cfg(not(web_platform))] let window_attributes = WindowAttributes::default(); #[cfg(web_platform)] - let window_attributes = WindowAttributes::default().with_append(true); + let window_attributes = WindowAttributes::default() + .with_platform_attributes(Box::new(WindowAttributesWeb::default().with_append(true))); self.window = match event_loop.create_window(window_attributes) { Ok(window) => Some(window), Err(err) => { diff --git a/examples/x11_embed.rs b/examples/x11_embed.rs index e600a5d052..c047f71ef4 100644 --- a/examples/x11_embed.rs +++ b/examples/x11_embed.rs @@ -6,7 +6,7 @@ fn main() -> Result<(), Box> { use winit::application::ApplicationHandler; use winit::event::WindowEvent; use winit::event_loop::{ActiveEventLoop, EventLoop}; - use winit::platform::x11::WindowAttributesExtX11; + use winit::platform::x11::WindowAttributesX11; use winit::window::{Window, WindowAttributes, WindowId}; #[path = "util/fill.rs"] @@ -20,10 +20,12 @@ fn main() -> Result<(), Box> { impl ApplicationHandler for XEmbedDemo { fn can_create_surfaces(&mut self, event_loop: &dyn ActiveEventLoop) { - let window_attributes = WindowAttributes::default() + let mut window_attributes = WindowAttributes::default() .with_title("An embedded window!") - .with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0)) - .with_embed_parent_window(self.parent_window_id); + .with_surface_size(winit::dpi::LogicalSize::new(128.0, 128.0)); + let x11_attrs = + WindowAttributesX11::default().with_embed_parent_window(self.parent_window_id); + window_attributes = window_attributes.with_platform_attributes(Box::new(x11_attrs)); self.window = Some(event_loop.create_window(window_attributes).unwrap()); } diff --git a/src/changelog/unreleased.md b/src/changelog/unreleased.md index d56b693180..2198bb7f48 100644 --- a/src/changelog/unreleased.md +++ b/src/changelog/unreleased.md @@ -61,7 +61,7 @@ changelog entry. - Add `MonitorHandle::current_video_mode()`. - Add `ApplicationHandlerExtMacOS` trait, and a `macos_handler` method to `ApplicationHandler` which returns a `dyn ApplicationHandlerExtMacOS` which allows for macOS specific extensions to winit. - Add a `standard_key_binding` method to the `ApplicationHandlerExtMacOS` trait. This allows handling of standard keybindings such as "go to end of line" on macOS. -- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesExtMacOS::with_unified_titlebar` +- On macOS, add `WindowExtMacOS::set_unified_titlebar` and `WindowAttributesMacOS::with_unified_titlebar` to use a larger style of titlebar. - Add `WindowId::into_raw()` and `from_raw()`. - Add `PointerKind`, `PointerSource`, `ButtonSource`, `FingerId`, `primary` and `position` to all @@ -77,6 +77,10 @@ changelog entry. - Add `CustomCursorSource::Url`, `CustomCursorSource::from_animation`. - Implement `CustomIconProvider` for `RgbaIcon`. - Add `icon` module that exposes winit's icon API. +- `VideoMode::new` to create a `VideoMode`. +- `keyboard::ModifiersKey` to track which modifier is exactly pressed. +- `ActivationToken::as_raw` to get a ref to raw token. +- Each platform now has corresponding `WindowAttributes` struct instead of trait extension. ### Changed @@ -233,6 +237,7 @@ changelog entry. the `Drop` impl on the application handler. - Remove `NamedKey::Space`, match on `Key::Character(" ")` instead. - Remove `PartialEq` impl for `WindowAttributes`. +- `WindowAttributesExt*` platform extensions; use `WindowAttributes*` instead. ### Fixed diff --git a/src/event_loop.rs b/src/event_loop.rs index 715d123de4..6b66b814ac 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -8,25 +8,18 @@ //! //! See the root-level documentation for information on how to create and use an event loop to //! handle events. -use std::fmt; use std::marker::PhantomData; #[cfg(any(x11_platform, wayland_platform))] use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::Arc; -#[cfg(not(web_platform))] -use std::time::{Duration, Instant}; +use std::sync::atomic::{AtomicBool, Ordering}; use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; -#[cfg(web_platform)] -use web_time::{Duration, Instant}; +pub use winit_core::event_loop::*; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::error::{EventLoopError, RequestError}; -use crate::monitor::MonitorHandle; use crate::platform_impl; -use crate::utils::{impl_dyn_casting, AsAny}; -use crate::window::{CustomCursor, CustomCursorSource, Theme, Window, WindowAttributes}; /// Provides a way to retrieve events from the system and from the windows that were registered to /// the events loop. @@ -118,51 +111,6 @@ impl EventLoopBuilder { } } -/// Set through [`ActiveEventLoop::set_control_flow()`]. -/// -/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called. -/// -/// Defaults to [`Wait`]. -/// -/// [`Wait`]: Self::Wait -/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] -pub enum ControlFlow { - /// When the current loop iteration finishes, immediately begin a new iteration regardless of - /// whether or not new events are available to process. - Poll, - - /// When the current loop iteration finishes, suspend the thread until another event arrives. - #[default] - Wait, - - /// When the current loop iteration finishes, suspend the thread until either another event - /// arrives or the given time is reached. - /// - /// Useful for implementing efficient timers. Applications which want to render at the - /// display's native refresh rate should instead use [`Poll`] and the VSync functionality - /// of a graphics API to reduce odds of missed frames. - /// - /// [`Poll`]: Self::Poll - WaitUntil(Instant), -} - -impl ControlFlow { - /// Creates a [`ControlFlow`] that waits until a timeout has expired. - /// - /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is - /// instead set to [`Wait`]. - /// - /// [`WaitUntil`]: Self::WaitUntil - /// [`Wait`]: Self::Wait - pub fn wait_duration(timeout: Duration) -> Self { - match Instant::now().checked_add(timeout) { - Some(instant) => Self::WaitUntil(instant), - None => Self::Wait, - } - } -} - impl EventLoop { /// Create the event loop. /// @@ -181,11 +129,11 @@ impl EventLoop { pub fn builder() -> EventLoopBuilder { EventLoopBuilder { platform_specific: Default::default() } } -} -impl EventLoop { /// Run the application with the event loop on the calling thread. /// + /// The `app` is dropped when the event loop is shut down. + /// /// ## Event loop flow /// /// This function internally handles the different parts of a traditional event-handling loop. @@ -346,241 +294,3 @@ impl AsRawFd for EventLoop { self.event_loop.as_raw_fd() } } - -pub trait ActiveEventLoop: AsAny + fmt::Debug { - /// Creates an [`EventLoopProxy`] that can be used to dispatch user events - /// to the main event loop, possibly from another thread. - fn create_proxy(&self) -> EventLoopProxy; - - /// Create the window. - /// - /// Possible causes of error include denied permission, incompatible system, and lack of memory. - /// - /// ## Platform-specific - /// - /// - **Web:** The window is created but not inserted into the Web page automatically. Please - /// see the Web platform module for more information. - fn create_window( - &self, - window_attributes: WindowAttributes, - ) -> Result, RequestError>; - - /// Create custom cursor. - /// - /// ## Platform-specific - /// - /// **iOS / Android / Orbital:** Unsupported. - fn create_custom_cursor( - &self, - custom_cursor: CustomCursorSource, - ) -> Result; - - /// Returns the list of all the monitors available on the system. - /// - /// ## Platform-specific - /// - /// **Web:** Only returns the current monitor without - #[cfg_attr( - web_platform, - doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] - fn available_monitors(&self) -> Box>; - - /// Returns the primary monitor of the system. - /// - /// Returns `None` if it can't identify any monitor as a primary one. - /// - /// ## Platform-specific - /// - /// - **Wayland:** Always returns `None`. - /// - **Web:** Always returns `None` without - #[cfg_attr( - web_platform, - doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")] - fn primary_monitor(&self) -> Option; - - /// Change if or when [`DeviceEvent`]s are captured. - /// - /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit - /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing - /// this at runtime to explicitly capture them again. - /// - /// ## Platform-specific - /// - /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported. - /// - /// [`DeviceEvent`]: crate::event::DeviceEvent - fn listen_device_events(&self, allowed: DeviceEvents); - - /// Returns the current system theme. - /// - /// Returns `None` if it cannot be determined on the current platform. - /// - /// ## Platform-specific - /// - /// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported. - fn system_theme(&self) -> Option; - - /// Sets the [`ControlFlow`]. - fn set_control_flow(&self, control_flow: ControlFlow); - - /// Gets the current [`ControlFlow`]. - fn control_flow(&self) -> ControlFlow; - - /// Stop the event loop. - /// - /// ## Platform-specific - /// - /// ### iOS - /// - /// It is not possible to programmatically exit/quit an application on iOS, so this function is - /// a no-op there. See also [this technical Q&A][qa1561]. - /// - /// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html - fn exit(&self); - - /// Returns whether the [`EventLoop`] is about to stop. - /// - /// Set by [`exit()`][Self::exit]. - fn exiting(&self) -> bool; - - /// Gets a persistent reference to the underlying platform display. - /// - /// See the [`OwnedDisplayHandle`] type for more information. - fn owned_display_handle(&self) -> OwnedDisplayHandle; - - /// Get the raw-window-handle handle. - fn rwh_06_handle(&self) -> &dyn HasDisplayHandle; -} - -impl HasDisplayHandle for dyn ActiveEventLoop + '_ { - fn display_handle(&self) -> Result, HandleError> { - self.rwh_06_handle().display_handle() - } -} - -impl_dyn_casting!(ActiveEventLoop); - -/// A proxy for the underlying display handle. -/// -/// The purpose of this type is to provide a cheaply cloneable handle to the underlying -/// display handle. This is often used by graphics APIs to connect to the underlying APIs. -/// It is difficult to keep a handle to the [`EventLoop`] type or the [`ActiveEventLoop`] -/// type. In contrast, this type involves no lifetimes and can be persisted for as long as -/// needed. -/// -/// For all platforms, this is one of the following: -/// -/// - A zero-sized type that is likely optimized out. -/// - A reference-counted pointer to the underlying type. -#[derive(Clone)] -pub struct OwnedDisplayHandle { - pub(crate) handle: Arc, -} - -impl OwnedDisplayHandle { - pub(crate) fn new(handle: Arc) -> Self { - Self { handle } - } -} - -impl HasDisplayHandle for OwnedDisplayHandle { - fn display_handle(&self) -> Result, HandleError> { - self.handle.display_handle() - } -} - -impl fmt::Debug for OwnedDisplayHandle { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive() - } -} - -impl PartialEq for OwnedDisplayHandle { - fn eq(&self, other: &Self) -> bool { - match (self.display_handle(), other.display_handle()) { - (Ok(lhs), Ok(rhs)) => lhs == rhs, - _ => false, - } - } -} - -impl Eq for OwnedDisplayHandle {} - -pub(crate) trait EventLoopProxyProvider: Send + Sync + fmt::Debug { - /// See [`EventLoopProxy::wake_up`] for details. - fn wake_up(&self); -} - -/// Control the [`EventLoop`], possibly from a different thread, without referencing it directly. -#[derive(Clone, Debug)] -pub struct EventLoopProxy { - pub(crate) proxy: Arc, -} - -impl EventLoopProxy { - /// Wake up the [`EventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being - /// called. - /// - /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the - /// documentation on that for details. - /// - /// If the event loop is no longer running, this is a no-op. - /// - /// [`proxy_wake_up`]: ApplicationHandler::proxy_wake_up - /// - /// # Platform-specific - /// - /// - **Windows**: The wake-up may be ignored under high contention, see [#3687]. - /// - /// [#3687]: https://github.com/rust-windowing/winit/pull/3687 - pub fn wake_up(&self) { - self.proxy.wake_up(); - } - - pub(crate) fn new(proxy: Arc) -> Self { - Self { proxy } - } -} - -/// Control when device events are captured. -#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum DeviceEvents { - /// Report device events regardless of window focus. - Always, - /// Only capture device events while the window is focused. - #[default] - WhenFocused, - /// Never capture device events. - Never, -} - -/// A unique identifier of the winit's async request. -/// -/// This could be used to identify the async request once it's done -/// and a specific action must be taken. -/// -/// One of the handling scenarios could be to maintain a working list -/// containing [`AsyncRequestSerial`] and some closure associated with it. -/// Then once event is arriving the working list is being traversed and a job -/// executed and removed from the list. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct AsyncRequestSerial { - serial: usize, -} - -impl AsyncRequestSerial { - // TODO(kchibisov): Remove `cfg` when the clipboard will be added. - #[allow(dead_code)] - pub(crate) fn get() -> Self { - static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0); - // NOTE: We rely on wrap around here, while the user may just request - // in the loop usize::MAX times that's issue is considered on them. - let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); - Self { serial } - } -} diff --git a/src/lib.rs b/src/lib.rs index 2a11bd6e31..bda19f102e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -296,19 +296,12 @@ pub use dpi; pub use rwh_06 as raw_window_handle; -pub mod application; #[cfg(any(doc, doctest, test))] pub mod changelog; -#[macro_use] -pub mod error; -mod cursor; -pub mod event; pub mod event_loop; -pub mod icon; -pub mod keyboard; -pub mod monitor; +pub use winit_core::{application, cursor, error, event, icon, keyboard, monitor, window}; +#[macro_use] +mod os_error; mod platform_impl; -mod utils; -pub mod window; pub mod platform; diff --git a/src/os_error.rs b/src/os_error.rs new file mode 100644 index 0000000000..30281c8d88 --- /dev/null +++ b/src/os_error.rs @@ -0,0 +1,6 @@ +#[allow(unused_macros)] +macro_rules! os_error { + ($error:expr) => {{ + winit_core::error::OsError::new(line!(), file!(), $error) + }}; +} diff --git a/src/platform/android.rs b/src/platform/android.rs index 086711ac38..d2250ed3f3 100644 --- a/src/platform/android.rs +++ b/src/platform/android.rs @@ -72,7 +72,7 @@ use self::activity::{AndroidApp, ConfigurationRef, Rect}; use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; -use crate::window::{Window, WindowAttributes}; +use crate::window::Window; /// Additional methods on [`EventLoop`] that are specific to Android. pub trait EventLoopExtAndroid { @@ -118,11 +118,6 @@ impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ { } } -/// Additional methods on [`WindowAttributes`] that are specific to Android. -pub trait WindowAttributesExtAndroid {} - -impl WindowAttributesExtAndroid for WindowAttributes {} - pub trait EventLoopBuilderExtAndroid { /// Associates the [`AndroidApp`] that was passed to `android_main()` with the event loop /// diff --git a/src/platform/ios.rs b/src/platform/ios.rs index 5cbf85898b..1051a30b67 100644 --- a/src/platform/ios.rs +++ b/src/platform/ios.rs @@ -103,10 +103,11 @@ use std::os::raw::c_void; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use winit_core::window::PlatformWindowAttributes; use crate::monitor::{MonitorHandle, VideoMode}; use crate::platform_impl::MonitorHandle as IosMonitorHandle; -use crate::window::{Window, WindowAttributes}; +use crate::window::Window; /// Additional methods on [`Window`] that are specific to iOS. pub trait WindowExtIOS { @@ -283,8 +284,18 @@ impl WindowExtIOS for dyn Window + '_ { } } -/// Additional methods on [`WindowAttributes`] that are specific to iOS. -pub trait WindowAttributesExtIOS { +/// Ios specific window attributes. +#[derive(Clone, Debug, Default, PartialEq)] +pub struct WindowAttributesIos { + pub(crate) scale_factor: Option, + pub(crate) valid_orientations: ValidOrientations, + pub(crate) prefers_home_indicator_hidden: bool, + pub(crate) prefers_status_bar_hidden: bool, + pub(crate) preferred_status_bar_style: StatusBarStyle, + pub(crate) preferred_screen_edges_deferring_system_gestures: ScreenEdge, +} + +impl WindowAttributesIos { /// Sets the [`contentScaleFactor`] of the underlying [`UIWindow`] to `scale_factor`. /// /// The default value is device dependent, and it's recommended GLES or Metal applications set @@ -293,7 +304,10 @@ pub trait WindowAttributesExtIOS { /// [`UIWindow`]: https://developer.apple.com/documentation/uikit/uiwindow?language=objc /// [`contentScaleFactor`]: https://developer.apple.com/documentation/uikit/uiview/1622657-contentscalefactor?language=objc /// [`MonitorHandleProvider::scale_factor()`]: crate::monitor::MonitorHandleProvider::scale_factor() - fn with_scale_factor(self, scale_factor: f64) -> Self; + pub fn with_scale_factor(mut self, scale_factor: f64) -> Self { + self.scale_factor = Some(scale_factor); + self + } /// Sets the valid orientations for the [`Window`]. /// @@ -301,7 +315,10 @@ pub trait WindowAttributesExtIOS { /// /// This sets the initial value returned by /// [`-[UIViewController supportedInterfaceOrientations]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621435-supportedinterfaceorientations?language=objc). - fn with_valid_orientations(self, valid_orientations: ValidOrientations) -> Self; + pub fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { + self.valid_orientations = valid_orientations; + self + } /// Sets whether the [`Window`] prefers the home indicator hidden. /// @@ -311,7 +328,10 @@ pub trait WindowAttributesExtIOS { /// [`-[UIViewController prefersHomeIndicatorAutoHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887510-prefershomeindicatorautohidden?language=objc). /// /// This only has an effect on iOS 11.0+. - fn with_prefers_home_indicator_hidden(self, hidden: bool) -> Self; + pub fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { + self.prefers_home_indicator_hidden = hidden; + self + } /// Sets the screen edges for which the system gestures will take a lower priority than the /// application's touch handling. @@ -320,7 +340,13 @@ pub trait WindowAttributesExtIOS { /// [`-[UIViewController preferredScreenEdgesDeferringSystemGestures]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/2887512-preferredscreenedgesdeferringsys?language=objc). /// /// This only has an effect on iOS 11.0+. - fn with_preferred_screen_edges_deferring_system_gestures(self, edges: ScreenEdge) -> Self; + pub fn with_preferred_screen_edges_deferring_system_gestures( + mut self, + edges: ScreenEdge, + ) -> Self { + self.preferred_screen_edges_deferring_system_gestures = edges; + self + } /// Sets whether the [`Window`] prefers the status bar hidden. /// @@ -328,7 +354,10 @@ pub trait WindowAttributesExtIOS { /// /// This sets the initial value returned by /// [`-[UIViewController prefersStatusBarHidden]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621440-prefersstatusbarhidden?language=objc). - fn with_prefers_status_bar_hidden(self, hidden: bool) -> Self; + pub fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { + self.prefers_status_bar_hidden = hidden; + self + } /// Sets the style of the [`Window`]'s status bar. /// @@ -336,44 +365,15 @@ pub trait WindowAttributesExtIOS { /// /// This sets the initial value returned by /// [`-[UIViewController preferredStatusBarStyle]`](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621416-preferredstatusbarstyle?language=objc), - fn with_preferred_status_bar_style(self, status_bar_style: StatusBarStyle) -> Self; -} - -impl WindowAttributesExtIOS for WindowAttributes { - #[inline] - fn with_scale_factor(mut self, scale_factor: f64) -> Self { - self.platform_specific.scale_factor = Some(scale_factor); - self - } - - #[inline] - fn with_valid_orientations(mut self, valid_orientations: ValidOrientations) -> Self { - self.platform_specific.valid_orientations = valid_orientations; - self - } - - #[inline] - fn with_prefers_home_indicator_hidden(mut self, hidden: bool) -> Self { - self.platform_specific.prefers_home_indicator_hidden = hidden; - self - } - - #[inline] - fn with_preferred_screen_edges_deferring_system_gestures(mut self, edges: ScreenEdge) -> Self { - self.platform_specific.preferred_screen_edges_deferring_system_gestures = edges; - self - } - - #[inline] - fn with_prefers_status_bar_hidden(mut self, hidden: bool) -> Self { - self.platform_specific.prefers_status_bar_hidden = hidden; + pub fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self { + self.preferred_status_bar_style = status_bar_style; self } +} - #[inline] - fn with_preferred_status_bar_style(mut self, status_bar_style: StatusBarStyle) -> Self { - self.platform_specific.preferred_status_bar_style = status_bar_style; - self +impl PlatformWindowAttributes for WindowAttributesIos { + fn box_clone(&self) -> Box { + Box::from(self.clone()) } } diff --git a/src/platform/macos.rs b/src/platform/macos.rs index a417406eba..8c938500be 100644 --- a/src/platform/macos.rs +++ b/src/platform/macos.rs @@ -69,12 +69,14 @@ use std::os::raw::c_void; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +#[doc(inline)] +pub use winit_core::application::macos::ApplicationHandlerExtMacOS; +use winit_core::window::PlatformWindowAttributes; -use crate::application::ApplicationHandler; use crate::event_loop::{ActiveEventLoop, EventLoopBuilder}; use crate::monitor::MonitorHandle; use crate::platform_impl::MonitorHandle as MacOsMonitorHandle; -use crate::window::{Window, WindowAttributes, WindowId}; +use crate::window::Window; /// Additional methods on [`Window`] that are specific to MacOS. pub trait WindowExtMacOS { @@ -292,7 +294,7 @@ pub enum ActivationPolicy { Prohibited, } -/// Additional methods on [`WindowAttributes`] that are specific to MacOS. +/// Window attributes that are specific to MacOS. /// /// **Note:** Properties dealing with the titlebar will be overwritten by the /// [`WindowAttributes::with_decorations`] method: @@ -301,130 +303,161 @@ pub enum ActivationPolicy { /// - `with_titlebar_hidden` /// - `with_titlebar_buttons_hidden` /// - `with_fullsize_content_view` -pub trait WindowAttributesExtMacOS { - /// Enables click-and-drag behavior for the entire window, not just the titlebar. - fn with_movable_by_window_background(self, movable_by_window_background: bool) -> Self; - /// Makes the titlebar transparent and allows the content to appear behind it. - fn with_titlebar_transparent(self, titlebar_transparent: bool) -> Self; - /// Hides the window title. - fn with_title_hidden(self, title_hidden: bool) -> Self; - /// Hides the window titlebar. - fn with_titlebar_hidden(self, titlebar_hidden: bool) -> Self; - /// Hides the window titlebar buttons. - fn with_titlebar_buttons_hidden(self, titlebar_buttons_hidden: bool) -> Self; - /// Makes the window content appear behind the titlebar. - fn with_fullsize_content_view(self, fullsize_content_view: bool) -> Self; - fn with_disallow_hidpi(self, disallow_hidpi: bool) -> Self; - fn with_has_shadow(self, has_shadow: bool) -> Self; - /// Window accepts click-through mouse events. - fn with_accepts_first_mouse(self, accepts_first_mouse: bool) -> Self; - /// Defines the window tabbing identifier. - /// - /// - fn with_tabbing_identifier(self, identifier: &str) -> Self; - /// Set how the Option keys are interpreted. - /// - /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set. - fn with_option_as_alt(self, option_as_alt: OptionAsAlt) -> Self; - /// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set. - fn with_borderless_game(self, borderless_game: bool) -> Self; - /// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set. - fn with_unified_titlebar(self, unified_titlebar: bool) -> Self; - /// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of - /// [`NSWindow`]. - /// - /// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc - /// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc - /// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc - fn with_panel(self, panel: bool) -> Self; +/// +/// [`WindowAttributes::with_decorations`]: crate::window::WindowAttributes::with_decorations +#[derive(Clone, Debug, PartialEq)] +pub struct WindowAttributesMacOS { + pub(crate) movable_by_window_background: bool, + pub(crate) titlebar_transparent: bool, + pub(crate) title_hidden: bool, + pub(crate) titlebar_hidden: bool, + pub(crate) titlebar_buttons_hidden: bool, + pub(crate) fullsize_content_view: bool, + pub(crate) disallow_hidpi: bool, + pub(crate) has_shadow: bool, + pub(crate) accepts_first_mouse: bool, + pub(crate) tabbing_identifier: Option, + pub(crate) option_as_alt: OptionAsAlt, + pub(crate) borderless_game: bool, + pub(crate) unified_titlebar: bool, + pub(crate) panel: bool, } -impl WindowAttributesExtMacOS for WindowAttributes { +impl WindowAttributesMacOS { + /// Enables click-and-drag behavior for the entire window, not just the titlebar. #[inline] - fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self { - self.platform_specific.movable_by_window_background = movable_by_window_background; + pub fn with_movable_by_window_background(mut self, movable_by_window_background: bool) -> Self { + self.movable_by_window_background = movable_by_window_background; self } + /// Makes the titlebar transparent and allows the content to appear behind it. #[inline] - fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self { - self.platform_specific.titlebar_transparent = titlebar_transparent; + pub fn with_titlebar_transparent(mut self, titlebar_transparent: bool) -> Self { + self.titlebar_transparent = titlebar_transparent; self } + /// Hides the window titlebar. #[inline] - fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self { - self.platform_specific.titlebar_hidden = titlebar_hidden; + pub fn with_titlebar_hidden(mut self, titlebar_hidden: bool) -> Self { + self.titlebar_hidden = titlebar_hidden; self } + /// Hides the window titlebar buttons. #[inline] - fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self { - self.platform_specific.titlebar_buttons_hidden = titlebar_buttons_hidden; + pub fn with_titlebar_buttons_hidden(mut self, titlebar_buttons_hidden: bool) -> Self { + self.titlebar_buttons_hidden = titlebar_buttons_hidden; self } + /// Hides the window title. #[inline] - fn with_title_hidden(mut self, title_hidden: bool) -> Self { - self.platform_specific.title_hidden = title_hidden; + pub fn with_title_hidden(mut self, title_hidden: bool) -> Self { + self.title_hidden = title_hidden; self } + /// Makes the window content appear behind the titlebar. #[inline] - fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self { - self.platform_specific.fullsize_content_view = fullsize_content_view; + pub fn with_fullsize_content_view(mut self, fullsize_content_view: bool) -> Self { + self.fullsize_content_view = fullsize_content_view; self } #[inline] - fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self { - self.platform_specific.disallow_hidpi = disallow_hidpi; + pub fn with_disallow_hidpi(mut self, disallow_hidpi: bool) -> Self { + self.disallow_hidpi = disallow_hidpi; self } #[inline] - fn with_has_shadow(mut self, has_shadow: bool) -> Self { - self.platform_specific.has_shadow = has_shadow; + pub fn with_has_shadow(mut self, has_shadow: bool) -> Self { + self.has_shadow = has_shadow; self } + /// Window accepts click-through mouse events. #[inline] - fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self { - self.platform_specific.accepts_first_mouse = accepts_first_mouse; + pub fn with_accepts_first_mouse(mut self, accepts_first_mouse: bool) -> Self { + self.accepts_first_mouse = accepts_first_mouse; self } + /// Defines the window tabbing identifier. + /// + /// #[inline] - fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { - self.platform_specific.tabbing_identifier.replace(tabbing_identifier.to_string()); + pub fn with_tabbing_identifier(mut self, tabbing_identifier: &str) -> Self { + self.tabbing_identifier.replace(tabbing_identifier.to_string()); self } + /// Set how the Option keys are interpreted. + /// + /// See [`WindowExtMacOS::set_option_as_alt`] for details on what this means if set. #[inline] - fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self { - self.platform_specific.option_as_alt = option_as_alt; + pub fn with_option_as_alt(mut self, option_as_alt: OptionAsAlt) -> Self { + self.option_as_alt = option_as_alt; self } + /// See [`WindowExtMacOS::set_borderless_game`] for details on what this means if set. #[inline] - fn with_borderless_game(mut self, borderless_game: bool) -> Self { - self.platform_specific.borderless_game = borderless_game; + pub fn with_borderless_game(mut self, borderless_game: bool) -> Self { + self.borderless_game = borderless_game; self } + /// See [`WindowExtMacOS::set_unified_titlebar`] for details on what this means if set. #[inline] - fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self { - self.platform_specific.unified_titlebar = unified_titlebar; + pub fn with_unified_titlebar(mut self, unified_titlebar: bool) -> Self { + self.unified_titlebar = unified_titlebar; self } + /// Use [`NSPanel`] window with [`NonactivatingPanel`] window style mask instead of + /// [`NSWindow`]. + /// + /// [`NSWindow`]: https://developer.apple.com/documentation/appkit/NSWindow?language=objc + /// [`NSPanel`]: https://developer.apple.com/documentation/appkit/NSPanel?language=objc + /// [`NonactivatingPanel`]: https://developer.apple.com/documentation/appkit/nswindow/stylemask-swift.struct/nonactivatingpanel?language=objc #[inline] - fn with_panel(mut self, panel: bool) -> Self { - self.platform_specific.panel = panel; + pub fn with_panel(mut self, panel: bool) -> Self { + self.panel = panel; self } } +impl Default for WindowAttributesMacOS { + #[inline] + fn default() -> Self { + Self { + movable_by_window_background: false, + titlebar_transparent: false, + title_hidden: false, + titlebar_hidden: false, + titlebar_buttons_hidden: false, + fullsize_content_view: false, + disallow_hidpi: false, + has_shadow: true, + accepts_first_mouse: true, + tabbing_identifier: None, + option_as_alt: Default::default(), + borderless_game: false, + unified_titlebar: false, + panel: false, + } + } +} + +impl PlatformWindowAttributes for WindowAttributesMacOS { + fn box_clone(&self) -> Box { + Box::from(self.clone()) + } +} + pub trait EventLoopBuilderExtMacOS { /// Sets the activation policy for the application. If used, this will override /// any relevant settings provided in the package manifest. @@ -580,52 +613,3 @@ pub enum OptionAsAlt { #[default] None, } - -/// Additional events on [`ApplicationHandler`] that are specific to macOS. -/// -/// This can be registered with [`ApplicationHandler::macos_handler`]. -pub trait ApplicationHandlerExtMacOS: ApplicationHandler { - /// The system interpreted a keypress as a standard key binding command. - /// - /// Examples include inserting tabs and newlines, or moving the insertion point, see - /// [`NSStandardKeyBindingResponding`] for the full list of key bindings. They are often text - /// editing related. - /// - /// This corresponds to the [`doCommandBySelector:`] method on `NSTextInputClient`. - /// - /// The `action` parameter contains the string representation of the selector. Examples include - /// `"insertBacktab:"`, `"indent:"` and `"noop:"`. - /// - /// # Example - /// - /// ```ignore - /// impl ApplicationHandlerExtMacOS for App { - /// fn standard_key_binding( - /// &mut self, - /// event_loop: &dyn ActiveEventLoop, - /// window_id: WindowId, - /// action: &str, - /// ) { - /// match action { - /// "moveBackward:" => self.cursor.position -= 1, - /// "moveForward:" => self.cursor.position += 1, - /// _ => {} // Ignore other actions - /// } - /// } - /// } - /// ``` - /// - /// [`NSStandardKeyBindingResponding`]: https://developer.apple.com/documentation/appkit/nsstandardkeybindingresponding?language=objc - /// [`doCommandBySelector:`]: https://developer.apple.com/documentation/appkit/nstextinputclient/1438256-docommandbyselector?language=objc - #[doc(alias = "doCommandBySelector:")] - fn standard_key_binding( - &mut self, - event_loop: &dyn ActiveEventLoop, - window_id: WindowId, - action: &str, - ) { - let _ = event_loop; - let _ = window_id; - let _ = action; - } -} diff --git a/src/platform/startup_notify.rs b/src/platform/startup_notify.rs index 2364bf4dce..80a3279f1c 100644 --- a/src/platform/startup_notify.rs +++ b/src/platform/startup_notify.rs @@ -27,7 +27,7 @@ use crate::error::{NotSupportedError, RequestError}; use crate::event_loop::{ActiveEventLoop, AsyncRequestSerial}; #[cfg(wayland_platform)] use crate::platform::wayland::ActiveEventLoopExtWayland; -use crate::window::{ActivationToken, Window, WindowAttributes}; +use crate::window::{ActivationToken, Window}; /// The variable which is used mostly on X11. const X11_VAR: &str = "DESKTOP_STARTUP_ID"; @@ -88,13 +88,6 @@ impl WindowExtStartupNotify for dyn Window + '_ { } } -impl WindowAttributesExtStartupNotify for WindowAttributes { - fn with_activation_token(mut self, token: ActivationToken) -> Self { - self.platform_specific.activation_token = Some(token); - self - } -} - /// Remove the activation environment variables from the current process. /// /// This is wise to do before running child processes, @@ -108,6 +101,7 @@ pub fn reset_activation_token_env() { /// /// This could be used before running daemon processes. pub fn set_activation_token_env(token: ActivationToken) { - env::set_var(X11_VAR, &token.token); - env::set_var(WAYLAND_VAR, token.token); + let token = token.into_raw(); + env::set_var(X11_VAR, &token); + env::set_var(WAYLAND_VAR, token); } diff --git a/src/platform/wayland.rs b/src/platform/wayland.rs index b26e5cd2ce..119358c959 100644 --- a/src/platform/wayland.rs +++ b/src/platform/wayland.rs @@ -13,14 +13,15 @@ //! * `wayland-csd-adwaita` (default). //! * `wayland-csd-adwaita-crossfont`. //! * `wayland-csd-adwaita-notitle`. - use std::ffi::c_void; use std::ptr::NonNull; +use winit_core::window::PlatformWindowAttributes; + use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; use crate::platform_impl::wayland::Window; -pub use crate::window::Theme; -use crate::window::{Window as CoreWindow, WindowAttributes}; +use crate::platform_impl::ApplicationName; +use crate::window::{ActivationToken, Window as CoreWindow}; /// Additional methods on [`ActiveEventLoop`] that are specific to Wayland. pub trait ActiveEventLoopExtWayland { @@ -89,8 +90,14 @@ impl WindowExtWayland for dyn CoreWindow + '_ { } } -/// Additional methods on [`WindowAttributes`] that are specific to Wayland. -pub trait WindowAttributesExtWayland { +/// Window attributes methods specific to Wayland. +#[derive(Debug, Default, Clone)] +pub struct WindowAttributesWayland { + pub(crate) name: Option, + pub(crate) activation_token: Option, +} + +impl WindowAttributesWayland { /// Build window with the given name. /// /// The `general` name sets an application ID, which should match the `.desktop` @@ -98,14 +105,22 @@ pub trait WindowAttributesExtWayland { /// /// For details about application ID conventions, see the /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) - fn with_name(self, general: impl Into, instance: impl Into) -> Self; -} - -impl WindowAttributesExtWayland for WindowAttributes { #[inline] - fn with_name(mut self, general: impl Into, instance: impl Into) -> Self { - self.platform_specific.name = + pub fn with_name(mut self, general: impl Into, instance: impl Into) -> Self { + self.name = Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); self } + + #[inline] + pub fn with_activation_token(mut self, token: ActivationToken) -> Self { + self.activation_token = Some(token); + self + } +} + +impl PlatformWindowAttributes for WindowAttributesWayland { + fn box_clone(&self) -> Box { + Box::from(self.clone()) + } } diff --git a/src/platform/web.rs b/src/platform/web.rs index db7387306a..51971bcd92 100644 --- a/src/platform/web.rs +++ b/src/platform/web.rs @@ -13,9 +13,9 @@ //! yourself. //! //! [canvas]: https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement -//! [with_canvas]: WindowAttributesExtWeb::with_canvas +//! [with_canvas]: WindowAttributesWeb::with_canvas //! [get]: WindowExtWeb::canvas -//! [insert]: WindowAttributesExtWeb::with_append +//! [insert]: WindowAttributesWeb::with_append #![cfg_attr(not(web_platform), doc = "[wasm_bindgen]: https://docs.rs/wasm-bindgen")] //! [Rust and WebAssembly book]: https://rustwasm.github.io/book //! @@ -53,13 +53,15 @@ use std::task::{Context, Poll}; use serde::{Deserialize, Serialize}; #[cfg(web_platform)] use web_sys::HtmlCanvasElement; +use winit_core::window::PlatformWindowAttributes; use crate::application::ApplicationHandler; -use crate::cursor::CustomCursorSource; +use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::error::NotSupportedError; use crate::event_loop::{ActiveEventLoop, EventLoop}; use crate::monitor::MonitorHandleProvider; -use crate::platform_impl::MonitorHandle as WebMonitorHandle; +use crate::platform_impl::main_thread::{MainThreadMarker, MainThreadSafe}; +use crate::platform_impl::{web_sys as backend, MonitorHandle as WebMonitorHandle}; #[cfg(web_platform)] use crate::platform_impl::{ CustomCursorFuture as PlatformCustomCursorFuture, @@ -67,7 +69,7 @@ use crate::platform_impl::{ MonitorPermissionFuture as PlatformMonitorPermissionFuture, OrientationLockFuture as PlatformOrientationLockFuture, }; -use crate::window::{CustomCursor, Window, WindowAttributes}; +use crate::window::Window; #[cfg(not(web_platform))] #[doc(hidden)] @@ -127,15 +129,34 @@ impl WindowExtWeb for dyn Window + '_ { } } -pub trait WindowAttributesExtWeb { +#[derive(Clone, Debug)] +pub struct WindowAttributesWeb { + pub(crate) canvas: Option>>, + pub(crate) prevent_default: bool, + pub(crate) focusable: bool, + pub(crate) append: bool, +} + +impl WindowAttributesWeb { /// Pass an [`HtmlCanvasElement`] to be used for this [`Window`]. If [`None`], - /// [`WindowAttributes::default()`] will create one. + /// the default one will be created. /// /// In any case, the canvas won't be automatically inserted into the Web page. /// /// [`None`] by default. #[cfg_attr(not(web_platform), doc = "", doc = "[`HtmlCanvasElement`]: #only-available-on-wasm")] - fn with_canvas(self, canvas: Option) -> Self; + pub fn with_canvas(mut self, canvas: Option) -> Self { + match canvas { + Some(canvas) => { + let main_thread = MainThreadMarker::new() + .expect("received a `HtmlCanvasElement` outside the window context"); + self.canvas = Some(Arc::new(MainThreadSafe::new(main_thread, canvas))); + }, + None => self.canvas = None, + } + + self + } /// Sets whether `event.preventDefault()` should be called on events on the /// canvas that have side effects. @@ -143,39 +164,50 @@ pub trait WindowAttributesExtWeb { /// See [`WindowExtWeb::set_prevent_default()`] for more details. /// /// Enabled by default. - fn with_prevent_default(self, prevent_default: bool) -> Self; + pub fn with_prevent_default(mut self, prevent_default: bool) -> Self { + self.prevent_default = prevent_default; + self + } /// Whether the canvas should be focusable using the tab key. This is necessary to capture /// canvas keyboard events. /// /// Enabled by default. - fn with_focusable(self, focusable: bool) -> Self; + pub fn with_focusable(mut self, focusable: bool) -> Self { + self.focusable = focusable; + self + } /// On window creation, append the canvas element to the Web page if it isn't already. /// /// Disabled by default. - fn with_append(self, append: bool) -> Self; -} - -impl WindowAttributesExtWeb for WindowAttributes { - fn with_canvas(mut self, canvas: Option) -> Self { - self.platform_specific.set_canvas(canvas); + pub fn with_append(mut self, append: bool) -> Self { + self.append = append; self } +} - fn with_prevent_default(mut self, prevent_default: bool) -> Self { - self.platform_specific.prevent_default = prevent_default; - self +impl PlatformWindowAttributes for WindowAttributesWeb { + fn box_clone(&self) -> Box { + Box::from(self.clone()) } +} - fn with_focusable(mut self, focusable: bool) -> Self { - self.platform_specific.focusable = focusable; - self +impl PartialEq for WindowAttributesWeb { + fn eq(&self, other: &Self) -> bool { + (match (&self.canvas, &other.canvas) { + (Some(this), Some(other)) => Arc::ptr_eq(this, other), + (None, None) => true, + _ => false, + }) && self.prevent_default.eq(&other.prevent_default) + && self.focusable.eq(&other.focusable) + && self.append.eq(&other.append) } +} - fn with_append(mut self, append: bool) -> Self { - self.platform_specific.append = append; - self +impl Default for WindowAttributesWeb { + fn default() -> Self { + Self { canvas: None, prevent_default: true, focusable: true, append: false } } } diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 02a7df2b76..55471db39c 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -12,13 +12,14 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; #[cfg(windows_platform)] use windows_sys::Win32::Foundation::HANDLE; +use winit_core::window::PlatformWindowAttributes; use crate::dpi::PhysicalSize; use crate::event::DeviceId; use crate::event_loop::EventLoopBuilder; -use crate::icon::BadIcon; +use crate::icon::{BadIcon, Icon}; use crate::platform_impl::RaiiIcon; -use crate::window::{Icon, Window, WindowAttributes}; +use crate::window::Window; /// Window Handle type used by Win32 API pub type HWND = *mut c_void; @@ -241,7 +242,7 @@ pub trait WindowExtWindows { /// /// A window must be enabled before it can be activated. /// If an application has create a modal dialog box by disabling its owner window - /// (as described in [`WindowAttributesExtWindows::with_owner_window`]), the application must + /// (as described in [`WindowAttributesWindows::with_owner_window`]), the application must /// enable the owner window before destroying the dialog box. /// Otherwise, another window will receive the keyboard focus and be activated. /// @@ -446,9 +447,49 @@ pub trait WindowBorrowExtWindows: Borrow + Sized { impl + Sized> WindowBorrowExtWindows for W {} -/// Additional methods on `WindowAttributes` that are specific to Windows. -#[allow(rustdoc::broken_intra_doc_links)] -pub trait WindowAttributesExtWindows { +#[derive(Clone, Debug)] +pub struct WindowAttributesWindows { + pub(crate) owner: Option, + pub(crate) menu: Option, + pub(crate) taskbar_icon: Option, + pub(crate) no_redirection_bitmap: bool, + pub(crate) drag_and_drop: bool, + pub(crate) skip_taskbar: bool, + pub(crate) class_name: String, + pub(crate) decoration_shadow: bool, + pub(crate) backdrop_type: BackdropType, + pub(crate) clip_children: bool, + pub(crate) border_color: Option, + pub(crate) title_background_color: Option, + pub(crate) title_text_color: Option, + pub(crate) corner_preference: Option, +} + +impl Default for WindowAttributesWindows { + fn default() -> Self { + Self { + owner: None, + menu: None, + taskbar_icon: None, + no_redirection_bitmap: false, + drag_and_drop: true, + skip_taskbar: false, + class_name: "Window Class".to_string(), + decoration_shadow: false, + backdrop_type: BackdropType::default(), + clip_children: true, + border_color: None, + title_background_color: None, + title_text_color: None, + corner_preference: None, + } + } +} + +unsafe impl Send for WindowAttributesWindows {} +unsafe impl Sync for WindowAttributesWindows {} + +impl WindowAttributesWindows { /// Set an owner to the window to be created. Can be used to create a dialog box, for example. /// This only works when [`WindowAttributes::with_parent_window`] isn't called or set to `None`. /// Can be used in combination with @@ -461,7 +502,12 @@ pub trait WindowAttributesExtWindows { /// - An owned window is hidden when its owner is minimized. /// /// For more information, see - fn with_owner_window(self, parent: HWND) -> Self; + /// + /// [`WindowAttributes::with_parent_window`]: crate::window::WindowAttributes::with_parent_window + pub fn with_owner_window(mut self, parent: HWND) -> Self { + self.owner = Some(parent); + self + } /// Sets a menu on the window to be created. /// @@ -472,18 +518,29 @@ pub trait WindowAttributesExtWindows { /// Note: Dark mode cannot be supported for win32 menus, it's simply not possible to change how /// the menus look. If you use this, it is recommended that you combine it with /// `with_theme(Some(Theme::Light))` to avoid a jarring effect. + #[rustfmt::skip] + /// #[cfg_attr( windows_platform, doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu" )] #[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")] - fn with_menu(self, menu: HMENU) -> Self; + pub fn with_menu(mut self, menu: HMENU) -> Self { + self.menu = Some(menu); + self + } /// This sets `ICON_BIG`. A good ceiling here is 256x256. - fn with_taskbar_icon(self, taskbar_icon: Option) -> Self; + pub fn with_taskbar_icon(mut self, taskbar_icon: Option) -> Self { + self.taskbar_icon = taskbar_icon; + self + } /// This sets `WS_EX_NOREDIRECTIONBITMAP`. - fn with_no_redirection_bitmap(self, flag: bool) -> Self; + pub fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { + self.no_redirection_bitmap = flag; + self + } /// Enables or disables drag and drop support (enabled by default). Will interfere with other /// crates that use multi-threaded COM API (`CoInitializeEx` with `COINIT_MULTITHREADED` @@ -491,132 +548,82 @@ pub trait WindowAttributesExtWindows { /// attempt to initialize COM API regardless of this option. Currently only fullscreen mode /// does that, but there may be more in the future. If you need COM API with /// `COINIT_MULTITHREADED` you must initialize it before calling any winit functions. See for more information. - fn with_drag_and_drop(self, flag: bool) -> Self; + pub fn with_drag_and_drop(mut self, flag: bool) -> Self { + self.drag_and_drop = flag; + self + } /// Whether show or hide the window icon in the taskbar. - fn with_skip_taskbar(self, skip: bool) -> Self; + pub fn with_skip_taskbar(mut self, skip: bool) -> Self { + self.skip_taskbar = skip; + self + } /// Customize the window class name. - fn with_class_name>(self, class_name: S) -> Self; + pub fn with_class_name>(mut self, class_name: S) -> Self { + self.class_name = class_name.into(); + self + } /// Shows or hides the background drop shadow for undecorated windows. /// /// The shadow is hidden by default. /// Enabling the shadow causes a thin 1px line to appear on the top of the window. - fn with_undecorated_shadow(self, shadow: bool) -> Self; + pub fn with_undecorated_shadow(mut self, shadow: bool) -> Self { + self.decoration_shadow = shadow; + self + } /// Sets system-drawn backdrop type. /// /// Requires Windows 11 build 22523+. - fn with_system_backdrop(self, backdrop_type: BackdropType) -> Self; + pub fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self { + self.backdrop_type = backdrop_type; + self + } /// This sets or removes `WS_CLIPCHILDREN` style. - fn with_clip_children(self, flag: bool) -> Self; + pub fn with_clip_children(mut self, flag: bool) -> Self { + self.clip_children = flag; + self + } /// Sets the color of the window border. /// /// Supported starting with Windows 11 Build 22000. - fn with_border_color(self, color: Option) -> Self; + pub fn with_border_color(mut self, color: Option) -> Self { + self.border_color = Some(color.unwrap_or(Color::NONE)); + self + } /// Sets the background color of the title bar. /// /// Supported starting with Windows 11 Build 22000. - fn with_title_background_color(self, color: Option) -> Self; + pub fn with_title_background_color(mut self, color: Option) -> Self { + self.title_background_color = Some(color.unwrap_or(Color::NONE)); + self + } /// Sets the color of the window title. /// /// Supported starting with Windows 11 Build 22000. - fn with_title_text_color(self, color: Color) -> Self; + pub fn with_title_text_color(mut self, color: Color) -> Self { + self.title_text_color = Some(color); + self + } /// Sets the preferred style of the window corners. /// /// Supported starting with Windows 11 Build 22000. - fn with_corner_preference(self, corners: CornerPreference) -> Self; -} - -impl WindowAttributesExtWindows for WindowAttributes { - #[inline] - fn with_owner_window(mut self, parent: HWND) -> Self { - self.platform_specific.owner = Some(parent); - self - } - - #[inline] - fn with_menu(mut self, menu: HMENU) -> Self { - self.platform_specific.menu = Some(menu); - self - } - - #[inline] - fn with_taskbar_icon(mut self, taskbar_icon: Option) -> Self { - self.platform_specific.taskbar_icon = taskbar_icon; - self - } - - #[inline] - fn with_no_redirection_bitmap(mut self, flag: bool) -> Self { - self.platform_specific.no_redirection_bitmap = flag; - self - } - - #[inline] - fn with_drag_and_drop(mut self, flag: bool) -> Self { - self.platform_specific.drag_and_drop = flag; - self - } - - #[inline] - fn with_skip_taskbar(mut self, skip: bool) -> Self { - self.platform_specific.skip_taskbar = skip; - self - } - - #[inline] - fn with_class_name>(mut self, class_name: S) -> Self { - self.platform_specific.class_name = class_name.into(); - self - } - - #[inline] - fn with_undecorated_shadow(mut self, shadow: bool) -> Self { - self.platform_specific.decoration_shadow = shadow; - self - } - - #[inline] - fn with_system_backdrop(mut self, backdrop_type: BackdropType) -> Self { - self.platform_specific.backdrop_type = backdrop_type; - self - } - - #[inline] - fn with_clip_children(mut self, flag: bool) -> Self { - self.platform_specific.clip_children = flag; - self - } - - #[inline] - fn with_border_color(mut self, color: Option) -> Self { - self.platform_specific.border_color = Some(color.unwrap_or(Color::NONE)); - self - } - - #[inline] - fn with_title_background_color(mut self, color: Option) -> Self { - self.platform_specific.title_background_color = Some(color.unwrap_or(Color::NONE)); - self - } - - #[inline] - fn with_title_text_color(mut self, color: Color) -> Self { - self.platform_specific.title_text_color = Some(color); + pub fn with_corner_preference(mut self, corners: CornerPreference) -> Self { + self.corner_preference = Some(corners); self } +} - #[inline] - fn with_corner_preference(mut self, corners: CornerPreference) -> Self { - self.platform_specific.corner_preference = Some(corners); - self +impl PlatformWindowAttributes for WindowAttributesWindows { + fn box_clone(&self) -> Box { + Box::from(self.clone()) } } @@ -733,7 +740,7 @@ impl WinIcon { /// /// ```rust,no_run /// # use winit::platform::windows::WinIcon; - /// # use winit::window::Icon; + /// # use winit::icon::Icon; /// assert!(WinIcon::from_resource_name("0", None).is_ok()); /// assert!(WinIcon::from_resource(0, None).is_err()); /// ``` diff --git a/src/platform/x11.rs b/src/platform/x11.rs index 4443b3a0a3..3d106fae21 100644 --- a/src/platform/x11.rs +++ b/src/platform/x11.rs @@ -1,10 +1,11 @@ //! # X11 #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use winit_core::window::{ActivationToken, PlatformWindowAttributes, Window as CoreWindow}; use crate::dpi::Size; use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; -use crate::window::{Window as CoreWindow, WindowAttributes}; +use crate::platform_impl::ApplicationName; /// X window type. Maps directly to /// [`_NET_WM_WINDOW_TYPE`](https://specifications.freedesktop.org/wm-spec/wm-spec-1.5.html). @@ -141,12 +142,46 @@ pub trait WindowExtX11 {} impl WindowExtX11 for dyn CoreWindow {} -/// Additional methods on [`WindowAttributes`] that are specific to X11. -pub trait WindowAttributesExtX11 { +#[derive(Clone, Debug)] +pub struct WindowAttributesX11 { + pub(crate) name: Option, + pub(crate) activation_token: Option, + pub(crate) visual_id: Option, + pub(crate) screen_id: Option, + pub(crate) base_size: Option, + pub(crate) override_redirect: bool, + pub(crate) x11_window_types: Vec, + + /// The parent window to embed this window into. + pub(crate) embed_window: Option, +} + +impl Default for WindowAttributesX11 { + fn default() -> Self { + Self { + name: None, + activation_token: None, + visual_id: None, + screen_id: None, + base_size: None, + override_redirect: false, + x11_window_types: vec![WindowType::Normal], + embed_window: None, + } + } +} + +impl WindowAttributesX11 { /// Create this window with a specific X11 visual. - fn with_x11_visual(self, visual_id: XVisualID) -> Self; + pub fn with_x11_visual(mut self, visual_id: XVisualID) -> Self { + self.visual_id = Some(visual_id); + self + } - fn with_x11_screen(self, screen_id: i32) -> Self; + pub fn with_x11_screen(mut self, screen_id: i32) -> Self { + self.screen_id = Some(screen_id); + self + } /// Build window with the given `general` and `instance` names. /// @@ -156,27 +191,40 @@ pub trait WindowAttributesExtX11 { /// /// For details about application ID conventions, see the /// [Desktop Entry Spec](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#desktop-file-id) - fn with_name(self, general: impl Into, instance: impl Into) -> Self; + pub fn with_name(mut self, general: impl Into, instance: impl Into) -> Self { + self.name = + Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); + self + } /// Build window with override-redirect flag; defaults to false. - fn with_override_redirect(self, override_redirect: bool) -> Self; + pub fn with_override_redirect(mut self, override_redirect: bool) -> Self { + self.override_redirect = override_redirect; + self + } /// Build window with `_NET_WM_WINDOW_TYPE` hints; defaults to `Normal`. - fn with_x11_window_type(self, x11_window_type: Vec) -> Self; + pub fn with_x11_window_type(mut self, x11_window_types: Vec) -> Self { + self.x11_window_types = x11_window_types; + self + } /// Build window with base size hint. /// /// ``` /// # use winit::dpi::{LogicalSize, PhysicalSize}; /// # use winit::window::{Window, WindowAttributes}; - /// # use winit::platform::x11::WindowAttributesExtX11; + /// # use winit::platform::x11::WindowAttributesX11; /// // Specify the size in logical dimensions like this: - /// WindowAttributes::default().with_base_size(LogicalSize::new(400.0, 200.0)); + /// WindowAttributesX11::default().with_base_size(LogicalSize::new(400.0, 200.0)); /// /// // Or specify the size in physical dimensions like this: - /// WindowAttributes::default().with_base_size(PhysicalSize::new(400, 200)); + /// WindowAttributesX11::default().with_base_size(PhysicalSize::new(400, 200)); /// ``` - fn with_base_size>(self, base_size: S) -> Self; + pub fn with_base_size>(mut self, base_size: S) -> Self { + self.base_size = Some(base_size.into()); + self + } /// Embed this window into another parent window. /// @@ -185,57 +233,28 @@ pub trait WindowAttributesExtX11 { /// ```no_run /// use winit::window::{Window, WindowAttributes}; /// use winit::event_loop::ActiveEventLoop; - /// use winit::platform::x11::{XWindow, WindowAttributesExtX11}; + /// use winit::platform::x11::{XWindow, WindowAttributesX11}; /// # fn create_window(event_loop: &dyn ActiveEventLoop) -> Result<(), Box> { /// let parent_window_id = std::env::args().nth(1).unwrap().parse::()?; - /// let window_attributes = WindowAttributes::default().with_embed_parent_window(parent_window_id); + /// let window_x11_attributes = WindowAttributesX11::default().with_embed_parent_window(parent_window_id); + /// let window_attributes = WindowAttributes::default().with_platform_attributes(Box::new(window_x11_attributes)); /// let window = event_loop.create_window(window_attributes)?; /// # Ok(()) } /// ``` - fn with_embed_parent_window(self, parent_window_id: XWindow) -> Self; -} - -impl WindowAttributesExtX11 for WindowAttributes { - #[inline] - fn with_x11_visual(mut self, visual_id: XVisualID) -> Self { - self.platform_specific.x11.visual_id = Some(visual_id); - self - } - - #[inline] - fn with_x11_screen(mut self, screen_id: i32) -> Self { - self.platform_specific.x11.screen_id = Some(screen_id); + pub fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self { + self.embed_window = Some(parent_window_id); self } #[inline] - fn with_name(mut self, general: impl Into, instance: impl Into) -> Self { - self.platform_specific.name = - Some(crate::platform_impl::ApplicationName::new(general.into(), instance.into())); - self - } - - #[inline] - fn with_override_redirect(mut self, override_redirect: bool) -> Self { - self.platform_specific.x11.override_redirect = override_redirect; - self - } - - #[inline] - fn with_x11_window_type(mut self, x11_window_types: Vec) -> Self { - self.platform_specific.x11.x11_window_types = x11_window_types; - self - } - - #[inline] - fn with_base_size>(mut self, base_size: S) -> Self { - self.platform_specific.x11.base_size = Some(base_size.into()); + pub fn with_activation_token(mut self, token: ActivationToken) -> Self { + self.activation_token = Some(token); self } +} - #[inline] - fn with_embed_parent_window(mut self, parent_window_id: XWindow) -> Self { - self.platform_specific.x11.embed_window = Some(parent_window_id); - self +impl PlatformWindowAttributes for WindowAttributesX11 { + fn box_clone(&self) -> Box { + Box::from(self.clone()) } } diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index c768e3bb2a..ab3c6f363b 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -11,7 +11,7 @@ use android_activity::{ use tracing::{debug, trace, warn}; use crate::application::ApplicationHandler; -use crate::cursor::Cursor; +use crate::cursor::{Cursor, CustomCursor, CustomCursorSource}; use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter}; @@ -23,8 +23,8 @@ use crate::event_loop::{ use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::platform::pump_events::PumpStatus; use crate::window::{ - self, CursorGrabMode, CustomCursor, CustomCursorSource, ImePurpose, ResizeDirection, Theme, - Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, + self, CursorGrabMode, ImePurpose, ResizeDirection, Theme, Window as CoreWindow, + WindowAttributes, WindowButtons, WindowId, WindowLevel, }; mod keycodes; diff --git a/src/platform_impl/apple/appkit/cursor.rs b/src/platform_impl/apple/appkit/cursor.rs index 0a965a4106..80903d507f 100644 --- a/src/platform_impl/apple/appkit/cursor.rs +++ b/src/platform_impl/apple/appkit/cursor.rs @@ -10,9 +10,8 @@ use objc2_foundation::{ ns_string, NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, }; -use crate::cursor::{CursorImage, CustomCursorProvider, CustomCursorSource}; +use crate::cursor::{CursorIcon, CursorImage, CustomCursorProvider, CustomCursorSource}; use crate::error::{NotSupportedError, RequestError}; -use crate::window::CursorIcon; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct CustomCursor(pub(crate) Retained); @@ -42,8 +41,8 @@ impl CustomCursor { } pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result, RequestError> { - let width = cursor.width; - let height = cursor.height; + let width = cursor.width(); + let height = cursor.height(); let bitmap = unsafe { NSBitmapImageRep::initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel( @@ -60,15 +59,16 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Result Modifiers { pressed_mods.set(ModifiersKeys::LMETA, flags.contains(NX_DEVICELCMDKEYMASK)); pressed_mods.set(ModifiersKeys::RMETA, flags.contains(NX_DEVICERCMDKEYMASK)); - Modifiers { state, pressed_mods } + Modifiers::new(state, pressed_mods) } pub(super) fn dummy_event() -> Option> { diff --git a/src/platform_impl/apple/appkit/event_loop.rs b/src/platform_impl/apple/appkit/event_loop.rs index 849e40a01c..b81319a2a7 100644 --- a/src/platform_impl/apple/appkit/event_loop.rs +++ b/src/platform_impl/apple/appkit/event_loop.rs @@ -24,6 +24,7 @@ use super::event::dummy_event; use super::monitor; use super::observer::setup_control_flow_observers; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource}; use crate::error::{EventLoopError, RequestError}; use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, @@ -33,7 +34,7 @@ use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::macos::ActivationPolicy; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::Window; -use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme}; +use crate::window::Theme; #[derive(Default)] pub struct PanicInfo { diff --git a/src/platform_impl/apple/appkit/mod.rs b/src/platform_impl/apple/appkit/mod.rs index ef2e597fc5..c9fc98b840 100644 --- a/src/platform_impl/apple/appkit/mod.rs +++ b/src/platform_impl/apple/appkit/mod.rs @@ -20,4 +20,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; diff --git a/src/platform_impl/apple/appkit/monitor.rs b/src/platform_impl/apple/appkit/monitor.rs index 09c6931c40..329620f568 100644 --- a/src/platform_impl/apple/appkit/monitor.rs +++ b/src/platform_impl/apple/appkit/monitor.rs @@ -79,14 +79,14 @@ impl VideoModeHandle { unimplemented!() }; - let mode = VideoMode { - size: PhysicalSize::new( + let mode = VideoMode::new( + PhysicalSize::new( CGDisplayMode::pixel_width(Some(&native_mode.0)) as u32, CGDisplayMode::pixel_height(Some(&native_mode.0)) as u32, ), + NonZeroU16::new(bit_depth), refresh_rate_millihertz, - bit_depth: NonZeroU16::new(bit_depth), - }; + ); VideoModeHandle { mode, monitor: monitor.clone(), native_mode } } diff --git a/src/platform_impl/apple/appkit/view.rs b/src/platform_impl/apple/appkit/view.rs index b836ec2ce2..8cb8c8816e 100644 --- a/src/platform_impl/apple/appkit/view.rs +++ b/src/platform_impl/apple/appkit/view.rs @@ -1096,7 +1096,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton { // we're getting from the operating system, which makes it // impossible to provide such events as extra in `KeyEvent`. fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained { - let ev_mods = event_mods(event).state; + let ev_mods = event_mods(event).state(); let ignore_alt_characters = match option_as_alt { OptionAsAlt::OnlyLeft if lalt_pressed(event) => true, OptionAsAlt::OnlyRight if ralt_pressed(event) => true, diff --git a/src/platform_impl/apple/appkit/window.rs b/src/platform_impl/apple/appkit/window.rs index 5e66bd4083..1e21177fb8 100644 --- a/src/platform_impl/apple/appkit/window.rs +++ b/src/platform_impl/apple/appkit/window.rs @@ -11,11 +11,13 @@ use objc2_foundation::NSObject; use super::event_loop::ActiveEventLoop; use super::window_delegate::WindowDelegate; +use crate::cursor::Cursor; use crate::error::RequestError; +use crate::icon::Icon; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; use crate::window::{ - Cursor, Icon, ImePurpose, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, - WindowButtons, WindowId, WindowLevel, + ImePurpose, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, + WindowId, WindowLevel, }; #[derive(Debug)] diff --git a/src/platform_impl/apple/appkit/window_delegate.rs b/src/platform_impl/apple/appkit/window_delegate.rs index 62555bc99e..826c3d4d28 100644 --- a/src/platform_impl/apple/appkit/window_delegate.rs +++ b/src/platform_impl/apple/appkit/window_delegate.rs @@ -45,59 +45,21 @@ use super::util::cgerr; use super::view::WinitView; use super::window::{window_id, WinitPanel, WinitWindow}; use super::{ffi, MonitorHandle}; +use crate::cursor::Cursor; use crate::dpi::{ LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size, }; use crate::error::{NotSupportedError, RequestError}; use crate::event::{SurfaceSizeWriter, WindowEvent}; +use crate::icon::Icon; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; -use crate::platform::macos::{OptionAsAlt, WindowExtMacOS}; +use crate::platform::macos::{OptionAsAlt, WindowAttributesMacOS, WindowExtMacOS}; use crate::window::{ - Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType, - WindowAttributes, WindowButtons, WindowId, WindowLevel, + CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, WindowAttributes, + WindowButtons, WindowId, WindowLevel, }; -#[derive(Clone, Debug, PartialEq)] -pub struct PlatformSpecificWindowAttributes { - pub movable_by_window_background: bool, - pub titlebar_transparent: bool, - pub title_hidden: bool, - pub titlebar_hidden: bool, - pub titlebar_buttons_hidden: bool, - pub fullsize_content_view: bool, - pub disallow_hidpi: bool, - pub has_shadow: bool, - pub accepts_first_mouse: bool, - pub tabbing_identifier: Option, - pub option_as_alt: OptionAsAlt, - pub borderless_game: bool, - pub unified_titlebar: bool, - pub panel: bool, -} - -impl Default for PlatformSpecificWindowAttributes { - #[inline] - fn default() -> Self { - Self { - movable_by_window_background: false, - titlebar_transparent: false, - title_hidden: false, - titlebar_hidden: false, - titlebar_buttons_hidden: false, - fullsize_content_view: false, - disallow_hidpi: false, - has_shadow: true, - accepts_first_mouse: true, - tabbing_identifier: None, - option_as_alt: Default::default(), - borderless_game: false, - unified_titlebar: false, - panel: false, - } - } -} - #[derive(Debug)] pub(crate) struct State { /// Strong reference to the global application state. @@ -552,6 +514,7 @@ impl Drop for WindowDelegate { fn new_window( app_state: &Rc, attrs: &WindowAttributes, + macos_attrs: &WindowAttributesMacOS, mtm: MainThreadMarker, ) -> Option> { autoreleasepool(|_| { @@ -592,9 +555,7 @@ fn new_window( }, }; - let mut masks = if (!attrs.decorations && screen.is_none()) - || attrs.platform_specific.titlebar_hidden - { + let mut masks = if (!attrs.decorations && screen.is_none()) || macos_attrs.titlebar_hidden { // Resizable without a titlebar or borders // if decorations is set to false, ignore pl_attrs // @@ -622,7 +583,7 @@ fn new_window( masks &= !NSWindowStyleMask::Closable; } - if attrs.platform_specific.fullsize_content_view { + if macos_attrs.fullsize_content_view { // NOTE: If we decide to add an option to change this at runtime, we must emit a // `SurfaceResized` event to let applications know that the safe area changed. // @@ -637,7 +598,7 @@ fn new_window( // confusing issues with the window not being properly activated. // // Winit ensures this by not allowing access to `ActiveEventLoop` before handling events. - let window: Retained = if attrs.platform_specific.panel { + let window: Retained = if macos_attrs.panel { masks |= NSWindowStyleMask::NonactivatingPanel; let window: Option> = unsafe { @@ -673,7 +634,7 @@ fn new_window( window.setTitle(&NSString::from_str(&attrs.title)); window.setAcceptsMouseMovedEvents(true); - if let Some(identifier) = &attrs.platform_specific.tabbing_identifier { + if let Some(identifier) = &macos_attrs.tabbing_identifier { window.setTabbingIdentifier(&NSString::from_str(identifier)); window.setTabbingMode(NSWindowTabbingMode::Preferred); } @@ -682,13 +643,13 @@ fn new_window( window.setSharingType(NSWindowSharingType::None); } - if attrs.platform_specific.titlebar_transparent { + if macos_attrs.titlebar_transparent { window.setTitlebarAppearsTransparent(true); } - if attrs.platform_specific.title_hidden { + if macos_attrs.title_hidden { window.setTitleVisibility(NSWindowTitleVisibility::Hidden); } - if attrs.platform_specific.titlebar_buttons_hidden { + if macos_attrs.titlebar_buttons_hidden { for titlebar_button in &[ #[allow(deprecated)] NSWindowFullScreenButton, @@ -701,10 +662,10 @@ fn new_window( } } } - if attrs.platform_specific.movable_by_window_background { + if macos_attrs.movable_by_window_background { window.setMovableByWindowBackground(true); } - if attrs.platform_specific.unified_titlebar { + if macos_attrs.unified_titlebar { unsafe { // The toolbar style is ignored if there is no toolbar, so it is // necessary to add one. @@ -719,7 +680,7 @@ fn new_window( } } - if !attrs.platform_specific.has_shadow { + if !macos_attrs.has_shadow { window.setHasShadow(false); } if attrs.position.is_none() { @@ -728,8 +689,8 @@ fn new_window( let view = WinitView::new( app_state, - attrs.platform_specific.accepts_first_mouse, - attrs.platform_specific.option_as_alt, + macos_attrs.accepts_first_mouse, + macos_attrs.option_as_alt, mtm, ); @@ -737,7 +698,7 @@ fn new_window( // macos 10.14 and `true` after 10.15, we should set it to `YES` or `NO` to avoid // always the default system value in favour of the user's code #[allow(deprecated)] - view.setWantsBestResolutionOpenGLSurface(!attrs.platform_specific.disallow_hidpi); + view.setWantsBestResolutionOpenGLSurface(!macos_attrs.disallow_hidpi); // On Mojave, views automatically become layer-backed shortly after being added to // a window. Changing the layer-backedness of a view breaks the association between @@ -785,13 +746,19 @@ fn new_window( impl WindowDelegate { pub(super) fn new( app_state: &Rc, - attrs: WindowAttributes, + mut attrs: WindowAttributes, mtm: MainThreadMarker, ) -> Result, RequestError> { - let window = new_window(app_state, &attrs, mtm) + let macos_attrs = attrs + .platform + .take() + .and_then(|attrs| attrs.cast::().ok()) + .unwrap_or_default(); + + let window = new_window(app_state, &attrs, &macos_attrs, mtm) .ok_or_else(|| os_error!("couldn't create `NSWindow`"))?; - match attrs.parent_window.map(|handle| handle.0) { + match attrs.parent_window() { Some(rwh_06::RawWindowHandle::AppKit(handle)) => { // SAFETY: Caller ensures the pointer is valid or NULL // Unwrap is fine, since the pointer comes from `NonNull`. @@ -843,7 +810,7 @@ impl WindowDelegate { standard_frame: Cell::new(None), is_simple_fullscreen: Cell::new(false), saved_style: Cell::new(None), - is_borderless_game: Cell::new(attrs.platform_specific.borderless_game), + is_borderless_game: Cell::new(macos_attrs.borderless_game), }); let delegate: Retained = unsafe { msg_send![super(delegate), init] }; diff --git a/src/platform_impl/apple/uikit/event_loop.rs b/src/platform_impl/apple/uikit/event_loop.rs index c9f4009a56..a0819923d6 100644 --- a/src/platform_impl/apple/uikit/event_loop.rs +++ b/src/platform_impl/apple/uikit/event_loop.rs @@ -21,6 +21,7 @@ use super::super::notification_center::create_observer; use super::app_state::{send_occluded_event_for_all_windows, AppState}; use super::{app_state, monitor, MonitorHandle}; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event_loop::{ ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, @@ -28,7 +29,7 @@ use crate::event_loop::{ }; use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform_impl::Window; -use crate::window::{CustomCursor, CustomCursorSource, Theme, Window as CoreWindow}; +use crate::window::{Theme, Window as CoreWindow}; #[derive(Debug)] pub(crate) struct ActiveEventLoop { diff --git a/src/platform_impl/apple/uikit/mod.rs b/src/platform_impl/apple/uikit/mod.rs index 6a7c088c02..764cf8e679 100644 --- a/src/platform_impl/apple/uikit/mod.rs +++ b/src/platform_impl/apple/uikit/mod.rs @@ -13,7 +13,7 @@ pub(crate) use self::event_loop::{ ActiveEventLoop, EventLoop, PlatformSpecificEventLoopAttributes, }; pub(crate) use self::monitor::MonitorHandle; -pub(crate) use self::window::{PlatformSpecificWindowAttributes, Window}; +pub(crate) use self::window::Window; #[derive(Debug)] pub enum OsError {} diff --git a/src/platform_impl/apple/uikit/monitor.rs b/src/platform_impl/apple/uikit/monitor.rs index c72b09e0ef..f33fb0d2f5 100644 --- a/src/platform_impl/apple/uikit/monitor.rs +++ b/src/platform_impl/apple/uikit/monitor.rs @@ -55,11 +55,11 @@ impl VideoModeHandle { ) -> VideoModeHandle { let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen); let size = screen_mode.size(); - let mode = VideoMode { - size: (size.width as u32, size.height as u32).into(), - bit_depth: None, + let mode = VideoMode::new( + (size.width as u32, size.height as u32).into(), + None, refresh_rate_millihertz, - }; + ); VideoModeHandle { mode, diff --git a/src/platform_impl/apple/uikit/view.rs b/src/platform_impl/apple/uikit/view.rs index af733a6016..5123fd61d6 100644 --- a/src/platform_impl/apple/uikit/view.rs +++ b/src/platform_impl/apple/uikit/view.rs @@ -22,7 +22,6 @@ use crate::event::{ WindowEvent, }; use crate::keyboard::{Key, KeyCode, KeyLocation, NamedKey, NativeKeyCode, PhysicalKey}; -use crate::window::WindowAttributes; pub struct WinitViewState { pinch_gesture_recognizer: RefCell>>, @@ -337,7 +336,7 @@ define_class!( impl WinitView { pub(crate) fn new( mtm: MainThreadMarker, - window_attributes: &WindowAttributes, + scale_factor: Option, frame: CGRect, ) -> Retained { let this = mtm.alloc().set_ivars(WinitViewState { @@ -357,7 +356,7 @@ impl WinitView { this.setMultipleTouchEnabled(true); - if let Some(scale_factor) = window_attributes.platform_specific.scale_factor { + if let Some(scale_factor) = scale_factor { this.setContentScaleFactor(scale_factor as _); } diff --git a/src/platform_impl/apple/uikit/view_controller.rs b/src/platform_impl/apple/uikit/view_controller.rs index f860838bd2..e3a3ddaa5e 100644 --- a/src/platform_impl/apple/uikit/view_controller.rs +++ b/src/platform_impl/apple/uikit/view_controller.rs @@ -8,8 +8,7 @@ use objc2_ui_kit::{ UIUserInterfaceIdiom, UIView, UIViewController, }; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; -use crate::window::WindowAttributes; +use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; pub struct ViewControllerState { prefers_status_bar_hidden: Cell, @@ -129,7 +128,7 @@ impl WinitViewController { pub(crate) fn new( mtm: MainThreadMarker, - window_attributes: &WindowAttributes, + ios_attributes: &WindowAttributesIos, view: &UIView, ) -> Retained { // These are set properly below, we just to set them to something in the meantime. @@ -142,25 +141,16 @@ impl WinitViewController { }); let this: Retained = unsafe { msg_send![super(this), init] }; - this.set_prefers_status_bar_hidden( - window_attributes.platform_specific.prefers_status_bar_hidden, - ); + this.set_prefers_status_bar_hidden(ios_attributes.prefers_status_bar_hidden); - this.set_preferred_status_bar_style( - window_attributes.platform_specific.preferred_status_bar_style, - ); + this.set_preferred_status_bar_style(ios_attributes.preferred_status_bar_style); - this.set_supported_interface_orientations( - mtm, - window_attributes.platform_specific.valid_orientations, - ); + this.set_supported_interface_orientations(mtm, ios_attributes.valid_orientations); - this.set_prefers_home_indicator_auto_hidden( - window_attributes.platform_specific.prefers_home_indicator_hidden, - ); + this.set_prefers_home_indicator_auto_hidden(ios_attributes.prefers_home_indicator_hidden); this.set_preferred_screen_edges_deferring_system_gestures( - window_attributes.platform_specific.preferred_screen_edges_deferring_system_gestures, + ios_attributes.preferred_screen_edges_deferring_system_gestures, ); this.setView(Some(view)); diff --git a/src/platform_impl/apple/uikit/window.rs b/src/platform_impl/apple/uikit/window.rs index a959f1b7d9..a6c56b8118 100644 --- a/src/platform_impl/apple/uikit/window.rs +++ b/src/platform_impl/apple/uikit/window.rs @@ -27,7 +27,7 @@ use crate::error::{NotSupportedError, RequestError}; use crate::event::WindowEvent; use crate::icon::Icon; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; -use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations}; +use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos}; use crate::window::{ CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, @@ -473,7 +473,7 @@ pub struct Window { impl Window { pub(crate) fn new( event_loop: &ActiveEventLoop, - window_attributes: WindowAttributes, + mut window_attributes: WindowAttributes, ) -> Result { let mtm = event_loop.mtm; @@ -484,6 +484,12 @@ impl Window { warn!("`WindowAttributes::max_surface_size` is ignored on iOS"); } + let ios_attributes = window_attributes + .platform + .take() + .and_then(|attrs| attrs.cast::().ok()) + .unwrap_or_default(); + // TODO: transparency, visible #[allow(deprecated)] @@ -512,12 +518,12 @@ impl Window { None => screen_bounds, }; - let view = WinitView::new(mtm, &window_attributes, frame); + let view = WinitView::new(mtm, ios_attributes.scale_factor, frame); let gl_or_metal_backed = view.isKindOfClass(class!(CAMetalLayer)) || view.isKindOfClass(class!(CAEAGLLayer)); - let view_controller = WinitViewController::new(mtm, &window_attributes, &view); + let view_controller = WinitViewController::new(mtm, &ios_attributes, &view); let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller); window.makeKeyAndVisible(); @@ -885,13 +891,3 @@ impl Inner { self.window.convertRect_fromCoordinateSpace(rect, &screen_space) } } - -#[derive(Clone, Debug, Default, PartialEq)] -pub struct PlatformSpecificWindowAttributes { - pub scale_factor: Option, - pub valid_orientations: ValidOrientations, - pub prefers_home_indicator_hidden: bool, - pub prefers_status_bar_hidden: bool, - pub preferred_status_bar_style: StatusBarStyle, - pub preferred_screen_edges_deferring_system_gestures: ScreenEdge, -} diff --git a/src/platform_impl/linux/wayland/event_loop/mod.rs b/src/platform_impl/linux/wayland/event_loop/mod.rs index d66349df58..57d109b515 100644 --- a/src/platform_impl/linux/wayland/event_loop/mod.rs +++ b/src/platform_impl/linux/wayland/event_loop/mod.rs @@ -18,6 +18,7 @@ use sctk::reexports::client::{globals, Connection, QueueHandle}; use tracing::warn; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource}; use crate::dpi::LogicalSize; use crate::error::{EventLoopError, NotSupportedError, OsError, RequestError}; use crate::event::{DeviceEvent, StartCause, SurfaceSizeWriter, WindowEvent}; @@ -29,7 +30,7 @@ use crate::monitor::MonitorHandle as CoreMonitorHandle; use crate::platform::pump_events::PumpStatus; use crate::platform_impl::platform::min_timeout; use crate::platform_impl::wayland::types::cursor::WaylandCustomCursor; -use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme}; +use crate::window::Theme; mod proxy; pub mod sink; diff --git a/src/platform_impl/linux/wayland/output.rs b/src/platform_impl/linux/wayland/output.rs index c4dedc5bd4..5d9e740b8b 100644 --- a/src/platform_impl/linux/wayland/output.rs +++ b/src/platform_impl/linux/wayland/output.rs @@ -83,9 +83,9 @@ impl Eq for MonitorHandle {} /// Convert the wayland's [`Mode`] to winit's [`VideoMode`]. fn wayland_mode_to_core_mode(mode: Mode) -> VideoMode { - VideoMode { - size: (mode.dimensions.0, mode.dimensions.1).into(), - bit_depth: None, - refresh_rate_millihertz: NonZeroU32::new(mode.refresh_rate as u32), - } + VideoMode::new( + (mode.dimensions.0, mode.dimensions.1).into(), + None, + NonZeroU32::new(mode.refresh_rate as u32), + ) } diff --git a/src/platform_impl/linux/wayland/seat/text_input/mod.rs b/src/platform_impl/linux/wayland/seat/text_input/mod.rs index 140f7dfc2b..2bce758ef3 100644 --- a/src/platform_impl/linux/wayland/seat/text_input/mod.rs +++ b/src/platform_impl/linux/wayland/seat/text_input/mod.rs @@ -163,9 +163,9 @@ pub trait ZwpTextInputV3Ext { impl ZwpTextInputV3Ext for ZwpTextInputV3 { fn set_content_type_by_purpose(&self, purpose: ImePurpose) { let (hint, purpose) = match purpose { - ImePurpose::Normal => (ContentHint::None, ContentPurpose::Normal), ImePurpose::Password => (ContentHint::SensitiveData, ContentPurpose::Password), ImePurpose::Terminal => (ContentHint::None, ContentPurpose::Terminal), + _ => (ContentHint::None, ContentPurpose::Normal), }; self.set_content_type(hint, purpose); } diff --git a/src/platform_impl/linux/wayland/types/cursor.rs b/src/platform_impl/linux/wayland/types/cursor.rs index 682d959afd..2f16816a08 100644 --- a/src/platform_impl/linux/wayland/types/cursor.rs +++ b/src/platform_impl/linux/wayland/types/cursor.rs @@ -39,14 +39,14 @@ impl CustomCursor { let image = &image.0; let (buffer, canvas) = pool .create_buffer( - image.width as i32, - image.height as i32, - 4 * (image.width as i32), + image.width() as i32, + image.height() as i32, + 4 * (image.width() as i32), Format::Argb8888, ) .unwrap(); - for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.rgba.chunks_exact(4)) { + for (canvas_chunk, rgba) in canvas.chunks_exact_mut(4).zip(image.buffer().chunks_exact(4)) { // Alpha in buffer is premultiplied. let alpha = rgba[3] as f32 / 255.; let r = (rgba[0] as f32 * alpha) as u32; @@ -59,10 +59,10 @@ impl CustomCursor { CustomCursor { buffer, - w: image.width as i32, - h: image.height as i32, - hotspot_x: image.hotspot_x as i32, - hotspot_y: image.hotspot_y as i32, + w: image.width() as i32, + h: image.height() as i32, + hotspot_x: image.hotspot_x() as i32, + hotspot_y: image.hotspot_y() as i32, } } } diff --git a/src/platform_impl/linux/wayland/window/mod.rs b/src/platform_impl/linux/wayland/window/mod.rs index 66e3d25a84..773116af29 100644 --- a/src/platform_impl/linux/wayland/window/mod.rs +++ b/src/platform_impl/linux/wayland/window/mod.rs @@ -19,15 +19,17 @@ use super::output::MonitorHandle; use super::state::WinitState; use super::types::xdg_activation::XdgActivationTokenData; use super::ActiveEventLoop; +use crate::cursor::Cursor; use crate::dpi::{LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{Ime, WindowEvent}; use crate::event_loop::AsyncRequestSerial; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle}; +use crate::platform::wayland::WindowAttributesWayland; use crate::platform_impl::wayland::output; use crate::window::{ - Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, - Window as CoreWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, + CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as CoreWindow, + WindowAttributes, WindowButtons, WindowId, WindowLevel, }; pub(crate) mod state; @@ -78,7 +80,7 @@ pub struct Window { impl Window { pub(crate) fn new( event_loop_window_target: &ActiveEventLoop, - attributes: WindowAttributes, + mut attributes: WindowAttributes, ) -> Result { let queue_handle = event_loop_window_target.queue_handle.clone(); let mut state = event_loop_window_target.state.borrow_mut(); @@ -121,8 +123,15 @@ impl Window { // Set the decorations hint. window_state.set_decorate(attributes.decorations); + let (app_name, activation_token) = + match attributes.platform.take().and_then(|p| p.cast::().ok()) + { + Some(attrs) => (attrs.name, attrs.activation_token), + None => (None, None), + }; + // Set the app_id. - if let Some(name) = attributes.platform_specific.name.map(|name| name.general) { + if let Some(name) = app_name.map(|name| name.general) { window.set_app_id(name); } @@ -162,10 +171,8 @@ impl Window { } // Activate the window when the token is passed. - if let (Some(xdg_activation), Some(token)) = - (xdg_activation.as_ref(), attributes.platform_specific.activation_token) - { - xdg_activation.activate(token.token, &surface); + if let (Some(xdg_activation), Some(token)) = (xdg_activation.as_ref(), activation_token) { + xdg_activation.activate(token.into_raw(), &surface); } // XXX Do initial commit. @@ -497,7 +504,7 @@ impl CoreWindow for Window { fn set_window_level(&self, _level: WindowLevel) {} - fn set_window_icon(&self, _window_icon: Option) {} + fn set_window_icon(&self, _window_icon: Option) {} #[inline] fn set_ime_cursor_area(&self, position: Position, size: Size) { @@ -678,23 +685,3 @@ impl WindowRequests { self.redraw_requested.swap(false, Ordering::Relaxed) } } - -impl TryFrom<&str> for Theme { - type Error = (); - - /// ``` - /// use winit::window::Theme; - /// - /// assert_eq!("dark".try_into(), Ok(Theme::Dark)); - /// assert_eq!("lIghT".try_into(), Ok(Theme::Light)); - /// ``` - fn try_from(theme: &str) -> Result { - if theme.eq_ignore_ascii_case("dark") { - Ok(Self::Dark) - } else if theme.eq_ignore_ascii_case("light") { - Ok(Self::Light) - } else { - Err(()) - } - } -} diff --git a/src/platform_impl/linux/wayland/window/state.rs b/src/platform_impl/linux/wayland/window/state.rs index 337b2af1b7..b4939cff96 100644 --- a/src/platform_impl/linux/wayland/window/state.rs +++ b/src/platform_impl/linux/wayland/window/state.rs @@ -28,7 +28,7 @@ use sctk::subcompositor::SubcompositorState; use tracing::{info, warn}; use wayland_protocols_plasma::blur::client::org_kde_kwin_blur::OrgKdeKwinBlur; -use crate::cursor::CustomCursor as CoreCustomCursor; +use crate::cursor::{CursorIcon, CustomCursor as CoreCustomCursor}; use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::platform_impl::wayland::event_loop::OwnedDisplayHandle; @@ -41,7 +41,7 @@ use crate::platform_impl::wayland::types::cursor::{ CustomCursor, SelectedCursor, WaylandCustomCursor, }; use crate::platform_impl::wayland::types::kwin_blur::KWinBlurManager; -use crate::window::{CursorGrabMode, CursorIcon, ImePurpose, ResizeDirection, Theme, WindowId}; +use crate::window::{CursorGrabMode, ImePurpose, ResizeDirection, Theme, WindowId}; #[cfg(feature = "sctk-adwaita")] pub type WinitFrame = sctk_adwaita::AdwaitaFrame; @@ -398,7 +398,7 @@ impl WindowState { self.apply_on_pointer(|_, data| { let serial = data.latest_button_serial(); let seat = data.seat(); - xdg_toplevel.resize(seat, serial, direction.into()); + xdg_toplevel.resize(seat, serial, resize_direction_to_xdg(direction)); }); Ok(()) @@ -1148,18 +1148,16 @@ pub enum FrameCallbackState { Received, } -impl From for XdgResizeEdge { - fn from(value: ResizeDirection) -> Self { - match value { - ResizeDirection::North => XdgResizeEdge::Top, - ResizeDirection::West => XdgResizeEdge::Left, - ResizeDirection::NorthWest => XdgResizeEdge::TopLeft, - ResizeDirection::NorthEast => XdgResizeEdge::TopRight, - ResizeDirection::East => XdgResizeEdge::Right, - ResizeDirection::SouthWest => XdgResizeEdge::BottomLeft, - ResizeDirection::SouthEast => XdgResizeEdge::BottomRight, - ResizeDirection::South => XdgResizeEdge::Bottom, - } +fn resize_direction_to_xdg(direction: ResizeDirection) -> XdgResizeEdge { + match direction { + ResizeDirection::North => XdgResizeEdge::Top, + ResizeDirection::West => XdgResizeEdge::Left, + ResizeDirection::NorthWest => XdgResizeEdge::TopLeft, + ResizeDirection::NorthEast => XdgResizeEdge::TopRight, + ResizeDirection::East => XdgResizeEdge::Right, + ResizeDirection::SouthWest => XdgResizeEdge::BottomLeft, + ResizeDirection::SouthEast => XdgResizeEdge::BottomRight, + ResizeDirection::South => XdgResizeEdge::Bottom, } } diff --git a/src/platform_impl/linux/x11/mod.rs b/src/platform_impl/linux/x11/mod.rs index 1a5ac78037..3a86326d6d 100644 --- a/src/platform_impl/linux/x11/mod.rs +++ b/src/platform_impl/linux/x11/mod.rs @@ -23,6 +23,7 @@ use x11rb::x11_utils::X11Error as LogicalError; use x11rb::xcb_ffi::ReplyOrIdError; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource}; use crate::error::{EventLoopError, RequestError}; use crate::event::{DeviceId, StartCause, WindowEvent}; use crate::event_loop::{ @@ -36,10 +37,7 @@ use crate::platform::x11::XlibErrorHook; use crate::platform_impl::common::xkb::Context; use crate::platform_impl::platform::min_timeout; use crate::platform_impl::x11::window::Window; -use crate::window::{ - CustomCursor as CoreCustomCursor, CustomCursorSource, Theme, Window as CoreWindow, - WindowAttributes, WindowId, -}; +use crate::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId}; mod activation; mod atoms; diff --git a/src/platform_impl/linux/x11/util/cursor.rs b/src/platform_impl/linux/x11/util/cursor.rs index ecef41f752..a76b6f9197 100644 --- a/src/platform_impl/linux/x11/util/cursor.rs +++ b/src/platform_impl/linux/x11/util/cursor.rs @@ -9,9 +9,8 @@ use x11rb::protocol::xproto; use super::super::ActiveEventLoop; use super::*; -use crate::cursor::{CustomCursorProvider, CustomCursorSource}; +use crate::cursor::{CursorIcon, CustomCursorProvider, CustomCursorSource}; use crate::error::{NotSupportedError, RequestError}; -use crate::window::CursorIcon; impl XConnection { pub fn set_cursor_icon( @@ -208,7 +207,7 @@ impl CustomCursor { }; // Reverse RGBA order to BGRA. - cursor.rgba.chunks_mut(4).for_each(|chunk| { + cursor.buffer_mut().chunks_mut(4).for_each(|chunk| { let chunk: &mut [u8; 4] = chunk.try_into().unwrap(); chunk[0..3].reverse(); @@ -222,11 +221,11 @@ impl CustomCursor { let cursor = event_loop .xconn .create_cursor_from_image( - cursor.width, - cursor.height, - cursor.hotspot_x, - cursor.hotspot_y, - &cursor.rgba, + cursor.width(), + cursor.height(), + cursor.hotspot_x(), + cursor.hotspot_y(), + cursor.buffer(), ) .map_err(|err| os_error!(err))?; diff --git a/src/platform_impl/linux/x11/util/icon.rs b/src/platform_impl/linux/x11/util/icon.rs index 07b5fee676..d8c57616a1 100644 --- a/src/platform_impl/linux/x11/util/icon.rs +++ b/src/platform_impl/linux/x11/util/icon.rs @@ -1,7 +1,18 @@ #![allow(clippy::assertions_on_constants)] use super::*; -use crate::icon::{Pixel, RgbaIcon, PIXEL_SIZE}; +use crate::icon::RgbaIcon; + +pub(crate) const PIXEL_SIZE: usize = mem::size_of::(); + +#[repr(C)] +#[derive(Debug)] +pub(crate) struct Pixel { + pub(crate) r: u8, + pub(crate) g: u8, + pub(crate) b: u8, + pub(crate) a: u8, +} impl Pixel { pub fn to_packed_argb(&self) -> Cardinal { @@ -18,19 +29,17 @@ impl Pixel { } } -impl RgbaIcon { - pub(crate) fn to_cardinals(&self) -> Vec { - assert_eq!(self.rgba.len() % PIXEL_SIZE, 0); - let pixel_count = self.rgba.len() / PIXEL_SIZE; - assert_eq!(pixel_count, (self.width * self.height) as usize); - let mut data = Vec::with_capacity(pixel_count); - data.push(self.width as Cardinal); - data.push(self.height as Cardinal); - let pixels = self.rgba.as_ptr() as *const Pixel; - for pixel_index in 0..pixel_count { - let pixel = unsafe { &*pixels.add(pixel_index) }; - data.push(pixel.to_packed_argb()); - } - data +pub(crate) fn rgba_to_cardinals(icon: &RgbaIcon) -> Vec { + assert_eq!(icon.buffer().len() % PIXEL_SIZE, 0); + let pixel_count = icon.buffer().len() / PIXEL_SIZE; + assert_eq!(pixel_count, (icon.width() * icon.height()) as usize); + let mut data = Vec::with_capacity(pixel_count); + data.push(icon.width() as Cardinal); + data.push(icon.height() as Cardinal); + let pixels = icon.buffer().as_ptr() as *const Pixel; + for pixel_index in 0..pixel_count { + let pixel = unsafe { &*pixels.add(pixel_index) }; + data.push(pixel.to_packed_argb()); } + data } diff --git a/src/platform_impl/linux/x11/util/mod.rs b/src/platform_impl/linux/x11/util/mod.rs index 9f034aff4d..1cd7ecf330 100644 --- a/src/platform_impl/linux/x11/util/mod.rs +++ b/src/platform_impl/linux/x11/util/mod.rs @@ -25,6 +25,7 @@ use x11rb::protocol::xproto::{self, ConnectionExt as _}; pub use self::cursor::*; pub use self::geometry::*; pub use self::hint::*; +pub(crate) use self::icon::rgba_to_cardinals; pub use self::input::*; pub use self::mouse::*; pub use self::window_property::*; diff --git a/src/platform_impl/linux/x11/util/randr.rs b/src/platform_impl/linux/x11/util/randr.rs index 5a2a1c695a..ab1fc57a96 100644 --- a/src/platform_impl/linux/x11/util/randr.rs +++ b/src/platform_impl/linux/x11/util/randr.rs @@ -85,11 +85,11 @@ impl XConnection { .filter(|x| output_modes.iter().any(|id| x.id == *id)) .map(|mode| VideoModeHandle { current: mode.id == current_mode, - mode: VideoMode { - size: (mode.width as u32, mode.height as u32).into(), - refresh_rate_millihertz: monitor::mode_refresh_rate_millihertz(mode), - bit_depth: NonZeroU16::new(bit_depth as u16), - }, + mode: VideoMode::new( + (mode.width as u32, mode.height as u32).into(), + NonZeroU16::new(bit_depth as u16), + monitor::mode_refresh_rate_millihertz(mode), + ), native_mode: mode.id, }) .collect(); diff --git a/src/platform_impl/linux/x11/window.rs b/src/platform_impl/linux/x11/window.rs index 14f8a8d943..af2422c4d3 100644 --- a/src/platform_impl/linux/x11/window.rs +++ b/src/platform_impl/linux/x11/window.rs @@ -32,9 +32,10 @@ use crate::icon::RgbaIcon; use crate::monitor::{ Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider, VideoMode, }; -use crate::platform::x11::WindowType; +use crate::platform::x11::{WindowAttributesX11, WindowType}; use crate::platform_impl::common; use crate::platform_impl::x11::atoms::*; +use crate::platform_impl::x11::util::rgba_to_cardinals; use crate::platform_impl::x11::{ xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, X11Error, }; @@ -203,9 +204,9 @@ impl CoreWindow for Window { self.0.set_window_level(level); } - fn set_window_icon(&self, window_icon: Option) { + fn set_window_icon(&self, window_icon: Option) { let icon = match window_icon.as_ref() { - Some(icon) => icon.0.cast_ref::(), + Some(icon) => icon.cast_ref::(), None => None, }; self.0.set_window_icon(icon) @@ -442,12 +443,18 @@ impl UnownedWindow { #[allow(clippy::unnecessary_cast)] pub(crate) fn new( event_loop: &ActiveEventLoop, - window_attrs: WindowAttributes, + mut window_attrs: WindowAttributes, ) -> Result { let xconn = &event_loop.xconn; let atoms = xconn.atoms(); - let screen_id = match window_attrs.platform_specific.x11.screen_id { + let x11_attributes = window_attrs + .platform + .take() + .and_then(|attrs| attrs.cast::().ok()) + .unwrap_or_default(); + + let screen_id = match x11_attributes.screen_id { Some(id) => id, None => xconn.default_screen_index() as c_int, }; @@ -460,7 +467,7 @@ impl UnownedWindow { )? }; - let root = match window_attrs.parent_window.as_ref().map(|handle| handle.0) { + let root = match window_attrs.parent_window() { Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window, Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(), Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"), @@ -527,33 +534,32 @@ impl UnownedWindow { .flat_map(|depth| depth.visuals.iter().map(move |visual| (visual, depth.depth))); // creating - let (visualtype, depth, require_colormap) = - match window_attrs.platform_specific.x11.visual_id { - Some(vi) => { - // Find this specific visual. - let (visualtype, depth) = all_visuals - .find(|(visual, _)| visual.visual_id == vi) - .ok_or_else(|| os_error!(X11Error::NoSuchVisual(vi)))?; - - (Some(visualtype), depth, true) - }, - None if window_attrs.transparent => { - // Find a suitable visual, true color with 32 bits of depth. - all_visuals - .find_map(|(visual, depth)| { - (depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR) - .then_some((Some(visual), depth, true)) - }) - .unwrap_or_else(|| { - debug!( - "Could not set transparency, because XMatchVisualInfo returned \ - zero for the required parameters" - ); - (None as _, x11rb::COPY_FROM_PARENT as _, false) - }) - }, - _ => (None, x11rb::COPY_FROM_PARENT as _, false), - }; + let (visualtype, depth, require_colormap) = match x11_attributes.visual_id { + Some(vi) => { + // Find this specific visual. + let (visualtype, depth) = all_visuals + .find(|(visual, _)| visual.visual_id == vi) + .ok_or_else(|| os_error!(X11Error::NoSuchVisual(vi)))?; + + (Some(visualtype), depth, true) + }, + None if window_attrs.transparent => { + // Find a suitable visual, true color with 32 bits of depth. + all_visuals + .find_map(|(visual, depth)| { + (depth == 32 && visual.class == xproto::VisualClass::TRUE_COLOR) + .then_some((Some(visual), depth, true)) + }) + .unwrap_or_else(|| { + debug!( + "Could not set transparency, because XMatchVisualInfo returned zero \ + for the required parameters" + ); + (None as _, x11rb::COPY_FROM_PARENT as _, false) + }) + }, + _ => (None, x11rb::COPY_FROM_PARENT as _, false), + }; let mut visual = visualtype.map_or(x11rb::COPY_FROM_PARENT, |v| v.visual_id); let window_attributes = { @@ -573,12 +579,12 @@ impl UnownedWindow { aux = aux.event_mask(event_mask).border_pixel(0); - if window_attrs.platform_specific.x11.override_redirect { + if x11_attributes.override_redirect { aux = aux.override_redirect(true as u32); } // Add a colormap if needed. - let colormap_visual = match window_attrs.platform_specific.x11.visual_id { + let colormap_visual = match x11_attributes.visual_id { Some(vi) => Some(vi), None if require_colormap => Some(visual), _ => None, @@ -601,7 +607,7 @@ impl UnownedWindow { }; // Figure out the window's parent. - let parent = window_attrs.platform_specific.x11.embed_window.unwrap_or(root); + let parent = x11_attributes.embed_window.unwrap_or(root); // finally creating the window let xwindow = { @@ -664,7 +670,7 @@ impl UnownedWindow { } // Embed the window if needed. - if window_attrs.platform_specific.x11.embed_window.is_some() { + if x11_attributes.embed_window.is_some() { window.embed_window()?; } @@ -685,7 +691,7 @@ impl UnownedWindow { // WM_CLASS must be set *before* mapping the window, as per ICCCM! { - let (instance, class) = if let Some(name) = window_attrs.platform_specific.name { + let (instance, class) = if let Some(name) = x11_attributes.name { (name.instance, name.general) } else { let class = env::args_os() @@ -716,8 +722,7 @@ impl UnownedWindow { flusher.ignore_error() } - leap!(window.set_window_types(window_attrs.platform_specific.x11.x11_window_types)) - .ignore_error(); + leap!(window.set_window_types(x11_attributes.x11_window_types)).ignore_error(); // Set size hints. let mut min_surface_size = @@ -738,7 +743,7 @@ impl UnownedWindow { shared_state.min_surface_size = min_surface_size.map(Into::into); shared_state.max_surface_size = max_surface_size.map(Into::into); shared_state.surface_resize_increments = window_attrs.surface_resize_increments; - shared_state.base_size = window_attrs.platform_specific.x11.base_size; + shared_state.base_size = x11_attributes.base_size; let normal_hints = WmSizeHints { position: position.map(|PhysicalPosition { x, y }| { @@ -754,9 +759,7 @@ impl UnownedWindow { size_increment: window_attrs .surface_resize_increments .map(|size| cast_size_to_hint(size, scale_factor)), - base_size: window_attrs - .platform_specific - .x11 + base_size: x11_attributes .base_size .map(|size| cast_size_to_hint(size, scale_factor)), aspect: None, @@ -771,7 +774,7 @@ impl UnownedWindow { // Set window icons if let Some(icon) = - window_attrs.window_icon.as_ref().and_then(|icon| icon.0.cast_ref::()) + window_attrs.window_icon.as_ref().and_then(|icon| icon.cast_ref::()) { leap!(window.set_icon_inner(icon)).ignore_error(); } @@ -883,8 +886,8 @@ impl UnownedWindow { window.set_cursor(window_attrs.cursor); // Remove the startup notification if we have one. - if let Some(startup) = window_attrs.platform_specific.activation_token.as_ref() { - leap!(xconn.remove_activation_token(xwindow, &startup.token)); + if let Some(startup) = x11_attributes.activation_token.as_ref() { + leap!(xconn.remove_activation_token(xwindow, startup.as_raw())); } // We never want to give the user a broken window, since by then, it's too late to handle. @@ -1414,7 +1417,7 @@ impl UnownedWindow { fn set_icon_inner(&self, icon: &RgbaIcon) -> Result, X11Error> { let atoms = self.xconn.atoms(); let icon_atom = atoms[_NET_WM_ICON]; - let data = icon.to_cardinals(); + let data = rgba_to_cardinals(icon); self.xconn.change_property( self.xwindow, icon_atom, diff --git a/src/platform_impl/linux/x11/xdisplay.rs b/src/platform_impl/linux/x11/xdisplay.rs index f14e5beb5b..bd9ddce488 100644 --- a/src/platform_impl/linux/x11/xdisplay.rs +++ b/src/platform_impl/linux/x11/xdisplay.rs @@ -16,7 +16,7 @@ use x11rb::xcb_ffi::XCBConnection; use super::atoms::Atoms; use super::ffi; use super::monitor::MonitorHandle; -use crate::window::CursorIcon; +use crate::cursor::CursorIcon; /// A connection to an X server. pub struct XConnection { diff --git a/src/platform_impl/orbital/event_loop.rs b/src/platform_impl/orbital/event_loop.rs index 4e7c9a43e6..c1d527b589 100644 --- a/src/platform_impl/orbital/event_loop.rs +++ b/src/platform_impl/orbital/event_loop.rs @@ -13,6 +13,7 @@ use smol_str::SmolStr; use super::{PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, WindowProperties}; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event::{self, Ime, Modifiers, StartCause}; use crate::event_loop::{ @@ -25,9 +26,7 @@ use crate::keyboard::{ PhysicalKey, }; use crate::platform_impl::Window; -use crate::window::{ - CustomCursor as RootCustomCursor, CustomCursorSource, Theme, Window as CoreWindow, WindowId, -}; +use crate::window::{Theme, Window as CoreWindow, WindowId}; fn convert_scancode(scancode: u8) -> (PhysicalKey, Option) { // Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html @@ -268,7 +267,7 @@ impl EventState { pressed_mods .set(ModifiersKeys::RMETA, self.keyboard.contains(KeyboardModifierState::RMETA)); - Modifiers { state, pressed_mods } + Modifiers::new(state, pressed_mods) } } @@ -699,10 +698,7 @@ impl RootActiveEventLoop for ActiveEventLoop { Ok(Box::new(Window::new(self, window_attributes)?)) } - fn create_custom_cursor( - &self, - _: CustomCursorSource, - ) -> Result { + fn create_custom_cursor(&self, _: CustomCursorSource) -> Result { Err(NotSupportedError::new("create_custom_cursor is not supported").into()) } diff --git a/src/platform_impl/web/cursor.rs b/src/platform_impl/web/cursor.rs index e67f08605e..02216762cb 100644 --- a/src/platform_impl/web/cursor.rs +++ b/src/platform_impl/web/cursor.rs @@ -24,9 +24,7 @@ use super::backend::Style; use super::main_thread::{MainThreadMarker, MainThreadSafe}; use super::r#async::{AbortHandle, Abortable, DropAbortHandle, Notified, Notifier}; use super::ActiveEventLoop; -use crate::cursor::{ - Cursor, CursorAnimation, CursorImage, CustomCursorProvider, CustomCursorSource, -}; +use crate::cursor::{Cursor, CursorImage, CustomCursorProvider, CustomCursorSource}; use crate::platform::web::CustomCursorError; #[derive(Clone, Debug)] @@ -48,7 +46,8 @@ impl CustomCursor { from_url(UrlType::Plain(url), hotspot_x, hotspot_y), false, ), - CustomCursorSource::Animation(CursorAnimation { duration, cursors }) => { + CustomCursorSource::Animation(animation) => { + let (duration, cursors) = animation.into_raw(); Self::build_spawn( event_loop, from_animation(event_loop.runner.main_thread(), duration, cursors.into_iter()), @@ -512,17 +511,17 @@ fn from_rgba( fn new(array: Uint8ClampedArray, sw: u32) -> Result; } - let array = Uint8Array::new_with_length(image.rgba.len() as u32); - array.copy_from(&image.rgba); + let array = Uint8Array::new_with_length(image.buffer().len() as u32); + array.copy_from(image.buffer()); let array = Uint8ClampedArray::new(&array); - ImageDataExt::new(array, image.width as u32) + ImageDataExt::new(array, image.width() as u32) .map(JsValue::from) .map(ImageData::unchecked_from_js) }; #[cfg(not(target_feature = "atomics"))] let result = ImageData::new_with_u8_clamped_array( - wasm_bindgen::Clamped(&image.rgba), - image.width as u32, + wasm_bindgen::Clamped(image.buffer()), + image.width() as u32, ); let image_data = result.expect("found wrong image size"); @@ -538,7 +537,10 @@ fn from_rgba( .expect("unexpected exception in `createImageBitmap()`"), ); - let CursorImage { width, height, hotspot_x, hotspot_y, .. } = *image; + let width = image.width(); + let height = image.height(); + let hotspot_x = image.hotspot_x(); + let hotspot_y = image.hotspot_y(); async move { let bitmap: ImageBitmap = bitmap.await.expect("found invalid state in `ImageData`").unchecked_into(); diff --git a/src/platform_impl/web/event_loop/runner.rs b/src/platform_impl/web/event_loop/runner.rs index fb87586fae..0145f462c6 100644 --- a/src/platform_impl/web/event_loop/runner.rs +++ b/src/platform_impl/web/event_loop/runner.rs @@ -24,6 +24,7 @@ use crate::platform::web::{PollStrategy, WaitUntilStrategy}; use crate::platform_impl::platform::backend::{EventListenerHandle, SafeAreaHandle}; use crate::platform_impl::platform::r#async::DispatchRunner; use crate::platform_impl::platform::window::Inner; +use crate::platform_impl::web::web_sys::event::mouse_button_to_id; use crate::window::WindowId; #[derive(Debug)] @@ -325,7 +326,10 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id, - event: DeviceEvent::Button { button: button.to_id().into(), state }, + event: DeviceEvent::Button { + button: mouse_button_to_id(button).into(), + state, + }, }); return; @@ -374,7 +378,7 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id: event::mkdid(event.pointer_id()), event: DeviceEvent::Button { - button: button.to_id().into(), + button: mouse_button_to_id(button).into(), state: ElementState::Pressed, }, }); @@ -393,7 +397,7 @@ impl Shared { runner.send_event(Event::DeviceEvent { device_id: event::mkdid(event.pointer_id()), event: DeviceEvent::Button { - button: button.to_id().into(), + button: mouse_button_to_id(button).into(), state: ElementState::Released, }, }); diff --git a/src/platform_impl/web/event_loop/window_target.rs b/src/platform_impl/web/event_loop/window_target.rs index 098dac30be..7952f91dfe 100644 --- a/src/platform_impl/web/event_loop/window_target.rs +++ b/src/platform_impl/web/event_loop/window_target.rs @@ -11,6 +11,7 @@ use super::super::monitor::MonitorPermissionFuture; use super::runner::Event; use super::{backend, runner}; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource}; use crate::error::{NotSupportedError, RequestError}; use crate::event::{ElementState, KeyEvent, TouchPhase, WindowEvent}; use crate::event_loop::{ @@ -23,7 +24,7 @@ use crate::platform::web::{CustomCursorFuture, PollStrategy, WaitUntilStrategy}; use crate::platform_impl::platform::cursor::CustomCursor; use crate::platform_impl::web::event_loop::proxy::EventLoopProxy; use crate::platform_impl::Window; -use crate::window::{CustomCursor as CoreCustomCursor, CustomCursorSource, Theme, WindowId}; +use crate::window::{Theme, WindowId}; #[derive(Default, Debug)] struct ModifiersShared(Rc>); diff --git a/src/platform_impl/web/keyboard.rs b/src/platform_impl/web/keyboard.rs index e1b0e7e6bd..83c3c1ab5d 100644 --- a/src/platform_impl/web/keyboard.rs +++ b/src/platform_impl/web/keyboard.rs @@ -2,8 +2,14 @@ use smol_str::SmolStr; use crate::keyboard::{Key, KeyCode, NamedKey, NativeKey, NativeKeyCode, PhysicalKey}; -impl Key { - pub(crate) fn from_key_attribute_value(kav: &str) -> Self { +pub trait FromAttributeValue { + fn from_attribute_value(kav: &str) -> Self + where + Self: Sized; +} + +impl FromAttributeValue for Key { + fn from_attribute_value(kav: &str) -> Self { Key::Named(match kav { "Unidentified" => return Key::Unidentified(NativeKey::Web(SmolStr::new(kav))), "Dead" => return Key::Dead(None), @@ -319,8 +325,8 @@ impl Key { } } -impl PhysicalKey { - pub fn from_key_code_attribute_value(kcav: &str) -> Self { +impl FromAttributeValue for PhysicalKey { + fn from_attribute_value(kcav: &str) -> Self { PhysicalKey::Code(match kcav { "Backquote" => KeyCode::Backquote, "Backslash" => KeyCode::Backslash, diff --git a/src/platform_impl/web/mod.rs b/src/platform_impl/web/mod.rs index 38161534bf..7c98b10672 100644 --- a/src/platform_impl/web/mod.rs +++ b/src/platform_impl/web/mod.rs @@ -24,13 +24,13 @@ mod r#async; mod cursor; mod error; mod event; -mod event_loop; +pub(crate) mod event_loop; mod keyboard; mod lock; -mod main_thread; +pub(crate) mod main_thread; mod monitor; -mod web_sys; -mod window; +pub(crate) mod web_sys; +pub(crate) mod window; pub(crate) use cursor::CustomCursorFuture; @@ -41,4 +41,4 @@ pub(crate) use self::monitor::{ HasMonitorPermissionFuture, MonitorHandle, MonitorPermissionFuture, OrientationLockFuture, }; use self::web_sys as backend; -pub use self::window::{PlatformSpecificWindowAttributes, Window}; +pub use self::window::Window; diff --git a/src/platform_impl/web/monitor.rs b/src/platform_impl/web/monitor.rs index 71f9391f13..9b3a9eb063 100644 --- a/src/platform_impl/web/monitor.rs +++ b/src/platform_impl/web/monitor.rs @@ -141,11 +141,11 @@ impl MonitorHandleProvider for MonitorHandle { } fn current_video_mode(&self) -> Option { - Some(VideoMode { - size: self.inner.queue(|inner| inner.size()), - bit_depth: self.inner.queue(|inner| inner.bit_depth()), - refresh_rate_millihertz: None, - }) + Some(VideoMode::new( + self.inner.queue(|inner| inner.size()), + self.inner.queue(|inner| inner.bit_depth()), + None, + )) } fn video_modes(&self) -> Box> { diff --git a/src/platform_impl/web/web_sys/canvas.rs b/src/platform_impl/web/web_sys/canvas.rs index c4ea043a01..76cf8d4a40 100644 --- a/src/platform_impl/web/web_sys/canvas.rs +++ b/src/platform_impl/web/web_sys/canvas.rs @@ -28,6 +28,7 @@ use crate::event::{ }; use crate::keyboard::{Key, KeyLocation, ModifiersState, PhysicalKey}; use crate::monitor::Fullscreen; +use crate::platform::web::WindowAttributesWeb; use crate::window::{WindowAttributes, WindowId}; #[allow(dead_code)] @@ -84,9 +85,15 @@ impl Canvas { window: web_sys::Window, navigator: Navigator, document: Document, - attr: WindowAttributes, + mut attr: WindowAttributes, ) -> Result { - let canvas = match attr.platform_specific.canvas.map(Arc::try_unwrap) { + let web_attributes = attr + .platform + .take() + .and_then(|attrs| attrs.cast::().ok()) + .unwrap_or_default(); + + let canvas = match web_attributes.canvas.map(Arc::try_unwrap) { Some(Ok(canvas)) => canvas.into_inner(main_thread), Some(Err(canvas)) => canvas.get(main_thread).clone(), None => document @@ -95,7 +102,7 @@ impl Canvas { .unchecked_into(), }; - if attr.platform_specific.append && !document.contains(Some(&canvas)) { + if web_attributes.append && !document.contains(Some(&canvas)) { document .body() .expect("Failed to get body from document") @@ -108,7 +115,7 @@ impl Canvas { // sequential keyboard navigation, but its order is defined by the // document's source order. // https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/tabindex - if attr.platform_specific.focusable { + if web_attributes.focusable { canvas .set_attribute("tabindex", "0") .map_err(|_| os_error!("Failed to set a tabindex"))?; @@ -161,7 +168,7 @@ impl Canvas { common, id, has_focus: Rc::new(Cell::new(false)), - prevent_default: Rc::new(Cell::new(attr.platform_specific.prevent_default)), + prevent_default: Rc::new(Cell::new(web_attributes.prevent_default)), is_intersecting: Cell::new(None), cursor, handlers: RefCell::new(Handlers { @@ -517,7 +524,7 @@ impl Canvas { self.set_old_size(new_size); runner.send_event(runner::Event::WindowEvent { window_id: self.id, - event: crate::event::WindowEvent::SurfaceResized(new_size), + event: WindowEvent::SurfaceResized(new_size), }) } } diff --git a/src/platform_impl/web/web_sys/event.rs b/src/platform_impl/web/web_sys/event.rs index dbecbf395e..4f2fdda651 100644 --- a/src/platform_impl/web/web_sys/event.rs +++ b/src/platform_impl/web/web_sys/event.rs @@ -9,6 +9,7 @@ use web_sys::{KeyboardEvent, MouseEvent, Navigator, PointerEvent, WheelEvent}; use super::Engine; use crate::event::{FingerId, MouseButton, MouseScrollDelta, PointerKind}; use crate::keyboard::{Key, KeyLocation, ModifiersState, NamedKey, PhysicalKey}; +use crate::platform_impl::web::keyboard::FromAttributeValue; bitflags::bitflags! { // https://www.w3.org/TR/pointerevents3/#the-buttons-property @@ -67,16 +68,14 @@ pub fn mouse_button(event: &MouseEvent) -> Option { } } -impl MouseButton { - pub fn to_id(self) -> u16 { - match self { - MouseButton::Left => 0, - MouseButton::Right => 1, - MouseButton::Middle => 2, - MouseButton::Back => 3, - MouseButton::Forward => 4, - MouseButton::Other(value) => value, - } +pub fn mouse_button_to_id(button: MouseButton) -> u16 { + match button { + MouseButton::Left => 0, + MouseButton::Right => 1, + MouseButton::Middle => 2, + MouseButton::Back => 3, + MouseButton::Forward => 4, + MouseButton::Other(value) => value, } } @@ -170,16 +169,16 @@ pub fn pointer_type(event: &PointerEvent, pointer_id: i32) -> PointerKind { pub fn key_code(event: &KeyboardEvent) -> PhysicalKey { let code = event.code(); - PhysicalKey::from_key_code_attribute_value(&code) + PhysicalKey::from_attribute_value(&code) } pub fn key(event: &KeyboardEvent) -> Key { - Key::from_key_attribute_value(&event.key()) + Key::from_attribute_value(&event.key()) } pub fn key_text(event: &KeyboardEvent) -> Option { let key = event.key(); - let key = Key::from_key_attribute_value(&key); + let key = Key::from_attribute_value(&key); match &key { Key::Character(text) => Some(text.clone()), Key::Named(NamedKey::Tab) => Some(SmolStr::new("\t")), diff --git a/src/platform_impl/web/web_sys/pointer.rs b/src/platform_impl/web/web_sys/pointer.rs index 7acfc1679e..ef52a2a496 100644 --- a/src/platform_impl/web/web_sys/pointer.rs +++ b/src/platform_impl/web/web_sys/pointer.rs @@ -10,6 +10,7 @@ use crate::dpi::PhysicalPosition; use crate::event::{ButtonSource, DeviceId, ElementState, Force, PointerKind, PointerSource}; use crate::keyboard::ModifiersState; use crate::platform_impl::web::event::mkdid; +use crate::platform_impl::web::web_sys::event::mouse_button_to_id; #[allow(dead_code)] pub(super) struct PointerHandler { @@ -89,7 +90,7 @@ impl PointerHandler { finger_id, force: Some(Force::Normalized(event.pressure().into())), }, - PointerKind::Unknown => ButtonSource::Unknown(button.to_id()), + PointerKind::Unknown => ButtonSource::Unknown(mouse_button_to_id(button)), }; handler( @@ -142,7 +143,7 @@ impl PointerHandler { finger_id, force: Some(Force::Normalized(event.pressure().into())), }, - PointerKind::Unknown => ButtonSource::Unknown(button.to_id()), + PointerKind::Unknown => ButtonSource::Unknown(mouse_button_to_id(button)), }; handler( @@ -206,7 +207,7 @@ impl PointerHandler { let button = match kind { PointerKind::Mouse => ButtonSource::Mouse(button), PointerKind::Touch(finger_id) => { - let button_id = button.to_id(); + let button_id = mouse_button_to_id(button); if button_id != 1 { tracing::error!("unexpected touch button id: {button_id}"); diff --git a/src/platform_impl/web/window.rs b/src/platform_impl/web/window.rs index 4a31c2d1c4..de0bd7ba91 100644 --- a/src/platform_impl/web/window.rs +++ b/src/platform_impl/web/window.rs @@ -1,22 +1,22 @@ use std::cell::Ref; use std::fmt; use std::rc::Rc; -use std::sync::Arc; use dpi::{LogicalPosition, LogicalSize}; use web_sys::HtmlCanvasElement; -use super::main_thread::{MainThreadMarker, MainThreadSafe}; +use super::main_thread::MainThreadMarker; use super::monitor::MonitorHandler; use super::r#async::Dispatcher; use super::{backend, lock, ActiveEventLoop}; +use crate::cursor::Cursor; use crate::dpi::{LogicalInsets, PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; use crate::error::{NotSupportedError, RequestError}; use crate::icon::Icon; use crate::monitor::{Fullscreen, MonitorHandle as CoremMonitorHandle}; use crate::window::{ - Cursor, CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, - Window as RootWindow, WindowAttributes, WindowButtons, WindowId, WindowLevel, + CursorGrabMode, ImePurpose, ResizeDirection, Theme, UserAttentionType, Window as RootWindow, + WindowAttributes, WindowButtons, WindowId, WindowLevel, }; pub struct Window { @@ -462,42 +462,3 @@ impl Drop for Inner { } } } -#[derive(Clone, Debug)] -pub struct PlatformSpecificWindowAttributes { - pub(crate) canvas: Option>>, - pub(crate) prevent_default: bool, - pub(crate) focusable: bool, - pub(crate) append: bool, -} - -impl PartialEq for PlatformSpecificWindowAttributes { - fn eq(&self, other: &Self) -> bool { - (match (&self.canvas, &other.canvas) { - (Some(this), Some(other)) => Arc::ptr_eq(this, other), - (None, None) => true, - _ => false, - }) && self.prevent_default.eq(&other.prevent_default) - && self.focusable.eq(&other.focusable) - && self.append.eq(&other.append) - } -} - -impl PlatformSpecificWindowAttributes { - pub(crate) fn set_canvas(&mut self, canvas: Option) { - let Some(canvas) = canvas else { - self.canvas = None; - return; - }; - - let main_thread = MainThreadMarker::new() - .expect("received a `HtmlCanvasElement` outside the window context"); - - self.canvas = Some(Arc::new(MainThreadSafe::new(main_thread, canvas))); - } -} - -impl Default for PlatformSpecificWindowAttributes { - fn default() -> Self { - Self { canvas: None, prevent_default: true, focusable: true, append: false } - } -} diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 0762599d02..b466a3a98f 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -63,6 +63,7 @@ pub(super) use self::runner::{Event, EventLoopRunner}; use super::window::set_skip_taskbar; use super::SelectedCursor; use crate::application::ApplicationHandler; +use crate::cursor::{CustomCursor, CustomCursorSource}; use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::error::{EventLoopError, NotSupportedError, RequestError}; use crate::event::{ @@ -91,10 +92,7 @@ use crate::platform_impl::platform::window_state::{ }; use crate::platform_impl::platform::{raw_input, util, wrap_device_id}; use crate::platform_impl::Window; -use crate::window::{ - CustomCursor as CoreCustomCursor, CustomCursorSource, Theme, Window as CoreWindow, - WindowAttributes, WindowId, -}; +use crate::window::{Theme, Window as CoreWindow, WindowAttributes, WindowId}; pub(crate) struct WindowData { pub window_state: Arc>, @@ -420,7 +418,7 @@ impl RootActiveEventLoop for ActiveEventLoop { fn create_custom_cursor( &self, source: CustomCursorSource, - ) -> Result { + ) -> Result { let cursor = match source { CustomCursorSource::Image(cursor) => cursor, CustomCursorSource::Animation { .. } | CustomCursorSource::Url { .. } => { @@ -428,7 +426,7 @@ impl RootActiveEventLoop for ActiveEventLoop { }, }; - Ok(CoreCustomCursor(Arc::new(WinCursor::new(&cursor)?))) + Ok(CustomCursor(Arc::new(WinCursor::new(&cursor)?))) } fn available_monitors(&self) -> Box> { diff --git a/src/platform_impl/windows/event_loop/runner.rs b/src/platform_impl/windows/event_loop/runner.rs index 2f86701c65..9d28059fd2 100644 --- a/src/platform_impl/windows/event_loop/runner.rs +++ b/src/platform_impl/windows/event_loop/runner.rs @@ -399,7 +399,7 @@ impl Event { } => Event::BufferedScaleFactorChanged( window_id.into_raw() as HWND, scale_factor, - *surface_size_writer.new_surface_size.upgrade().unwrap().lock().unwrap(), + surface_size_writer.surface_size().unwrap(), ), event => event, } diff --git a/src/platform_impl/windows/icon.rs b/src/platform_impl/windows/icon.rs index 8670f9c959..15b9173d21 100644 --- a/src/platform_impl/windows/icon.rs +++ b/src/platform_impl/windows/icon.rs @@ -20,6 +20,8 @@ use crate::error::RequestError; use crate::icon::*; use crate::platform::windows::WinIcon; +pub(crate) const PIXEL_SIZE: usize = mem::size_of::(); + unsafe impl Send for WinIcon {} impl WinIcon { @@ -92,10 +94,10 @@ impl WinIcon { } pub(crate) fn from_rgba(rgba: &RgbaIcon) -> Result { - let pixel_count = rgba.rgba.len() / PIXEL_SIZE; + let pixel_count = rgba.buffer().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) + std::slice::from_raw_parts_mut(rgba.buffer().as_ptr() as *mut Pixel, pixel_count) }; for pixel in pixels { and_mask.push(pixel.a.wrapping_sub(u8::MAX)); // invert alpha channel @@ -105,12 +107,12 @@ impl WinIcon { let handle = unsafe { CreateIcon( ptr::null_mut(), - rgba.width as i32, - rgba.height as i32, + rgba.width() as i32, + rgba.height() as i32, 1, (PIXEL_SIZE * 8) as u8, and_mask.as_ptr(), - rgba.rgba.as_ptr(), + rgba.buffer().as_ptr(), ) }; if !handle.is_null() { @@ -182,11 +184,11 @@ impl CustomCursorProvider for WinCursor { impl WinCursor { pub(crate) fn new(image: &CursorImage) -> Result { - let mut bgra = image.rgba.clone(); + let mut bgra = Vec::from(image.buffer()); bgra.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2)); - let w = image.width as i32; - let h = image.height as i32; + let w = image.width() as i32; + let h = image.height() as i32; unsafe { let hdc_screen = GetDC(ptr::null_mut()); @@ -213,8 +215,8 @@ impl WinCursor { let icon_info = ICONINFO { fIcon: 0, - xHotspot: image.hotspot_x as u32, - yHotspot: image.hotspot_y as u32, + xHotspot: image.hotspot_x() as u32, + yHotspot: image.hotspot_y() as u32, hbmMask: hbm_mask, hbmColor: hbm_color, }; @@ -250,3 +252,12 @@ impl RaiiCursor { self.handle } } + +#[repr(C)] +#[derive(Debug)] +pub(crate) struct Pixel { + pub(crate) r: u8, + pub(crate) g: u8, + pub(crate) b: u8, + pub(crate) a: u8, +} diff --git a/src/platform_impl/windows/mod.rs b/src/platform_impl/windows/mod.rs index ab007776c8..152b1fecc2 100644 --- a/src/platform_impl/windows/mod.rs +++ b/src/platform_impl/windows/mod.rs @@ -1,5 +1,5 @@ use windows_sys::Win32::Foundation::HWND; -use windows_sys::Win32::UI::WindowsAndMessaging::{HMENU, WINDOW_LONG_PTR_INDEX}; +use windows_sys::Win32::UI::WindowsAndMessaging::WINDOW_LONG_PTR_INDEX; pub(crate) use self::event_loop::{EventLoop, PlatformSpecificEventLoopAttributes}; pub(crate) use self::icon::{RaiiIcon, SelectedCursor}; @@ -7,50 +7,6 @@ pub(crate) use self::keyboard::{physicalkey_to_scancode, scancode_to_physicalkey pub(crate) use self::monitor::MonitorHandle; pub(crate) use self::window::Window; use crate::event::DeviceId; -use crate::icon::Icon; -use crate::platform::windows::{BackdropType, Color, CornerPreference}; - -#[derive(Clone, Debug)] -pub struct PlatformSpecificWindowAttributes { - pub owner: Option, - pub menu: Option, - pub taskbar_icon: Option, - pub no_redirection_bitmap: bool, - pub drag_and_drop: bool, - pub skip_taskbar: bool, - pub class_name: String, - pub decoration_shadow: bool, - pub backdrop_type: BackdropType, - pub clip_children: bool, - pub border_color: Option, - pub title_background_color: Option, - pub title_text_color: Option, - pub corner_preference: Option, -} - -impl Default for PlatformSpecificWindowAttributes { - fn default() -> Self { - Self { - owner: None, - menu: None, - taskbar_icon: None, - no_redirection_bitmap: false, - drag_and_drop: true, - skip_taskbar: false, - class_name: "Window Class".to_string(), - decoration_shadow: false, - backdrop_type: BackdropType::default(), - clip_children: true, - border_color: None, - title_background_color: None, - title_text_color: None, - corner_preference: None, - } - } -} - -unsafe impl Send for PlatformSpecificWindowAttributes {} -unsafe impl Sync for PlatformSpecificWindowAttributes {} fn wrap_device_id(id: u32) -> DeviceId { DeviceId::from_raw(id as i64) diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index 1e4179df7f..069c721200 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -50,11 +50,11 @@ impl VideoModeHandle { DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY; assert!(has_flag(native_video_mode.dmFields, REQUIRED_FIELDS)); - let mode = VideoMode { - size: (native_video_mode.dmPelsWidth, native_video_mode.dmPelsHeight).into(), - bit_depth: NonZeroU16::new(native_video_mode.dmBitsPerPel as u16), - refresh_rate_millihertz: NonZeroU32::new(native_video_mode.dmDisplayFrequency * 1000), - }; + let mode = VideoMode::new( + (native_video_mode.dmPelsWidth, native_video_mode.dmPelsHeight).into(), + NonZeroU16::new(native_video_mode.dmBitsPerPel as u16), + NonZeroU32::new(native_video_mode.dmDisplayFrequency * 1000), + ); VideoModeHandle { mode, native_video_mode: Box::new(native_video_mode) } } diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index 2bef7915b6..162c40a6b4 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -24,7 +24,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ WINDOWPLACEMENT, }; -use crate::window::CursorIcon; +use crate::cursor::CursorIcon; pub fn encode_wide(string: impl AsRef) -> Vec { string.as_ref().encode_wide().chain(once(0)).collect() diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 894cb6053b..0c9f1a2dd6 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -55,7 +55,9 @@ use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size} use crate::error::RequestError; use crate::icon::{Icon, RgbaIcon}; use crate::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle, MonitorHandleProvider}; -use crate::platform::windows::{BackdropType, Color, CornerPreference, WinIcon}; +use crate::platform::windows::{ + BackdropType, Color, CornerPreference, WinIcon, WindowAttributesWindows, +}; use crate::platform_impl::platform::dark_mode::try_theme; use crate::platform_impl::platform::definitions::{ CLSID_TaskbarList, IID_ITaskbarList, IID_ITaskbarList2, ITaskbarList, ITaskbarList2, @@ -351,7 +353,7 @@ impl Window { } fn set_icon(&self, mut new_icon: Icon, icon_type: IconType) { - if let Some(icon) = new_icon.0.cast_ref::() { + if let Some(icon) = new_icon.cast_ref::() { let icon = match WinIcon::from_rgba(icon) { Ok(icon) => icon, Err(err) => { @@ -362,7 +364,7 @@ impl Window { new_icon = Icon(Arc::new(icon)); } - if let Some(icon) = new_icon.0.cast_ref::() { + if let Some(icon) = new_icon.cast_ref::() { unsafe { SendMessageW( self.hwnd(), @@ -1137,6 +1139,7 @@ pub(super) struct InitData<'a> { // inputs pub runner: &'a Rc, pub attributes: WindowAttributes, + pub win_attributes: Box, pub window_flags: WindowFlags, // outputs pub window: Option, @@ -1186,7 +1189,7 @@ impl InitData<'_> { } unsafe fn create_window_data(&self, win: &Window) -> event_loop::WindowData { - let file_drop_handler = if self.attributes.platform_specific.drag_and_drop { + let file_drop_handler = if self.win_attributes.drag_and_drop { let ole_init_result = unsafe { OleInitialize(ptr::null_mut()) }; // It is ok if the initialize result is `S_FALSE` because it might happen that // multiple windows are created on the same thread. @@ -1250,7 +1253,7 @@ impl InitData<'_> { let win = self.window.as_mut().expect("failed window creation"); // making the window transparent - if self.attributes.transparent && !self.attributes.platform_specific.no_redirection_bitmap { + if self.attributes.transparent && !self.win_attributes.no_redirection_bitmap { // Empty region for the blur effect, so the window is fully transparent let region = unsafe { CreateRectRgn(0, 0, -1, -1) }; @@ -1267,9 +1270,9 @@ impl InitData<'_> { unsafe { DeleteObject(region) }; } - win.set_skip_taskbar(self.attributes.platform_specific.skip_taskbar); + win.set_skip_taskbar(self.win_attributes.skip_taskbar); win.set_window_icon(self.attributes.window_icon.clone()); - win.set_taskbar_icon(self.attributes.platform_specific.taskbar_icon.clone()); + win.set_taskbar_icon(self.win_attributes.taskbar_icon.clone()); let attributes = self.attributes.clone(); @@ -1306,43 +1309,45 @@ impl InitData<'_> { win.set_outer_position(position); } - win.set_system_backdrop(self.attributes.platform_specific.backdrop_type); + win.set_system_backdrop(self.win_attributes.backdrop_type); - if let Some(color) = self.attributes.platform_specific.border_color { + if let Some(color) = self.win_attributes.border_color { win.set_border_color(color); } - if let Some(color) = self.attributes.platform_specific.title_background_color { + if let Some(color) = self.win_attributes.title_background_color { win.set_title_background_color(color); } - if let Some(color) = self.attributes.platform_specific.title_text_color { + if let Some(color) = self.win_attributes.title_text_color { win.set_title_text_color(color); } - if let Some(corner) = self.attributes.platform_specific.corner_preference { + if let Some(corner) = self.win_attributes.corner_preference { win.set_corner_preference(corner); } } } unsafe fn init( - attributes: WindowAttributes, + mut attributes: WindowAttributes, runner: &Rc, ) -> Result { let title = util::encode_wide(&attributes.title); - let class_name = util::encode_wide(&attributes.platform_specific.class_name); + let win_attributes = attributes + .platform + .take() + .and_then(|attrs| attrs.cast::().ok()) + .unwrap_or_default(); + + let class_name = util::encode_wide(&win_attributes.class_name); unsafe { register_window_class(&class_name) }; let mut window_flags = WindowFlags::empty(); window_flags.set(WindowFlags::MARKER_DECORATIONS, attributes.decorations); - window_flags.set( - WindowFlags::MARKER_UNDECORATED_SHADOW, - attributes.platform_specific.decoration_shadow, - ); + window_flags.set(WindowFlags::MARKER_UNDECORATED_SHADOW, win_attributes.decoration_shadow); window_flags .set(WindowFlags::ALWAYS_ON_TOP, attributes.window_level == WindowLevel::AlwaysOnTop); window_flags .set(WindowFlags::ALWAYS_ON_BOTTOM, attributes.window_level == WindowLevel::AlwaysOnBottom); - window_flags - .set(WindowFlags::NO_BACK_BUFFER, attributes.platform_specific.no_redirection_bitmap); + window_flags.set(WindowFlags::NO_BACK_BUFFER, win_attributes.no_redirection_bitmap); window_flags.set(WindowFlags::MARKER_ACTIVATE, attributes.active); window_flags.set(WindowFlags::TRANSPARENT, attributes.transparent); // WindowFlags::VISIBLE and MAXIMIZED are set down below after the window has been configured. @@ -1350,9 +1355,9 @@ unsafe fn init( // Will be changed later using `window.set_enabled_buttons` but we need to set a default here // so the diffing later can work. window_flags.set(WindowFlags::CLOSABLE, true); - window_flags.set(WindowFlags::CLIP_CHILDREN, attributes.platform_specific.clip_children); + window_flags.set(WindowFlags::CLIP_CHILDREN, win_attributes.clip_children); - let mut fallback_parent = || match attributes.platform_specific.owner { + let mut fallback_parent = || match win_attributes.owner { Some(parent) => { window_flags.set(WindowFlags::POPUP, true); Some(parent) @@ -1363,10 +1368,10 @@ unsafe fn init( }, }; - let parent = match attributes.parent_window.as_ref().map(|handle| handle.0) { + let parent = match attributes.parent_window() { Some(rwh_06::RawWindowHandle::Win32(handle)) => { window_flags.set(WindowFlags::CHILD, true); - if attributes.platform_specific.menu.is_some() { + if win_attributes.menu.is_some() { warn!("Setting a menu on a child window is unsupported"); } Some(handle.hwnd.get() as HWND) @@ -1375,10 +1380,10 @@ unsafe fn init( None => fallback_parent(), }; - let menu = attributes.platform_specific.menu; + let menu = win_attributes.menu; let fullscreen = attributes.fullscreen.clone(); let maximized = attributes.maximized; - let mut initdata = InitData { runner, attributes, window_flags, window: None }; + let mut initdata = InitData { runner, attributes, win_attributes, window_flags, window: None }; let (style, ex_style) = window_flags.to_window_styles(); let handle = unsafe { diff --git a/tests/send_objects.rs b/tests/send_objects.rs index 9bc4db8eb6..48bfa3b94f 100644 --- a/tests/send_objects.rs +++ b/tests/send_objects.rs @@ -25,6 +25,6 @@ fn ids_send() { #[test] fn custom_cursor_send() { - needs_send::(); - needs_send::(); + needs_send::(); + needs_send::(); } diff --git a/tests/serde_objects.rs b/tests/serde_objects.rs index c427373f01..18e796cd31 100644 --- a/tests/serde_objects.rs +++ b/tests/serde_objects.rs @@ -1,10 +1,10 @@ #![cfg(feature = "serde")] use serde::{Deserialize, Serialize}; +use winit::cursor::CursorIcon; use winit::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}; use winit::event::{ElementState, MouseButton, MouseScrollDelta, TouchPhase}; use winit::keyboard::{Key, KeyCode, KeyLocation, ModifiersState, NamedKey, PhysicalKey}; -use winit::window::CursorIcon; #[allow(dead_code)] fn needs_serde>() {} diff --git a/tests/sync_object.rs b/tests/sync_object.rs index dd65e2384c..1c156c0120 100644 --- a/tests/sync_object.rs +++ b/tests/sync_object.rs @@ -18,6 +18,6 @@ fn window_builder_sync() { #[test] fn custom_cursor_sync() { - needs_sync::(); - needs_sync::(); + needs_sync::(); + needs_sync::(); } diff --git a/winit-core/Cargo.toml b/winit-core/Cargo.toml new file mode 100644 index 0000000000..5ea87dd5d6 --- /dev/null +++ b/winit-core/Cargo.toml @@ -0,0 +1,33 @@ +[package] +authors = ["The winit contributors", "Kirill Chibisov "] +categories = ["gui"] +description = "winit core API." +documentation = "https://docs.rs/winit-core" +edition.workspace = true +keywords = ["windowing"] +license.workspace = true +name = "winit-core" +readme = "README.md" +repository.workspace = true +rust-version.workspace = true +version = "0.0.0" + +[features] +serde = ["dep:serde", "cursor-icon/serde", "smol_str/serde", "dpi/serde", "bitflags/serde"] + +[dependencies] +bitflags = "2" +cursor-icon = "1.1.0" +dpi = { version = "0.1.1", path = "../dpi" } +rwh_06 = { package = "raw-window-handle", version = "0.6", features = ["std"] } +serde = { workspace = true, optional = true } +smol_str = "0.3" + +[target.'cfg(target_family = "wasm")'.dependencies] +web-time = "1" + +[build-dependencies] +cfg_aliases = "0.2.1" + +[dev-dependencies] +winit = { path = ".." } diff --git a/winit-core/LICENSE b/winit-core/LICENSE new file mode 120000 index 0000000000..ea5b60640b --- /dev/null +++ b/winit-core/LICENSE @@ -0,0 +1 @@ +../LICENSE \ No newline at end of file diff --git a/winit-core/README.md b/winit-core/README.md new file mode 120000 index 0000000000..32d46ee883 --- /dev/null +++ b/winit-core/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/winit-core/build.rs b/winit-core/build.rs new file mode 100644 index 0000000000..915ec231b8 --- /dev/null +++ b/winit-core/build.rs @@ -0,0 +1,11 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // The script doesn't depend on our code. + println!("cargo:rerun-if-changed=build.rs"); + + // Setup cfg aliases. + cfg_aliases! { + web_platform: { all(target_family = "wasm", target_os = "unknown") }, + } +} diff --git a/winit-core/src/application/macos.rs b/winit-core/src/application/macos.rs new file mode 100644 index 0000000000..d18b4090e5 --- /dev/null +++ b/winit-core/src/application/macos.rs @@ -0,0 +1,52 @@ +use crate::application::ApplicationHandler; +use crate::event_loop::ActiveEventLoop; +use crate::window::WindowId; + +/// Additional events on [`ApplicationHandler`] that are specific to macOS. +/// +/// This can be registered with [`ApplicationHandler::macos_handler`]. +pub trait ApplicationHandlerExtMacOS: ApplicationHandler { + /// The system interpreted a keypress as a standard key binding command. + /// + /// Examples include inserting tabs and newlines, or moving the insertion point, see + /// [`NSStandardKeyBindingResponding`] for the full list of key bindings. They are often text + /// editing related. + /// + /// This corresponds to the [`doCommandBySelector:`] method on `NSTextInputClient`. + /// + /// The `action` parameter contains the string representation of the selector. Examples include + /// `"insertBacktab:"`, `"indent:"` and `"noop:"`. + /// + /// # Example + /// + /// ```ignore + /// impl ApplicationHandlerExtMacOS for App { + /// fn standard_key_binding( + /// &mut self, + /// event_loop: &dyn ActiveEventLoop, + /// window_id: WindowId, + /// action: &str, + /// ) { + /// match action { + /// "moveBackward:" => self.cursor.position -= 1, + /// "moveForward:" => self.cursor.position += 1, + /// _ => {} // Ignore other actions + /// } + /// } + /// } + /// ``` + /// + /// [`NSStandardKeyBindingResponding`]: https://developer.apple.com/documentation/appkit/nsstandardkeybindingresponding?language=objc + /// [`doCommandBySelector:`]: https://developer.apple.com/documentation/appkit/nstextinputclient/1438256-docommandbyselector?language=objc + #[doc(alias = "doCommandBySelector:")] + fn standard_key_binding( + &mut self, + event_loop: &dyn ActiveEventLoop, + window_id: WindowId, + action: &str, + ) { + let _ = event_loop; + let _ = window_id; + let _ = action; + } +} diff --git a/src/application.rs b/winit-core/src/application/mod.rs similarity index 94% rename from src/application.rs rename to winit-core/src/application/mod.rs index 0020ca9929..b9fd34bff1 100644 --- a/src/application.rs +++ b/winit-core/src/application/mod.rs @@ -2,21 +2,11 @@ use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent}; use crate::event_loop::ActiveEventLoop; -#[cfg(macos_platform)] -use crate::platform::macos::ApplicationHandlerExtMacOS; use crate::window::WindowId; +pub mod macos; + /// The handler of application-level events. -/// -/// See [the top-level docs] for example usage, and [`EventLoop::run_app`] for an overview of when -/// events are delivered. -/// -/// This is [dropped] when the event loop is shut down. Note that this only works if you're passing -/// the entire state to [`EventLoop::run_app`] (passing `&mut app` won't work). -/// -/// [the top-level docs]: crate -/// [`EventLoop::run_app`]: crate::event_loop::EventLoop::run_app -/// [dropped]: std::ops::Drop pub trait ApplicationHandler { /// Emitted when new events arrive from the OS to be processed. /// @@ -135,8 +125,9 @@ pub trait ApplicationHandler { /// use std::thread; /// use std::time::Duration; /// - /// use winit::application::ApplicationHandler; - /// use winit::event_loop::{ActiveEventLoop, EventLoop}; + /// use winit::event_loop::EventLoop; + /// use winit_core::application::ApplicationHandler; + /// use winit_core::event_loop::ActiveEventLoop; /// /// struct MyApp { /// receiver: mpsc::Receiver, @@ -210,9 +201,7 @@ pub trait ApplicationHandler { /// Emitted when the OS sends an event to a device. /// - /// For this to be called, it must be enabled with [`EventLoop::listen_device_events`]. - /// - /// [`EventLoop::listen_device_events`]: crate::event_loop::EventLoop::listen_device_events + /// Whether device events are delivered depends on the backend in use. fn device_event( &mut self, event_loop: &dyn ActiveEventLoop, @@ -350,9 +339,8 @@ pub trait ApplicationHandler { /// The macOS-specific handler. /// /// The return value from this should not change at runtime. - #[cfg(macos_platform)] #[inline(always)] - fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { + fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { None } } @@ -419,9 +407,8 @@ impl ApplicationHandler for &mut A { (**self).memory_warning(event_loop); } - #[cfg(macos_platform)] #[inline] - fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { + fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { (**self).macos_handler() } } @@ -488,9 +475,8 @@ impl ApplicationHandler for Box { (**self).memory_warning(event_loop); } - #[cfg(macos_platform)] #[inline] - fn macos_handler(&mut self) -> Option<&mut dyn ApplicationHandlerExtMacOS> { + fn macos_handler(&mut self) -> Option<&mut dyn macos::ApplicationHandlerExtMacOS> { (**self).macos_handler() } } diff --git a/src/utils.rs b/winit-core/src/as_any.rs similarity index 98% rename from src/utils.rs rename to winit-core/src/as_any.rs index 3ef0b9f3e3..c22c3621d0 100644 --- a/src/utils.rs +++ b/winit-core/src/as_any.rs @@ -31,6 +31,7 @@ impl AsAny for T { } } +#[macro_export] macro_rules! impl_dyn_casting { ($trait:ident) => { impl dyn $trait + '_ { @@ -67,4 +68,4 @@ macro_rules! impl_dyn_casting { }; } -pub(crate) use impl_dyn_casting; +pub use impl_dyn_casting; diff --git a/src/cursor.rs b/winit-core/src/cursor.rs similarity index 90% rename from src/cursor.rs rename to winit-core/src/cursor.rs index 69dba15f99..9acebd4095 100644 --- a/src/cursor.rs +++ b/winit-core/src/cursor.rs @@ -5,9 +5,10 @@ use std::ops::Deref; use std::sync::Arc; use std::time::Duration; -use cursor_icon::CursorIcon; +#[doc(inline)] +pub use cursor_icon::CursorIcon; -use crate::utils::{impl_dyn_casting, AsAny}; +use crate::as_any::{impl_dyn_casting, AsAny}; /// The maximum width and height for a cursor when using [`CustomCursorSource::from_rgba`]. pub const MAX_CURSOR_SIZE: u16 = 2048; @@ -50,10 +51,10 @@ impl From for Cursor { /// # Example /// /// ```no_run -/// # use winit::event_loop::ActiveEventLoop; -/// # use winit::window::Window; +/// # use winit_core::event_loop::ActiveEventLoop; +/// # use winit_core::window::Window; /// # fn scope(event_loop: &dyn ActiveEventLoop, window: &dyn Window) { -/// use winit::window::CustomCursorSource; +/// use winit_core::cursor::CustomCursorSource; /// /// let w = 10; /// let h = 10; @@ -75,7 +76,7 @@ impl From for Cursor { /// # } /// ``` #[derive(Clone, Debug)] -pub struct CustomCursor(pub(crate) Arc); +pub struct CustomCursor(pub Arc); pub trait CustomCursorProvider: AsAny + fmt::Debug + Send + Sync { /// Whether a cursor was backed by animation. @@ -235,7 +236,6 @@ impl fmt::Display for BadAnimation { impl Error for BadAnimation {} #[derive(Debug, Clone, Eq, Hash, PartialEq)] -#[allow(dead_code)] pub struct CursorImage { pub(crate) rgba: Vec, pub(crate) width: u16, @@ -277,6 +277,30 @@ impl CursorImage { Ok(CursorImage { rgba, width, height, hotspot_x, hotspot_y }) } + + pub fn buffer(&self) -> &[u8] { + self.rgba.as_slice() + } + + pub fn buffer_mut(&mut self) -> &mut [u8] { + self.rgba.as_mut_slice() + } + + pub fn width(&self) -> u16 { + self.width + } + + pub fn height(&self) -> u16 { + self.height + } + + pub fn hotspot_x(&self) -> u16 { + self.hotspot_x + } + + pub fn hotspot_y(&self) -> u16 { + self.hotspot_y + } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -297,4 +321,16 @@ impl CursorAnimation { Ok(Self { duration, cursors }) } + + pub fn duration(&self) -> Duration { + self.duration + } + + pub fn cursors(&self) -> &[CustomCursor] { + self.cursors.as_slice() + } + + pub fn into_raw(self) -> (Duration, Vec) { + (self.duration, self.cursors) + } } diff --git a/src/error.rs b/winit-core/src/error.rs similarity index 97% rename from src/error.rs rename to winit-core/src/error.rs index 697b37dd15..39635639ac 100644 --- a/src/error.rs +++ b/winit-core/src/error.rs @@ -100,7 +100,7 @@ pub struct NotSupportedError { } impl NotSupportedError { - pub(crate) fn new(reason: &'static str) -> Self { + pub fn new(reason: &'static str) -> Self { Self { reason } } } @@ -121,8 +121,7 @@ pub struct OsError { } impl OsError { - #[allow(dead_code)] - pub(crate) fn new( + pub fn new( line: u32, file: &'static str, error: impl Into>, diff --git a/src/event.rs b/winit-core/src/event.rs similarity index 97% rename from src/event.rs rename to winit-core/src/event.rs index c92ff1a676..40e46dff68 100644 --- a/src/event.rs +++ b/winit-core/src/event.rs @@ -4,13 +4,13 @@ use std::sync::{Mutex, Weak}; #[cfg(not(web_platform))] use std::time::Instant; +use dpi::{PhysicalPosition, PhysicalSize}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; use smol_str::SmolStr; #[cfg(web_platform)] use web_time::Instant; -use crate::dpi::{PhysicalPosition, PhysicalSize}; use crate::error::RequestError; use crate::event_loop::AsyncRequestSerial; use crate::keyboard::{self, ModifiersKeyState, ModifiersKeys, ModifiersState}; @@ -46,10 +46,6 @@ pub enum StartCause { #[derive(Debug, Clone, PartialEq)] pub enum WindowEvent { /// The activation token was delivered back and now could be used. - #[cfg_attr(not(any(x11_platform, wayland_platform)), allow(rustdoc::broken_intra_doc_links))] - /// Delivered in response to [`request_activation_token`]. - /// - /// [`request_activation_token`]: crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token ActivationTokenDone { serial: AsyncRequestSerial, token: ActivationToken }, /// The size of the window's surface has changed. @@ -563,16 +559,14 @@ impl DeviceId { /// Convert the [`DeviceId`] into the underlying integer. /// /// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic. - #[allow(dead_code)] - pub(crate) const fn into_raw(self) -> i64 { + pub const fn into_raw(self) -> i64 { self.0 } /// Construct a [`DeviceId`] from the underlying integer. /// /// This should only be called with integers returned from [`DeviceId::into_raw`]. - #[allow(dead_code)] - pub(crate) const fn from_raw(id: i64) -> Self { + pub const fn from_raw(id: i64) -> Self { Self(id) } } @@ -588,16 +582,14 @@ impl FingerId { /// Convert the [`FingerId`] into the underlying integer. /// /// This is useful if you need to pass the ID across an FFI boundary, or store it in an atomic. - #[allow(dead_code)] - pub(crate) const fn into_raw(self) -> usize { + pub const fn into_raw(self) -> usize { self.0 } /// Construct a [`FingerId`] from the underlying integer. /// /// This should only be called with integers returned from [`FingerId::into_raw`]. - #[allow(dead_code)] - pub(crate) const fn from_raw(id: usize) -> Self { + pub const fn from_raw(id: usize) -> Self { Self(id) } } @@ -622,17 +614,8 @@ pub enum DeviceEvent { /// ## Platform-specific /// /// **Web:** Only returns raw data, not OS accelerated, if [`CursorGrabMode::Locked`] is used - /// and browser support is available, see - #[cfg_attr( - web_platform, - doc = "[`ActiveEventLoopExtWeb::is_cursor_lock_raw()`][crate::platform::web::ActiveEventLoopExtWeb::is_cursor_lock_raw()]." - )] - #[cfg_attr( - not(web_platform), - doc = "`ActiveEventLoopExtWeb::is_cursor_lock_raw()`." - )] - /// - #[rustfmt::skip] + /// and browser support is available. + /// /// [`CursorGrabMode::Locked`]: crate::window::CursorGrabMode::Locked PointerMotion { /// (x, y) change in position in unspecified units. @@ -769,8 +752,8 @@ pub struct KeyEvent { /// done by ignoring events where this property is set. /// /// ```no_run - /// use winit::event::{ElementState, KeyEvent, WindowEvent}; - /// use winit::keyboard::{KeyCode, PhysicalKey}; + /// use winit_core::event::{ElementState, KeyEvent, WindowEvent}; + /// use winit_core::keyboard::{KeyCode, PhysicalKey}; /// # let window_event = WindowEvent::RedrawRequested; // To make the example compile /// match window_event { /// WindowEvent::KeyboardInput { @@ -832,6 +815,11 @@ pub struct Modifiers { } impl Modifiers { + /// Create a new modifiers from state and pressed mods. + pub fn new(state: ModifiersState, pressed_mods: ModifiersKeys) -> Self { + Self { state, pressed_mods } + } + /// The state of the modifiers. pub fn state(&self) -> ModifiersState { self.state @@ -1091,8 +1079,7 @@ pub struct SurfaceSizeWriter { } impl SurfaceSizeWriter { - #[cfg(not(orbital_platform))] - pub(crate) fn new(new_surface_size: Weak>>) -> Self { + pub fn new(new_surface_size: Weak>>) -> Self { Self { new_surface_size } } @@ -1108,6 +1095,15 @@ impl SurfaceSizeWriter { Err(RequestError::Ignored) } } + + /// Get the currently stashed surface size. + pub fn surface_size(&self) -> Result, RequestError> { + if let Some(inner) = self.new_surface_size.upgrade() { + Ok(*inner.lock().unwrap()) + } else { + Err(RequestError::Ignored) + } + } } impl PartialEq for SurfaceSizeWriter { @@ -1122,7 +1118,8 @@ impl Eq for SurfaceSizeWriter {} mod tests { use std::collections::{BTreeSet, HashSet}; - use crate::dpi::PhysicalPosition; + use dpi::PhysicalPosition; + use crate::event; macro_rules! foreach_event { diff --git a/winit-core/src/event_loop.rs b/winit-core/src/event_loop.rs new file mode 100644 index 0000000000..ca82ff62b1 --- /dev/null +++ b/winit-core/src/event_loop.rs @@ -0,0 +1,288 @@ +use std::fmt::{self, Debug}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::Arc; +#[cfg(not(web_platform))] +use std::time::{Duration, Instant}; + +use rwh_06::{DisplayHandle, HandleError, HasDisplayHandle}; +#[cfg(web_platform)] +use web_time::{Duration, Instant}; + +use crate::as_any::AsAny; +use crate::cursor::{CustomCursor, CustomCursorSource}; +use crate::error::RequestError; +use crate::monitor::MonitorHandle; +use crate::window::{Theme, Window, WindowAttributes}; + +pub trait ActiveEventLoop: AsAny + fmt::Debug { + /// Creates an [`EventLoopProxy`] that can be used to dispatch user events + /// to the main event loop, possibly from another thread. + fn create_proxy(&self) -> EventLoopProxy; + + /// Create the window. + /// + /// Possible causes of error include denied permission, incompatible system, and lack of memory. + /// + /// ## Platform-specific + /// + /// - **Web:** The window is created but not inserted into the Web page automatically. Please + /// see the Web platform module for more information. + fn create_window( + &self, + window_attributes: WindowAttributes, + ) -> Result, RequestError>; + + /// Create custom cursor. + /// + /// ## Platform-specific + /// + /// **iOS / Android / Orbital:** Unsupported. + fn create_custom_cursor( + &self, + custom_cursor: CustomCursorSource, + ) -> Result; + + /// Returns the list of all the monitors available on the system. + /// + /// ## Platform-specific + /// + /// **Web:** Only returns the current monitor without `detailed monitor permissions`. + fn available_monitors(&self) -> Box>; + + /// Returns the primary monitor of the system. + /// + /// Returns `None` if it can't identify any monitor as a primary one. + /// + /// ## Platform-specific + /// + /// - **Wayland:** Always returns `None`. + /// - **Web:** Always returns `None` without `detailed monitor permissions`. + fn primary_monitor(&self) -> Option; + + /// Change if or when [`DeviceEvent`]s are captured. + /// + /// Since the [`DeviceEvent`] capture can lead to high CPU usage for unfocused windows, winit + /// will ignore them by default for unfocused windows on Linux/BSD. This method allows changing + /// this at runtime to explicitly capture them again. + /// + /// ## Platform-specific + /// + /// - **Wayland / macOS / iOS / Android / Orbital:** Unsupported. + /// + /// [`DeviceEvent`]: crate::event::DeviceEvent + fn listen_device_events(&self, allowed: DeviceEvents); + + /// Returns the current system theme. + /// + /// Returns `None` if it cannot be determined on the current platform. + /// + /// ## Platform-specific + /// + /// - **iOS / Android / Wayland / x11 / Orbital:** Unsupported. + fn system_theme(&self) -> Option; + + /// Sets the [`ControlFlow`]. + fn set_control_flow(&self, control_flow: ControlFlow); + + /// Gets the current [`ControlFlow`]. + fn control_flow(&self) -> ControlFlow; + + /// Stop the event loop. + /// + /// ## Platform-specific + /// + /// ### iOS + /// + /// It is not possible to programmatically exit/quit an application on iOS, so this function is + /// a no-op there. See also [this technical Q&A][qa1561]. + /// + /// [qa1561]: https://developer.apple.com/library/archive/qa/qa1561/_index.html + fn exit(&self); + + /// Returns whether the [`ActiveEventLoop`] is about to stop. + /// + /// Set by [`exit()`][Self::exit]. + fn exiting(&self) -> bool; + + /// Gets a persistent reference to the underlying platform display. + /// + /// See the [`OwnedDisplayHandle`] type for more information. + fn owned_display_handle(&self) -> OwnedDisplayHandle; + + /// Get the raw-window-handle handle. + fn rwh_06_handle(&self) -> &dyn HasDisplayHandle; +} + +impl HasDisplayHandle for dyn ActiveEventLoop + '_ { + fn display_handle(&self) -> Result, HandleError> { + self.rwh_06_handle().display_handle() + } +} + +impl_dyn_casting!(ActiveEventLoop); + +/// Control the [`ActiveEventLoop`], possibly from a different thread, without referencing it +/// directly. +#[derive(Clone, Debug)] +pub struct EventLoopProxy { + pub(crate) proxy: Arc, +} + +impl EventLoopProxy { + /// Wake up the [`ActiveEventLoop`], resulting in [`ApplicationHandler::proxy_wake_up()`] being + /// called. + /// + /// Calls to this method are coalesced into a single call to [`proxy_wake_up`], see the + /// documentation on that for details. + /// + /// If the event loop is no longer running, this is a no-op. + /// + /// [`proxy_wake_up`]: crate::application::ApplicationHandler::proxy_wake_up + /// [`ApplicationHandler::proxy_wake_up()`]: crate::application::ApplicationHandler::proxy_wake_up + /// + /// # Platform-specific + /// + /// - **Windows**: The wake-up may be ignored under high contention, see [#3687]. + /// + /// [#3687]: https://github.com/rust-windowing/winit/pull/3687 + pub fn wake_up(&self) { + self.proxy.wake_up(); + } + + pub fn new(proxy: Arc) -> Self { + Self { proxy } + } +} + +pub trait EventLoopProxyProvider: Send + Sync + Debug { + /// See [`EventLoopProxy::wake_up`] for details. + fn wake_up(&self); +} + +/// A proxy for the underlying display handle. +/// +/// The purpose of this type is to provide a cheaply cloneable handle to the underlying +/// display handle. This is often used by graphics APIs to connect to the underlying APIs. +/// It is difficult to keep a handle to the underlying event loop type or the [`ActiveEventLoop`] +/// type. In contrast, this type involves no lifetimes and can be persisted for as long as +/// needed. +/// +/// For all platforms, this is one of the following: +/// +/// - A zero-sized type that is likely optimized out. +/// - A reference-counted pointer to the underlying type. +#[derive(Clone)] +pub struct OwnedDisplayHandle { + pub(crate) handle: Arc, +} + +impl OwnedDisplayHandle { + pub fn new(handle: Arc) -> Self { + Self { handle } + } +} + +impl HasDisplayHandle for OwnedDisplayHandle { + fn display_handle(&self) -> Result, HandleError> { + self.handle.display_handle() + } +} + +impl fmt::Debug for OwnedDisplayHandle { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OwnedDisplayHandle").finish_non_exhaustive() + } +} + +impl PartialEq for OwnedDisplayHandle { + fn eq(&self, other: &Self) -> bool { + match (self.display_handle(), other.display_handle()) { + (Ok(lhs), Ok(rhs)) => lhs == rhs, + _ => false, + } + } +} + +impl Eq for OwnedDisplayHandle {} + +/// Set through [`ActiveEventLoop::set_control_flow()`]. +/// +/// Indicates the desired behavior of the event loop after [`about_to_wait`] is called. +/// +/// Defaults to [`Wait`]. +/// +/// [`Wait`]: Self::Wait +/// [`about_to_wait`]: crate::application::ApplicationHandler::about_to_wait +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] +pub enum ControlFlow { + /// When the current loop iteration finishes, immediately begin a new iteration regardless of + /// whether or not new events are available to process. + Poll, + + /// When the current loop iteration finishes, suspend the thread until another event arrives. + #[default] + Wait, + + /// When the current loop iteration finishes, suspend the thread until either another event + /// arrives or the given time is reached. + /// + /// Useful for implementing efficient timers. Applications which want to render at the + /// display's native refresh rate should instead use [`Poll`] and the VSync functionality + /// of a graphics API to reduce odds of missed frames. + /// + /// [`Poll`]: Self::Poll + WaitUntil(Instant), +} + +impl ControlFlow { + /// Creates a [`ControlFlow`] that waits until a timeout has expired. + /// + /// In most cases, this is set to [`WaitUntil`]. However, if the timeout overflows, it is + /// instead set to [`Wait`]. + /// + /// [`WaitUntil`]: Self::WaitUntil + /// [`Wait`]: Self::Wait + pub fn wait_duration(timeout: Duration) -> Self { + match Instant::now().checked_add(timeout) { + Some(instant) => Self::WaitUntil(instant), + None => Self::Wait, + } + } +} + +/// Control when device events are captured. +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum DeviceEvents { + /// Report device events regardless of window focus. + Always, + /// Only capture device events while the window is focused. + #[default] + WhenFocused, + /// Never capture device events. + Never, +} + +/// A unique identifier of the winit's async request. +/// +/// This could be used to identify the async request once it's done +/// and a specific action must be taken. +/// +/// One of the handling scenarios could be to maintain a working list +/// containing [`AsyncRequestSerial`] and some closure associated with it. +/// Then once event is arriving the working list is being traversed and a job +/// executed and removed from the list. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct AsyncRequestSerial { + serial: usize, +} + +impl AsyncRequestSerial { + pub fn get() -> Self { + static CURRENT_SERIAL: AtomicUsize = AtomicUsize::new(0); + // NOTE: We rely on wrap around here, while the user may just request + // in the loop usize::MAX times that's issue is considered on them. + let serial = CURRENT_SERIAL.fetch_add(1, Ordering::Relaxed); + Self { serial } + } +} diff --git a/src/icon.rs b/winit-core/src/icon.rs similarity index 90% rename from src/icon.rs rename to winit-core/src/icon.rs index 9aa3ec724c..ba5f8caa60 100644 --- a/src/icon.rs +++ b/winit-core/src/icon.rs @@ -1,30 +1,29 @@ use std::error::Error; +use std::ops::Deref; use std::sync::Arc; use std::{fmt, io, mem}; -use crate::utils::{impl_dyn_casting, AsAny}; +use crate::as_any::{impl_dyn_casting, AsAny}; -pub(crate) const PIXEL_SIZE: usize = mem::size_of::(); +pub(crate) const PIXEL_SIZE: usize = mem::size_of::(); /// An icon used for the window titlebar, taskbar, etc. -#[allow(dead_code)] #[derive(Debug, Clone)] -pub struct Icon(pub(crate) Arc); +pub struct Icon(pub Arc); // TODO remove that once split. pub trait IconProvider: AsAny + fmt::Debug + Send + Sync {} -impl_dyn_casting!(IconProvider); +impl Deref for Icon { + type Target = dyn IconProvider; -#[repr(C)] -#[derive(Debug)] -pub(crate) struct Pixel { - pub(crate) r: u8, - pub(crate) g: u8, - pub(crate) b: u8, - pub(crate) a: u8, + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } } +impl_dyn_casting!(IconProvider); + #[derive(Debug)] /// An error produced when using [`RgbaIcon::new`] with invalid arguments. pub enum BadIcon { diff --git a/src/keyboard.rs b/winit-core/src/keyboard.rs similarity index 99% rename from src/keyboard.rs rename to winit-core/src/keyboard.rs index f1838d9ff9..216eb8fa6d 100644 --- a/src/keyboard.rs +++ b/winit-core/src/keyboard.rs @@ -1564,7 +1564,7 @@ impl NamedKey { /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] /// # fn main() { - /// use winit::keyboard::NamedKey; + /// use winit_core::keyboard::NamedKey; /// /// assert_eq!(NamedKey::Enter.to_text(), Some("\r")); /// assert_eq!(NamedKey::F20.to_text(), None); @@ -1591,7 +1591,7 @@ impl Key { /// # wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); /// # #[cfg_attr(web_platform, wasm_bindgen_test::wasm_bindgen_test)] /// # fn main() { - /// use winit::keyboard::{Key, NamedKey}; + /// use winit_core::keyboard::{Key, NamedKey}; /// /// assert_eq!(Key::Character("a".into()).to_text(), Some("a")); /// assert_eq!(Key::Named(NamedKey::Enter).to_text(), Some("\r")); @@ -1750,7 +1750,7 @@ pub enum ModifiersKeyState { bitflags! { #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] - pub(crate) struct ModifiersKeys: u8 { + pub struct ModifiersKeys: u8 { const LSHIFT = 0b0000_0001; const RSHIFT = 0b0000_0010; const LCONTROL = 0b0000_0100; diff --git a/winit-core/src/lib.rs b/winit-core/src/lib.rs new file mode 100644 index 0000000000..358f0c3a6f --- /dev/null +++ b/winit-core/src/lib.rs @@ -0,0 +1,21 @@ +//! # Core types for Winit +//! +//! Platform-agnostic types and traits useful when implementing Winit backends, +//! or otherwise interfacing with Winit from library code. +//! +//! See the [`winit`] crate for the full user-facing API. +//! +//! [`winit`]: https://docs.rs/winit + +#[macro_use] +pub mod as_any; +pub mod cursor; +#[macro_use] +pub mod error; +pub mod application; +pub mod event; +pub mod event_loop; +pub mod icon; +pub mod keyboard; +pub mod monitor; +pub mod window; diff --git a/src/monitor.rs b/winit-core/src/monitor.rs similarity index 75% rename from src/monitor.rs rename to winit-core/src/monitor.rs index 88dc84a6bc..6fff6c9450 100644 --- a/src/monitor.rs +++ b/winit-core/src/monitor.rs @@ -11,8 +11,9 @@ use std::num::{NonZeroU16, NonZeroU32}; use std::ops::Deref; use std::sync::Arc; -use crate::dpi::{PhysicalPosition, PhysicalSize}; -use crate::utils::{impl_dyn_casting, AsAny}; +use dpi::{PhysicalPosition, PhysicalSize}; + +use crate::as_any::{impl_dyn_casting, AsAny}; /// Handle to a monitor. /// @@ -28,24 +29,13 @@ use crate::utils::{impl_dyn_casting, AsAny}; /// /// ## Platform-specific /// -/// **Web:** A [`MonitorHandle`] created without -#[cfg_attr( - web_platform, - doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." -)] -#[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] +/// **Web:** A [`MonitorHandle`] created without `detailed monitor permissions` /// will always represent the current monitor the browser window is in instead of a specific -/// monitor. See -#[cfg_attr( - web_platform, - doc = "[`MonitorHandleExtWeb::is_detailed()`][crate::platform::web::MonitorHandleExtWeb::is_detailed]" -)] -#[cfg_attr(not(web_platform), doc = "`MonitorHandleExtWeb::is_detailed()`")] -/// to check. +/// monitor. /// /// [`Window`]: crate::window::Window #[derive(Debug, Clone)] -pub struct MonitorHandle(pub(crate) Arc); +pub struct MonitorHandle(pub Arc); impl Deref for MonitorHandle { type Target = dyn MonitorHandleProvider; @@ -87,14 +77,10 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync { /// /// Returns `None` if the monitor doesn't exist anymore or the name couldn't be obtained. /// + /// /// ## Platform-specific /// - /// **Web:** Always returns [`None`] without - #[cfg_attr( - web_platform, - doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] + /// **Web:** Always returns [`None`] without `detailed monitor permissions`. fn name(&self) -> Option>; /// Returns the top-left corner position of the monitor in desktop coordinates. @@ -105,12 +91,7 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync { /// /// ## Platform-specific /// - /// **Web:** Always returns [`None`] without - #[cfg_attr( - web_platform, - doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = "detailed monitor permissions.")] + /// **Web:** Always returns [`None`] without `detailed monitor permissions`. fn position(&self) -> Option>; /// Returns the scale factor of the underlying monitor. To map logical pixels to physical @@ -118,19 +99,9 @@ pub trait MonitorHandleProvider: AsAny + fmt::Debug + Send + Sync { /// /// See the [`dpi`] module for more information. /// - /// ## Platform-specific - /// - /// - **X11:** Can be overridden using the `WINIT_X11_SCALE_FACTOR` environment variable. /// - **Wayland:** May differ from [`Window::scale_factor`]. - /// - **Android:** Always returns 1.0. - /// - **Web:** Always returns `0.0` without - #[cfg_attr( - web_platform, - doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")] + /// - **Web:** Always returns `0.0` without `detailed_monitor_permissions`. /// - #[rustfmt::skip] /// [`Window::scale_factor`]: crate::window::Window::scale_factor fn scale_factor(&self) -> f64; @@ -161,6 +132,14 @@ pub struct VideoMode { } impl VideoMode { + pub fn new( + size: PhysicalSize, + bit_depth: Option, + refresh_rate_millihertz: Option, + ) -> Self { + Self { size, bit_depth, refresh_rate_millihertz } + } + /// Returns the resolution of this video mode. This **must not** be used to create your /// rendering surface. Use [`Window::surface_size()`] instead. /// diff --git a/src/window.rs b/winit-core/src/window.rs similarity index 93% rename from src/window.rs rename to winit-core/src/window.rs index dde0167faf..742ae2db9b 100644 --- a/src/window.rs +++ b/winit-core/src/window.rs @@ -1,18 +1,16 @@ -//! The [`Window`] struct and associated types. +//! The [`Window`] trait and associated types. use std::fmt; -#[doc(inline)] -pub use cursor_icon::{CursorIcon, ParseError as CursorIconParseError}; +use cursor_icon::CursorIcon; +use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; -pub use crate::cursor::{BadImage, Cursor, CustomCursor, CustomCursorSource, MAX_CURSOR_SIZE}; -use crate::dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size}; +use crate::as_any::AsAny; +use crate::cursor::Cursor; use crate::error::RequestError; -pub use crate::icon::{BadIcon, Icon}; +use crate::icon::Icon; use crate::monitor::{Fullscreen, MonitorHandle}; -use crate::platform_impl::PlatformSpecificWindowAttributes; -use crate::utils::{impl_dyn_casting, AsAny}; /// Identifier of a window. Unique for each window. /// @@ -46,7 +44,8 @@ impl fmt::Debug for WindowId { } /// Attributes used when creating a window. -#[derive(Debug, Clone)] +#[derive(Debug)] +#[non_exhaustive] pub struct WindowAttributes { pub surface_size: Option, pub min_surface_size: Option, @@ -69,53 +68,9 @@ pub struct WindowAttributes { pub cursor: Cursor, pub(crate) parent_window: Option, pub fullscreen: Option, - // Platform-specific configuration. - #[allow(dead_code)] - pub(crate) platform_specific: PlatformSpecificWindowAttributes, -} - -impl Default for WindowAttributes { - #[inline] - fn default() -> WindowAttributes { - WindowAttributes { - surface_size: None, - min_surface_size: None, - max_surface_size: None, - surface_resize_increments: None, - position: None, - resizable: true, - enabled_buttons: WindowButtons::all(), - title: "winit window".to_owned(), - maximized: false, - fullscreen: None, - visible: true, - transparent: false, - blur: false, - decorations: true, - window_level: Default::default(), - window_icon: None, - preferred_theme: None, - content_protected: false, - cursor: Cursor::default(), - parent_window: None, - active: true, - platform_specific: Default::default(), - } - } + pub platform: Option>, } -/// Wrapper for [`rwh_06::RawWindowHandle`] for [`WindowAttributes::parent_window`]. -/// -/// # Safety -/// -/// The user has to account for that when using [`WindowAttributes::with_parent_window()`], -/// which is `unsafe`. -#[derive(Debug, Clone, PartialEq)] -pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle); - -unsafe impl Send for SendSyncRawWindowHandle {} -unsafe impl Sync for SendSyncRawWindowHandle {} - impl WindowAttributes { /// Get the parent window stored on the attributes. pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> { @@ -411,8 +366,94 @@ impl WindowAttributes { self.parent_window = parent_window.map(SendSyncRawWindowHandle); self } + + /// Set the platform specific opaque attribute object. + /// + /// The interpretation will depend on the underlying backend that will be used. + #[inline] + pub fn with_platform_attributes(mut self, platform: Box) -> Self { + self.platform = Some(platform); + self + } } +impl Clone for WindowAttributes { + fn clone(&self) -> Self { + Self { + surface_size: self.surface_size, + min_surface_size: self.min_surface_size, + max_surface_size: self.max_surface_size, + surface_resize_increments: self.surface_resize_increments, + position: self.position, + resizable: self.resizable, + enabled_buttons: self.enabled_buttons, + title: self.title.clone(), + maximized: self.maximized, + visible: self.visible, + transparent: self.transparent, + blur: self.blur, + decorations: self.decorations, + window_icon: self.window_icon.clone(), + preferred_theme: self.preferred_theme, + content_protected: self.content_protected, + window_level: self.window_level, + active: self.active, + cursor: self.cursor.clone(), + parent_window: self.parent_window.clone(), + fullscreen: self.fullscreen.clone(), + platform: self.platform.as_ref().map(|platform| platform.box_clone()), + } + } +} + +impl Default for WindowAttributes { + #[inline] + fn default() -> WindowAttributes { + WindowAttributes { + enabled_buttons: WindowButtons::all(), + title: String::from("winit window"), + decorations: true, + resizable: true, + visible: true, + active: true, + surface_resize_increments: Default::default(), + content_protected: Default::default(), + min_surface_size: Default::default(), + max_surface_size: Default::default(), + preferred_theme: Default::default(), + parent_window: Default::default(), + surface_size: Default::default(), + window_level: Default::default(), + window_icon: Default::default(), + transparent: Default::default(), + fullscreen: Default::default(), + maximized: Default::default(), + position: Default::default(), + platform: Default::default(), + cursor: Cursor::default(), + blur: Default::default(), + } + } +} + +/// Wrapper for [`rwh_06::RawWindowHandle`] for [`WindowAttributes::parent_window`]. +/// +/// # Safety +/// +/// The user has to account for that when using [`WindowAttributes::with_parent_window()`], +/// which is `unsafe`. +#[derive(Debug, Clone, PartialEq)] +pub(crate) struct SendSyncRawWindowHandle(pub(crate) rwh_06::RawWindowHandle); + +unsafe impl Send for SendSyncRawWindowHandle {} +unsafe impl Sync for SendSyncRawWindowHandle {} + +pub trait PlatformWindowAttributes: AsAny + std::fmt::Debug + Send + Sync { + fn box_clone(&self) -> Box; +} + +impl_dyn_casting!(PlatformWindowAttributes); + /// Represents a window. /// /// The window is closed when dropped. @@ -540,7 +581,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// APIs and software rendering. /// /// ```no_run - /// # use winit::window::Window; + /// # use winit_core::window::Window; /// # fn swap_buffers() {} /// # fn scope(window: &dyn Window) { /// // Do the actual drawing with OpenGL. @@ -583,7 +624,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// /// This may also be useful for figuring out the size of the window's decorations (such as /// buttons, title, etc.), but may also not correspond to that (e.g. if the title bar is made - /// transparent using [`with_titlebar_transparent`] on macOS, or your are drawing window + /// transparent on macOS, or your are drawing window /// decorations yourself). /// /// This may be negative. @@ -592,15 +633,6 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// as the window itself, this simply returns `(0, 0)`. /// /// [`outer_position`]: Self::outer_position - #[cfg_attr( - macos_platform, - doc = "[`with_titlebar_transparent`]: \ - crate::platform::macos::WindowAttributesExtMacOS::with_titlebar_transparent" - )] - #[cfg_attr( - not(macos_platform), - doc = "[`with_titlebar_transparent`]: #only-available-on-macos" - )] fn surface_position(&self) -> PhysicalPosition; /// The position of the top-left hand corner of the window relative to the top-left hand corner @@ -625,8 +657,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// This automatically un-maximizes the window if it's maximized. /// /// ```no_run - /// # use winit::dpi::{LogicalPosition, PhysicalPosition}; - /// # use winit::window::Window; + /// # use dpi::{LogicalPosition, PhysicalPosition}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the position in logical dimensions like this: /// window.set_outer_position(LogicalPosition::new(400.0, 200.0).into()); @@ -683,8 +715,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// The request could automatically un-maximize the window if it's maximized. /// /// ```no_run - /// # use winit::dpi::{LogicalSize, PhysicalSize}; - /// # use winit::window::Window; + /// # use dpi::{LogicalSize, PhysicalSize}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the size in logical dimensions like this: /// let _ = window.request_surface_size(LogicalSize::new(400.0, 200.0).into()); @@ -740,7 +772,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// Convert safe area insets to a size and a position. /// /// ``` - /// use winit::dpi::{PhysicalPosition, PhysicalSize}; + /// use dpi::{PhysicalPosition, PhysicalSize}; /// /// # let surface_size = dpi::PhysicalSize::new(0, 0); /// # #[cfg(requires_window)] @@ -760,8 +792,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// Sets a minimum dimensions of the window's surface. /// /// ```no_run - /// # use winit::dpi::{LogicalSize, PhysicalSize}; - /// # use winit::window::Window; + /// # use dpi::{LogicalSize, PhysicalSize}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the size in logical dimensions like this: /// window.set_min_surface_size(Some(LogicalSize::new(400.0, 200.0).into())); @@ -779,8 +811,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// Sets a maximum dimensions of the window's surface. /// /// ```no_run - /// # use winit::dpi::{LogicalSize, PhysicalSize}; - /// # use winit::window::Window; + /// # use dpi::{LogicalSize, PhysicalSize}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the size in logical dimensions like this: /// window.set_max_surface_size(Some(LogicalSize::new(400.0, 200.0).into())); @@ -963,14 +995,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// The dock and the menu bar are disabled in exclusive fullscreen mode. /// - **Wayland:** Does not support exclusive fullscreen mode and will no-op a request. /// - **Windows:** Screen saver is disabled in fullscreen mode. - /// - **Android / Orbital:** Unsupported. - /// - **Web:** Passing a [`MonitorHandle`] or [`VideoMode`] that was not created with - #[cfg_attr( - web_platform, - doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]" - )] - #[cfg_attr(not(web_platform), doc = " detailed monitor permissions")] - /// or calling without a [transient activation] does nothing. + /// - **Web:** Passing a [`MonitorHandle`] or [`VideoMode`] that was not created with detailed + /// monitor permissions or calling without a [transient activation] does nothing. /// /// [transient activation]: https://developer.mozilla.org/en-US/docs/Glossary/Transient_activation /// [`VideoMode`]: crate::monitor::VideoMode @@ -1045,8 +1071,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// ## Example /// /// ```no_run - /// # use winit::dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize}; - /// # use winit::window::Window; + /// # use dpi::{LogicalPosition, PhysicalPosition, LogicalSize, PhysicalSize}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the position in logical dimensions like this: /// window.set_ime_cursor_area( @@ -1188,8 +1214,8 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// Changes the position of the cursor in window coordinates. /// /// ```no_run - /// # use winit::dpi::{LogicalPosition, PhysicalPosition}; - /// # use winit::window::Window; + /// # use dpi::{LogicalPosition, PhysicalPosition}; + /// # use winit_core::window::Window; /// # fn scope(window: &dyn Window) { /// // Specify the position in logical dimensions like this: /// window.set_cursor_position(LogicalPosition::new(400.0, 200.0).into()); @@ -1212,7 +1238,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// First try confining the cursor, and if that fails, try locking it instead. /// /// ```no_run - /// # use winit::window::{CursorGrabMode, Window}; + /// # use winit_core::window::{CursorGrabMode, Window}; /// # fn scope(window: &dyn Window) { /// window /// .set_cursor_grab(CursorGrabMode::Confined) @@ -1292,17 +1318,6 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// This is the same as [`ActiveEventLoop::available_monitors`], and is provided for /// convenience. /// - /// - /// ## Platform-specific - /// - /// **Web:** Only returns the current monitor without - #[cfg_attr( - web_platform, - doc = "[detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(any(web_platform,)), doc = "detailed monitor permissions.")] - /// - #[rustfmt::skip] /// [`ActiveEventLoop::available_monitors`]: crate::event_loop::ActiveEventLoop::available_monitors fn available_monitors(&self) -> Box>; @@ -1315,14 +1330,7 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { /// ## Platform-specific /// /// - **Wayland:** Always returns `None`. - /// - **Web:** Always returns `None` without - #[cfg_attr( - web_platform, - doc = " [detailed monitor permissions][crate::platform::web::ActiveEventLoopExtWeb::request_detailed_monitor_permission]." - )] - #[cfg_attr(not(web_platform), doc = " detailed monitor permissions.")] - /// - #[rustfmt::skip] + /// /// [`ActiveEventLoop::primary_monitor`]: crate::event_loop::ActiveEventLoop::primary_monitor fn primary_monitor(&self) -> Option; @@ -1333,14 +1341,6 @@ pub trait Window: AsAny + Send + Sync + fmt::Debug { fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle; } -impl dyn Window + '_ { - /// Create a new [`WindowAttributes`] which allows modifying the window's attributes before - /// creation. - pub fn default_attributes() -> WindowAttributes { - WindowAttributes::default() - } -} - impl_dyn_casting!(Window); impl PartialEq for dyn Window + '_ { @@ -1546,12 +1546,7 @@ impl ActivationToken { /// only result in the side effect of the operation involving it being ignored (e.g. window /// won't get focused automatically), but won't yield any errors. /// - /// To obtain a valid token, use - #[cfg_attr( - any(x11_platform, wayland_platform), - doc = " [`request_activation_token`](crate::platform::startup_notify::WindowExtStartupNotify::request_activation_token)." - )] - #[cfg_attr(not(any(x11_platform, wayland_platform)), doc = " `request_activation_token`.")] + /// To obtain a valid token consult the backend implementation. pub fn from_raw(token: String) -> Self { Self { token } } @@ -1560,4 +1555,9 @@ impl ActivationToken { pub fn into_raw(self) -> String { self.token } + + /// Get a reference to a raw token. + pub fn as_raw(&self) -> &str { + &self.token + } }