1
0
Fork 0

Add WindowHandle::try_send_message, implement it on macOS

This commit is contained in:
Joakim Frostegård 2020-11-29 14:37:59 +01:00
parent 6f81e28d90
commit 35a9841b29
4 changed files with 140 additions and 13 deletions

View file

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

View file

@ -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

View file

@ -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),

View file

@ -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