diff --git a/examples/open_parented/src/main.rs b/examples/open_parented/src/main.rs index ccfc72cf..8eedd262 100644 --- a/examples/open_parented/src/main.rs +++ b/examples/open_parented/src/main.rs @@ -1,7 +1,6 @@ use baseview::dpi::LogicalSize; use baseview::{ - Event, EventStatus, Window, WindowContext, WindowHandle, WindowHandler, WindowOpenOptions, - WindowSize, + Event, EventStatus, WindowContext, WindowHandle, WindowHandler, WindowOpenOptions, WindowSize, }; use std::cell::{Cell, RefCell}; use std::num::NonZeroU32; @@ -24,10 +23,10 @@ impl ParentWindowHandler { let window_open_options = WindowOpenOptions::new() .with_size(LogicalSize::new(256, 256)) + .with_parent(&window) .with_title("baseview child"); - let child_window = - Window::open_parented(&window, window_open_options, ChildWindowHandler::new); + let child_window = baseview::create_window(window_open_options, ChildWindowHandler::new); Self { surface: surface.into(), damaged: true.into(), _child_window: Some(child_window) } } @@ -122,5 +121,5 @@ impl WindowHandler for ChildWindowHandler { fn main() { let window_open_options = WindowOpenOptions::new().with_size(LogicalSize::new(512.0, 512.0)); - Window::open_blocking(window_open_options, ParentWindowHandler::new); + baseview::create_window(window_open_options, ParentWindowHandler::new).run_until_closed(); } diff --git a/examples/open_window/src/main.rs b/examples/open_window/src/main.rs index ee68627b..adae5fe5 100644 --- a/examples/open_window/src/main.rs +++ b/examples/open_window/src/main.rs @@ -8,8 +8,7 @@ use rtrb::{Consumer, RingBuffer}; use baseview::copy_to_clipboard; use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::{ - Event, EventStatus, MouseEvent, Window, WindowContext, WindowHandler, WindowOpenOptions, - WindowSize, + Event, EventStatus, MouseEvent, WindowContext, WindowHandler, WindowOpenOptions, WindowSize, }; #[derive(Debug, Clone)] @@ -148,7 +147,7 @@ fn main() { } }); - Window::open_blocking(window_open_options, |window| { + baseview::create_window(window_open_options, |window| { let ctx = softbuffer::Context::new(window.clone()).unwrap(); let mut surface = softbuffer::Surface::new(&ctx, window.clone()).unwrap(); let size = window.size().physical; @@ -164,7 +163,8 @@ fn main() { is_cursor_inside: false.into(), damaged: true.into(), } - }); + }) + .run_until_closed(); } fn log_event(event: &Event) { diff --git a/examples/render_femtovg/src/main.rs b/examples/render_femtovg/src/main.rs index 69904f64..edbb8a81 100644 --- a/examples/render_femtovg/src/main.rs +++ b/examples/render_femtovg/src/main.rs @@ -1,8 +1,7 @@ use baseview::dpi::{LogicalSize, PhysicalPosition}; use baseview::gl::{GlConfig, GlContext}; use baseview::{ - Event, EventStatus, MouseEvent, Window, WindowContext, WindowHandler, WindowOpenOptions, - WindowSize, + Event, EventStatus, MouseEvent, WindowContext, WindowHandler, WindowOpenOptions, WindowSize, }; use femtovg::renderer::OpenGl; use femtovg::{Canvas, Color}; @@ -117,7 +116,7 @@ fn main() { .with_size(LogicalSize::new(512, 512)) .with_gl_config(GlConfig { alpha_bits: 8, ..GlConfig::default() }); - Window::open_blocking(window_open_options, FemtovgExample::new); + baseview::create_window(window_open_options, FemtovgExample::new).run_until_closed(); } fn log_event(event: &Event) { diff --git a/examples/render_wgpu/src/main.rs b/examples/render_wgpu/src/main.rs index 043e1c47..0b5ca20a 100644 --- a/examples/render_wgpu/src/main.rs +++ b/examples/render_wgpu/src/main.rs @@ -1,7 +1,5 @@ use baseview::dpi::{LogicalSize, PhysicalSize}; -use baseview::{ - Event, EventStatus, Window, WindowContext, WindowHandler, WindowOpenOptions, WindowSize, -}; +use baseview::{Event, EventStatus, WindowContext, WindowHandler, WindowOpenOptions, WindowSize}; use log::LevelFilter; use std::cell::RefCell; @@ -210,7 +208,8 @@ fn main() { .with_title("WGPU on Baseview") .with_size(LogicalSize::new(512, 512)); - Window::open_blocking(window_open_options, |c| pollster::block_on(WgpuExample::new(c))); + baseview::create_window(window_open_options, |c| pollster::block_on(WgpuExample::new(c))) + .run_until_closed(); } fn log_event(event: &Event) { diff --git a/src/handler.rs b/src/handler.rs index a3ee7087..39b12d0b 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -1,7 +1,24 @@ -use crate::{Event, EventStatus, WindowSize}; +use super::*; pub trait WindowHandler: 'static { fn on_frame(&self); fn resized(&self, new_size: WindowSize); fn on_event(&self, event: Event) -> EventStatus; } + +#[allow(unused)] +pub struct WindowHandlerBuilder { + inner: Box Box + Send + 'static>, +} + +impl WindowHandlerBuilder { + pub fn new( + f: impl FnOnce(WindowContext) -> H + Send + 'static, + ) -> WindowHandlerBuilder { + Self { inner: Box::new(|c| Box::new(f(c))) } + } + + pub fn build(self, ctx: WindowContext) -> Box { + (self.inner)(ctx) + } +} diff --git a/src/platform/macos/mod.rs b/src/platform/macos/mod.rs index 556526e9..84af49f0 100644 --- a/src/platform/macos/mod.rs +++ b/src/platform/macos/mod.rs @@ -5,12 +5,14 @@ mod view; mod window; use crate::platform::macos::view::BaseviewView; -use crate::wrappers::appkit::View; +use crate::wrappers::appkit::{extract_raw_window_handle, View}; pub use context::WindowContext; use dispatch2::MainThreadBound; +use objc2::__framework_prelude::Retained; use objc2::rc::Weak; use objc2::MainThreadMarker; -use raw_window_handle::DisplayHandle; +use objc2_app_kit::NSView; +use raw_window_handle::{DisplayHandle, HasWindowHandle}; use std::fmt; use std::fmt::Formatter; pub use window::*; @@ -63,3 +65,14 @@ impl fmt::Debug for PlatformHandle { f.debug_struct("PlatformHandle (AppKit)").field("ns_view", &PtrFmt(self)).finish() } } + +#[derive(Debug, Clone, PartialEq)] +pub struct ParentWindowHandle { + view: Retained, +} + +impl ParentWindowHandle { + pub fn extract(window: &impl HasWindowHandle) -> Self { + Self { view: extract_raw_window_handle(window.window_handle().unwrap()).unwrap() } + } +} diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index f7a21b4e..e5cd28d6 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -2,6 +2,7 @@ use super::keyboard::{make_modifiers, KeyboardState}; use super::window::WindowSharedState; +use crate::handler::WindowHandlerBuilder; use crate::platform::macos::context::WindowContext; use crate::wrappers::appkit::*; use crate::MouseEvent::{ButtonPressed, ButtonReleased}; @@ -44,10 +45,9 @@ pub(crate) struct BaseviewView { } impl BaseviewView { - pub fn new( - _options: WindowOpenOptions, - builder: impl FnOnce(crate::WindowContext) -> H + Send + 'static, - parenting: ViewParentingType, final_size: LogicalSize, mtm: MainThreadMarker, + pub fn new( + _options: WindowOpenOptions, builder: WindowHandlerBuilder, parenting: ViewParentingType, + final_size: LogicalSize, mtm: MainThreadMarker, ) -> (Retained>, Rc) { let view_rect = NSRect::new(NSPoint::ZERO, NSSize::new(final_size.width, final_size.height)); @@ -92,7 +92,7 @@ impl BaseviewView { } let context = WindowContext::new(view); - let handler = Box::new(builder(crate::WindowContext::new(context))); + let handler = builder.build(crate::WindowContext::new(context)); // Initialize handler let Ok(()) = view.window_handler.set(handler) else { unreachable!() }; diff --git a/src/platform/macos/window.rs b/src/platform/macos/window.rs index e8805eaa..51cf91b1 100644 --- a/src/platform/macos/window.rs +++ b/src/platform/macos/window.rs @@ -1,103 +1,110 @@ use dpi::LogicalSize; -use objc2::rc::{autoreleasepool, Weak}; +use objc2::rc::{autoreleasepool, Retained, Weak}; use objc2::MainThreadMarker; -use objc2_app_kit::{ - NSApplication, NSApplicationActivationPolicy, NSPasteboard, NSPasteboardTypeString, -}; +use objc2_app_kit::{NSApplication, NSPasteboard, NSPasteboardTypeString, NSView, NSWindow}; use objc2_foundation::{NSSize, NSString}; -use raw_window_handle::HasWindowHandle; -use std::cell::{Cell, RefCell}; +use std::cell::Cell; use std::rc::Rc; +use crate::handler::WindowHandlerBuilder; use crate::platform::macos::view::{BaseviewView, ViewParentingType}; -use crate::wrappers::appkit::{create_window, extract_raw_window_handle, View}; -use crate::{WindowContext, WindowHandler, WindowOpenOptions}; +use crate::wrappers::appkit::{create_window, View}; +use crate::*; pub struct WindowHandle { - view: RefCell>>>, + mtm: MainThreadMarker, + view: Weak>, + _window: Option>, state: Rc, } impl WindowHandle { - pub fn close(&self) { - let Some(view) = self.view.take().and_then(|w| w.load()) else { - return; - }; + pub fn create_window(mut options: WindowOpenOptions, handler: WindowHandlerBuilder) -> Self { + autoreleasepool(|_| { + let Some(mtm) = MainThreadMarker::new() else { + panic!("macOS: Windows can only be created on the main thread!") + }; - BaseviewView::close(view.inner_ref()); - } + // Creates the global NSApplication instance, if it doesn't exist yet + let _ = NSApplication::sharedApplication(mtm); - pub fn is_open(&self) -> bool { - self.state.closed.get() + if let Some(parent) = options.parent.take() { + return Self::create_window_parented(options, handler, parent.view, mtm); + } + + Self::create_window_standalone(options, handler, mtm) + }) } -} -pub struct Window; + pub fn create_window_parented( + builder: WindowOpenOptions, handler: WindowHandlerBuilder, parent_view: Retained, + mtm: MainThreadMarker, + ) -> Self { + let parenting = + ViewParentingType::Parented { parent_view: Weak::from_retained(&parent_view) }; -impl Window { - pub fn open_parented( - parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) -> WindowHandle { - autoreleasepool(|_| { - let Some(parent_view) = extract_raw_window_handle(parent.window_handle().unwrap()) - else { - panic!("Invalid window handle: ns_view is NULL"); - }; + let backing_scale_factor = + parent_view.window().map(|w| w.backingScaleFactor()).unwrap_or(1.0); + let final_size = builder.size.to_logical(backing_scale_factor); - let Some(mtm) = MainThreadMarker::new() else { - panic!("macOS: open_blocking can only be called on the main thread!") - }; - - let parenting = - ViewParentingType::Parented { parent_view: Weak::from_retained(&parent_view) }; + let (ns_view, state) = BaseviewView::new(builder, handler, parenting, final_size, mtm); - let backing_scale_factor = - parent_view.window().map(|w| w.backingScaleFactor()).unwrap_or(1.0); - let final_size = options.size.to_logical(backing_scale_factor); + Self { mtm, state, _window: None, view: Weak::from_retained(&ns_view) } + } - let (ns_view, state) = BaseviewView::new(options, build, parenting, final_size, mtm); + pub fn create_window_standalone( + builder: WindowOpenOptions, handler: WindowHandlerBuilder, mtm: MainThreadMarker, + ) -> Self { + let app = NSApplication::sharedApplication(mtm); + let window = create_window_with_options(&builder, mtm); - WindowHandle { view: Some(Weak::from_retained(&ns_view)).into(), state } - }) - } + let final_size = window.contentRectForFrameRect(window.frame()).size; + let final_size = LogicalSize::new(final_size.width, final_size.height); - pub fn open_blocking( - options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) { - autoreleasepool(|_| { - let Some(mtm) = MainThreadMarker::new() else { - panic!("macOS: open_blocking can only be called on the main thread!") - }; + let parenting = ViewParentingType::Windowed { + running_app: Weak::from_retained(&app), + owned_window: Weak::from_retained(&window), + }; - // Creates the global NSApplication instance, if it doesn't exist yet - let app = NSApplication::sharedApplication(mtm); + let (view, state) = BaseviewView::new(builder, handler, parenting, final_size, mtm); - let _ = app.setActivationPolicy(NSApplicationActivationPolicy::Regular); + Self { mtm, state, view: Weak::from_retained(&view), _window: Some(window) } + } - let initial_size = options.size.to_logical(1.0); - let window = create_window(initial_size, mtm); - window.center(); + pub fn run_until_closed(self) { + NSApplication::sharedApplication(self.mtm).run(); + } - let final_size = options.size.to_logical(window.backingScaleFactor()); - if final_size != initial_size { - window.setContentSize(NSSize::new(final_size.width, final_size.height)); - } + pub fn close(&self) { + let Some(view) = self.view.load() else { + return; + }; - let title = NSString::from_str(&options.title); - window.setTitle(&title); - window.makeKeyAndOrderFront(None); + BaseviewView::close(view.inner_ref()); + } - let parenting = ViewParentingType::Windowed { - running_app: Weak::from_retained(&app), - owned_window: Weak::from_retained(&window), - }; + pub fn is_open(&self) -> bool { + self.state.closed.get() + } +} - let _ = BaseviewView::new(options, build, parenting, final_size, mtm); +fn create_window_with_options( + options: &WindowOpenOptions, mtm: MainThreadMarker, +) -> Retained { + let initial_size = options.size.to_logical(1.0); + let window = create_window(initial_size, mtm); + window.center(); - app.run(); - }) + let final_size = options.size.to_logical(window.backingScaleFactor()); + if final_size != initial_size { + window.setContentSize(NSSize::new(final_size.width, final_size.height)); } + + let title = NSString::from_str(&options.title); + window.setTitle(&title); + + window.makeKeyAndOrderFront(None); + window } pub(crate) struct WindowSharedState { diff --git a/src/platform/win/mod.rs b/src/platform/win/mod.rs index 3d75c41c..f75b0d70 100644 --- a/src/platform/win/mod.rs +++ b/src/platform/win/mod.rs @@ -5,7 +5,8 @@ mod window; mod window_state; use crate::wrappers::win32::h_instance::HInstance; -use raw_window_handle::{DisplayHandle, Win32WindowHandle}; +use crate::wrappers::win32::window::HWnd; +use raw_window_handle::{DisplayHandle, HasWindowHandle, RawWindowHandle, Win32WindowHandle}; use std::fmt::Debug; use std::num::NonZeroIsize; use std::rc::Rc; @@ -42,3 +43,19 @@ impl Debug for PlatformHandle { .finish() } } + +#[derive(Debug, Clone, PartialEq)] +pub struct ParentWindowHandle { + handle: HWnd, +} + +impl ParentWindowHandle { + pub fn extract(parent: &impl HasWindowHandle) -> Self { + let parent = match parent.window_handle().unwrap().as_raw() { + RawWindowHandle::Win32(h) => h.hwnd, + h => panic!("unsupported parent handle {:?}", h), + }; + + Self { handle: unsafe { HWnd::from_raw(parent.get() as _) } } + } +} diff --git a/src/platform/win/window.rs b/src/platform/win/window.rs index e45d2f4d..dbfc32e3 100644 --- a/src/platform/win/window.rs +++ b/src/platform/win/window.rs @@ -14,27 +14,25 @@ use windows_sys::Win32::{ }; use dpi::{PhysicalPosition, PhysicalSize, Size}; -use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use std::cell::Cell; use std::num::NonZeroUsize; -use std::ptr::null_mut; pub(crate) const BV_WINDOW_MUST_CLOSE: u32 = WM_USER + 1; use super::drop_target::DropTarget; use super::*; +use crate::handler::WindowHandlerBuilder; use crate::platform::win::window_state::WindowState; use crate::wrappers::win32::cursor::SystemCursor; -use crate::{ - Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowOpenOptions, - WindowScalePolicy, WindowSize, -}; - use crate::wrappers::win32::window::*; use crate::wrappers::win32::{ ole_initialize, run_thread_message_loop_until, Dpi, DpiAwarenessContext, ExtendedUser32, Rect, WindowStyle, }; +use crate::{ + Event, MouseButton, MouseEvent, ScrollDelta, WindowEvent, WindowOpenOptions, WindowScalePolicy, + WindowSize, +}; #[allow(non_snake_case)] fn HIWORD(wparam: WPARAM) -> u16 { @@ -57,6 +55,10 @@ pub struct WindowHandle { } impl WindowHandle { + pub fn run_until_closed(self) { + run_thread_message_loop_until(|| !self.is_open()).unwrap(); + } + pub fn close(&self) { if let Some(hwnd) = self.hwnd.take() { unsafe { @@ -80,13 +82,11 @@ impl Drop for ParentHandle { } } -type HandlerBuilder = dyn FnOnce(crate::WindowContext) -> Box; - pub struct BaseviewWindow { window_state: Rc, initial_size: Size, - handler_builder: Cell>>, + handler_builder: Cell>, // Things not directly used, but kept so their Drop impl runs when the window is destroyed _parent_handle: ParentHandle, @@ -145,7 +145,7 @@ impl WindowImpl for BaseviewWindow { let handler = { let context = crate::WindowContext::new(Rc::clone(&self.window_state)); - self.handler_builder.take().unwrap()(context) + self.handler_builder.take().unwrap().build(context) }; let Ok(()) = window_state.handler.set(handler) else { unreachable!() }; @@ -406,34 +406,8 @@ unsafe fn wnd_proc_inner( } } -pub struct Window; - -impl Window { - pub fn open_parented( - parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, - ) -> WindowHandle { - let parent = match parent.window_handle().unwrap().as_raw() { - RawWindowHandle::Win32(h) => h.hwnd, - h => panic!("unsupported parent handle {:?}", h), - }; - - Self::open(true, parent.get() as *mut _, options, build) - } - - pub fn open_blocking( - options: WindowOpenOptions, - build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, - ) { - let window_handle = Self::open(false, null_mut(), options, build); - - run_thread_message_loop_until(|| !window_handle.is_open()).unwrap(); - } - - fn open( - parented: bool, parent: HWND, options: WindowOpenOptions, - build: impl for<'a> FnOnce(crate::WindowContext) -> H + Send + 'static, - ) -> WindowHandle { +impl WindowHandle { + pub fn create_window(options: WindowOpenOptions, build: WindowHandlerBuilder) -> WindowHandle { let extended_user_32 = ExtendedUser32::load().unwrap(); let title = HSTRING::from(options.title); @@ -444,7 +418,11 @@ impl Window { let window_size = options.size.to_physical(scaling_factor); - let style = if parented { WindowStyle::parented() } else { WindowStyle::embedded() }; + let style = if options.parent.is_some() { + WindowStyle::parented() + } else { + WindowStyle::embedded() + }; let dpi_ctx = DpiAwarenessContext::new(&extended_user_32).unwrap(); let rect = @@ -467,7 +445,7 @@ impl Window { BaseviewWindow { window_state, initial_size: options.size, - handler_builder: Cell::new(Some(Box::new(|w| Box::new(build(w))))), + handler_builder: Cell::new(Some(build)), _parent_handle: parent_handle, _drop_target: None.into(), @@ -479,9 +457,10 @@ impl Window { } }; + let parent = options.parent.map(|p| p.handle); + let hwnd = - create_window(&title, style, rect.size(), parent as *mut _, &dpi_ctx, initializer) - .unwrap(); + create_window(&title, style, rect.size(), parent, &dpi_ctx, initializer).unwrap(); // SAFETY: this handle should be safe to use let window = unsafe { HWnd::from_raw(hwnd) }; diff --git a/src/platform/x11/event_loop.rs b/src/platform/x11/event_loop.rs index 63163f9f..34139729 100644 --- a/src/platform/x11/event_loop.rs +++ b/src/platform/x11/event_loop.rs @@ -28,12 +28,12 @@ pub(crate) struct EventLoop { impl EventLoop { pub fn new( - window: Rc, handler: impl WindowHandler, parent_handle: Option, - xkb_state: Option, + window: Rc, handler: Box, + parent_handle: Option, xkb_state: Option, ) -> Self { Self { window, - handler: Box::new(handler), + handler, parent_handle, frame_interval: Duration::from_millis(15), event_loop_running: false, diff --git a/src/platform/x11/mod.rs b/src/platform/x11/mod.rs index 220fb807..0514d583 100644 --- a/src/platform/x11/mod.rs +++ b/src/platform/x11/mod.rs @@ -1,6 +1,6 @@ mod xcb_connection; -use raw_window_handle::{DisplayHandle, XcbWindowHandle}; +use raw_window_handle::{DisplayHandle, HasWindowHandle, RawWindowHandle, XcbWindowHandle}; use std::fmt::Formatter; use std::num::NonZero; use std::rc::Rc; @@ -56,3 +56,20 @@ impl std::fmt::Debug for PlatformHandle { .finish() } } + +#[derive(Debug, Clone, PartialEq)] +pub struct ParentWindowHandle { + window_id: u32, +} + +impl ParentWindowHandle { + pub fn extract(window: &impl HasWindowHandle) -> Self { + let window_id = match window.window_handle().unwrap().as_raw() { + RawWindowHandle::Xlib(h) => h.window as u32, + RawWindowHandle::Xcb(h) => h.window.get(), + h => panic!("unsupported parent handle type {:?}", h), + }; + + Self { window_id } + } +} diff --git a/src/platform/x11/window.rs b/src/platform/x11/window.rs index 1b4c0501..43245e2f 100644 --- a/src/platform/x11/window.rs +++ b/src/platform/x11/window.rs @@ -1,4 +1,3 @@ -use raw_window_handle::{HasWindowHandle, RawWindowHandle}; use std::cell::Cell; use std::error::Error; use std::num::NonZero; @@ -17,8 +16,9 @@ use x11rb::wrapper::ConnectionExt as _; use super::X11Connection; use super::{event_loop::EventLoop, visual_info::WindowVisualConfig}; use crate::context::WindowContext; +use crate::handler::WindowHandlerBuilder; use crate::platform::x11::window_shared::WindowInner; -use crate::{WindowHandler, WindowOpenOptions, WindowScalePolicy, WindowSize}; +use crate::{WindowOpenOptions, WindowScalePolicy, WindowSize}; pub struct WindowHandle { window_id: Option>, @@ -28,6 +28,29 @@ pub struct WindowHandle { } impl WindowHandle { + pub fn create_window( + options: WindowOpenOptions, handler: WindowHandlerBuilder, + ) -> WindowHandle { + let (tx, rx) = mpsc::sync_channel::(1); + let (parent_handle, mut window_handle) = ParentHandle::new(); + let join_handle = thread::spawn(move || { + Window::window_thread(options, handler, tx.clone(), Some(parent_handle)).unwrap(); + }); + + let raw_window_handle = rx.recv().unwrap().unwrap(); + window_handle.window_id = Some(raw_window_handle); + window_handle.event_loop_handle = Some(join_handle).into(); + window_handle + } + + pub fn run_until_closed(self) { + let Some(thread) = self.event_loop_handle.take() else { return }; + + thread.join().unwrap_or_else(|err| { + eprintln!("Window thread panicked: {:#?}", err); + }); + } + pub fn close(&self) { self.close_requested.store(true, Ordering::Relaxed); if let Some(event_loop) = self.event_loop_handle.take() { @@ -75,49 +98,8 @@ pub struct Window; type WindowOpenResult = Result, ()>; impl Window { - pub fn open_parented( - parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) -> WindowHandle { - // Convert parent into something that X understands - let parent_id = match parent.window_handle().unwrap().as_raw() { - RawWindowHandle::Xlib(h) => h.window as u32, - RawWindowHandle::Xcb(h) => h.window.get(), - h => panic!("unsupported parent handle type {:?}", h), - }; - - let (tx, rx) = mpsc::sync_channel::(1); - let (parent_handle, mut window_handle) = ParentHandle::new(); - let join_handle = thread::spawn(move || { - Self::window_thread(Some(parent_id), options, build, tx.clone(), Some(parent_handle)) - .unwrap(); - }); - - let raw_window_handle = rx.recv().unwrap().unwrap(); - window_handle.window_id = Some(raw_window_handle); - window_handle.event_loop_handle = Some(join_handle).into(); - window_handle - } - - pub fn open_blocking( - options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) { - let (tx, rx) = mpsc::sync_channel::(1); - - let thread = thread::spawn(move || { - Self::window_thread(None, options, build, tx, None).unwrap(); - }); - - let _ = rx.recv().unwrap().unwrap(); - - thread.join().unwrap_or_else(|err| { - eprintln!("Window thread panicked: {:#?}", err); - }); - } - - fn window_thread( - parent: Option, options: WindowOpenOptions, - build: impl FnOnce(WindowContext) -> H + Send + 'static, + fn window_thread( + options: WindowOpenOptions, build: WindowHandlerBuilder, tx: mpsc::SyncSender, parent_handle: Option, ) -> Result<(), Box> { // Connect to the X server @@ -129,7 +111,7 @@ impl Window { // Get screen information let screen = xcb_connection.screen(); - let parent_id = parent.unwrap_or(screen.root); + let parent_id = options.parent.map(|p| p.window_id).unwrap_or(screen.root); let gc_id = xcb_connection.conn.generate_id()?; xcb_connection.conn.create_gc( @@ -241,7 +223,7 @@ impl Window { gl_context, )); - let handler = build(WindowContext::new(Rc::clone(&inner))); + let handler = build.build(WindowContext::new(Rc::clone(&inner))); // Send an initial window resized event so the user is alerted of // the correct dpi scaling. diff --git a/src/window.rs b/src/window.rs index 5334d35a..240092d8 100644 --- a/src/window.rs +++ b/src/window.rs @@ -1,9 +1,7 @@ -use crate::context::WindowContext; -use crate::handler::WindowHandler; +use crate::handler::WindowHandlerBuilder; use crate::platform; -use crate::window_open_options::WindowOpenOptions; +use crate::*; use dpi::{LogicalSize, PhysicalSize, Pixel}; -use raw_window_handle::HasWindowHandle; use std::marker::PhantomData; pub struct WindowHandle { @@ -17,6 +15,10 @@ impl WindowHandle { Self { window_handle, phantom: PhantomData } } + pub fn run_until_closed(self) { + self.window_handle.run_until_closed() + } + /// Close the window pub fn close(&self) { self.window_handle.close(); @@ -29,24 +31,13 @@ impl WindowHandle { } } -pub struct Window { - _private: (), -} - -impl Window { - pub fn open_parented( - parent: &impl HasWindowHandle, options: WindowOpenOptions, - build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) -> WindowHandle { - let window_handle = platform::Window::open_parented(parent, options, build); - WindowHandle::new(window_handle) - } - - pub fn open_blocking( - options: WindowOpenOptions, build: impl FnOnce(WindowContext) -> H + Send + 'static, - ) { - platform::Window::open_blocking(options, build) - } +pub fn create_window( + builder: WindowOpenOptions, handler: impl FnOnce(WindowContext) -> H + Send + 'static, +) -> WindowHandle { + WindowHandle::new(platform::WindowHandle::create_window( + builder, + WindowHandlerBuilder::new(handler), + )) } /// A window's size, which can be read in either logical or physical pixels. diff --git a/src/window_open_options.rs b/src/window_open_options.rs index e276f9d8..5d32db91 100644 --- a/src/window_open_options.rs +++ b/src/window_open_options.rs @@ -1,7 +1,8 @@ -use dpi::{LogicalSize, Size}; - #[cfg(feature = "opengl")] use crate::gl::GlConfig; +use crate::platform::ParentWindowHandle; +use dpi::{LogicalSize, Size}; +use raw_window_handle::HasWindowHandle; /// The dpi scaling policy of the window #[derive(Default, Debug, Clone, Copy, PartialEq)] @@ -25,6 +26,8 @@ pub struct WindowOpenOptions { /// The dpi scaling policy pub scale: WindowScalePolicy, + pub(crate) parent: Option, + /// If provided, then an OpenGL context will be created for this window. You'll be able to /// access this context through [crate::WindowContext::gl_context]. /// @@ -57,6 +60,12 @@ impl WindowOpenOptions { self } + #[inline] + pub fn with_parent(mut self, parent: &impl HasWindowHandle) -> Self { + self.parent = Some(ParentWindowHandle::extract(parent)); + self + } + #[cfg(feature = "opengl")] #[inline] pub fn with_gl_config(mut self, gl_config: impl Into>) -> Self { @@ -71,6 +80,7 @@ impl Default for WindowOpenOptions { title: String::from("baseview window"), size: LogicalSize { width: 500.0, height: 400.0 }.into(), scale: WindowScalePolicy::default(), + parent: None, #[cfg(feature = "opengl")] gl_config: None, } diff --git a/src/wrappers/win32/window.rs b/src/wrappers/win32/window.rs index 94bd72a8..9dcea9c3 100644 --- a/src/wrappers/win32/window.rs +++ b/src/wrappers/win32/window.rs @@ -51,7 +51,7 @@ pub trait WindowImpl: 'static { /// For any non-trivial operations (e.g. window resizing, GL context creation, etc.), put them in /// [`WindowImpl::after_create`] instead. pub fn create_window( - title: &HSTRING, style: WindowStyle, nc_size: PhysicalSize, parent: HWND, + title: &HSTRING, style: WindowStyle, nc_size: PhysicalSize, parent: Option, _dpi_ctx: &DpiAwarenessContext, initializer: impl FnOnce(HWnd) -> W + 'static, ) -> Result { let instance = HInstance::get_from_dll(); @@ -69,7 +69,7 @@ pub fn create_window( 0, nc_size.width.try_into().unwrap_or(i32::MAX), nc_size.height.try_into().unwrap_or(i32::MAX), - parent, + parent.map(|p| p.as_raw()).unwrap_or(null_mut()), null_mut(), instance.as_raw(), Rc::into_raw(data).cast(), diff --git a/src/wrappers/win32/window/handle.rs b/src/wrappers/win32/window/handle.rs index 8051a7d6..9f323095 100644 --- a/src/wrappers/win32/window/handle.rs +++ b/src/wrappers/win32/window/handle.rs @@ -23,7 +23,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{ /// a handle from this type might still return an "invalid handle" error). /// /// The role of this type is to help safely encapsulating most of the unsafe Win32 HWND APIs. -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Debug)] pub struct HWnd(HWND); impl HWnd { diff --git a/src/wrappers/xlib/error_handler.rs b/src/wrappers/xlib/error_handler.rs index 3a07bd5b..6261248a 100644 --- a/src/wrappers/xlib/error_handler.rs +++ b/src/wrappers/xlib/error_handler.rs @@ -135,7 +135,7 @@ impl Debug for XLibError { impl Display for XLibError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "XLib error: {} (error code {})", &self.display_name, self.inner.error_code) + write!(f, "XLib error: {} (error code {})", self.display_name, self.inner.error_code) } }