Skip to content

Commit

Permalink
Add OwnedWindowHandle to avoid lifetime on WindowHandle<'_>
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Oct 13, 2023
1 parent 101905c commit 2e66158
Show file tree
Hide file tree
Showing 15 changed files with 182 additions and 66 deletions.
7 changes: 3 additions & 4 deletions examples/child_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() -> Result<(), impl std::error::Error> {
dpi::{LogicalPosition, LogicalSize, Position},
event::{ElementState, Event, KeyEvent, WindowEvent},
event_loop::{EventLoop, EventLoopWindowTarget},
raw_window_handle::HasRawWindowHandle,
raw_window_handle::HasWindowHandle,
window::{Window, WindowBuilder, WindowId},
};

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

let id = child_window.id();
Expand Down
12 changes: 12 additions & 0 deletions src/platform_impl/android/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,18 @@ impl DeviceId {
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
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 {
app: AndroidApp,
redraw_requester: RedrawRequester,
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/ios/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub(crate) use self::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
},
monitor::{MonitorHandle, VideoMode},
window::{PlatformSpecificWindowBuilderAttributes, Window, WindowId},
window::{OwnedWindowHandle, PlatformSpecificWindowBuilderAttributes, Window, WindowId},
};

use self::uikit::UIScreen;
Expand Down
13 changes: 13 additions & 0 deletions src/platform_impl/ios/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>,
Expand Down
36 changes: 36 additions & 0 deletions src/platform_impl/linux/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,42 @@ impl fmt::Display for OsError {
}
}

#[derive(Debug, Clone)]
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 {
#[cfg(x11_platform)]
X(x11::Window),
Expand Down
12 changes: 5 additions & 7 deletions src/platform_impl/linux/x11/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ use crate::{
event_loop::AsyncRequestSerial,
platform_impl::{
x11::{atoms::*, MonitorHandle as X11MonitorHandle, WakeSender, X11Error},
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError, PlatformIcon,
Fullscreen, MonitorHandle as PlatformMonitorHandle, OsError,
OwnedWindowHandle as PlatformOwnedWindowHandle, PlatformIcon,
PlatformSpecificWindowBuilderAttributes, VideoMode as PlatformVideoMode,
},
window::{
Expand Down Expand Up @@ -144,15 +145,12 @@ impl UnownedWindow {
) -> Result<UnownedWindow, RootOsError> {
let xconn = &event_loop.xconn;
let atoms = xconn.atoms();
#[cfg(feature = "rwh_06")]
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"),
Some(PlatformOwnedWindowHandle::X(handle)) => handle,
#[cfg(wayland_platform)]
Some(handle) => panic!("invalid window handle {handle:?} on X11"),
None => event_loop.root,
};
#[cfg(not(feature = "rwh_06"))]
let root = event_loop.root;

let mut monitors = leap!(xconn.available_monitors());
let guessed_monitor = if monitors.is_empty() {
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/macos/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub(crate) use self::{
};
use crate::event::DeviceId as RootDeviceId;

pub(crate) use self::window::Window;
pub(crate) use self::window::{OwnedWindowHandle, Window};
pub(crate) use crate::icon::NoIcon as PlatformIcon;
pub(crate) use crate::platform_impl::Fullscreen;

Expand Down
67 changes: 47 additions & 20 deletions src/platform_impl/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ impl Window {
) -> Result<Self, RootOsError> {
let mtm = MainThreadMarker::new()
.expect("windows can only be created on the main thread on macOS");
let (window, _delegate) = autoreleasepool(|_| WinitWindow::new(attributes, pl_attribs))?;
let (window, _delegate) =
autoreleasepool(|_| WinitWindow::new(mtm, attributes, pl_attribs))?;
Ok(Window {
window: MainThreadBound::new(window, mtm),
_delegate: MainThreadBound::new(_delegate, mtm),
Expand Down Expand Up @@ -109,6 +110,40 @@ 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),
}
}
}

#[derive(Clone)]
pub struct PlatformSpecificWindowBuilderAttributes {
pub movable_by_window_background: bool,
Expand Down Expand Up @@ -289,6 +324,7 @@ impl Drop for SharedStateMutexGuard<'_> {
impl WinitWindow {
#[allow(clippy::type_complexity)]
fn new(
mtm: MainThreadMarker,
attrs: WindowAttributes,
pl_attrs: PlatformSpecificWindowBuilderAttributes,
) -> Result<(Id<Self>, Id<WinitWindowDelegate>), RootOsError> {
Expand Down Expand Up @@ -448,25 +484,16 @@ impl WinitWindow {
})
.ok_or_else(|| os_error!(OsError::CreationError("Couldn't create `NSWindow`")))?;

#[cfg(feature = "rwh_06")]
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`.
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`
// where we allow making a window a child window is right here, just after it's been created.
unsafe { parent.addChildWindow(&this, NSWindowOrderingMode::NSWindowAbove) };
}
Some(raw) => panic!("Invalid raw window handle {raw:?} on macOS"),
None => (),
if let Some(parent_window) = attrs.parent_window {
let parent = parent_window.ns_view.get(mtm).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`
// where we allow making a window a child window is right here, just after it's been created.
unsafe { parent.addChildWindow(&this, NSWindowOrderingMode::NSWindowAbove) };
}

let view = WinitView::new(&this, pl_attrs.accepts_first_mouse);
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/orbital/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::dpi::{PhysicalPosition, PhysicalSize};
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
mod event_loop;

pub use self::window::Window;
pub(crate) use self::window::{OwnedWindowHandle, Window};
mod window;

struct RedoxSocket {
Expand Down
12 changes: 12 additions & 0 deletions src/platform_impl/orbital/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,18 @@ const ORBITAL_FLAG_BORDERLESS: char = 'l';
const ORBITAL_FLAG_RESIZABLE: char = 'r';
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 {
window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>,
Expand Down
4 changes: 3 additions & 1 deletion src/platform_impl/web/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ pub(crate) use self::event_loop::{
EventLoop, EventLoopProxy, EventLoopWindowTarget, PlatformSpecificEventLoopAttributes,
};
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 crate::icon::NoIcon as PlatformIcon;
Expand Down
12 changes: 12 additions & 0 deletions src/platform_impl/web/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;

#[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 {
inner: Dispatcher<Inner>,
}
Expand Down
2 changes: 1 addition & 1 deletion src/platform_impl/windows/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ pub(crate) use self::{
},
icon::WinIcon,
monitor::{MonitorHandle, VideoMode},
window::Window,
window::{OwnedWindowHandle, Window},
};

pub use self::icon::WinIcon as PlatformIcon;
Expand Down
53 changes: 31 additions & 22 deletions src/platform_impl/windows/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,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.
pub(crate) struct Window {
/// Main handle for the window.
Expand Down Expand Up @@ -1177,33 +1194,25 @@ where
// so the diffing later can work.
window_flags.set(WindowFlags::CLOSABLE, true);

let mut fallback_parent = || match pl_attribs.owner {
Some(parent) => {
window_flags.set(WindowFlags::POPUP, true);
Some(parent)
let parent = if let Some(parent_window) = &attributes.parent_window {
window_flags.set(WindowFlags::CHILD, true);
if pl_attribs.menu.is_some() {
warn!("Setting a menu on a child window is unsupported");
}
None => {
window_flags.set(WindowFlags::ON_TASKBAR, true);
None
}
};

#[cfg(feature = "rwh_06")]
let parent = match attributes.parent_window {
Some(rwh_06::RawWindowHandle::Win32(handle)) => {
window_flags.set(WindowFlags::CHILD, true);
if pl_attribs.menu.is_some() {
warn!("Setting a menu on a child window is unsupported");
Some(parent_window.hwnd)
} else {
match pl_attribs.owner {
Some(parent) => {
window_flags.set(WindowFlags::POPUP, true);
Some(parent)
}
None => {
window_flags.set(WindowFlags::ON_TASKBAR, true);
None
}
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 {
event_loop,
attributes,
Expand Down

0 comments on commit 2e66158

Please sign in to comment.