Skip to content

Commit

Permalink
remove app data. all state is now stored in window and timer handlers
Browse files Browse the repository at this point in the history
Remove the app data (T in App<T>). Building the App struct no longer
requires a builder closure that constructs the app data. App now has a
context method for getting access to the AppContext outside an event
handler, which makes it possible to open windows before entering the
event loop. Window handlers are also now passed a &Window, which makes
it possible to call methods on the window without passing it in via an
Rc or similar.

These APIs will get cleaned up in the future. AppContext will become an
AppHandle which implements Clone, and window and timer handlers will be
passed a WindowContext and TimerContext which allow getting access to
both the AppHandle and the Window and Timer objects respectively.
  • Loading branch information
micahrj committed Oct 30, 2023
1 parent 8f45e19 commit 288792e
Show file tree
Hide file tree
Showing 12 changed files with 282 additions and 442 deletions.
52 changes: 25 additions & 27 deletions examples/basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const WIDTH: usize = 512;
const HEIGHT: usize = 512;

struct State {
window: Window,
framebuffer: Vec<u32>,
width: usize,
height: usize,
Expand All @@ -19,14 +18,20 @@ impl Drop for State {
}

impl State {
fn handle_event(&mut self, cx: &AppContext<Self>, 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);
Expand All @@ -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();
}
37 changes: 18 additions & 19 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,29 +52,29 @@ impl AppOptions {
self
}

pub fn build<T, F>(&self, build: F) -> Result<App<T>>
where
F: FnOnce(&AppContext<T>) -> Result<T>,
T: 'static,
{
Ok(App::from_inner(backend::AppInner::new(self, build)?))
pub fn build(&self) -> Result<App> {
Ok(App::from_inner(backend::AppInner::new(self)?))
}
}

pub struct App<T> {
inner: backend::AppInner<T>,
pub struct App {
inner: backend::AppInner,
// ensure !Send and !Sync on all platforms
_marker: PhantomData<*mut ()>,
}

impl<T: 'static> App<T> {
pub(crate) fn from_inner(inner: backend::AppInner<T>) -> App<T> {
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()
}
Expand All @@ -84,7 +84,7 @@ impl<T: 'static> App<T> {
}
}

impl<T> fmt::Debug for App<T> {
impl fmt::Debug for App {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("App").finish_non_exhaustive()
}
Expand All @@ -94,20 +94,20 @@ impl<T> fmt::Debug for App<T> {
use std::os::unix::io::{AsRawFd, RawFd};

#[cfg(target_os = "linux")]
impl<T> AsRawFd for App<T> {
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<T>) -> AppContext<T> {
impl<'a> AppContext<'a> {
pub(crate) fn from_inner(inner: backend::AppContextInner) -> AppContext {
AppContext {
inner,
_marker: PhantomData,
Expand All @@ -116,8 +116,7 @@ impl<'a, T: 'static> AppContext<'a, T> {

pub fn set_timer<H>(&self, duration: Duration, handler: H) -> Timer
where
H: 'static,
H: FnMut(&mut T, &AppContext<T>),
H: FnMut(&AppContext) + 'static,
{
Timer {
inner: self.inner.set_timer(duration, handler),
Expand All @@ -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()
}
Expand Down
61 changes: 18 additions & 43 deletions src/backend/cocoa/app.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -23,7 +21,6 @@ pub struct AppState {
pub timers: Timers,
pub display_links: DisplayLinks,
pub windows: RefCell<HashMap<*const WindowState, Rc<WindowState>>>,
pub data: RefCell<Option<Box<dyn Any>>>,
}

impl Drop for AppState {
Expand All @@ -34,17 +31,12 @@ impl Drop for AppState {
}
}

pub struct AppInner<T> {
pub struct AppInner {
pub state: Rc<AppState>,
_marker: PhantomData<T>,
}

impl<T: 'static> AppInner<T> {
pub fn new<F>(options: &AppOptions, build: F) -> Result<AppInner<T>>
where
F: FnOnce(&AppContext<T>) -> Result<T>,
T: 'static,
{
impl AppInner {
pub fn new(options: &AppOptions) -> Result<AppInner> {
autoreleasepool(|_| {
assert!(
NSThread::isMainThread_class(),
Expand All @@ -71,19 +63,10 @@ impl<T: 'static> AppInner<T> {
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();
Expand All @@ -92,13 +75,14 @@ impl<T: 'static> AppInner<T> {
}
}

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();
Expand All @@ -112,39 +96,30 @@ impl<T: 'static> AppInner<T> {
}
}

impl<T> Drop for AppInner<T> {
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<AppState>,
_marker: PhantomData<T>,
}

impl<'a, T: 'static> AppContextInner<'a, T> {
pub(super) fn new(state: &'a Rc<AppState>) -> AppContextInner<'a, T> {
AppContextInner {
state,
_marker: PhantomData,
}
impl<'a> AppContextInner<'a> {
pub(super) fn new(state: &'a Rc<AppState>) -> AppContextInner<'a> {
AppContextInner { state }
}

pub fn set_timer<H>(&self, duration: Duration, handler: H) -> TimerInner
where
H: 'static,
H: FnMut(&mut T, &AppContext<T>),
H: FnMut(&AppContext) + 'static,
{
self.state.timers.set_timer(self.state, duration, handler)
}
Expand Down
25 changes: 6 additions & 19 deletions src/backend/cocoa/timer.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::any::Any;
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ffi::c_void;
Expand Down Expand Up @@ -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<Option<CFRunLoopTimerRef>>,
app_state: Rc<AppState>,
handler: RefCell<Box<dyn FnMut(&mut dyn Any, &Rc<AppState>)>>,
handler: RefCell<Box<dyn FnMut(&AppContext)>>,
}

impl TimerState {
Expand All @@ -65,28 +61,19 @@ impl Timers {
}
}

pub fn set_timer<T, H>(
pub fn set_timer<H>(
&self,
app_state: &Rc<AppState>,
duration: Duration,
handler: H,
) -> TimerInner
where
T: 'static,
H: 'static,
H: FnMut(&mut T, &AppContext<T>),
H: FnMut(&AppContext) + 'static,
{
let mut handler = handler;
let handler_wrapper = move |data_any: &mut dyn Any, app_state: &Rc<AppState>| {
let data = data_any.downcast_mut::<T>().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 {
Expand Down
Loading

0 comments on commit 288792e

Please sign in to comment.