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(),
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 }
);
}

View file

@ -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);
}

View file

@ -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<H, B>(
options: WindowOpenOptions,
build: B
) -> Option<crate::AppRunner>
where H: WindowHandler + 'static,
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static
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 _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<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 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
}
}

View file

@ -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<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 {
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<H, B>(
fn open<H, B>(
parented: bool,
parent: HWND,
options: WindowOpenOptions,
build: B
) -> Option<crate::AppRunner>
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<u16> = 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
}
}
}

View file

@ -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<H, B>(
options: WindowOpenOptions,
build: B
) -> Option<AppRunner>
where H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H,
B: Send + 'static
pub fn open_parented<P, H, B>(parent: &P, options: WindowOpenOptions, build: B)
where
P: HasRawWindowHandle,
H: WindowHandler + 'static,
B: FnOnce(&mut Window) -> H,
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
#[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,
}
}

View file

@ -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<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 {
thread: std::thread::JoinHandle<()>,
}
impl AppRunner {
pub fn app_run_blocking(self) {
let _ = self.thread.join();
}
}
type WindowOpenResult = Result<(), ()>;
type WindowOpenResult = Result<SendableRwh, ()>;
impl Window {
pub fn open<H, B>(
options: WindowOpenOptions,
build: B
) -> Option<crate::AppRunner>
where H: WindowHandler,
B: FnOnce(&mut crate::Window) -> H,
B: Send + 'static
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 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 thread = thread::spawn(move || {
if let Err(e) = Self::window_thread::<H, B>(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<H, B>(options: WindowOpenOptions, build: B) -> RawWindowHandle
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());
});
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>(
parent: Option<u32>,
options: WindowOpenOptions, build: B,
tx: mpsc::SyncSender<WindowOpenResult>,
) -> 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 {