1
0
Fork 0

separate Window::open() into three functions (parented, as_if_parented, and blocking)

This commit is contained in:
micah 2020-12-12 16:21:33 -05:00 committed by glowcoil
parent 86bf222601
commit 36e4474c8a
7 changed files with 288 additions and 235 deletions

View file

@ -34,16 +34,10 @@ fn main() {
title: "baseview".into(), title: "baseview".into(),
size: baseview::Size::new(512.0, 512.0), size: baseview::Size::new(512.0, 512.0),
scale: WindowScalePolicy::SystemScaleFactor, scale: WindowScalePolicy::SystemScaleFactor,
parent: baseview::Parent::None,
}; };
let (mut tx, rx) = RingBuffer::new(128).split(); let (mut tx, rx) = RingBuffer::new(128).split();
let opt_app_runner = Window::open(
window_open_options,
|_| OpenWindowExample { rx }
);
::std::thread::spawn(move || { ::std::thread::spawn(move || {
loop { loop {
::std::thread::sleep(Duration::from_secs(5)); ::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 }
);
} }

View file

@ -1,5 +1,3 @@
use raw_window_handle::RawWindowHandle;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
mod win; mod win;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
@ -19,17 +17,3 @@ pub use mouse_cursor::MouseCursor;
pub use window::*; pub use window::*;
pub use window_info::*; pub use window_info::*;
pub use window_open_options::*; 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);
}

View file

@ -17,27 +17,13 @@ use objc::{msg_send, runtime::Object, sel, sel_impl};
use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{macos::MacOSHandle, HasRawWindowHandle, RawWindowHandle};
use crate::{ use crate::{
Event, Parent, WindowHandler, WindowOpenOptions, Event, WindowHandler, WindowOpenOptions, WindowScalePolicy, WindowInfo,
WindowScalePolicy, WindowInfo
}; };
use super::view::{create_view, BASEVIEW_STATE_IVAR}; use super::view::{create_view, BASEVIEW_STATE_IVAR};
use super::keyboard::KeyboardState; 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 { pub struct Window {
/// Only set if we created the parent window, i.e. we are running in /// Only set if we created the parent window, i.e. we are running in
/// parentless mode /// parentless mode
@ -47,104 +33,149 @@ pub struct Window {
} }
impl Window { impl Window {
pub fn open<H, B>( pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
options: WindowOpenOptions, where
build: B P: HasRawWindowHandle,
) -> Option<crate::AppRunner> H: WindowHandler + 'static,
where H: WindowHandler + 'static, B: FnOnce(&mut crate::Window) -> H,
B: FnOnce(&mut crate::Window) -> H, B: Send + 'static,
B: Send + 'static
{ {
let _pool = unsafe { NSAutoreleasePool::new(nil) }; let _pool = unsafe { NSAutoreleasePool::new(nil) };
let mut window = unsafe { let handle = if let RawWindowHandle::MacOS(handle) = parent.raw_window_handle() {
Window { handle
ns_window: None, } else {
ns_view: create_view(&options), 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<H, B>(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<H, B>(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<H, B>(
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 window_handler = Box::new(build(&mut crate::Window::new(&mut window)));
let retain_count_after_build: usize = unsafe { let retain_count_after_build: usize = unsafe {
msg_send![window.ns_view, retainCount] 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 { let window_state_ptr = Box::into_raw(Box::new(WindowState {
window, window,
window_handler, window_handler,
@ -161,8 +192,6 @@ impl Window {
WindowState::setup_timer(window_state_ptr); WindowState::setup_timer(window_state_ptr);
} }
opt_app_runner
} }
} }

View file

@ -29,7 +29,7 @@ use raw_window_handle::{
}; };
use crate::{ use crate::{
Event, MouseButton, MouseEvent, Parent::WithParent, WindowEvent, Event, MouseButton, MouseEvent, WindowEvent,
WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint, WindowHandler, WindowInfo, WindowOpenOptions, WindowScalePolicy, PhyPoint,
}; };
@ -225,17 +225,49 @@ pub struct Window {
hwnd: HWND, hwnd: HWND,
} }
pub struct AppRunner { impl Window {
hwnd: HWND, pub fn open_parented<P, H, B>(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<H, B>(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<H, B>(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 { unsafe {
let mut msg: MSG = std::mem::zeroed(); let mut msg: MSG = std::mem::zeroed();
loop { loop {
let status = GetMessageW(&mut msg, self.hwnd, 0, 0); let status = GetMessageW(&mut msg, hwnd, 0, 0);
if status == -1 { if status == -1 {
break; break;
@ -246,16 +278,16 @@ impl AppRunner {
} }
} }
} }
}
impl Window { fn open<H, B>(
pub fn open<H, B>( parented: bool,
parent: HWND,
options: WindowOpenOptions, options: WindowOpenOptions,
build: B build: B
) -> Option<crate::AppRunner> ) -> HWND
where H: WindowHandler + 'static, where H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static B: Send + 'static,
{ {
unsafe { unsafe {
let mut title: Vec<u16> = OsStr::new(&options.title[..]) let mut title: Vec<u16> = OsStr::new(&options.title[..])
@ -266,14 +298,6 @@ impl Window {
let window_class = register_wnd_class(); let window_class = register_wnd_class();
// todo: manage error ^ // 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 { let scaling = match options.scale {
WindowScalePolicy::SystemScaleFactor => get_scaling().unwrap_or(1.0), WindowScalePolicy::SystemScaleFactor => get_scaling().unwrap_or(1.0),
WindowScalePolicy::ScaleFactor(scale) => scale WindowScalePolicy::ScaleFactor(scale) => scale
@ -289,21 +313,22 @@ impl Window {
bottom: window_info.physical_size().height as i32, 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 flags = if parented {
let parent = match options.parent { WS_CHILD | WS_VISIBLE
WithParent(RawWindowHandle::Windows(h)) => { } else {
flags = WS_CHILD | WS_VISIBLE; WS_POPUPWINDOW
h.hwnd | WS_CAPTION
} | WS_VISIBLE
| WS_SIZEBOX
WithParent(h) => panic!("unsupported parent handle {:?}", h), | WS_MINIMIZEBOX
| WS_MAXIMIZEBOX
_ => { | WS_CLIPSIBLINGS
AdjustWindowRectEx(&mut rect, flags, FALSE, 0);
null_mut()
}
}; };
if !parented {
AdjustWindowRectEx(&mut rect, flags, FALSE, 0);
}
let hwnd = CreateWindowExW( let hwnd = CreateWindowExW(
0, 0,
window_class as _, window_class as _,
@ -333,11 +358,7 @@ impl Window {
SetWindowLongPtrW(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _); SetWindowLongPtrW(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _);
SetTimer(hwnd, WIN_FRAME_TIMER, 15, None); SetTimer(hwnd, WIN_FRAME_TIMER, 15, None);
if let crate::Parent::None = options.parent { hwnd
Some(crate::AppRunner(AppRunner { hwnd }))
} else {
None
}
} }
} }
} }

View file

@ -2,22 +2,19 @@ use std::marker::PhantomData;
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle}; use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
use crate::WindowHandler; use crate::event::Event;
use crate::window_open_options::WindowOpenOptions; use crate::window_open_options::WindowOpenOptions;
#[cfg(target_os = "macos")]
use crate::macos as platform;
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
use crate::win as platform; use crate::win as platform;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
use crate::x11 as platform; use crate::x11 as platform;
#[cfg(target_os = "macos")]
use crate::macos as platform;
pub struct AppRunner(pub(crate) platform::AppRunner); pub trait WindowHandler {
fn on_frame(&mut self);
impl AppRunner { fn on_event(&mut self, window: &mut Window, event: Event);
pub fn app_run_blocking(self){
self.0.app_run_blocking();
}
} }
pub struct Window<'a> { pub struct Window<'a> {
@ -34,15 +31,32 @@ impl<'a> Window<'a> {
} }
} }
pub fn open<H, B>( pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
options: WindowOpenOptions, where
build: B P: HasRawWindowHandle,
) -> Option<AppRunner> H: WindowHandler + 'static,
where H: WindowHandler + 'static, B: FnOnce(&mut Window) -> H,
B: FnOnce(&mut Window) -> H, B: Send + 'static,
B: Send + 'static
{ {
platform::Window::open::<H, B>(options, build) platform::Window::open_parented::<P, H, B>(parent, options, build)
}
pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
where
H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H,
B: Send + 'static,
{
platform::Window::open_as_if_parented::<H, B>(options, build)
}
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where
H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H,
B: Send + 'static,
{
platform::Window::open_blocking::<H, B>(options, build)
} }
} }

View file

@ -1,4 +1,4 @@
use crate::{Parent, Size}; use crate::Size;
/// The dpi scaling policy of the window /// The dpi scaling policy of the window
#[derive(Debug)] #[derive(Debug)]
@ -10,7 +10,6 @@ pub enum WindowScalePolicy {
} }
/// The options for opening a new window /// The options for opening a new window
#[derive(Debug)]
pub struct WindowOpenOptions { pub struct WindowOpenOptions {
pub title: String, pub title: String,
@ -22,6 +21,4 @@ pub struct WindowOpenOptions {
/// The dpi scaling policy /// The dpi scaling policy
pub scale: WindowScalePolicy, pub scale: WindowScalePolicy,
pub parent: Parent,
} }

View file

@ -11,7 +11,7 @@ use raw_window_handle::{
use super::XcbConnection; use super::XcbConnection;
use crate::{ use crate::{
Event, MouseButton, MouseCursor, MouseEvent, Parent, ScrollDelta, Event, MouseButton, MouseCursor, MouseEvent, ScrollDelta,
WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions, WindowEvent, WindowHandler, WindowInfo, WindowOpenOptions,
WindowScalePolicy, PhyPoint, PhySize, WindowScalePolicy, PhyPoint, PhySize,
}; };
@ -30,57 +30,77 @@ pub struct Window {
new_physical_size: Option<PhySize>, new_physical_size: Option<PhySize>,
} }
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 { type WindowOpenResult = Result<SendableRwh, ()>;
thread: std::thread::JoinHandle<()>,
}
impl AppRunner {
pub fn app_run_blocking(self) {
let _ = self.thread.join();
}
}
type WindowOpenResult = Result<(), ()>;
impl Window { impl Window {
pub fn open<H, B>( pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
options: WindowOpenOptions, where
build: B P: HasRawWindowHandle,
) -> Option<crate::AppRunner> H: WindowHandler + 'static,
where H: WindowHandler, B: FnOnce(&mut crate::Window) -> H,
B: FnOnce(&mut crate::Window) -> H, B: Send + 'static,
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::<WindowOpenResult>(1); let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let thread = thread::spawn(move || { let thread = thread::spawn(move || {
if let Err(e) = Self::window_thread::<H, B>(options, build, tx.clone()) { Self::window_thread(Some(parent_id), options, build, tx.clone());
let _ = tx.send(Err(e));
}
}); });
// FIXME: placeholder types for returning errors in the future let _ = rx.recv().unwrap().unwrap();
let _ = rx.recv(); }
if is_not_parented { pub fn open_as_if_parented<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
Some(crate::AppRunner(AppRunner { thread })) where
} else { H: WindowHandler + 'static,
None B: FnOnce(&mut crate::Window) -> H,
} B: Send + 'static,
{
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let thread = thread::spawn(move || {
Self::window_thread(None, options, build, tx.clone());
});
rx.recv().unwrap().unwrap().0
}
pub fn open_blocking<H, B>(options: WindowOpenOptions, build: B)
where
H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static,
{
let (tx, rx) = mpsc::sync_channel::<WindowOpenResult>(1);
let thread = thread::spawn(move || {
Self::window_thread(None, options, build, tx.clone());
});
let _ = rx.recv().unwrap().unwrap();
thread.join();
} }
fn window_thread<H, B>( fn window_thread<H, B>(
parent: Option<u32>,
options: WindowOpenOptions, build: B, options: WindowOpenOptions, build: B,
tx: mpsc::SyncSender<WindowOpenResult>, tx: mpsc::SyncSender<WindowOpenResult>,
) -> WindowOpenResult )
where H: WindowHandler, where H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H, B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static B: Send + 'static,
{ {
// Connect to the X server // Connect to the X server
// FIXME: baseview error type instead of unwrap() // FIXME: baseview error type instead of unwrap()
@ -95,15 +115,7 @@ impl Window {
let foreground = xcb_connection.conn.generate_id(); let foreground = xcb_connection.conn.generate_id();
// Convert parent into something that X understands let parent_id = parent.unwrap_or_else(|| screen.root());
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(),
};
xcb::create_gc( xcb::create_gc(
&xcb_connection.conn, &xcb_connection.conn,
@ -188,10 +200,9 @@ impl Window {
let mut handler = build(&mut crate::Window::new(&mut 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); window.run_event_loop(&mut handler);
Ok(())
} }
pub fn window_info(&self) -> &WindowInfo { pub fn window_info(&self) -> &WindowInfo {