Skip to content

Commit

Permalink
Add binding for setSerialMessageCallback (#327)
Browse files Browse the repository at this point in the history
* Add binding to setSerialMessageCallback

* Add example

---------

Co-authored-by: Alexander Koz <a@koz.email>
  • Loading branch information
paulyoung and boozook authored May 1, 2024
1 parent 5f03a23 commit 28d8ba0
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 1 deletion.
6 changes: 6 additions & 0 deletions api/system/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ crate-type = ["dylib", "staticlib"]
path = "examples/handler-pinned.rs"
required-features = ["sys/entry-point", "sys/lang-items"]

[[example]]
name = "set-serial-message-callback"
crate-type = ["dylib", "staticlib"]
path = "examples/set-serial-message-callback.rs"
required-features = ["sys/entry-point", "sys/lang-items"]

[package.metadata.playdate]
bundle-id = "rs.playdate.system"

Expand Down
2 changes: 2 additions & 0 deletions api/system/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ cargo playdate run -p=playdate-system --example=handler-static --features=sys/la
cargo playdate run -p=playdate-system --example=handler-boxed --features=sys/lang-items,sys/entry-point

cargo playdate run -p=playdate-system --example=handler-pinned --features=sys/lang-items,sys/entry-point

cargo playdate run -p=playdate-system --example=set-serial-message-callback --features=sys/lang-items,sys/entry-point
```

More information how to use [cargo-playdate][] in help: `cargo playdate --help`.
Expand Down
59 changes: 59 additions & 0 deletions api/system/examples/set-serial-message-callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#![no_std]
extern crate alloc;

#[macro_use]
extern crate sys;
extern crate playdate_system as system;

use core::ptr::NonNull;

use sys::EventLoopCtrl;
use sys::ffi::*;
use system::System;
use system::event::SystemEventExt as _;
use system::update::UpdateCtrl;


/// Entry point, event handler
#[no_mangle]
fn event_handler(_api: NonNull<PlaydateAPI>, event: PDSystemEvent, _: u32) -> EventLoopCtrl {
// Just for this example, ignore all events except init:
if event != PDSystemEvent::Init {
return EventLoopCtrl::Continue;
}


let mut counter: u32 = 0;

let callback = move |msg| {
counter += 1;

println!("[{counter}/3] serial_message_callback: '{}'", msg);

if counter == 3 {
println!("stop receiving serial messages");
System::Default().set_serial_message_callback(None::<fn(_)>);
}
};

// Register callback to start receiving serial messages:
System::Default().set_serial_message_callback(Some(callback));


// Also set update callback:
System::Default().set_update_callback_static(Some(on_update), ());

// Continue event-loop:
EventLoopCtrl::Continue
}


/// Update handler
fn on_update(_: &mut ()) -> UpdateCtrl {
// Continue updates
UpdateCtrl::Continue
}


// Needed for debug build
ll_symbols!();
64 changes: 63 additions & 1 deletion api/system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use core::ffi::c_float;
use core::ffi::c_int;
use core::ffi::c_uint;
use core::time::Duration;
use alloc::string::String;


pub mod time;
pub mod lang;
Expand Down Expand Up @@ -247,10 +249,53 @@ impl<Api: api::Api> System<Api> {
let _ = dt; // this to prevent earlier drop.
epoch
}
}

/// Equivalent to [`sys::ffi::playdate_sys::setSerialMessageCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setSerialMessageCallback")]
pub fn set_serial_message_callback<F>(&self, callback: Option<F>)
where F: 'static + FnMut(String) + Sized {
use core::ffi::c_char;
use core::ffi::CStr;
use alloc::boxed::Box;
use alloc::string::String;


static mut STORE: Option<Box<dyn FnMut(String)>> = None;

pub unsafe extern "C" fn proxy_serial_message_callback<F: FnMut(String)>(data: *const c_char) {
let data = CStr::from_ptr(data as _).to_string_lossy().into_owned();
if let Some(ref mut f) = STORE.as_mut() {
f(data)
} else {
// Highly unlikely, mostly impossible case.
// Should be unreachable, but still possible in case when
// 0. new callback is None, we have to register it in the System;
// 1. write callback to `STORE`
// 2. interrupt, proxy_serial_message_callback called, BOOM!
// 3. call API::set_serial_message_callback to set our new (None) callback
// So, see difference in how to store & reg callback at couple lines below.
panic!("missed callback")
}
}


let f = self.0.set_serial_message_callback();

if let Some(callback) = callback {
let boxed = Box::new(callback);
// Store firstly, then register it.
unsafe { STORE = Some(boxed as _) }
unsafe { f(Some(proxy_serial_message_callback::<F>)) }
} else {
// Set firstly, then clear the `STORE`.
unsafe { f(None) }
unsafe { STORE = None }
}
}
}

pub mod api {
use core::ffi::c_char;
use core::ffi::c_float;
use core::ffi::c_int;
use core::ffi::c_uint;
Expand All @@ -263,6 +308,9 @@ pub mod api {
use sys::ffi::playdate_sys;


pub type FnSerialMessageCallback = Option<unsafe extern "C" fn(data: *const c_char)>;


/// Default system api end-point, ZST.
///
/// All calls approximately costs ~3 derefs.
Expand Down Expand Up @@ -418,6 +466,13 @@ pub mod api {
fn convert_date_time_to_epoch(&self) -> unsafe extern "C" fn(datetime: *mut PDDateTime) -> u32 {
self.0.convertDateTimeToEpoch.expect("convertDateTimeToEpoch")
}

/// Equivalent to [`sys::ffi::playdate_sys::setSerialMessageCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setSerialMessageCallback")]
#[inline(always)]
fn set_serial_message_callback(&self) -> unsafe extern "C" fn(callback: FnSerialMessageCallback) {
self.0.setSerialMessageCallback.expect("setSerialMessageCallback")
}
}


Expand Down Expand Up @@ -518,5 +573,12 @@ pub mod api {
fn convert_date_time_to_epoch(&self) -> unsafe extern "C" fn(datetime: *mut PDDateTime) -> u32 {
*sys::api!(system.convertDateTimeToEpoch)
}

/// Equivalent to [`sys::ffi::playdate_sys::setSerialMessageCallback`]
#[doc(alias = "sys::ffi::playdate_sys::setSerialMessageCallback")]
#[inline(always)]
fn set_serial_message_callback(&self) -> unsafe extern "C" fn(callback: FnSerialMessageCallback) {
*sys::api!(system.setSerialMessageCallback)
}
}
}

0 comments on commit 28d8ba0

Please sign in to comment.