Add WindowHandle::try_send_message, implement it on macOS
This commit is contained in:
parent
6f81e28d90
commit
35a9841b29
|
@ -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(),
|
||||||
|
@ -27,5 +40,20 @@ fn main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
let handle = Window::open(window_open_options, |_| OpenWindowExample);
|
let handle = Window::open(window_open_options, |_| OpenWindowExample);
|
||||||
|
|
||||||
|
{
|
||||||
|
let handle = handle.clone();
|
||||||
|
|
||||||
|
::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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handle.app_run_blocking();
|
handle.app_run_blocking();
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,10 @@ 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,
|
||||||
|
RUNTIME_TIMER_IVAR_NAME
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
pub(super) unsafe fn create_view<H: WindowHandler>(
|
pub(super) unsafe fn create_view<H: WindowHandler>(
|
||||||
|
@ -67,6 +70,10 @@ unsafe fn create_view_class<H: WindowHandler>() -> &'static Class {
|
||||||
accepts_first_mouse::<H> as extern "C" fn(&Object, Sel, id) -> BOOL
|
accepts_first_mouse::<H> as extern "C" fn(&Object, Sel, id) -> BOOL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
class.add_method(
|
||||||
|
sel!(runtimeTick:),
|
||||||
|
runtime_tick::<H> as extern "C" fn(&Object, Sel, id)
|
||||||
|
);
|
||||||
class.add_method(
|
class.add_method(
|
||||||
sel!(triggerOnFrame:),
|
sel!(triggerOnFrame:),
|
||||||
trigger_on_frame::<H> as extern "C" fn(&Object, Sel, id)
|
trigger_on_frame::<H> as extern "C" fn(&Object, Sel, id)
|
||||||
|
@ -150,6 +157,7 @@ unsafe fn create_view_class<H: WindowHandler>() -> &'static Class {
|
||||||
|
|
||||||
class.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR_NAME);
|
class.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR_NAME);
|
||||||
class.add_ivar::<*mut c_void>(FRAME_TIMER_IVAR_NAME);
|
class.add_ivar::<*mut c_void>(FRAME_TIMER_IVAR_NAME);
|
||||||
|
class.add_ivar::<*mut c_void>(RUNTIME_TIMER_IVAR_NAME);
|
||||||
|
|
||||||
class.register()
|
class.register()
|
||||||
}
|
}
|
||||||
|
@ -193,6 +201,19 @@ extern "C" fn trigger_on_frame<H: WindowHandler>(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern "C" fn runtime_tick<H: WindowHandler>(
|
||||||
|
this: &Object,
|
||||||
|
_sel: Sel,
|
||||||
|
_event: id
|
||||||
|
){
|
||||||
|
let state: &mut WindowState<H> = unsafe {
|
||||||
|
WindowState::from_field(this)
|
||||||
|
};
|
||||||
|
|
||||||
|
state.handle_messages();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern "C" fn release<H: WindowHandler>(this: &Object, _sel: Sel) {
|
extern "C" fn release<H: WindowHandler>(this: &Object, _sel: Sel) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let superclass = msg_send![this, superclass];
|
let superclass = msg_send![this, superclass];
|
||||||
|
@ -210,6 +231,12 @@ extern "C" fn release<H: WindowHandler>(this: &Object, _sel: Sel) {
|
||||||
);
|
);
|
||||||
let _: () = msg_send![frame_timer_ptr as id, invalidate];
|
let _: () = msg_send![frame_timer_ptr as id, invalidate];
|
||||||
|
|
||||||
|
// Invalidate runtime timer
|
||||||
|
let frame_timer_ptr: *mut c_void = *this.get_ivar(
|
||||||
|
RUNTIME_TIMER_IVAR_NAME
|
||||||
|
);
|
||||||
|
let _: () = msg_send![frame_timer_ptr as id, invalidate];
|
||||||
|
|
||||||
// Drop WindowState
|
// Drop WindowState
|
||||||
let state_ptr: *mut c_void = *this.get_ivar(
|
let state_ptr: *mut c_void = *this.get_ivar(
|
||||||
WINDOW_STATE_IVAR_NAME
|
WINDOW_STATE_IVAR_NAME
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
/// Inspired by implementation in https://github.com/antonok-edm/vst_window
|
/// 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, mpsc::{self, Sender, Receiver}};
|
||||||
|
|
||||||
use cocoa::appkit::{
|
use cocoa::appkit::{
|
||||||
NSApp, NSApplication, NSApplicationActivationPolicyRegular,
|
NSApp, NSApplication, NSApplicationActivationPolicyRegular,
|
||||||
|
@ -31,6 +31,7 @@ use super::keyboard::KeyboardState;
|
||||||
pub(super) const WINDOW_STATE_IVAR_NAME: &str = "WINDOW_STATE_IVAR_NAME";
|
pub(super) const WINDOW_STATE_IVAR_NAME: &str = "WINDOW_STATE_IVAR_NAME";
|
||||||
|
|
||||||
pub(super) const FRAME_TIMER_IVAR_NAME: &str = "FRAME_TIMER";
|
pub(super) const FRAME_TIMER_IVAR_NAME: &str = "FRAME_TIMER";
|
||||||
|
pub(super) const RUNTIME_TIMER_IVAR_NAME: &str = "RUNTIME_TIMER";
|
||||||
|
|
||||||
|
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
|
@ -42,10 +43,22 @@ pub struct Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct WindowHandle;
|
pub struct WindowHandle<H: WindowHandler> {
|
||||||
|
message_tx: Sender<H::Message>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
impl WindowHandle {
|
// Implement Clone manually to avoid H: Clone bound
|
||||||
|
impl <H: WindowHandler>Clone for WindowHandle<H> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
message_tx: self.message_tx.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
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
|
||||||
|
@ -53,13 +66,21 @@ impl WindowHandle {
|
||||||
NSApp().run();
|
NSApp().run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_send_message(
|
||||||
|
&self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
self.message_tx.send(message)
|
||||||
|
.map_err(|err| err.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>
|
||||||
where H: WindowHandler,
|
where H: WindowHandler,
|
||||||
B: FnOnce(&mut crate::Window) -> H,
|
B: FnOnce(&mut crate::Window) -> H,
|
||||||
B: Send + 'static
|
B: Send + 'static
|
||||||
|
@ -160,10 +181,13 @@ impl Window {
|
||||||
|
|
||||||
let window_handler = build(&mut crate::Window(&mut window));
|
let window_handler = build(&mut crate::Window(&mut window));
|
||||||
|
|
||||||
|
let (message_tx, message_rx) = mpsc::channel();
|
||||||
|
|
||||||
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,37 @@ 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)
|
// Setup runtime timer once window state is stored
|
||||||
|
unsafe {
|
||||||
|
let timer_interval = 0.01;
|
||||||
|
let selector = sel!(runtimeTick:);
|
||||||
|
|
||||||
|
let timer: id = msg_send![
|
||||||
|
::objc::class!(NSTimer),
|
||||||
|
scheduledTimerWithTimeInterval:timer_interval
|
||||||
|
target:window_state_arc.window.ns_view
|
||||||
|
selector:selector
|
||||||
|
userInfo:nil
|
||||||
|
repeats:YES
|
||||||
|
];
|
||||||
|
|
||||||
|
// Store pointer to timer for invalidation when view is released
|
||||||
|
(*window_state_arc.window.ns_view).set_ivar(
|
||||||
|
RUNTIME_TIMER_IVAR_NAME,
|
||||||
|
timer as *mut c_void,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::WindowHandle(WindowHandle {
|
||||||
|
message_tx
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,6 +254,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: Receiver<H::Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -222,6 +270,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){
|
||||||
|
for message in self.message_rx.try_iter(){
|
||||||
|
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),
|
||||||
|
|
|
@ -11,13 +11,28 @@ use crate::x11 as platform;
|
||||||
use crate::macos as platform;
|
use crate::macos as platform;
|
||||||
|
|
||||||
|
|
||||||
pub struct WindowHandle(pub(crate) platform::WindowHandle);
|
pub struct WindowHandle<H: WindowHandler>(pub(crate) platform::WindowHandle<H>);
|
||||||
|
|
||||||
|
|
||||||
impl WindowHandle {
|
// Implement Clone manually to avoid H: Clone bound
|
||||||
|
impl <H: WindowHandler>Clone for WindowHandle<H> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
WindowHandle(self.0.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl <H: WindowHandler>WindowHandle<H> {
|
||||||
pub fn app_run_blocking(self){
|
pub fn app_run_blocking(self){
|
||||||
self.0.app_run_blocking();
|
self.0.app_run_blocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_send_message(
|
||||||
|
&self,
|
||||||
|
message: H::Message
|
||||||
|
) -> Result<(), H::Message> {
|
||||||
|
self.0.try_send_message(message)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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>
|
||||||
where H: WindowHandler,
|
where H: WindowHandler,
|
||||||
B: FnOnce(&mut Window) -> H,
|
B: FnOnce(&mut Window) -> H,
|
||||||
B: Send + 'static
|
B: Send + 'static
|
||||||
|
|
Loading…
Reference in a new issue