diff --git a/examples/basic.rs b/examples/basic.rs index eee567e..94717c5 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -6,7 +6,6 @@ const WIDTH: usize = 512; const HEIGHT: usize = 512; struct State { - window: Window, framebuffer: Vec, width: usize, height: usize, @@ -19,14 +18,20 @@ impl Drop for State { } impl State { - fn handle_event(&mut self, cx: &AppContext, event: Event) -> Response { + fn handle_event(&mut self, window: &Window, cx: &AppContext, event: Event) -> Response { match event { Event::Expose(rects) => { println!("expose: {:?}", rects); - self.window.present(Bitmap::new(&self.framebuffer, self.width, self.height)); } Event::Frame => { println!("frame"); + + let scale = window.scale(); + self.width = (WIDTH as f64 * scale) as usize; + self.height = (HEIGHT as f64 * scale) as usize; + self.framebuffer.resize(self.width * self.height, 0xFFFF00FF); + + window.present(Bitmap::new(&self.framebuffer, self.width, self.height)); } Event::MouseMove(pos) => { println!("mouse move: {:?}", pos); @@ -53,34 +58,27 @@ impl State { } fn main() { - let app = AppOptions::new() - .build(|cx| { - let window = WindowOptions::new() - .title("window") - .size(Size::new(512.0, 512.0)) - .open(cx, State::handle_event) - .unwrap(); - - cx.set_timer(Duration::from_millis(1000), |_, _| { - println!("timer"); - }); + let app = AppOptions::new().build().unwrap(); - let scale = window.scale(); - let width = (WIDTH as f64 * scale) as usize; - let height = (HEIGHT as f64 * scale) as usize; - let framebuffer = vec![0xFFFF00FF; width * height]; - window.present(Bitmap::new(&framebuffer, width, height)); + let mut state = State { + framebuffer: Vec::new(), + width: 0, + height: 0, + }; - window.show(); - - Ok(State { - window, - framebuffer, - width, - height, - }) + let window = WindowOptions::new() + .title("window") + .size(Size::new(512.0, 512.0)) + .open(&app.context(), move |window, cx, event| { + state.handle_event(window, cx, event) }) .unwrap(); + app.context().set_timer(Duration::from_millis(1000), |_| { + println!("timer"); + }); + + window.show(); + app.run().unwrap(); } diff --git a/src/app.rs b/src/app.rs index 780f904..6674950 100644 --- a/src/app.rs +++ b/src/app.rs @@ -52,29 +52,29 @@ impl AppOptions { self } - pub fn build(&self, build: F) -> Result> - where - F: FnOnce(&AppContext) -> Result, - T: 'static, - { - Ok(App::from_inner(backend::AppInner::new(self, build)?)) + pub fn build(&self) -> Result { + Ok(App::from_inner(backend::AppInner::new(self)?)) } } -pub struct App { - inner: backend::AppInner, +pub struct App { + inner: backend::AppInner, // ensure !Send and !Sync on all platforms _marker: PhantomData<*mut ()>, } -impl App { - pub(crate) fn from_inner(inner: backend::AppInner) -> App { +impl App { + pub(crate) fn from_inner(inner: backend::AppInner) -> App { App { inner, _marker: PhantomData, } } + pub fn context(&self) -> AppContext { + AppContext::from_inner(self.inner.context()) + } + pub fn run(&self) -> Result<()> { self.inner.run() } @@ -84,7 +84,7 @@ impl App { } } -impl fmt::Debug for App { +impl fmt::Debug for App { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("App").finish_non_exhaustive() } @@ -94,20 +94,20 @@ impl fmt::Debug for App { use std::os::unix::io::{AsRawFd, RawFd}; #[cfg(target_os = "linux")] -impl AsRawFd for App { +impl AsRawFd for App { fn as_raw_fd(&self) -> RawFd { self.inner.as_raw_fd() } } -pub struct AppContext<'a, T> { - pub(crate) inner: backend::AppContextInner<'a, T>, +pub struct AppContext<'a> { + pub(crate) inner: backend::AppContextInner<'a>, // ensure !Send and !Sync on all platforms _marker: PhantomData<*mut ()>, } -impl<'a, T: 'static> AppContext<'a, T> { - pub(crate) fn from_inner(inner: backend::AppContextInner) -> AppContext { +impl<'a> AppContext<'a> { + pub(crate) fn from_inner(inner: backend::AppContextInner) -> AppContext { AppContext { inner, _marker: PhantomData, @@ -116,8 +116,7 @@ impl<'a, T: 'static> AppContext<'a, T> { pub fn set_timer(&self, duration: Duration, handler: H) -> Timer where - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { Timer { inner: self.inner.set_timer(duration, handler), @@ -130,7 +129,7 @@ impl<'a, T: 'static> AppContext<'a, T> { } } -impl<'a, T> fmt::Debug for AppContext<'a, T> { +impl<'a> fmt::Debug for AppContext<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("AppContext").finish_non_exhaustive() } diff --git a/src/backend/cocoa/app.rs b/src/backend/cocoa/app.rs index 674e8d2..1f67789 100644 --- a/src/backend/cocoa/app.rs +++ b/src/backend/cocoa/app.rs @@ -1,7 +1,5 @@ -use std::any::Any; use std::cell::RefCell; use std::collections::HashMap; -use std::marker::PhantomData; use std::rc::Rc; use std::time::Duration; @@ -23,7 +21,6 @@ pub struct AppState { pub timers: Timers, pub display_links: DisplayLinks, pub windows: RefCell>>, - pub data: RefCell>>, } impl Drop for AppState { @@ -34,17 +31,12 @@ impl Drop for AppState { } } -pub struct AppInner { +pub struct AppInner { pub state: Rc, - _marker: PhantomData, } -impl AppInner { - pub fn new(options: &AppOptions, build: F) -> Result> - where - F: FnOnce(&AppContext) -> Result, - T: 'static, - { +impl AppInner { + pub fn new(options: &AppOptions) -> Result { autoreleasepool(|_| { assert!( NSThread::isMainThread_class(), @@ -71,19 +63,10 @@ impl AppInner { timers: Timers::new(), display_links: DisplayLinks::new(), windows: RefCell::new(HashMap::new()), - data: RefCell::new(None), }); state.display_links.init(&state); - let cx = AppContext::from_inner(AppContextInner { - state: &state, - _marker: PhantomData, - }); - let data = build(&cx)?; - - state.data.replace(Some(Box::new(data))); - if options.mode == AppMode::Owner { unsafe { let app = NSApplication::sharedApplication(); @@ -92,13 +75,14 @@ impl AppInner { } } - Ok(AppInner { - state, - _marker: PhantomData, - }) + Ok(AppInner { state }) }) } + pub fn context(&self) -> AppContextInner { + AppContextInner::new(&self.state) + } + pub fn run(&self) -> Result<()> { autoreleasepool(|_| unsafe { NSApplication::sharedApplication().run(); @@ -112,39 +96,30 @@ impl AppInner { } } -impl Drop for AppInner { +impl Drop for AppInner { fn drop(&mut self) { autoreleasepool(|_| { - if let Ok(mut data) = self.state.data.try_borrow_mut() { - drop(data.take()); - - for window_state in self.state.windows.take().into_values() { - window_state.close(); - } - - self.state.timers.shutdown(); + for window_state in self.state.windows.take().into_values() { + window_state.close(); } + + self.state.timers.shutdown(); }) } } -pub struct AppContextInner<'a, T> { +pub struct AppContextInner<'a> { pub state: &'a Rc, - _marker: PhantomData, } -impl<'a, T: 'static> AppContextInner<'a, T> { - pub(super) fn new(state: &'a Rc) -> AppContextInner<'a, T> { - AppContextInner { - state, - _marker: PhantomData, - } +impl<'a> AppContextInner<'a> { + pub(super) fn new(state: &'a Rc) -> AppContextInner<'a> { + AppContextInner { state } } pub fn set_timer(&self, duration: Duration, handler: H) -> TimerInner where - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { self.state.timers.set_timer(self.state, duration, handler) } diff --git a/src/backend/cocoa/timer.rs b/src/backend/cocoa/timer.rs index 4c44b34..674ce7e 100644 --- a/src/backend/cocoa/timer.rs +++ b/src/backend/cocoa/timer.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::ffi::c_void; @@ -30,17 +29,14 @@ extern "C" fn release(info: *const c_void) { extern "C" fn callback(_timer: CFRunLoopTimerRef, info: *mut c_void) { let state = unsafe { &*(info as *const TimerState) }; - if let Ok(mut data) = state.app_state.data.try_borrow_mut() { - if let Some(data) = &mut *data { - state.handler.borrow_mut()(&mut **data, &state.app_state); - } - } + let cx = AppContext::from_inner(AppContextInner::new(&state.app_state)); + state.handler.borrow_mut()(&cx); } struct TimerState { timer_ref: Cell>, app_state: Rc, - handler: RefCell)>>, + handler: RefCell>, } impl TimerState { @@ -65,28 +61,19 @@ impl Timers { } } - pub fn set_timer( + pub fn set_timer( &self, app_state: &Rc, duration: Duration, handler: H, ) -> TimerInner where - T: 'static, - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { - let mut handler = handler; - let handler_wrapper = move |data_any: &mut dyn Any, app_state: &Rc| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx) - }; - let state = Rc::new(TimerState { timer_ref: Cell::new(None), app_state: Rc::clone(app_state), - handler: RefCell::new(Box::new(handler_wrapper)), + handler: RefCell::new(Box::new(handler)), }); let mut context = CFRunLoopTimerContext { diff --git a/src/backend/cocoa/window.rs b/src/backend/cocoa/window.rs index 7564181..d57cf85 100644 --- a/src/backend/cocoa/window.rs +++ b/src/backend/cocoa/window.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::cell::{Cell, RefCell}; use std::ffi::c_void; use std::ops::{Deref, DerefMut}; @@ -6,7 +5,7 @@ use std::rc::Rc; use objc2::declare::{ClassBuilder, Ivar, IvarEncode, IvarType}; use objc2::encode::Encoding; -use objc2::rc::{Allocated, Id}; +use objc2::rc::{autoreleasepool, Allocated, Id}; use objc2::runtime::{AnyClass, Bool, ProtocolObject, Sel}; use objc2::{class, msg_send, msg_send_id, sel}; use objc2::{ClassType, Message, MessageReceiver, RefEncode}; @@ -23,7 +22,7 @@ use super::surface::Surface; use super::OsError; use crate::{ AppContext, Bitmap, Cursor, Error, Event, MouseButton, Point, RawParent, Rect, Response, - Result, Size, WindowOptions, + Result, Size, Window, WindowOptions, }; fn class_name() -> String { @@ -167,8 +166,13 @@ impl View { objc_disposeClassPair(class as *const _ as *mut objc_class); } - pub fn state(&self) -> &WindowState { - unsafe { &*(self.state.get() as *const WindowState) } + pub fn state(&self) -> Rc { + let state_ptr = self.state.get() as *const WindowState; + let state_rc = unsafe { Rc::from_raw(state_ptr) }; + let state = Rc::clone(&state_rc); + let _ = Rc::into_raw(state_rc); + + state } pub fn retain(&self) -> Id { @@ -295,7 +299,7 @@ pub struct WindowState { surface: RefCell>, cursor: Cell, app_state: Rc, - handler: RefCell, Event) -> Response>>, + handler: RefCell Response>>, } impl WindowState { @@ -307,13 +311,13 @@ impl WindowState { self.window.borrow().clone() } - pub fn handle_event(&self, event: Event) -> Option { + pub fn handle_event(self: &Rc, event: Event) -> Option { if let Ok(mut handler) = self.handler.try_borrow_mut() { - if let Ok(mut data) = self.app_state.data.try_borrow_mut() { - if let Some(data) = &mut *data { - return Some(handler(&mut **data, &self.app_state, event)); - } - } + let window = Window::from_inner(WindowInner { + state: self.clone(), + }); + let cx = AppContext::from_inner(AppContextInner::new(&self.app_state)); + return Some(handler(&window, &cx, event)); } None @@ -356,13 +360,15 @@ impl WindowState { } pub fn close(&self) { - if let Some(window) = self.window.take() { - unsafe { window.close() }; - } + autoreleasepool(|_| { + if let Some(window) = self.window.take() { + unsafe { window.close() }; + } - if let Some(view) = self.view.take() { - unsafe { view.removeFromSuperview() }; - } + if let Some(view) = self.view.take() { + unsafe { view.removeFromSuperview() }; + } + }) } } @@ -372,154 +378,144 @@ pub struct WindowInner { } impl WindowInner { - pub fn open( - options: &WindowOptions, - cx: &AppContext, - handler: H, - ) -> Result + pub fn open(options: &WindowOptions, cx: &AppContext, handler: H) -> Result where - T: 'static, - H: FnMut(&mut T, &AppContext, Event) -> Response, - H: 'static, + H: FnMut(&Window, &AppContext, Event) -> Response + 'static, { - let app_state = cx.inner.state; - - let parent_view = if let Some(parent) = options.parent { - if let RawParent::Cocoa(parent_view) = parent { - Some(parent_view as *const NSView) + autoreleasepool(|_| { + let app_state = cx.inner.state; + + let parent_view = if let Some(parent) = options.parent { + if let RawParent::Cocoa(parent_view) = parent { + Some(parent_view as *const NSView) + } else { + return Err(Error::InvalidWindowHandle); + } } else { - return Err(Error::InvalidWindowHandle); - } - } else { - None - }; - - let origin = if options.parent.is_some() { - Point::new(0.0, 0.0) - } else { - options.position.unwrap_or(Point::new(0.0, 0.0)) - }; - let frame = NSRect::new( - NSPoint::new(origin.x, origin.y), - NSSize::new(options.size.width, options.size.height), - ); - - let mut handler = handler; - let handler_wrapper = - move |data_any: &mut dyn Any, app_state: &Rc, event: Event<'_>| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx, event) + None }; - let state = Rc::new(WindowState { - view: RefCell::new(None), - window: RefCell::new(None), - surface: RefCell::new(None), - cursor: Cell::new(Cursor::Arrow), - app_state: Rc::clone(app_state), - handler: RefCell::new(Box::new(handler_wrapper)), - }); - - let view: Option> = unsafe { msg_send_id![app_state.class, alloc] }; - let view: Id = unsafe { msg_send_id![view, initWithFrame: frame] }; - view.state.set(Rc::into_raw(Rc::clone(&state)) as *mut c_void); - - state.view.replace(Some(view.retain())); - - let tracking_options = icrate::AppKit::NSTrackingMouseEnteredAndExited - | icrate::AppKit::NSTrackingMouseMoved - | icrate::AppKit::NSTrackingCursorUpdate - | icrate::AppKit::NSTrackingActiveAlways - | icrate::AppKit::NSTrackingInVisibleRect - | icrate::AppKit::NSTrackingEnabledDuringMouseDrag; - - unsafe { - let tracking_area = NSTrackingArea::initWithRect_options_owner_userInfo( - NSTrackingArea::alloc(), - NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(0.0, 0.0)), - tracking_options, - Some(&view), - None, - ); - view.addTrackingArea(&tracking_area); - } - - if let Some(parent_view) = parent_view { - unsafe { - view.setHidden(true); - (*parent_view).addSubview(&view); - } - } else { - let origin = options.position.unwrap_or(Point::new(0.0, 0.0)); - let content_rect = NSRect::new( + let origin = if options.parent.is_some() { + Point::new(0.0, 0.0) + } else { + options.position.unwrap_or(Point::new(0.0, 0.0)) + }; + let frame = NSRect::new( NSPoint::new(origin.x, origin.y), NSSize::new(options.size.width, options.size.height), ); - let style_mask = icrate::AppKit::NSWindowStyleMaskTitled - | icrate::AppKit::NSWindowStyleMaskClosable - | icrate::AppKit::NSWindowStyleMaskMiniaturizable - | icrate::AppKit::NSWindowStyleMaskResizable; - - let window = unsafe { - NSWindow::initWithContentRect_styleMask_backing_defer( - NSWindow::alloc(), - content_rect, - style_mask, - icrate::AppKit::NSBackingStoreBuffered, - false, - ) - }; + let state = Rc::new(WindowState { + view: RefCell::new(None), + window: RefCell::new(None), + surface: RefCell::new(None), + cursor: Cell::new(Cursor::Arrow), + app_state: Rc::clone(app_state), + handler: RefCell::new(Box::new(handler)), + }); - unsafe { - window.setReleasedWhenClosed(false); + let view: Option> = unsafe { msg_send_id![app_state.class, alloc] }; + let view: Id = unsafe { msg_send_id![view, initWithFrame: frame] }; + view.state.set(Rc::into_raw(Rc::clone(&state)) as *mut c_void); - window.setTitle(&NSString::from_str(&options.title)); + state.view.replace(Some(view.retain())); - let delegate = ProtocolObject::::from_ref(&*view); - window.setDelegate(Some(&delegate)); - window.setContentView(Some(&view)); + let tracking_options = icrate::AppKit::NSTrackingMouseEnteredAndExited + | icrate::AppKit::NSTrackingMouseMoved + | icrate::AppKit::NSTrackingCursorUpdate + | icrate::AppKit::NSTrackingActiveAlways + | icrate::AppKit::NSTrackingInVisibleRect + | icrate::AppKit::NSTrackingEnabledDuringMouseDrag; - if options.position.is_none() { - window.center(); - } + unsafe { + let tracking_area = NSTrackingArea::initWithRect_options_owner_userInfo( + NSTrackingArea::alloc(), + NSRect::new(NSPoint::new(0.0, 0.0), NSSize::new(0.0, 0.0)), + tracking_options, + Some(&view), + None, + ); + view.addTrackingArea(&tracking_area); } - state.window.replace(Some(window)); - } + if let Some(parent_view) = parent_view { + unsafe { + view.setHidden(true); + (*parent_view).addSubview(&view); + } + } else { + let origin = options.position.unwrap_or(Point::new(0.0, 0.0)); + let content_rect = NSRect::new( + NSPoint::new(origin.x, origin.y), + NSSize::new(options.size.width, options.size.height), + ); + + let style_mask = icrate::AppKit::NSWindowStyleMaskTitled + | icrate::AppKit::NSWindowStyleMaskClosable + | icrate::AppKit::NSWindowStyleMaskMiniaturizable + | icrate::AppKit::NSWindowStyleMaskResizable; + + let window = unsafe { + NSWindow::initWithContentRect_styleMask_backing_defer( + NSWindow::alloc(), + content_rect, + style_mask, + icrate::AppKit::NSBackingStoreBuffered, + false, + ) + }; + + unsafe { + window.setReleasedWhenClosed(false); + + window.setTitle(&NSString::from_str(&options.title)); + + let delegate = ProtocolObject::::from_ref(&*view); + window.setDelegate(Some(&delegate)); + window.setContentView(Some(&view)); + + if options.position.is_none() { + window.center(); + } + } - app_state.windows.borrow_mut().insert(Rc::as_ptr(&state), Rc::clone(&state)); + state.window.replace(Some(window)); + } - let inner = WindowInner { state }; + app_state.windows.borrow_mut().insert(Rc::as_ptr(&state), Rc::clone(&state)); - let scale = inner.scale(); + let inner = WindowInner { state }; - let surface = Surface::new( - (scale * options.size.width).round() as usize, - (scale * options.size.height).round() as usize, - ); + let scale = inner.scale(); - unsafe { - let () = msg_send![&*view, setLayer: &*surface.layer]; - view.setWantsLayer(true); + let surface = Surface::new( + (scale * options.size.width).round() as usize, + (scale * options.size.height).round() as usize, + ); - surface.layer.setContentsScale(scale); - } + unsafe { + let () = msg_send![&*view, setLayer: &*surface.layer]; + view.setWantsLayer(true); + + surface.layer.setContentsScale(scale); + } - inner.state.surface.replace(Some(surface)); + inner.state.surface.replace(Some(surface)); - Ok(inner) + Ok(inner) + }) } pub fn show(&self) { - if let Some(window) = self.state.window() { - unsafe { window.orderFront(None) }; - } + autoreleasepool(|_| { + if let Some(window) = self.state.window() { + unsafe { window.orderFront(None) }; + } - if let Some(view) = self.state.view() { - unsafe { view.setHidden(false) }; - } + if let Some(view) = self.state.view() { + unsafe { view.setHidden(false) }; + } + }) } pub fn hide(&self) { diff --git a/src/backend/win32/app.rs b/src/backend/win32/app.rs index f88cfef..7664a0a 100644 --- a/src/backend/win32/app.rs +++ b/src/backend/win32/app.rs @@ -1,7 +1,5 @@ -use std::any::Any; use std::cell::RefCell; use std::collections::HashMap; -use std::marker::PhantomData; use std::rc::{Rc, Weak}; use std::time::Duration; use std::{mem, ptr}; @@ -21,7 +19,7 @@ use super::timer::{TimerInner, Timers}; use super::vsync::VsyncThreads; use super::window::{self, WindowState}; use super::{class_name, hinstance, to_wstring, WM_USER_VBLANK}; -use crate::{AppContext, AppMode, AppOptions, Error, Result}; +use crate::{AppContext, AppMode, AppOptions, Result}; fn register_message_class() -> Result { let class_name = to_wstring(&class_name("message-")); @@ -93,7 +91,6 @@ pub struct AppState { pub timers: Timers, pub vsync_threads: VsyncThreads, pub windows: RefCell>>, - pub data: RefCell>>, } impl Drop for AppState { @@ -109,17 +106,12 @@ impl Drop for AppState { } } -pub struct AppInner { +pub struct AppInner { pub state: Rc, - _marker: PhantomData, } -impl AppInner { - pub fn new(options: &AppOptions, build: F) -> Result> - where - F: FnOnce(&AppContext) -> Result, - T: 'static, - { +impl AppInner { + pub fn new(options: &AppOptions) -> Result { let message_class = register_message_class()?; let message_hwnd = unsafe { @@ -161,7 +153,6 @@ impl AppInner { timers, vsync_threads, windows: RefCell::new(HashMap::new()), - data: RefCell::new(None), }); let state_ptr = Weak::into_raw(Rc::downgrade(&state)); @@ -171,25 +162,14 @@ impl AppInner { state.vsync_threads.init(&state); - let cx = AppContext::from_inner(AppContextInner { - state: &state, - _marker: PhantomData, - }); - let data = build(&cx)?; - - state.data.replace(Some(Box::new(data))); + Ok(AppInner { state }) + } - Ok(AppInner { - state, - _marker: PhantomData, - }) + pub fn context(&self) -> AppContextInner { + AppContextInner::new(&self.state) } pub fn run(&self) -> Result<()> { - if self.state.data.try_borrow().is_err() { - return Err(Error::InsideEventHandler); - } - loop { unsafe { let mut msg: MSG = mem::zeroed(); @@ -208,10 +188,6 @@ impl AppInner { } pub fn poll(&self) -> Result<()> { - if self.state.data.try_borrow().is_err() { - return Err(Error::InsideEventHandler); - } - loop { unsafe { let mut msg: MSG = mem::zeroed(); @@ -228,37 +204,28 @@ impl AppInner { } } -impl Drop for AppInner { +impl Drop for AppInner { fn drop(&mut self) { - if let Ok(mut data) = self.state.data.try_borrow_mut() { - drop(data.take()); - - for window_state in self.state.windows.take().into_values() { - window_state.close(); - } - - self.state.timers.shutdown(); + for window_state in self.state.windows.take().into_values() { + window_state.close(); } + + self.state.timers.shutdown(); } } -pub struct AppContextInner<'a, T> { +pub struct AppContextInner<'a> { pub state: &'a Rc, - pub _marker: PhantomData, } -impl<'a, T: 'static> AppContextInner<'a, T> { - pub(super) fn new(state: &'a Rc) -> AppContextInner<'a, T> { - AppContextInner { - state, - _marker: PhantomData, - } +impl<'a> AppContextInner<'a> { + pub(super) fn new(state: &'a Rc) -> AppContextInner<'a> { + AppContextInner { state } } pub fn set_timer(&self, duration: Duration, handler: H) -> TimerInner where - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { self.state.timers.set_timer(self.state, duration, handler) } diff --git a/src/backend/win32/timer.rs b/src/backend/win32/timer.rs index be7a87a..e9229d0 100644 --- a/src/backend/win32/timer.rs +++ b/src/backend/win32/timer.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::cell::{Cell, RefCell}; use std::collections::HashMap; use std::rc::Rc; @@ -12,7 +11,7 @@ use crate::AppContext; struct TimerState { timer_id: Cell>, app_state: Rc, - handler: RefCell)>>, + handler: RefCell>, } impl TimerState { @@ -36,31 +35,22 @@ impl Timers { } } - pub fn set_timer( + pub fn set_timer( &self, app_state: &Rc, duration: Duration, handler: H, ) -> TimerInner where - T: 'static, - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { let timer_id = self.next_id.get(); self.next_id.set(timer_id + 1); - let mut handler = handler; - let handler_wrapper = move |data_any: &mut dyn Any, app_state: &Rc| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx) - }; - let state = Rc::new(TimerState { timer_id: Cell::new(Some(timer_id)), app_state: Rc::clone(app_state), - handler: RefCell::new(Box::new(handler_wrapper)), + handler: RefCell::new(Box::new(handler)), }); self.timers.borrow_mut().insert(timer_id, Rc::clone(&state)); @@ -76,11 +66,8 @@ impl Timers { pub fn handle_timer(&self, app_state: &Rc, timer_id: usize) { let timer_state = app_state.timers.timers.borrow().get(&timer_id).cloned(); if let Some(timer_state) = timer_state { - if let Ok(mut data) = app_state.data.try_borrow_mut() { - if let Some(data) = &mut *data { - timer_state.handler.borrow_mut()(&mut **data, &app_state); - } - } + let cx = AppContext::from_inner(AppContextInner::new(app_state)); + timer_state.handler.borrow_mut()(&cx); } } diff --git a/src/backend/win32/window.rs b/src/backend/win32/window.rs index 4a11239..c63c2a0 100644 --- a/src/backend/win32/window.rs +++ b/src/backend/win32/window.rs @@ -1,5 +1,4 @@ use std::alloc::{alloc, dealloc, Layout}; -use std::any::Any; use std::cell::{Cell, RefCell}; use std::ffi::{c_int, c_void}; use std::mem::MaybeUninit; @@ -20,7 +19,7 @@ use super::app::{AppContextInner, AppState}; use super::{class_name, hinstance, to_wstring}; use crate::{ AppContext, Bitmap, Cursor, Error, Event, MouseButton, Point, RawParent, Rect, Response, - Result, Size, WindowOptions, + Result, Size, Window, WindowOptions, }; #[allow(non_snake_case)] @@ -256,7 +255,7 @@ pub struct WindowState { mouse_down_count: Cell, cursor: Cell, app_state: Rc, - handler: RefCell, Event) -> Response>>, + handler: RefCell Response>>, } impl WindowState { @@ -282,13 +281,13 @@ impl WindowState { } } - pub fn handle_event(&self, event: Event) -> Option { + pub fn handle_event(self: &Rc, event: Event) -> Option { if let Ok(mut handler) = self.handler.try_borrow_mut() { - if let Ok(mut data) = self.app_state.data.try_borrow_mut() { - if let Some(data) = &mut *data { - return Some(handler(&mut **data, &self.app_state, event)); - } - } + let window = Window::from_inner(WindowInner { + state: self.clone(), + }); + let cx = AppContext::from_inner(AppContextInner::new(&self.app_state)); + return Some(handler(&window, &cx, event)); } None @@ -307,15 +306,9 @@ pub struct WindowInner { } impl WindowInner { - pub fn open( - options: &WindowOptions, - cx: &AppContext, - handler: H, - ) -> Result + pub fn open(options: &WindowOptions, cx: &AppContext, handler: H) -> Result where - T: 'static, - H: FnMut(&mut T, &AppContext, Event) -> Response, - H: 'static, + H: FnMut(&Window, &AppContext, Event) -> Response + 'static, { unsafe { let window_name = to_wstring(&options.title); @@ -376,20 +369,12 @@ impl WindowInner { return Err(windows::core::Error::from_win32().into()); } - let mut handler = handler; - let handler_wrapper = - move |data_any: &mut dyn Any, app_state: &Rc, event: Event<'_>| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx, event) - }; - let state = Rc::new(WindowState { hwnd: Cell::new(Some(hwnd)), mouse_down_count: Cell::new(0), cursor: Cell::new(Cursor::Arrow), app_state: Rc::clone(cx.inner.state), - handler: RefCell::new(Box::new(handler_wrapper)), + handler: RefCell::new(Box::new(handler)), }); let state_ptr = Rc::into_raw(Rc::clone(&state)); diff --git a/src/backend/x11/app.rs b/src/backend/x11/app.rs index cf2f028..e9370ab 100644 --- a/src/backend/x11/app.rs +++ b/src/backend/x11/app.rs @@ -1,6 +1,5 @@ use std::cell::{Cell, RefCell}; use std::collections::HashMap; -use std::marker::PhantomData; use std::os::unix::io::{AsRawFd, RawFd}; use std::rc::Rc; use std::time::{Duration, Instant}; @@ -68,17 +67,12 @@ impl Drop for AppState { } } -pub struct AppInner { +pub struct AppInner { state: Rc, - data: RefCell, } -impl AppInner { - pub fn new(_options: &AppOptions, build: F) -> Result> - where - F: FnOnce(&AppContext) -> Result, - T: 'static, - { +impl AppInner { + pub fn new(_options: &AppOptions) -> Result { let (connection, screen_index) = x11rb::connect(None)?; let atoms = Atoms::new(&connection)?.reply()?; let shm_supported = connection.extension_information(shm::X11_EXTENSION_NAME)?.is_some(); @@ -106,17 +100,15 @@ impl AppInner { timers: Timers::new(), }); - let cx = AppContext::from_inner(AppContextInner::new(&state)); - let data = build(&cx)?; - - let inner = AppInner { - state, - data: RefCell::new(data), - }; + let inner = AppInner { state }; Ok(inner) } + pub fn context(&self) -> AppContextInner { + AppContextInner::new(&self.state) + } + pub fn run(&self) -> Result<()> { self.state.running.set(true); @@ -124,7 +116,7 @@ impl AppInner { while self.state.running.get() { self.drain_events()?; - self.state.timers.poll(&mut *self.data.borrow_mut(), &self.state); + self.state.timers.poll(&self.state); self.drain_events()?; if !self.state.running.get() { @@ -154,7 +146,7 @@ impl AppInner { pub fn poll(&self) -> Result<()> { self.drain_events()?; - self.state.timers.poll(&mut *self.data.borrow_mut(), &self.state); + self.state.timers.poll(&self.state); self.drain_events()?; Ok(()) @@ -175,11 +167,7 @@ impl AppInner { if event.count == 0 { let rects = window.expose_rects.take(); - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::Expose(&rects), - ); + window.handle_event(Event::Expose(&rects)); } } } @@ -189,11 +177,7 @@ impl AppInner { { let window = self.state.windows.borrow().get(&event.window).cloned(); if let Some(window) = window { - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::Close, - ); + window.handle_event(Event::Close); } } } @@ -205,28 +189,16 @@ impl AppInner { y: event.event_y as f64, }; - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::MouseMove(point), - ); + window.handle_event(Event::MouseMove(point)); } } protocol::Event::ButtonPress(event) => { let window = self.state.windows.borrow().get(&event.event).cloned(); if let Some(window) = window { if let Some(button) = mouse_button_from_code(event.detail) { - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::MouseDown(button), - ); + window.handle_event(Event::MouseDown(button)); } else if let Some(delta) = scroll_delta_from_code(event.detail) { - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::Scroll(delta), - ); + window.handle_event(Event::Scroll(delta)); } } } @@ -234,11 +206,7 @@ impl AppInner { let window = self.state.windows.borrow().get(&event.event).cloned(); if let Some(window) = window { if let Some(button) = mouse_button_from_code(event.detail) { - window.handler.borrow_mut()( - &mut *self.data.borrow_mut(), - &self.state, - Event::MouseUp(button), - ); + window.handle_event(Event::MouseUp(button)); } } } @@ -250,7 +218,7 @@ impl AppInner { } } -impl Drop for AppInner { +impl Drop for AppInner { fn drop(&mut self) { for window_state in self.state.windows.take().into_values() { window_state.close(); @@ -259,29 +227,25 @@ impl Drop for AppInner { } } -impl AsRawFd for AppInner { +impl AsRawFd for AppInner { fn as_raw_fd(&self) -> RawFd { self.state.connection.stream().as_raw_fd() } } -pub struct AppContextInner<'a, T> { +pub struct AppContextInner<'a> { pub state: &'a Rc, - _marker: PhantomData, } -impl<'a, T: 'static> AppContextInner<'a, T> { - pub(super) fn new(state: &'a Rc) -> AppContextInner<'a, T> { - AppContextInner { - state, - _marker: PhantomData, - } +impl<'a> AppContextInner<'a> { + pub(super) fn new(state: &'a Rc) -> AppContextInner<'a> { + AppContextInner { state } } pub fn set_timer(&self, duration: Duration, handler: H) -> TimerInner where H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext), { self.state.timers.set_timer(self.state, duration, handler) } diff --git a/src/backend/x11/timer.rs b/src/backend/x11/timer.rs index eda3a4a..5aa6e84 100644 --- a/src/backend/x11/timer.rs +++ b/src/backend/x11/timer.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::cell::{Cell, RefCell}; use std::cmp::Ordering; use std::collections::{BinaryHeap, HashMap}; @@ -12,7 +11,7 @@ pub type TimerId = usize; struct TimerState { duration: Duration, - handler: RefCell)>>, + handler: RefCell>, } #[derive(Clone)] @@ -60,34 +59,25 @@ impl Timers { self.queue.borrow().peek().map(|e| e.time) } - pub fn set_timer( + pub fn set_timer( &self, app_state: &Rc, duration: Duration, handler: H, ) -> TimerInner where - T: 'static, - H: 'static, - H: FnMut(&mut T, &AppContext), + H: FnMut(&AppContext) + 'static, { let now = Instant::now(); let timer_id = self.next_id.get(); self.next_id.set(timer_id + 1); - let mut handler = handler; - let handler_wrapper = move |data_any: &mut dyn Any, app_state: &Rc| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx) - }; - self.timers.borrow_mut().insert( timer_id, Rc::new(TimerState { duration, - handler: RefCell::new(Box::new(handler_wrapper)), + handler: RefCell::new(Box::new(handler)), }), ); @@ -102,7 +92,7 @@ impl Timers { } } - pub fn poll(&self, data: &mut dyn Any, app_state: &Rc) { + pub fn poll(&self, app_state: &Rc) { let now = Instant::now(); // Check with < and not <= so that we don't process a timer twice during this loop @@ -111,7 +101,8 @@ impl Timers { // If we don't find the timer in `self.timers`, it has been canceled if let Some(timer) = self.timers.borrow().get(&next.timer_id).cloned() { - timer.handler.borrow_mut()(data, app_state); + let cx = AppContext::from_inner(AppContextInner::new(app_state)); + timer.handler.borrow_mut()(&cx); // If we fall behind by more than one timer interval, reset the timer's phase let next_time = (next.time + timer.duration).max(now); diff --git a/src/backend/x11/window.rs b/src/backend/x11/window.rs index f9de2e0..164c544 100644 --- a/src/backend/x11/window.rs +++ b/src/backend/x11/window.rs @@ -1,4 +1,3 @@ -use std::any::Any; use std::cell::{Cell, RefCell}; use std::ffi::{c_int, c_void}; use std::rc::Rc; @@ -33,7 +32,7 @@ pub struct WindowState { pub shm_state: RefCell>, pub expose_rects: RefCell>, pub app_state: Rc, - pub handler: RefCell, Event) -> Response>>, + pub handler: RefCell Response>>, } impl WindowState { @@ -92,6 +91,14 @@ impl WindowState { } } + pub fn handle_event(self: &Rc, event: Event) -> Response { + let window = crate::Window::from_inner(WindowInner { + state: self.clone(), + }); + let cx = AppContext::from_inner(AppContextInner::new(&self.app_state)); + self.handler.borrow_mut()(&window, &cx, event) + } + pub fn close(&self) { if let Some(window_id) = self.window_id.take() { let _ = self.app_state.connection.destroy_window(window_id); @@ -111,15 +118,9 @@ pub struct WindowInner { } impl WindowInner { - pub fn open( - options: &WindowOptions, - cx: &AppContext, - handler: H, - ) -> Result + pub fn open(options: &WindowOptions, cx: &AppContext, handler: H) -> Result where - T: 'static, - H: FnMut(&mut T, &AppContext, Event) -> Response, - H: 'static, + H: FnMut(&crate::Window, &AppContext, Event) -> Response + 'static, { let connection = &cx.inner.state.connection; @@ -190,21 +191,13 @@ impl WindowInner { connection.flush()?; - let mut handler = handler; - let handler_wrapper = - move |data_any: &mut dyn Any, app_state: &Rc, event: Event<'_>| { - let data = data_any.downcast_mut::().unwrap(); - let cx = AppContext::from_inner(AppContextInner::new(app_state)); - handler(data, &cx, event) - }; - let state = Rc::new(WindowState { window_id: Cell::new(Some(window_id)), gc_id: Cell::new(Some(gc_id)), shm_state: RefCell::new(shm_state), expose_rects: RefCell::new(Vec::new()), app_state: Rc::clone(&cx.inner.state), - handler: RefCell::new(Box::new(handler_wrapper)), + handler: RefCell::new(Box::new(handler)), }); cx.inner.state.windows.borrow_mut().insert(window_id, Rc::clone(&state)); diff --git a/src/window.rs b/src/window.rs index 2e35ed6..0d2e0e1 100644 --- a/src/window.rs +++ b/src/window.rs @@ -186,11 +186,9 @@ impl WindowOptions { self } - pub fn open(&self, cx: &AppContext, handler: H) -> Result + pub fn open(&self, cx: &AppContext, handler: H) -> Result where - H: 'static, - H: FnMut(&mut T, &AppContext, Event) -> Response, - T: 'static, + H: FnMut(&Window, &AppContext, Event) -> Response + 'static, { Ok(Window::from_inner(backend::WindowInner::open( self, cx, handler, @@ -206,7 +204,7 @@ pub struct Window { } impl Window { - fn from_inner(inner: backend::WindowInner) -> Window { + pub(crate) fn from_inner(inner: backend::WindowInner) -> Window { Window { inner, _marker: PhantomData,