Split WindowHandle and add try_send_message method (#61)
* Add WindowHandle::try_send_message, implement it on macOS * Add Send constraint to WindowHandler::Message * Assert at compile-time that WindowHandle is Sync * macOS: remove runtime timer, use frame timer instead * Use wait-free spsc message chan; split off AppRunner from WindowHandle * Add comment to WindowState static assertions code * Clean up * Split off AppRunner from WindowHandle on Windows and Linux * README: add messages milestone row, add check mark to macOS column * macOS: add and use MESSAGE_QUEUE_LEN constant * macOS: clean up
This commit is contained in:
commit
72d55c9dba
|
@ -17,6 +17,8 @@ license = "MIT OR Apache-2.0"
|
||||||
log = "0.4.11"
|
log = "0.4.11"
|
||||||
keyboard-types = { version = "0.5.0", default-features = false }
|
keyboard-types = { version = "0.5.0", default-features = false }
|
||||||
raw-window-handle = "0.3.3"
|
raw-window-handle = "0.3.3"
|
||||||
|
rtrb = "0.1.1"
|
||||||
|
static_assertions = "1.1.0"
|
||||||
|
|
||||||
[target.'cfg(target_os="linux")'.dependencies]
|
[target.'cfg(target_os="linux")'.dependencies]
|
||||||
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
|
xcb = { version = "0.9", features = ["thread", "xlib_xcb", "dri2"] }
|
||||||
|
|
|
@ -18,6 +18,7 @@ Below is a proposed list of milestones (roughly in-order) and their status. Subj
|
||||||
| Can find DPI scale factor | | | :heavy_check_mark: |
|
| Can find DPI scale factor | | | :heavy_check_mark: |
|
||||||
| Basic event handling (mouse, keyboard) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Basic event handling (mouse, keyboard) | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
| Parent window support | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
| Parent window support | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||||
|
| Send messages | | :heavy_check_mark: | |
|
||||||
| *(Converge on a common API for all platforms?)* | | | |
|
| *(Converge on a common API for all platforms?)* | | | |
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use baseview::{Event, Window, WindowHandler, WindowScalePolicy};
|
use baseview::{Event, Window, WindowHandler, WindowScalePolicy};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum Message {
|
||||||
|
Hello
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct OpenWindowExample;
|
struct OpenWindowExample;
|
||||||
|
|
||||||
|
|
||||||
impl WindowHandler for OpenWindowExample {
|
impl WindowHandler for OpenWindowExample {
|
||||||
type Message = ();
|
type Message = Message;
|
||||||
|
|
||||||
fn on_frame(&mut self) {}
|
fn on_frame(&mut self) {}
|
||||||
|
|
||||||
|
@ -15,9 +25,12 @@ impl WindowHandler for OpenWindowExample {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_message(&mut self, _window: &mut Window, _message: Self::Message) {}
|
fn on_message(&mut self, _window: &mut Window, message: Self::Message) {
|
||||||
|
println!("Message: {:?}", message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let window_open_options = baseview::WindowOpenOptions {
|
let window_open_options = baseview::WindowOpenOptions {
|
||||||
title: "baseview".into(),
|
title: "baseview".into(),
|
||||||
|
@ -26,6 +39,20 @@ fn main() {
|
||||||
parent: baseview::Parent::None,
|
parent: baseview::Parent::None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle = Window::open(window_open_options, |_| OpenWindowExample);
|
let (mut handle, opt_app_runner) = Window::open(
|
||||||
handle.app_run_blocking();
|
window_open_options,
|
||||||
|
|_| OpenWindowExample
|
||||||
|
);
|
||||||
|
|
||||||
|
::std::thread::spawn(move || {
|
||||||
|
loop {
|
||||||
|
::std::thread::sleep(Duration::from_secs(5));
|
||||||
|
|
||||||
|
if let Err(_) = handle.try_send_message(Message::Hello){
|
||||||
|
println!("Failed sending message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
opt_app_runner.unwrap().app_run_blocking();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub use window::*;
|
||||||
pub use window_info::*;
|
pub use window_info::*;
|
||||||
pub use window_open_options::*;
|
pub use window_open_options::*;
|
||||||
|
|
||||||
|
const MESSAGE_QUEUE_LEN: usize = 128;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Parent {
|
pub enum Parent {
|
||||||
None,
|
None,
|
||||||
|
@ -30,7 +32,7 @@ pub enum Parent {
|
||||||
unsafe impl Send for Parent {}
|
unsafe impl Send for Parent {}
|
||||||
|
|
||||||
pub trait WindowHandler {
|
pub trait WindowHandler {
|
||||||
type Message;
|
type Message: Send;
|
||||||
|
|
||||||
fn on_frame(&mut self);
|
fn on_frame(&mut self);
|
||||||
fn on_event(&mut self, window: &mut Window, event: Event);
|
fn on_event(&mut self, window: &mut Window, event: Event);
|
||||||
|
|
|
@ -20,7 +20,9 @@ use crate::{
|
||||||
};
|
};
|
||||||
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
|
use crate::MouseEvent::{ButtonPressed, ButtonReleased};
|
||||||
|
|
||||||
use super::window::{WindowState, WINDOW_STATE_IVAR_NAME, FRAME_TIMER_IVAR_NAME};
|
use super::window::{
|
||||||
|
WindowState, WINDOW_STATE_IVAR_NAME, FRAME_TIMER_IVAR_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
pub(super) unsafe fn create_view<H: WindowHandler>(
|
pub(super) unsafe fn create_view<H: WindowHandler>(
|
||||||
|
@ -189,6 +191,7 @@ extern "C" fn trigger_on_frame<H: WindowHandler>(
|
||||||
WindowState::from_field(this)
|
WindowState::from_field(this)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state.handle_messages();
|
||||||
state.trigger_frame();
|
state.trigger_frame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
/// macOS window handling
|
|
||||||
///
|
|
||||||
/// Inspired by implementation in https://github.com/antonok-edm/vst_window
|
|
||||||
|
|
||||||
use std::ffi::c_void;
|
use std::ffi::c_void;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -16,10 +12,11 @@ use keyboard_types::KeyboardEvent;
|
||||||
use objc::{msg_send, runtime::Object, sel, sel_impl};
|
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 rtrb::{RingBuffer, Producer, Consumer, PushError};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Event, Parent, WindowHandler, WindowOpenOptions,
|
Event, Parent, WindowHandler, WindowOpenOptions,
|
||||||
WindowScalePolicy, WindowInfo
|
WindowScalePolicy, WindowInfo, MESSAGE_QUEUE_LEN
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::view::create_view;
|
use super::view::create_view;
|
||||||
|
@ -42,10 +39,10 @@ pub struct Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct WindowHandle;
|
pub struct AppRunner;
|
||||||
|
|
||||||
|
|
||||||
impl WindowHandle {
|
impl AppRunner {
|
||||||
pub fn app_run_blocking(self) {
|
pub fn app_run_blocking(self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
// Get reference to already created shared NSApplication object
|
// Get reference to already created shared NSApplication object
|
||||||
|
@ -55,18 +52,35 @@ impl WindowHandle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct WindowHandle<H: WindowHandler> {
|
||||||
|
message_tx: Producer<H::Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
|
pub fn try_send_message(
|
||||||
|
&mut self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
self.message_tx.push(message)
|
||||||
|
.map_err(|PushError::Full(message)| message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
pub fn open<H, B>(
|
pub fn open<H, B>(
|
||||||
options: WindowOpenOptions,
|
options: WindowOpenOptions,
|
||||||
build: B
|
build: B
|
||||||
) -> crate::WindowHandle
|
) -> (crate::WindowHandle<H>, Option<crate::AppRunner>)
|
||||||
where H: WindowHandler,
|
where H: WindowHandler,
|
||||||
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 = match options.parent {
|
let (mut window, opt_app_runner) = match options.parent {
|
||||||
Parent::WithParent(parent) => {
|
Parent::WithParent(parent) => {
|
||||||
if let RawWindowHandle::MacOS(handle) = parent {
|
if let RawWindowHandle::MacOS(handle) = parent {
|
||||||
let ns_view = handle.ns_view as *mut objc::runtime::Object;
|
let ns_view = handle.ns_view as *mut objc::runtime::Object;
|
||||||
|
@ -76,10 +90,12 @@ impl Window {
|
||||||
|
|
||||||
let _: id = msg_send![ns_view, addSubview: subview];
|
let _: id = msg_send![ns_view, addSubview: subview];
|
||||||
|
|
||||||
Window {
|
let window = Window {
|
||||||
ns_window: None,
|
ns_window: None,
|
||||||
ns_view: subview,
|
ns_view: subview,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(window, None)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("Not a macOS window");
|
panic!("Not a macOS window");
|
||||||
|
@ -90,10 +106,12 @@ impl Window {
|
||||||
create_view::<H>(&options)
|
create_view::<H>(&options)
|
||||||
};
|
};
|
||||||
|
|
||||||
Window {
|
let window = Window {
|
||||||
ns_window: None,
|
ns_window: None,
|
||||||
ns_view,
|
ns_view,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(window, None)
|
||||||
},
|
},
|
||||||
Parent::None => {
|
Parent::None => {
|
||||||
// It seems prudent to run NSApp() here before doing other
|
// It seems prudent to run NSApp() here before doing other
|
||||||
|
@ -150,20 +168,26 @@ impl Window {
|
||||||
|
|
||||||
ns_window.setContentView_(subview);
|
ns_window.setContentView_(subview);
|
||||||
|
|
||||||
Window {
|
let window = Window {
|
||||||
ns_window: Some(ns_window),
|
ns_window: Some(ns_window),
|
||||||
ns_view: subview,
|
ns_view: subview,
|
||||||
}
|
};
|
||||||
|
|
||||||
|
(window, Some(crate::AppRunner(AppRunner)))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
let window_handler = build(&mut crate::Window(&mut window));
|
let window_handler = build(&mut crate::Window(&mut window));
|
||||||
|
|
||||||
|
let (message_tx, message_rx) = RingBuffer::new(MESSAGE_QUEUE_LEN)
|
||||||
|
.split();
|
||||||
|
|
||||||
let window_state_arc = Arc::new(WindowState {
|
let window_state_arc = Arc::new(WindowState {
|
||||||
window,
|
window,
|
||||||
window_handler,
|
window_handler,
|
||||||
keyboard_state: KeyboardState::new(),
|
keyboard_state: KeyboardState::new(),
|
||||||
|
message_rx
|
||||||
});
|
});
|
||||||
|
|
||||||
let window_state_pointer = Arc::into_raw(
|
let window_state_pointer = Arc::into_raw(
|
||||||
|
@ -177,8 +201,7 @@ impl Window {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Activate timer after window handler is setup and save a pointer to
|
// Setup frame timer once window state is stored
|
||||||
// it, so that it can be invalidated when view is released.
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let timer_interval = 0.015;
|
let timer_interval = 0.015;
|
||||||
let selector = sel!(triggerOnFrame:);
|
let selector = sel!(triggerOnFrame:);
|
||||||
|
@ -192,13 +215,18 @@ impl Window {
|
||||||
repeats:YES
|
repeats:YES
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Store pointer to timer for invalidation when view is released
|
||||||
(*window_state_arc.window.ns_view).set_ivar(
|
(*window_state_arc.window.ns_view).set_ivar(
|
||||||
FRAME_TIMER_IVAR_NAME,
|
FRAME_TIMER_IVAR_NAME,
|
||||||
timer as *mut c_void,
|
timer as *mut c_void,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
crate::WindowHandle(WindowHandle)
|
let window_handle = crate::WindowHandle(WindowHandle {
|
||||||
|
message_tx
|
||||||
|
});
|
||||||
|
|
||||||
|
(window_handle, opt_app_runner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +235,7 @@ pub(super) struct WindowState<H: WindowHandler> {
|
||||||
window: Window,
|
window: Window,
|
||||||
window_handler: H,
|
window_handler: H,
|
||||||
keyboard_state: KeyboardState,
|
keyboard_state: KeyboardState,
|
||||||
|
message_rx: Consumer<H::Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,6 +251,15 @@ impl <H: WindowHandler>WindowState<H> {
|
||||||
&mut *(state_ptr as *mut Self)
|
&mut *(state_ptr as *mut Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn handle_messages(&mut self){
|
||||||
|
while let Ok(message) = self.message_rx.pop(){
|
||||||
|
self.window_handler.on_message(
|
||||||
|
&mut crate::Window(&mut self.window),
|
||||||
|
message
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn trigger_event(&mut self, event: Event){
|
pub(super) fn trigger_event(&mut self, event: Event){
|
||||||
self.window_handler.on_event(
|
self.window_handler.on_event(
|
||||||
&mut crate::Window(&mut self.window),
|
&mut crate::Window(&mut self.window),
|
||||||
|
|
|
@ -207,11 +207,25 @@ pub struct Window {
|
||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct WindowHandle {
|
pub struct WindowHandle<H: WindowHandler> {
|
||||||
|
// FIXME: replace this with channel sender
|
||||||
|
phantom_data: std::marker::PhantomData<H::Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
|
pub fn try_send_message(
|
||||||
|
&mut self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
Err(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppRunner {
|
||||||
hwnd: HWND,
|
hwnd: HWND,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowHandle {
|
impl AppRunner {
|
||||||
pub fn app_run_blocking(self) {
|
pub fn app_run_blocking(self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut msg: MSG = std::mem::zeroed();
|
let mut msg: MSG = std::mem::zeroed();
|
||||||
|
@ -234,7 +248,7 @@ impl Window {
|
||||||
pub fn open<H, B>(
|
pub fn open<H, B>(
|
||||||
options: WindowOpenOptions,
|
options: WindowOpenOptions,
|
||||||
build: B
|
build: B
|
||||||
) -> crate::WindowHandle
|
) -> (crate::WindowHandle<H>, Option<crate::AppRunner>)
|
||||||
where H: WindowHandler,
|
where H: WindowHandler,
|
||||||
B: FnOnce(&mut crate::Window) -> H,
|
B: FnOnce(&mut crate::Window) -> H,
|
||||||
B: Send + 'static
|
B: Send + 'static
|
||||||
|
@ -311,7 +325,17 @@ impl Window {
|
||||||
SetWindowLongPtrA(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _);
|
SetWindowLongPtrA(hwnd, GWLP_USERDATA, Box::into_raw(window_state) as *const _ as _);
|
||||||
SetTimer(hwnd, 4242, 13, None);
|
SetTimer(hwnd, 4242, 13, None);
|
||||||
|
|
||||||
crate::WindowHandle(WindowHandle { hwnd })
|
let window_handle = crate::WindowHandle(WindowHandle {
|
||||||
|
phantom_data: std::marker::PhantomData,
|
||||||
|
});
|
||||||
|
|
||||||
|
let opt_app_runner = if let crate::Parent::None = options.parent {
|
||||||
|
Some(crate::AppRunner(AppRunner { hwnd }))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(window_handle, opt_app_runner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,16 +11,31 @@ use crate::x11 as platform;
|
||||||
use crate::macos as platform;
|
use crate::macos as platform;
|
||||||
|
|
||||||
|
|
||||||
pub struct WindowHandle(pub(crate) platform::WindowHandle);
|
pub struct AppRunner(pub(crate) platform::AppRunner);
|
||||||
|
|
||||||
|
|
||||||
impl WindowHandle {
|
impl AppRunner {
|
||||||
pub fn app_run_blocking(self){
|
pub fn app_run_blocking(self){
|
||||||
self.0.app_run_blocking();
|
self.0.app_run_blocking();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct WindowHandle<H: WindowHandler>(
|
||||||
|
pub(crate) platform::WindowHandle<H>
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
|
pub fn try_send_message(
|
||||||
|
&mut self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
self.0.try_send_message(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct Window<'a>(pub(crate) &'a mut platform::Window);
|
pub struct Window<'a>(pub(crate) &'a mut platform::Window);
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,7 +43,7 @@ impl <'a>Window<'a> {
|
||||||
pub fn open<H, B>(
|
pub fn open<H, B>(
|
||||||
options: WindowOpenOptions,
|
options: WindowOpenOptions,
|
||||||
build: B
|
build: B
|
||||||
) -> WindowHandle
|
) -> (WindowHandle<H>, Option<AppRunner>)
|
||||||
where H: WindowHandler,
|
where H: WindowHandler,
|
||||||
B: FnOnce(&mut Window) -> H,
|
B: FnOnce(&mut Window) -> H,
|
||||||
B: Send + 'static
|
B: Send + 'static
|
||||||
|
@ -42,4 +57,28 @@ unsafe impl <'a>HasRawWindowHandle for Window<'a> {
|
||||||
fn raw_window_handle(&self) -> RawWindowHandle {
|
fn raw_window_handle(&self) -> RawWindowHandle {
|
||||||
self.0.raw_window_handle()
|
self.0.raw_window_handle()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Compile-time API assertions
|
||||||
|
#[doc(hidden)]
|
||||||
|
mod assertions {
|
||||||
|
use crate::{WindowHandle, WindowHandler, Event, Window};
|
||||||
|
|
||||||
|
struct TestWindowHandler {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
ptr: *mut ::std::ffi::c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WindowHandler for TestWindowHandler {
|
||||||
|
type Message = ();
|
||||||
|
|
||||||
|
fn on_event(&mut self, _: &mut Window, _: Event) {}
|
||||||
|
fn on_message(&mut self, _: &mut Window, _: Self::Message) {}
|
||||||
|
fn on_frame(&mut self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert that WindowHandle is Send even if WindowHandler isn't
|
||||||
|
static_assertions::assert_not_impl_any!(TestWindowHandler: Send);
|
||||||
|
static_assertions::assert_impl_all!(WindowHandle<TestWindowHandler>: Send);
|
||||||
|
}
|
||||||
|
|
|
@ -30,12 +30,25 @@ pub struct Window {
|
||||||
new_physical_size: Option<PhySize>
|
new_physical_size: Option<PhySize>
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: move to outer crate context
|
pub struct WindowHandle<H: WindowHandler> {
|
||||||
pub struct WindowHandle {
|
// FIXME: replace this with channel sender
|
||||||
|
phantom_data: std::marker::PhantomData<H::Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
|
pub fn try_send_message(
|
||||||
|
&mut self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
Err(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppRunner {
|
||||||
thread: std::thread::JoinHandle<()>,
|
thread: std::thread::JoinHandle<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowHandle {
|
impl AppRunner {
|
||||||
pub fn app_run_blocking(self) {
|
pub fn app_run_blocking(self) {
|
||||||
let _ = self.thread.join();
|
let _ = self.thread.join();
|
||||||
}
|
}
|
||||||
|
@ -47,11 +60,13 @@ impl Window {
|
||||||
pub fn open<H, B>(
|
pub fn open<H, B>(
|
||||||
options: WindowOpenOptions,
|
options: WindowOpenOptions,
|
||||||
build: B
|
build: B
|
||||||
) -> crate::WindowHandle
|
) -> (crate::WindowHandle<H>, Option<crate::AppRunner>)
|
||||||
where H: WindowHandler,
|
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);
|
||||||
|
|
||||||
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 || {
|
||||||
|
@ -63,7 +78,17 @@ impl Window {
|
||||||
// FIXME: placeholder types for returning errors in the future
|
// FIXME: placeholder types for returning errors in the future
|
||||||
let _ = rx.recv();
|
let _ = rx.recv();
|
||||||
|
|
||||||
crate::WindowHandle(WindowHandle { thread })
|
let window_handle = crate::WindowHandle(WindowHandle {
|
||||||
|
phantom_data: std::marker::PhantomData
|
||||||
|
});
|
||||||
|
|
||||||
|
let opt_app_runner = if is_not_parented {
|
||||||
|
Some(crate::AppRunner(AppRunner { thread }))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
(window_handle, opt_app_runner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn window_thread<H, B>(options: WindowOpenOptions, build: B,
|
fn window_thread<H, B>(options: WindowOpenOptions, build: B,
|
||||||
|
|
Loading…
Reference in a new issue