Compare commits

...

1 Commits

Author SHA1 Message Date
Mads Marquart
e66eba38f5 Add OwnedWindowHandle to avoid lifetime on WindowHandle<'_> 2023-12-24 00:46:13 +01:00
15 changed files with 196 additions and 71 deletions

View File

@@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
dpi::{LogicalPosition, LogicalSize, Position}, dpi::{LogicalPosition, LogicalSize, Position},
event::{ElementState, Event, KeyEvent, WindowEvent}, event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget}, event_loop::{EventLoop, EventLoopWindowTarget},
raw_window_handle::HasRawWindowHandle, raw_window_handle::HasWindowHandle,
window::{Window, WindowBuilder, WindowId}, window::{Window, WindowBuilder, WindowId},
}; };
@@ -26,14 +26,13 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop: &EventLoopWindowTarget<()>, event_loop: &EventLoopWindowTarget<()>,
windows: &mut HashMap<WindowId, Window>, windows: &mut HashMap<WindowId, Window>,
) { ) {
let parent = parent.raw_window_handle().unwrap(); let parent = parent.window_handle().unwrap();
let mut builder = WindowBuilder::new() let mut builder = WindowBuilder::new()
.with_title("child window") .with_title("child window")
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32)) .with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0))) .with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_visible(true); .with_visible(true);
// `with_parent_window` is unsafe. Parent window must be a valid window. builder = builder.with_parent_window(Some(parent));
builder = unsafe { builder.with_parent_window(Some(parent)) };
let child_window = builder.build(event_loop).unwrap(); let child_window = builder.build(event_loop).unwrap();
let id = child_window.id(); let id = child_window.id();

View File

@@ -751,6 +751,18 @@ impl DeviceId {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes; pub struct PlatformSpecificWindowBuilderAttributes;
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable.
Self {}
}
}
pub(crate) struct Window { pub(crate) struct Window {
app: AndroidApp, app: AndroidApp,
redraw_requester: RedrawRequester, redraw_requester: RedrawRequester,

View File

@@ -73,7 +73,7 @@ pub(crate) use self::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes, EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
}, },
monitor::{MonitorHandle, VideoMode}, monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId}, window::{OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId},
}; };
use self::uikit::UIScreen; use self::uikit::UIScreen;

View File

@@ -25,6 +25,19 @@ use crate::{
}, },
}; };
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable (would work similar to macOS).
warn!("parent windows are unsupported on iOS");
Self {}
}
}
pub struct Inner { pub struct Inner {
window: Id<WinitUIWindow>, window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>, view_controller: Id<WinitViewController>,

View File

@@ -141,6 +141,43 @@ impl fmt::Display for OsError {
} }
} }
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub(crate) enum OwnedWindowHandle {
#[cfg(x11_platform)]
X(x11rb::protocol::xproto::Window),
#[cfg(wayland_platform)]
Wayland,
}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
// TODO: Do we need to do something extra to extend the lifetime of
// the window lives beyond the passed-in handle?
match handle.as_raw() {
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xlib(handle) => {
Self::X(handle.window as x11rb::protocol::xproto::Window)
}
#[cfg(x11_platform)]
rwh_06::RawWindowHandle::Xcb(handle) => Self::X(handle.window.get()),
#[cfg(wayland_platform)]
rwh_06::RawWindowHandle::Wayland(_handle) => {
// Wayland does not currently support parent windows, but it
// could support owned handles.
Self::Wayland
}
#[cfg(not(x11_platform))]
handle => panic!("invalid window handle {handle:?} on Wayland"),
#[cfg(not(wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11"),
#[cfg(all(x11_platform, wayland_platform))]
handle => panic!("invalid window handle {handle:?} on X11 or Wayland"),
}
}
}
pub(crate) enum Window { pub(crate) enum Window {
#[cfg(x11_platform)] #[cfg(x11_platform)]
X(x11::Window), X(x11::Window),

View File

@@ -30,6 +30,7 @@ use crate::{
atoms::*, xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender, atoms::*, xinput_fp1616_to_float, MonitorHandle as X11MonitorHandle, WakeSender,
X11Error, X11Error,
}, },
OwnedWindowHandle as PlatformOwnedWindowHandle,
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor, Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformCustomCursor,
PlatformIcon, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode, PlatformIcon, PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
}, },
@@ -157,15 +158,12 @@ impl UnownedWindow {
) -> Result<UnownedWindow, RootOsError> { ) -> Result<UnownedWindow, RootOsError> {
let xconn = &event_loop.xconn; let xconn = &event_loop.xconn;
let atoms = xconn.atoms(); let atoms = xconn.atoms();
#[cfg(feature = "rwh_06")] let root = match window_attrs.parent_window {
let root = match window_attrs.parent_window.0 { Some(PlatformOwnedWindowHandle::X(handle)) => handle,
Some(rwh_06::RawWindowHandle::Xlib(handle)) => handle.window as xproto::Window, #[cfg(wayland_platform)]
Some(rwh_06::RawWindowHandle::Xcb(handle)) => handle.window.get(), Some(handle) => panic!("invalid window handle {handle:?} on X11"),
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on X11"),
None => event_loop.root, None => event_loop.root,
}; };
#[cfg(not(feature = "rwh_06"))]
let root = event_loop.root;
let mut monitors = leap!(xconn.available_monitors()); let mut monitors = leap!(xconn.available_monitors());
let guessed_monitor = if monitors.is_empty() { let guessed_monitor = if monitors.is_empty() {

View File

@@ -28,7 +28,7 @@ pub(crate) use self::{
use crate::event::DeviceId as RootDeviceId; use crate::event::DeviceId as RootDeviceId;
pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor; pub(crate) use self::cursor::CustomCursor as PlatformCustomCursor;
pub(crate) use self::window::Window; pub(crate) use self::window::{OwnedWindowHandle, Window};
pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder; pub(crate) use crate::cursor::OnlyCursorImageBuilder as PlatformCustomCursorBuilder;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen; pub(crate) use crate::platform_impl::Fullscreen;

View File

@@ -129,6 +129,54 @@ impl From<u64> for WindowId {
} }
} }
#[derive(Debug)]
pub(crate) struct OwnedWindowHandle {
ns_view: MainThreadBound<Id<NSView>>,
}
impl Clone for OwnedWindowHandle {
fn clone(&self) -> Self {
Self {
ns_view: MainThreadMarker::run_on_main(|mtm| {
MainThreadBound::new(self.ns_view.get(mtm).clone(), mtm)
}),
}
}
}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
let mtm =
MainThreadMarker::new().expect("can only have handles on macOS on the main thread");
let ns_view = match handle.as_raw() {
rwh_06::RawWindowHandle::AppKit(handle) => {
// SAFETY: Taking `WindowHandle<'_>` ensures that the pointer is valid.
// Unwrap is fine, since the pointer comes from `NonNull`.
unsafe { Id::retain(handle.ns_view.as_ptr().cast()) }.unwrap()
}
handle => panic!("invalid window handle {handle:?} on macOS"),
};
Self {
ns_view: MainThreadBound::new(ns_view, mtm),
}
}
#[cfg(feature = "rwh_06")]
pub(crate) fn raw_window_handle(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
if let Some(mtm) = MainThreadMarker::new() {
let window_handle = rwh_06::AppKitWindowHandle::new({
let ptr = Id::as_ptr(self.ns_view.get(mtm)) as *mut _;
std::ptr::NonNull::new(ptr).expect("Id<T> should never be null")
});
let handle = rwh_06::RawWindowHandle::AppKit(window_handle);
Ok(handle)
} else {
Err(rwh_06::HandleError::Unavailable)
}
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes { pub struct PlatformSpecificWindowBuilderAttributes {
pub movable_by_window_background: bool, pub movable_by_window_background: bool,
@@ -439,25 +487,16 @@ impl WinitWindow {
}) })
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?; .ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?;
#[cfg(feature = "rwh_06")] if let Some(parent_window) = attrs.parent_window {
match attrs.parent_window.0 { let parent = parent_window.ns_view.get(mtm).window().ok_or_else(|| {
Some(rwh_06::RawWindowHandle::AppKit(handle)) => { os_error!(OsError::CreationError(
// SAFETY: Caller ensures the pointer is valid or NULL "parent view should be installed in a window"
// Unwrap is fine, since the pointer comes from `NonNull`. ))
let parent_view: Id<NSView> = })?;
unsafe { Id::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
let parent = parent_view.window().ok_or_else(|| {
os_error!(OsError::CreationError(
"parent view should be installed in a window"
))
})?;
// SAFETY: We know that there are no parent -> child -> parent cycles since the only place in `winit` // SAFETY: We know that there are no parent -> child -> parent cycles since the only place in `winit`
// where we allow making a window a child window is right here, just after it's been created. // where we allow making a window a child window is right here, just after it's been created.
unsafe { parent.addChildWindow_ordered(&this, NSWindowAbove) }; unsafe { parent.addChildWindow_ordered(&this, NSWindowAbove) };
}
Some(raw) => panic!("Invalid raw window handle {raw:?} on macOS"),
None => (),
} }
let view = WinitView::new(&this, pl_attrs.accepts_first_mouse); let view = WinitView::new(&this, pl_attrs.accepts_first_mouse);

View File

@@ -9,7 +9,7 @@ use crate::dpi::{PhysicalPosition, PhysicalSize};
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget}; pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
mod event_loop; mod event_loop;
pub use self::window::Window; pub(crate) use self::window::{OwnedWindowHandle, Window};
mod window; mod window;
struct RedoxSocket { struct RedoxSocket {

View File

@@ -25,6 +25,18 @@ const ORBITAL_FLAG_BORDERLESS: char = 'l';
const ORBITAL_FLAG_RESIZABLE: char = 'r'; const ORBITAL_FLAG_RESIZABLE: char = 'r';
const ORBITAL_FLAG_TRANSPARENT: char = 't'; const ORBITAL_FLAG_TRANSPARENT: char = 't';
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable.
Self {}
}
}
pub struct Window { pub struct Window {
window_socket: Arc<RedoxSocket>, window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>, redraws: Arc<Mutex<VecDeque<WindowId>>>,

View File

@@ -35,7 +35,9 @@ pub(crate) use self::event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes, EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
}; };
pub use self::monitor::{MonitorHandle, VideoMode}; pub use self::monitor::{MonitorHandle, VideoMode};
pub use self::window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId}; pub(crate) use self::window::{
OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId,
};
pub(crate) use self::keyboard::KeyEventExtra; pub(crate) use self::keyboard::KeyEventExtra;
pub(crate) use crate::icon::NoIcon as PlatformIcon; pub(crate) use crate::icon::NoIcon as PlatformIcon;

View File

@@ -17,6 +17,18 @@ use std::cell::RefCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::rc::Rc; use std::rc::Rc;
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(_handle: rwh_06::WindowHandle<'_>) -> Self {
// Parent windows are currently unsupported, though owned window
// handles would be implementable.
Self {}
}
}
pub struct Window { pub struct Window {
inner: Dispatcher<Inner>, inner: Dispatcher<Inner>,
} }

View File

@@ -12,7 +12,7 @@ pub(crate) use self::{
}, },
icon::{SelectedCursor, WinIcon}, icon::{SelectedCursor, WinIcon},
monitor::{MonitorHandle, VideoMode}, monitor::{MonitorHandle, VideoMode},
window::Window, window::{OwnedWindowHandle, Window},
}; };
pub use self::icon::WinIcon as PlatformIcon; pub use self::icon::WinIcon as PlatformIcon;

View File

@@ -81,6 +81,23 @@ use crate::{
}, },
}; };
#[derive(Debug, Clone)]
pub(crate) struct OwnedWindowHandle {
hwnd: HWND,
}
impl OwnedWindowHandle {
#[cfg(feature = "rwh_06")]
pub(crate) fn new_parent_window(handle: rwh_06::WindowHandle<'_>) -> Self {
// TODO: Do we need to do something to extend the lifetime of the window handle?
let hwnd = match handle.as_raw() {
rwh_06::RawWindowHandle::Win32(handle) => handle.hwnd.get() as HWND,
handle => panic!("invalid window handle {handle:?} on Windows"),
};
Self { hwnd }
}
}
/// The Win32 implementation of the main `Window` object. /// The Win32 implementation of the main `Window` object.
pub(crate) struct Window { pub(crate) struct Window {
/// Main handle for the window. /// Main handle for the window.
@@ -1301,33 +1318,25 @@ where
// so the diffing later can work. // so the diffing later can work.
window_flags.set(WindowFlags::CLOSABLE, true); window_flags.set(WindowFlags::CLOSABLE, true);
let mut fallback_parent = || match pl_attribs.owner { let parent = if let Some(parent_window) = &attributes.parent_window {
Some(parent) => { window_flags.set(WindowFlags::CHILD, true);
window_flags.set(WindowFlags::POPUP, true); if pl_attribs.menu.is_some() {
Some(parent) warn!("Setting a menu on a child window is unsupported");
} }
None => { Some(parent_window.hwnd)
window_flags.set(WindowFlags::ON_TASKBAR, true); } else {
None match pl_attribs.owner {
} Some(parent) => {
}; window_flags.set(WindowFlags::POPUP, true);
Some(parent)
#[cfg(feature = "rwh_06")] }
let parent = match attributes.parent_window.0 { None => {
Some(rwh_06::RawWindowHandle::Win32(handle)) => { window_flags.set(WindowFlags::ON_TASKBAR, true);
window_flags.set(WindowFlags::CHILD, true); None
if pl_attribs.menu.is_some() {
warn!("Setting a menu on a child window is unsupported");
} }
Some(handle.hwnd.get() as HWND)
} }
Some(raw) => unreachable!("Invalid raw window handle {raw:?} on Windows"),
None => fallback_parent(),
}; };
#[cfg(not(feature = "rwh_06"))]
let parent = fallback_parent();
let mut initdata = InitData { let mut initdata = InitData {
event_loop, event_loop,
attributes, attributes,

View File

@@ -153,8 +153,7 @@ pub struct WindowAttributes {
pub content_protected: bool, pub content_protected: bool,
pub window_level: WindowLevel, pub window_level: WindowLevel,
pub active: bool, pub active: bool,
#[cfg(feature = "rwh_06")] pub(crate) parent_window: Option<platform_impl::OwnedWindowHandle>,
pub(crate) parent_window: SendSyncWrapper<Option<rwh_06::RawWindowHandle>>,
pub(crate) fullscreen: SendSyncWrapper<Option<Fullscreen>>, pub(crate) fullscreen: SendSyncWrapper<Option<Fullscreen>>,
} }
@@ -180,8 +179,7 @@ impl Default for WindowAttributes {
preferred_theme: None, preferred_theme: None,
resize_increments: None, resize_increments: None,
content_protected: false, content_protected: false,
#[cfg(feature = "rwh_06")] parent_window: None,
parent_window: SendSyncWrapper(None),
active: true, active: true,
} }
} }
@@ -190,8 +188,8 @@ impl Default for WindowAttributes {
impl WindowAttributes { impl WindowAttributes {
/// Get the parent window stored on the attributes. /// Get the parent window stored on the attributes.
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
pub fn parent_window(&self) -> Option<&rwh_06::RawWindowHandle> { pub fn parent_window(&self) -> Option<Result<rwh_06::RawWindowHandle, rwh_06::HandleError>> {
self.parent_window.0.as_ref() Some(self.parent_window.as_ref()?.raw_window_handle())
} }
/// Get `Fullscreen` option stored on the attributes. /// Get `Fullscreen` option stored on the attributes.
@@ -476,10 +474,6 @@ impl WindowBuilder {
/// ///
/// The default is `None`. /// The default is `None`.
/// ///
/// ## Safety
///
/// `parent_window` must be a valid window handle.
///
/// ## Platform-specific /// ## Platform-specific
/// ///
/// - **Windows** : A child window has the WS_CHILD style and is confined /// - **Windows** : A child window has the WS_CHILD style and is confined
@@ -489,11 +483,9 @@ impl WindowBuilder {
/// - **Android / iOS / Wayland / Web:** Unsupported. /// - **Android / iOS / Wayland / Web:** Unsupported.
#[cfg(feature = "rwh_06")] #[cfg(feature = "rwh_06")]
#[inline] #[inline]
pub unsafe fn with_parent_window( pub fn with_parent_window(mut self, parent_window: Option<rwh_06::WindowHandle<'_>>) -> Self {
mut self, self.window.parent_window =
parent_window: Option<rwh_06::RawWindowHandle>, parent_window.map(platform_impl::OwnedWindowHandle::new_parent_window);
) -> Self {
self.window.parent_window = SendSyncWrapper(parent_window);
self self
} }