diff --git a/examples/open_window.rs b/examples/open_window.rs index 97994ad..ed3d0fc 100644 --- a/examples/open_window.rs +++ b/examples/open_window.rs @@ -34,16 +34,10 @@ fn main() { title: "baseview".into(), size: baseview::Size::new(512.0, 512.0), scale: WindowScalePolicy::SystemScaleFactor, - parent: baseview::Parent::None, }; let (mut tx, rx) = RingBuffer::new(128).split(); - let opt_app_runner = Window::open( - window_open_options, - |_| OpenWindowExample { rx } - ); - ::std::thread::spawn(move || { loop { ::std::thread::sleep(Duration::from_secs(5)); @@ -54,5 +48,8 @@ fn main() { } }); - opt_app_runner.unwrap().app_run_blocking(); + Window::open_blocking( + window_open_options, + |_| OpenWindowExample { rx } + ); } diff --git a/src/lib.rs b/src/lib.rs index f81b9a0..a7bbf1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -use raw_window_handle::RawWindowHandle; - #[cfg(target_os = "windows")] mod win; #[cfg(target_os = "linux")] @@ -19,17 +17,3 @@ pub use mouse_cursor::MouseCursor; pub use window::*; pub use window_info::*; pub use window_open_options::*; - -#[derive(Debug)] -pub enum Parent { - None, - AsIfParented, - WithParent(RawWindowHandle), -} - -unsafe impl Send for Parent {} - -pub trait WindowHandler { - fn on_frame(&mut self); - fn on_event(&mut self, window: &mut Window, event: Event); -} diff --git a/src/macos/window.rs b/src/macos/window.rs index ad9f0b1..3ff19bb 100644 --- a/src/macos/window.rs +++ b/src/macos/window.rs @@ -17,27 +17,13 @@ use objc::{msg_send, runtime::Object, sel, sel_impl}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; use crate::{ - Event, Parent, WindowHandler, WindowOpenOptions, - WindowScalePolicy, WindowInfo + Event, WindowHandler, WindowOpenOptions, WindowScalePolicy, WindowInfo, }; use super::view::{create_view, BASEVIEW_STATE_IVAR}; use super::keyboard::KeyboardState; -pub struct AppRunner; - -impl AppRunner { - pub fn app_run_blocking(self) { - unsafe { - // Get reference to already created shared NSApplication object - // and run the main loop - NSApp().run(); - } - } -} - - pub struct Window { /// Only set if we created the parent window, i.e. we are running in /// parentless mode @@ -47,104 +33,149 @@ pub struct Window { } impl Window { - pub fn open( - options: WindowOpenOptions, - build: B - ) -> Option - where H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static + pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) + where + P: HasRawWindowHandle, + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, { let _pool = unsafe { NSAutoreleasePool::new(nil) }; - let mut window = unsafe { - Window { - ns_window: None, - ns_view: create_view(&options), - } + let handle = if let RawWindowHandle::MacOS(handle) = parent.raw_window_handle() { + handle + } else { + panic!("Not a macOS window"); }; + let ns_view = unsafe { create_view(&options) }; + + let window = Window { + ns_window: None, + ns_view, + }; + + Self::init(window, build); + + unsafe { + let _: id = msg_send![handle.ns_view as *mut Object, addSubview: ns_view]; + } + } + + pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> RawWindowHandle + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let _pool = unsafe { NSAutoreleasePool::new(nil) }; + + let ns_view = unsafe { create_view(&options) }; + + let window = Window { + ns_window: None, + ns_view, + }; + + let raw_window_handle = window.raw_window_handle(); + + Self::init(window, build); + + raw_window_handle + } + + pub fn open_blocking(options: WindowOpenOptions, build: B) + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let _pool = unsafe { NSAutoreleasePool::new(nil) }; + + // It seems prudent to run NSApp() here before doing other + // work. It runs [NSApplication sharedApplication], which is + // what is run at the very start of the Xcode-generated main + // function of a cocoa app according to: + // https://developer.apple.com/documentation/appkit/nsapplication + let app = unsafe { NSApp() }; + + unsafe { + app.setActivationPolicy_( + NSApplicationActivationPolicyRegular + ); + } + + let scaling = match options.scale { + WindowScalePolicy::ScaleFactor(scale) => scale, + WindowScalePolicy::SystemScaleFactor => 1.0, + }; + + let window_info = WindowInfo::from_logical_size( + options.size, + scaling + ); + + let rect = NSRect::new( + NSPoint::new(0.0, 0.0), + NSSize::new( + window_info.logical_size().width as f64, + window_info.logical_size().height as f64 + ), + ); + + let ns_window = unsafe { + let ns_window = NSWindow::alloc(nil) + .initWithContentRect_styleMask_backing_defer_( + rect, + NSWindowStyleMask::NSTitledWindowMask, + NSBackingStoreBuffered, + NO, + ) + .autorelease(); + ns_window.center(); + + let title = NSString::alloc(nil) + .init_str(&options.title) + .autorelease(); + ns_window.setTitle_(title); + + ns_window.makeKeyAndOrderFront_(nil); + + ns_window + }; + + let ns_view = unsafe { create_view(&options) }; + + let window = Window { + ns_window: Some(ns_window), + ns_view, + }; + + Self::init(window, build); + + unsafe { + ns_window.setContentView_(ns_view); + } + + unsafe { + app.run(); + } + } + + fn init( + mut window: Window, + build: B + ) + where H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { let window_handler = Box::new(build(&mut crate::Window::new(&mut window))); let retain_count_after_build: usize = unsafe { msg_send![window.ns_view, retainCount] }; - let opt_app_runner = match options.parent { - Parent::WithParent(RawWindowHandle::MacOS(handle)) => { - unsafe { - let () = msg_send![ - handle.ns_view as *mut Object, - addSubview: window.ns_view - ]; - } - - None - }, - Parent::WithParent(_) => { - panic!("Not a macOS window"); - }, - Parent::AsIfParented => { - None - }, - Parent::None => { - // It seems prudent to run NSApp() here before doing other - // work. It runs [NSApplication sharedApplication], which is - // what is run at the very start of the Xcode-generated main - // function of a cocoa app according to: - // https://developer.apple.com/documentation/appkit/nsapplication - unsafe { - let app = NSApp(); - app.setActivationPolicy_( - NSApplicationActivationPolicyRegular - ); - } - - let scaling = match options.scale { - WindowScalePolicy::ScaleFactor(scale) => scale, - WindowScalePolicy::SystemScaleFactor => 1.0, - }; - - let window_info = WindowInfo::from_logical_size( - options.size, - scaling - ); - - let rect = NSRect::new( - NSPoint::new(0.0, 0.0), - NSSize::new( - window_info.logical_size().width as f64, - window_info.logical_size().height as f64 - ), - ); - - unsafe { - let ns_window = NSWindow::alloc(nil) - .initWithContentRect_styleMask_backing_defer_( - rect, - NSWindowStyleMask::NSTitledWindowMask, - NSBackingStoreBuffered, - NO, - ) - .autorelease(); - ns_window.center(); - - let title = NSString::alloc(nil) - .init_str(&options.title) - .autorelease(); - ns_window.setTitle_(title); - - ns_window.makeKeyAndOrderFront_(nil); - - ns_window.setContentView_(window.ns_view); - - window.ns_window = Some(ns_window); - - Some(crate::AppRunner(AppRunner)) - } - }, - }; - let window_state_ptr = Box::into_raw(Box::new(WindowState { window, window_handler, @@ -161,8 +192,6 @@ impl Window { WindowState::setup_timer(window_state_ptr); } - - opt_app_runner } } diff --git a/src/win/window.rs b/src/win/window.rs index 67f2af9..57c762f 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -29,7 +29,7 @@ use raw_window_handle::{ }; use crate::{ - Event, MouseButton, MouseEvent, Parent::WithParent, WindowEvent, + Event, MouseButton, MouseEvent, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, }; @@ -225,17 +225,49 @@ pub struct Window { hwnd: HWND, } -pub struct AppRunner { - hwnd: HWND, -} +impl Window { + pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) + where + P: HasRawWindowHandle, + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let parent = match parent.raw_window_handle() { + RawWindowHandle::Windows(h) => h.hwnd as HWND, + h => panic!("unsupported parent handle {:?}", h), + }; + + Self::open(true, parent, options, build); + } + + pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> RawWindowHandle + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let hwnd = Self::open(true, null_mut(), options, build); + + RawWindowHandle::Windows(WindowsHandle { + hwnd: hwnd as *mut std::ffi::c_void, + ..WindowsHandle::empty() + }) + } + + pub fn open_blocking(options: WindowOpenOptions, build: B) + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let hwnd = Self::open(false, null_mut(), options, build); -impl AppRunner { - pub fn app_run_blocking(self) { unsafe { let mut msg: MSG = std::mem::zeroed(); loop { - let status = GetMessageW(&mut msg, self.hwnd, 0, 0); + let status = GetMessageW(&mut msg, hwnd, 0, 0); if status == -1 { break; @@ -246,16 +278,16 @@ impl AppRunner { } } } -} -impl Window { - pub fn open( + fn open( + parented: bool, + parent: HWND, options: WindowOpenOptions, build: B - ) -> Option - where H: WindowHandler + 'static, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static + ) -> HWND + where H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, { unsafe { let mut title: Vec = OsStr::new(&options.title[..]) @@ -265,14 +297,6 @@ impl Window { let window_class = register_wnd_class(); // todo: manage error ^ - - let mut flags = WS_POPUPWINDOW - | WS_CAPTION - | WS_VISIBLE - | WS_SIZEBOX - | WS_MINIMIZEBOX - | WS_MAXIMIZEBOX - | WS_CLIPSIBLINGS; let scaling = match options.scale { WindowScalePolicy::SystemScaleFactor => get_scaling().unwrap_or(1.0), @@ -289,21 +313,22 @@ impl Window { bottom: window_info.physical_size().height as i32, }; - // todo: add check flags https://github.com/wrl/rutabaga/blob/f30ff67e157375cafdbafe5fb549f1790443a3a8/src/platform/win/window.c#L351 - let parent = match options.parent { - WithParent(RawWindowHandle::Windows(h)) => { - flags = WS_CHILD | WS_VISIBLE; - h.hwnd - } - - WithParent(h) => panic!("unsupported parent handle {:?}", h), - - _ => { - AdjustWindowRectEx(&mut rect, flags, FALSE, 0); - null_mut() - } + let flags = if parented { + WS_CHILD | WS_VISIBLE + } else { + WS_POPUPWINDOW + | WS_CAPTION + | WS_VISIBLE + | WS_SIZEBOX + | WS_MINIMIZEBOX + | WS_MAXIMIZEBOX + | WS_CLIPSIBLINGS }; + if !parented { + AdjustWindowRectEx(&mut rect, flags, FALSE, 0); + } + let hwnd = CreateWindowExW( 0, window_class as _, @@ -333,11 +358,7 @@ impl Window { SetWindowLongPtrW(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _); SetTimer(hwnd, WIN_FRAME_TIMER, 15, None); - if let crate::Parent::None = options.parent { - Some(crate::AppRunner(AppRunner { hwnd })) - } else { - None - } + hwnd } } } diff --git a/src/window.rs b/src/window.rs index eff9a57..480f2ce 100644 --- a/src/window.rs +++ b/src/window.rs @@ -2,22 +2,19 @@ use std::marker::PhantomData; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; -use crate::WindowHandler; +use crate::event::Event; use crate::window_open_options::WindowOpenOptions; +#[cfg(target_os = "macos")] +use crate::macos as platform; #[cfg(target_os = "windows")] use crate::win as platform; #[cfg(target_os = "linux")] use crate::x11 as platform; -#[cfg(target_os = "macos")] -use crate::macos as platform; -pub struct AppRunner(pub(crate) platform::AppRunner); - -impl AppRunner { - pub fn app_run_blocking(self){ - self.0.app_run_blocking(); - } +pub trait WindowHandler { + fn on_frame(&mut self); + fn on_event(&mut self, window: &mut Window, event: Event); } pub struct Window<'a> { @@ -34,15 +31,32 @@ impl<'a> Window<'a> { } } - pub fn open( - options: WindowOpenOptions, - build: B - ) -> Option - where H: WindowHandler + 'static, - B: FnOnce(&mut Window) -> H, - B: Send + 'static + pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) + where + P: HasRawWindowHandle, + H: WindowHandler + 'static, + B: FnOnce(&mut Window) -> H, + B: Send + 'static, { - platform::Window::open::(options, build) + platform::Window::open_parented::(parent, options, build) + } + + pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> RawWindowHandle + where + H: WindowHandler + 'static, + B: FnOnce(&mut Window) -> H, + B: Send + 'static, + { + platform::Window::open_as_if_parented::(options, build) + } + + pub fn open_blocking(options: WindowOpenOptions, build: B) + where + H: WindowHandler + 'static, + B: FnOnce(&mut Window) -> H, + B: Send + 'static, + { + platform::Window::open_blocking::(options, build) } } diff --git a/src/window_open_options.rs b/src/window_open_options.rs index 2cf7a9d..4299ae4 100644 --- a/src/window_open_options.rs +++ b/src/window_open_options.rs @@ -1,4 +1,4 @@ -use crate::{Parent, Size}; +use crate::Size; /// The dpi scaling policy of the window #[derive(Debug)] @@ -10,7 +10,6 @@ pub enum WindowScalePolicy { } /// The options for opening a new window -#[derive(Debug)] pub struct WindowOpenOptions { pub title: String, @@ -22,6 +21,4 @@ pub struct WindowOpenOptions { /// The dpi scaling policy pub scale: WindowScalePolicy, - - pub parent: Parent, -} \ No newline at end of file +} diff --git a/src/x11/window.rs b/src/x11/window.rs index aad7ba3..0cfe30ec 100644 --- a/src/x11/window.rs +++ b/src/x11/window.rs @@ -11,7 +11,7 @@ use raw_window_handle::{ use super::XcbConnection; use crate::{ - Event, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, + Event, MouseButton, MouseCursor, MouseEvent, ScrollDelta, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, PhySize, }; @@ -30,57 +30,77 @@ pub struct Window { new_physical_size: Option, } -pub struct WindowHandle; +// Hack to allow sending a RawWindowHandle between threads. Do not make public +struct SendableRwh(RawWindowHandle); +unsafe impl Send for SendableRwh {} -pub struct AppRunner { - thread: std::thread::JoinHandle<()>, -} - -impl AppRunner { - pub fn app_run_blocking(self) { - let _ = self.thread.join(); - } -} - -type WindowOpenResult = Result<(), ()>; +type WindowOpenResult = Result; impl Window { - pub fn open( - options: WindowOpenOptions, - build: B - ) -> Option - where H: WindowHandler, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static + pub fn open_parented(parent: &P, options: WindowOpenOptions, build: B) + where + P: HasRawWindowHandle, + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, { - let is_not_parented = matches!(options.parent, Parent::None); + // Convert parent into something that X understands + let parent_id = match parent.raw_window_handle() { + RawWindowHandle::Xlib(h) => h.window as u32, + RawWindowHandle::Xcb(h) => h.window, + h => panic!("unsupported parent handle type {:?}", h), + }; let (tx, rx) = mpsc::sync_channel::(1); let thread = thread::spawn(move || { - if let Err(e) = Self::window_thread::(options, build, tx.clone()) { - let _ = tx.send(Err(e)); - } + Self::window_thread(Some(parent_id), options, build, tx.clone()); }); - // FIXME: placeholder types for returning errors in the future - let _ = rx.recv(); + let _ = rx.recv().unwrap().unwrap(); + } - if is_not_parented { - Some(crate::AppRunner(AppRunner { thread })) - } else { - None - } + pub fn open_as_if_parented(options: WindowOpenOptions, build: B) -> RawWindowHandle + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let (tx, rx) = mpsc::sync_channel::(1); + + let thread = thread::spawn(move || { + Self::window_thread(None, options, build, tx.clone()); + }); + + rx.recv().unwrap().unwrap().0 + } + + pub fn open_blocking(options: WindowOpenOptions, build: B) + where + H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, + { + let (tx, rx) = mpsc::sync_channel::(1); + + let thread = thread::spawn(move || { + Self::window_thread(None, options, build, tx.clone()); + }); + + let _ = rx.recv().unwrap().unwrap(); + + thread.join(); } fn window_thread( + parent: Option, options: WindowOpenOptions, build: B, tx: mpsc::SyncSender, - ) -> WindowOpenResult - where H: WindowHandler, - B: FnOnce(&mut crate::Window) -> H, - B: Send + 'static + ) + where H: WindowHandler + 'static, + B: FnOnce(&mut crate::Window) -> H, + B: Send + 'static, { // Connect to the X server // FIXME: baseview error type instead of unwrap() @@ -95,15 +115,7 @@ impl Window { let foreground = xcb_connection.conn.generate_id(); - // Convert parent into something that X understands - let parent_id = match options.parent { - Parent::WithParent(RawWindowHandle::Xlib(h)) => h.window as u32, - Parent::WithParent(RawWindowHandle::Xcb(h)) => h.window, - Parent::WithParent(h) => - panic!("unsupported parent handle type {:?}", h), - - Parent::None | Parent::AsIfParented => screen.root(), - }; + let parent_id = parent.unwrap_or_else(|| screen.root()); xcb::create_gc( &xcb_connection.conn, @@ -188,10 +200,9 @@ impl Window { let mut handler = build(&mut crate::Window::new(&mut window)); - let _ = tx.send(Ok(())); + let _ = tx.send(Ok(SendableRwh(window.raw_window_handle()))); window.run_event_loop(&mut handler); - Ok(()) } pub fn window_info(&self) -> &WindowInfo {