Skip to content

Commit

Permalink
Add some support for the Lua API (#319)
Browse files Browse the repository at this point in the history
* Add some support for the Lua API

* Add example

* Update README

* Update authors

* Minor bump `playdate` crate

* CI: add package test for example of `playdate-lua` crate

---------

Co-authored-by: Alexander Koz <[email protected]>
  • Loading branch information
paulyoung and boozook authored May 7, 2024
1 parent e893117 commit 0779ab8
Show file tree
Hide file tree
Showing 15 changed files with 414 additions and 4 deletions.
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@ jobs:
cargo playdate package --features=$FEATURES --examples --device --simulator -p=playdate-sound
cargo playdate package --features=$FEATURES --examples --device --simulator -p=playdate-sprite
cargo playdate package --features=$FEATURES --examples --device --simulator -p=playdate-system
cargo playdate package --features=$FEATURES --examples --device --simulator -p=playdate-lua
cargo playdate package --features=lang-items,entry-point --examples --device --simulator -p=playdate
# TODO: build crankstart with examples for compatibility test
Expand Down
11 changes: 10 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ctrl = { version = "0.3", path = "api/ctrl", package = "playdate-controls", defa
display = { version = "0.3", path = "api/display", package = "playdate-display", default-features = false }
fs = { version = "0.2", path = "api/fs", package = "playdate-fs", default-features = false }
gfx = { version = "0.3", path = "api/gfx", package = "playdate-graphics", default-features = false }
lua = { version = "0.1", path = "api/lua", package = "playdate-lua", default-features = false }
menu = { version = "0.2", path = "api/menu", package = "playdate-menu", default-features = false }
scoreboards = { version = "0.1", path = "api/scoreboards", package = "playdate-scoreboards", default-features = false }
sound = { version = "0.3", path = "api/sound", package = "playdate-sound", default-features = false }
Expand Down
67 changes: 67 additions & 0 deletions api/lua/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
[package]
name = "playdate-lua"
version = "0.1.0"
readme = "README.md"
description = "High-level Lua API built on-top of Playdate API"
keywords = ["playdate", "sdk", "api", "gamedev"]
categories = ["game-development", "api-bindings", "no-std"]
edition.workspace = true
license.workspace = true
authors = ["Paul Young", "Alex Koz <[email protected]>"]
homepage.workspace = true
repository.workspace = true


[features]
default = ["sys/default"]

# playdate-sys features, should be shared because it's build configuration:

bindgen-runtime = ["sys/bindgen-runtime"]
bindgen-static = ["sys/bindgen-static"]
bindings-derive-debug = ["sys/bindings-derive-debug"]


[dependencies]
sys = { workspace = true, default-features = false }

[dev-dependencies]
system = { workspace = true, default-features = false }


[[example]]
name = "add-function-get-arg-string"
crate-type = ["dylib", "staticlib"]
path = "examples/add-function-get-arg-string.rs"
required-features = ["sys/entry-point", "sys/lang-items"]


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

[package.metadata.playdate.dev-assets]
# The Lua runtime expects us to provide a main.pdz file at the root.
#
# Compiled with `pdc --skip-unknown --strip sources/main.lua Example.pdx`
"main.pdz" = "examples/Example.pdx/main.pdz"


[package.metadata.docs.rs]
all-features = false
features = [
"sys/bindings-derive-default",
"sys/bindings-derive-eq",
"sys/bindings-derive-copy",
"bindings-derive-debug",
"sys/bindings-derive-hash",
"sys/bindings-derive-ord",
"sys/bindings-derive-partialeq",
"sys/bindings-derive-partialord",
]
rustdoc-args = ["--cfg", "docsrs", "--show-type-layout"]
default-target = "thumbv7em-none-eabihf"
cargo-args = [
"-Zunstable-options",
"-Zrustdoc-scrape-examples",
"-Zbuild-std=core,alloc",
]
17 changes: 17 additions & 0 deletions api/lua/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Lua API for PlayDate

High-level Lua API built on-top of [playdate-sys][].


## Usage

See [examples][].

[examples]: ./examples
[playdate-sys]: https://crates.io/crates/playdate-sys



- - -

This software is not sponsored or supported by Panic.
Binary file added api/lua/examples/Example.pdx/main.pdz
Binary file not shown.
2 changes: 2 additions & 0 deletions api/lua/examples/Example.pdx/pdxinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pdxversion=20400
buildtime=767932885
16 changes: 16 additions & 0 deletions api/lua/examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Examples

These examples additionally use other crates with parts of Playdate API to minimize the amount of code.


# How to run

```bash
cargo playdate run -p=playdate-lua --example=add-function-get-arg-string --features=sys/lang-items,sys/entry-point
```

More information how to use [cargo-playdate][] in help: `cargo playdate --help`.



[cargo-playdate]: https://crates.io/crates/cargo-playdate
68 changes: 68 additions & 0 deletions api/lua/examples/add-function-get-arg-string.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#![no_std]
extern crate alloc;

#[macro_use]
extern crate sys;
extern crate playdate_lua as lua;

use core::ffi::c_int;
use core::ptr::NonNull;

use lua::Lua;
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 {
// We need to set our update callback in the InitLua handler instead of Init.
// https://devforum.play.date/t/lua-c-minimal-example/4354/5
//
// Just for this example, ignore all other events.
if event != PDSystemEvent::InitLua {
return EventLoopCtrl::Continue;
}

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

// Add a function that we depend on and call in main.lua
Lua::Default().add_function(Some(log_to_console_from_main_dot_lua), "example.logToConsole")
.expect("add_function 'log_to_console_from_main_dot_lua' should succeed");

// Continue event loop
EventLoopCtrl::Continue
}


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


// The function we add to the Lua runtime and call from main.lua
pub unsafe extern "C" fn log_to_console_from_main_dot_lua(_lua_state: *mut lua_State) -> c_int {
// We know that our function takes a single argument which is a string.
let arg_string = Lua::Default().get_arg_string(1)
.expect("get_arg_string should succeed");

// Avoid going from CString to str and back with playdate::sys::log::println
let f = (*(*sys::API).system).logToConsole
.expect("get logToConsole to succeed");

f(arg_string.as_ptr());

// A `lua_CFunction` should return the number of return values it has pushed
// onto the stack.
0
}


// Needed for debug build
ll_symbols!();
1 change: 1 addition & 0 deletions api/lua/examples/sources/main.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
example.logToConsole("hello from main.lua");
38 changes: 38 additions & 0 deletions api/lua/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use alloc::borrow::ToOwned;
use core::fmt;
use sys::ffi::CStr;
use sys::ffi::CString;


pub type ApiError = sys::error::Error<self::Error>;


#[derive(Debug)]
pub enum Error {
AddFunction(CString),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Error::AddFunction(cs) => {
match cs.to_str() {
Ok(err) => err.fmt(f),
Err(_) => f.write_fmt(format_args!("Add function error: {cs:?}")),
}
},
}
}
}


impl From<Error> for ApiError {
fn from(err: Error) -> Self { ApiError::Api(err) }
}

impl From<&'_ CStr> for Error {
fn from(cs: &CStr) -> Self { Self::AddFunction(cs.to_owned()) }
}


impl core::error::Error for Error {}
Loading

0 comments on commit 0779ab8

Please sign in to comment.