Add DocC documentation and a runnable example game
- DocC catalog (Sources/PlayDate/PlayDate.docc) with a curated landing page and a Getting Started article; swift-docc-plugin wired up for `swift package generate-documentation`, and a GitHub Pages deployment workflow renders docs on every push to main. - Examples/HelloPlaydate: a minimal game (bouncing box, crank needle, button handling, system menu item) whose build.sh compiles the game as a dylib and produces a runnable simulator .pdx via the SDK's pdc. Verified: the pdx builds and exports the eventHandler entry point. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
name: Documentation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: docs
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
PLAYDATE_SDK_VERSION: "3.1.1"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Cache Playdate SDK
|
||||
id: sdk-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/PlaydateSDK
|
||||
key: playdate-sdk-${{ env.PLAYDATE_SDK_VERSION }}
|
||||
|
||||
- name: Download Playdate SDK headers
|
||||
if: steps.sdk-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
curl -sL "https://download.panic.com/playdate_sdk/Linux/PlaydateSDK-${PLAYDATE_SDK_VERSION}.tar.gz" | tar xz -C "$HOME"
|
||||
mv "$HOME/PlaydateSDK-${PLAYDATE_SDK_VERSION}" "$HOME/PlaydateSDK"
|
||||
test -f "$HOME/PlaydateSDK/C_API/pd_api.h"
|
||||
|
||||
- name: Install pkg-config module
|
||||
run: |
|
||||
PLAYDATE_SDK_PATH="$HOME/PlaydateSDK" Scripts/install-pkgconfig.sh "$HOME/pkgconfig"
|
||||
echo "PKG_CONFIG_PATH=$HOME/pkgconfig" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Generate documentation
|
||||
run: |
|
||||
swift package --allow-writing-to-directory docs \
|
||||
generate-documentation --target PlayDate \
|
||||
--disable-indexing \
|
||||
--transform-for-static-hosting \
|
||||
--hosting-base-path play-date \
|
||||
--output-path docs
|
||||
|
||||
- uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docs
|
||||
|
||||
- id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
.DS_Store
|
||||
/.build
|
||||
.build/
|
||||
/Packages
|
||||
# Example build products
|
||||
Examples/*/Source/pdex.dylib
|
||||
*.pdx
|
||||
xcuserdata/
|
||||
DerivedData/
|
||||
.swiftpm/configuration/registries.json
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// swift-tools-version: 6.4
|
||||
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "HelloPlaydate",
|
||||
products: [
|
||||
// The Playdate Simulator loads the game as pdex.dylib.
|
||||
.library(name: "pdex", type: .dynamic, targets: ["HelloPlaydate"]),
|
||||
],
|
||||
dependencies: [
|
||||
.package(path: "../.."),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
name: "HelloPlaydate",
|
||||
dependencies: [.product(name: "PlayDate", package: "play-date")]
|
||||
),
|
||||
]
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Hello Playdate
|
||||
|
||||
A minimal game built on the `play-date` bindings: a bouncing box, a
|
||||
crank-aimed needle, button handling, a system menu item, and an FPS counter.
|
||||
|
||||
## Build and run (Playdate Simulator)
|
||||
|
||||
With the Playdate SDK installed (and the one-time
|
||||
`Scripts/install-pkgconfig.sh` setup from the repository root done):
|
||||
|
||||
```sh
|
||||
./build.sh
|
||||
open -a "$HOME/Developer/PlaydateSDK/bin/Playdate Simulator.app" HelloPlaydate.pdx
|
||||
```
|
||||
|
||||
The script compiles the game as a dylib, places it in `Source/` next to
|
||||
`pdxinfo`, and runs the SDK's `pdc` to produce `HelloPlaydate.pdx`.
|
||||
|
||||
## Device builds
|
||||
|
||||
Running on hardware requires the Embedded Swift + ARM toolchain pipeline
|
||||
(see the repository README and Apple's swift-playdate-examples for the
|
||||
Makefile setup). This example covers the simulator workflow only.
|
||||
@@ -0,0 +1,6 @@
|
||||
name=Hello Playdate
|
||||
author=Röck+Cöde
|
||||
description=Minimal example for the play-date Swift bindings
|
||||
bundleID=com.rock-n-code.hello-playdate
|
||||
version=0.1
|
||||
buildNumber=1
|
||||
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Game.swift
|
||||
// A minimal Playdate game built on the play-date Swift bindings: a
|
||||
// bouncing box, a crank-aimed needle, button logging, and a system menu
|
||||
// item.
|
||||
//
|
||||
|
||||
import CPlaydate
|
||||
import PlayDate
|
||||
|
||||
@_cdecl("eventHandler")
|
||||
public func eventHandler(
|
||||
pointer: UnsafeMutableRawPointer,
|
||||
event: PDSystemEvent,
|
||||
argument: UInt32
|
||||
) -> Int32 {
|
||||
if case .initialize = SystemEvent(event: event, argument: argument) {
|
||||
Playdate.initialize(with: pointer) // must happen before anything else
|
||||
Game.shared.start()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
final class Game {
|
||||
nonisolated(unsafe) static let shared = Game()
|
||||
|
||||
private var x: Float = 200
|
||||
private var y: Float = 120
|
||||
private var dx: Float = 3
|
||||
private var dy: Float = 2
|
||||
private let boxSize = 24
|
||||
|
||||
func start() {
|
||||
Display.setRefreshRate(50)
|
||||
|
||||
System.addMenuItem(title: "reset") { _ in
|
||||
Game.shared.reset()
|
||||
}
|
||||
|
||||
System.setUpdateCallback {
|
||||
Game.shared.update()
|
||||
return true // redraw the display this frame
|
||||
}
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
x = 200
|
||||
y = 120
|
||||
}
|
||||
|
||||
private func update() {
|
||||
moveBox()
|
||||
handleInput()
|
||||
draw()
|
||||
}
|
||||
|
||||
private func moveBox() {
|
||||
let width = Float(Display.width)
|
||||
let height = Float(Display.height)
|
||||
|
||||
x += dx
|
||||
y += dy
|
||||
if x < 0 || x > width - Float(boxSize) { dx = -dx }
|
||||
if y < 24 || y > height - Float(boxSize) { dy = -dy }
|
||||
}
|
||||
|
||||
private func handleInput() {
|
||||
let (_, pushed, _) = System.buttonState
|
||||
if pushed.contains(.a) {
|
||||
System.log("A pressed at \(System.currentTimeMilliseconds)ms")
|
||||
}
|
||||
if pushed.contains(.b) {
|
||||
(dx, dy) = (-dx, -dy)
|
||||
}
|
||||
}
|
||||
|
||||
private func draw() {
|
||||
Graphics.clear(color: .white)
|
||||
Graphics.drawText("Hëllo from Swift — Ⓑ reverses", x: 8, y: 4)
|
||||
Graphics.fillRect(x: Int(x), y: Int(y), width: boxSize, height: boxSize, color: .black)
|
||||
|
||||
if !System.isCrankDocked {
|
||||
// A needle from the screen center pointing where the crank points.
|
||||
let radians = System.crankAngle * .pi / 180
|
||||
let centerX = Display.width / 2
|
||||
let centerY = Display.height / 2
|
||||
Graphics.drawLine(
|
||||
x1: centerX, y1: centerY,
|
||||
x2: centerX + Int(40 * sinf(radians)),
|
||||
y2: centerY - Int(40 * cosf(radians)),
|
||||
width: 2, color: .xor)
|
||||
}
|
||||
|
||||
System.drawFPS(x: 380, y: 4)
|
||||
}
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Builds HelloPlaydate.pdx for the Playdate Simulator: compiles the game as
|
||||
# a dylib, places it in the pdc source folder, and runs the SDK's pdc.
|
||||
#
|
||||
# Device builds need the Embedded Swift + ARM toolchain setup described in
|
||||
# the repository README; this script covers the simulator only.
|
||||
|
||||
set -eu
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
sdk_path="${PLAYDATE_SDK_PATH:-$HOME/Developer/PlaydateSDK}"
|
||||
|
||||
swift build -c release
|
||||
bin_path="$(swift build -c release --show-bin-path)"
|
||||
cp "$bin_path/libpdex.dylib" Source/pdex.dylib
|
||||
|
||||
"$sdk_path/bin/pdc" Source HelloPlaydate.pdx
|
||||
|
||||
echo "Built HelloPlaydate.pdx — run it with:"
|
||||
echo " open -a \"$sdk_path/bin/Playdate Simulator.app\" HelloPlaydate.pdx"
|
||||
@@ -10,6 +10,10 @@ let package = Package(
|
||||
targets: ["PlayDate"]
|
||||
),
|
||||
],
|
||||
dependencies: [
|
||||
// Documentation generation only; not linked into the library.
|
||||
.package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.4.0"),
|
||||
],
|
||||
targets: [
|
||||
// The Playdate C API headers, resolved through the "playdate"
|
||||
// pkg-config module. Run Scripts/install-pkgconfig.sh once to point
|
||||
|
||||
@@ -4,12 +4,7 @@
|
||||
|
||||
Swift bindings to the [Playdate](https://play.date) C API.
|
||||
|
||||
The Playdate C API is delivered as a `PlaydateAPI*` struct of function
|
||||
pointers that the firmware hands to your game at launch. This package wraps
|
||||
that surface in idiomatic Swift: namespaced APIs, wrapper types with
|
||||
ownership semantics, closures instead of function-pointer/userdata pairs,
|
||||
`OptionSet`s and `enum`s instead of raw constants, and typed `throws` for
|
||||
fallible calls.
|
||||
The Playdate C API is delivered as a `PlaydateAPI*` struct of function pointers that the firmware hands to your game at launch. This package wraps that surface in idiomatic Swift: namespaced APIs, wrapper types with ownership semantics, closures instead of function-pointer/userdata pairs, `OptionSet`s and `enum`s instead of raw constants, and typed `throws` for fallible calls.
|
||||
|
||||
All ten C subsystems are covered:
|
||||
|
||||
@@ -28,28 +23,20 @@ All ten C subsystems are covered:
|
||||
|
||||
## Requirements
|
||||
|
||||
- The [Playdate SDK](https://play.date/dev/) (3.1.1 or later). The SDK is
|
||||
not vendored into this repository.
|
||||
- The [Playdate SDK](https://play.date/dev/) (3.1.1 or later). The SDK is not vendored into this repository.
|
||||
- Swift 6.4 tools or later.
|
||||
|
||||
### One-time SDK setup
|
||||
|
||||
The `CPlaydate` target resolves `pd_api.h` through a `playdate` pkg-config
|
||||
module, so the package works as a normal SwiftPM dependency without unsafe
|
||||
build flags. Generate the pkg-config file once per machine:
|
||||
The `CPlaydate` target resolves `pd_api.h` through a `playdate` pkg-config module, so the package works as a normal SwiftPM dependency without unsafe build flags. Generate the pkg-config file once per machine:
|
||||
|
||||
```sh
|
||||
Scripts/install-pkgconfig.sh
|
||||
```
|
||||
|
||||
The script locates the SDK via `PLAYDATE_SDK_PATH` (default
|
||||
`~/Developer/PlaydateSDK`) and writes `playdate.pc` into a directory SwiftPM
|
||||
searches by default (`/usr/local/lib/pkgconfig`). Pass a custom destination
|
||||
as an argument if you prefer another location on your `PKG_CONFIG_PATH`.
|
||||
The script locates the SDK via `PLAYDATE_SDK_PATH` (default `~/Developer/PlaydateSDK`) and writes `playdate.pc` into a directory SwiftPM searches by default (`/usr/local/lib/pkgconfig`). Pass a custom destination as an argument if you prefer another location on your `PKG_CONFIG_PATH`.
|
||||
|
||||
If Xcode had the package open before you ran the script, make it re-read the
|
||||
manifest (File ▸ Packages ▸ Reset Package Caches) — Xcode caches package
|
||||
resolution and won't notice the new `.pc` file on its own.
|
||||
If Xcode had the package open before you ran the script, make it re-read the manifest (File ▸ Packages ▸ Reset Package Caches) — Xcode caches package resolution and won't notice the new `.pc` file on its own.
|
||||
|
||||
## Adding the dependency
|
||||
|
||||
@@ -70,18 +57,18 @@ targets: [
|
||||
|
||||
## Getting started
|
||||
|
||||
A Playdate game has a single C entry point, `eventHandler`. Export it with
|
||||
`@_cdecl`, initialize the binding on the first event, and install an update
|
||||
callback:
|
||||
A Playdate game has a single C entry point, `eventHandler`. Export it with `@_cdecl`, initialize the binding on the first event, and install an update callback:
|
||||
|
||||
```swift
|
||||
import CPlaydate
|
||||
import PlayDate
|
||||
|
||||
@_cdecl("eventHandler")
|
||||
func eventHandler(pointer: UnsafeMutableRawPointer,
|
||||
event: PDSystemEvent,
|
||||
argument: UInt32) -> Int32 {
|
||||
func eventHandler(
|
||||
pointer: UnsafeMutableRawPointer,
|
||||
event: PDSystemEvent,
|
||||
argument: UInt32
|
||||
) -> Int32 {
|
||||
switch SystemEvent(event: event, argument: argument) {
|
||||
case .initialize:
|
||||
Playdate.initialize(with: pointer) // must happen before anything else
|
||||
@@ -91,6 +78,7 @@ func eventHandler(pointer: UnsafeMutableRawPointer,
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -120,9 +108,7 @@ final class Game {
|
||||
}
|
||||
```
|
||||
|
||||
`Playdate.initialize(with:)` stores the API pointer once; every wrapper in
|
||||
the module uses it from then on. Calling any wrapper before `initialize` is
|
||||
a programmer error and will crash.
|
||||
`Playdate.initialize(with:)` stores the API pointer once; every wrapper in the module uses it from then on. Calling any wrapper before `initialize` is a programmer error and will crash.
|
||||
|
||||
## Tour of the API
|
||||
|
||||
@@ -202,10 +188,7 @@ for collision in collisions where collision.other.tag == Tags.brick {
|
||||
}
|
||||
```
|
||||
|
||||
Sprite callbacks (`setUpdateFunction`, `setDrawFunction`,
|
||||
`setCollisionResponseFunction`) receive the Swift wrapper back. The C-level
|
||||
sprite userdata slot is reserved by the binding for that recovery — use the
|
||||
`userdata` property on `Sprite` for your own per-sprite storage instead.
|
||||
Sprite callbacks (`setUpdateFunction`, `setDrawFunction`, `setCollisionResponseFunction`) receive the Swift wrapper back. The C-level sprite userdata slot is reserved by the binding for that recovery — use the `userdata` property on `Sprite` for your own per-sprite storage instead.
|
||||
|
||||
### Sound
|
||||
|
||||
@@ -295,61 +278,56 @@ try Lua.addFunction(double, name: "mylib.double")
|
||||
|
||||
## Conventions
|
||||
|
||||
- **Namespaces.** The subsystem namespaces (`System`, `Graphics`, `Sound`,
|
||||
…) live at the top level of the module; only the raw C API bootstrap
|
||||
stays under `Playdate` (`Playdate.initialize(with:)`, `Playdate.api`).
|
||||
On a name collision with another module, qualify with the module name:
|
||||
`PlayDate.System`.
|
||||
- **Errors.** Fallible operations use typed throws — `throws(PlaydateError)`
|
||||
generally, `throws(Network.NetError)` for network I/O — so `catch`
|
||||
gives you a concrete type, and no `any Error` existentials are needed.
|
||||
- **Ownership.** A wrapper that *creates* a C object frees it on `deinit`;
|
||||
keep the wrapper referenced for as long as you use it. Wrappers vending
|
||||
OS-owned objects (a `Bitmap` from a `BitmapTable`, a track from a
|
||||
`Sequence`, …) don't free them — keep the owner alive instead, as
|
||||
documented on each API. Resources a C object keeps referencing (a sprite's
|
||||
image, a synth's sample, modulators, menu-item option titles) are retained
|
||||
by the wrapper automatically.
|
||||
- **Callbacks.** Where the C API provides a userdata slot, closures are
|
||||
supported everywhere and delivered back with the right wrapper. A few C
|
||||
callbacks have no userdata (serial messages, headphone changes, scoreboard
|
||||
completions, `getServerTime`); those track one Swift closure at a time, as
|
||||
noted in their documentation.
|
||||
- **Threading.** The Playdate runtime is single-threaded (audio callbacks
|
||||
excepted); statics in the binding are `nonisolated(unsafe)` on that basis.
|
||||
Don't call the API from other threads.
|
||||
- **Namespaces.** The subsystem namespaces (`System`, `Graphics`, `Sound`, …) live at the top level of the module; only the raw C API bootstrap stays under `Playdate` (`Playdate.initialize(with:)`, `Playdate.api`). On a name collision with another module, qualify with the module name: `PlayDate.System`.
|
||||
- **Errors.** Fallible operations use typed throws — `throws(PlaydateError)` generally, `throws(Network.NetError)` for network I/O — so `catch` gives you a concrete type, and no `any Error` existentials are needed.
|
||||
- **Ownership.** A wrapper that *creates* a C object frees it on `deinit`; keep the wrapper referenced for as long as you use it. Wrappers vending OS-owned objects (a `Bitmap` from a `BitmapTable`, a track from a `Sequence`, …) don't free them — keep the owner alive instead, as documented on each API. Resources a C object keeps referencing (a sprite's image, a synth's sample, modulators, menu-item option titles) are retained by the wrapper automatically.
|
||||
- **Callbacks.** Where the C API provides a userdata slot, closures are supported everywhere and delivered back with the right wrapper. A few C callbacks have no userdata (serial messages, headphone changes, scoreboard
|
||||
completions, `getServerTime`); those track one Swift closure at a time, as noted in their documentation.
|
||||
- **Threading.** The Playdate runtime is single-threaded (audio callbacks excepted); statics in the binding are `nonisolated(unsafe)` on that basis. Don't call the API from other threads.
|
||||
|
||||
## Building for the simulator and device
|
||||
|
||||
This package builds as a plain Swift library, which is how you develop and
|
||||
unit-test game logic on the host (`swift test` works out of the box).
|
||||
This package builds as a plain Swift library, which is how you develop and unit-test game logic on the host (`swift test` works out of the box).
|
||||
|
||||
Shipping a `.pdx` needs the Playdate toolchain on top:
|
||||
|
||||
- **Simulator** builds compile your game as a host dylib placed in the pdx.
|
||||
- **Device** builds require Embedded Swift for ARM Cortex-M7
|
||||
(`-enable-experimental-feature Embedded`, triple `armv7em-none-none-eabi`).
|
||||
- **Device** builds require Embedded Swift for ARM Cortex-M7 (`-enable-experimental-feature Embedded`, triple `armv7em-none-none-eabi`).
|
||||
|
||||
The wrappers are written within the Embedded Swift subset for exactly this
|
||||
reason: no Foundation, no reflection, no untyped throws. That claim is
|
||||
enforced, not aspirational: `Scripts/build-embedded.sh` cross-compiles the
|
||||
whole module for `armv7em-none-none-eabi` with Embedded Swift enabled, and
|
||||
CI runs it on every push. Running it locally needs a swift.org development
|
||||
snapshot toolchain (Xcode's toolchain doesn't ship the bare-metal embedded
|
||||
stdlib) and the Arm GNU toolchain headers:
|
||||
The wrappers are written within the Embedded Swift subset for exactly this reason: no Foundation, no reflection, no untyped throws. That claim is enforced, not aspirational: `Scripts/build-embedded.sh` cross-compiles the whole module for `armv7em-none-none-eabi` with Embedded Swift enabled, and CI runs it on every push. Running it locally needs a swift.org development snapshot toolchain (Xcode's toolchain doesn't ship the bare-metal embedded stdlib) and the Arm GNU toolchain headers:
|
||||
|
||||
```sh
|
||||
SWIFT_BIN=~/Library/Developer/Toolchains/swift-DEVELOPMENT-SNAPSHOT-<date>.xctoolchain/usr/bin/swift \
|
||||
Scripts/build-embedded.sh
|
||||
```
|
||||
|
||||
See Apple's
|
||||
[swift-playdate-examples](https://github.com/apple/swift-playdate-examples)
|
||||
for a working Makefile/toolchain setup that this library slots into.
|
||||
See Apple's [swift-playdate-examples](https://github.com/apple/swift-playdate-examples) for a working Makefile/toolchain setup that this library slots into.
|
||||
|
||||
## Example
|
||||
|
||||
[`Examples/HelloPlaydate`](Examples/HelloPlaydate) is a complete minimal game — bouncing box, crank needle, button handling, a system menu item — that builds into a runnable simulator `.pdx`:
|
||||
|
||||
```sh
|
||||
cd Examples/HelloPlaydate
|
||||
./build.sh
|
||||
open -a "$HOME/Developer/PlaydateSDK/bin/Playdate Simulator.app" HelloPlaydate.pdx
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
The API reference is a DocC catalog. Generate it locally with:
|
||||
|
||||
```sh
|
||||
swift package generate-documentation --target PlayDate
|
||||
```
|
||||
|
||||
or browse it with Xcode's documentation viewer (Product ▸ Build Documentation). Pushes to `main` publish the rendered docs to GitHub Pages via `.github/workflows/docs.yml` (enable Pages ▸ Source: GitHub Actions in the repository settings once).
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
Examples/
|
||||
HelloPlaydate/ Minimal game buildable into a simulator .pdx
|
||||
Scripts/
|
||||
install-pkgconfig.sh One-time setup: points the "playdate" pkg-config
|
||||
module at your SDK installation
|
||||
@@ -362,12 +340,12 @@ Sources/
|
||||
importing pd_api.h from the SDK, plus inline shims for
|
||||
the variadic log/error functions
|
||||
PlayDate/ The Swift bindings, one file per subsystem
|
||||
(Sound and Graphics are split across several files)
|
||||
(Sound and Graphics are split across several files),
|
||||
plus the PlayDate.docc documentation catalog
|
||||
Tests/
|
||||
PlayDate/ Host-runnable tests for the pure value types
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT — see [LICENSE](LICENSE). The Playdate SDK itself is licensed separately
|
||||
by Panic, Inc. and is not distributed with this package.
|
||||
MIT — see [LICENSE](LICENSE). The Playdate SDK itself is licensed separately by Panic, Inc. and is not distributed with this package.
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
# Getting Started
|
||||
|
||||
Bootstrap the bindings from your game's entry point and drive a frame loop.
|
||||
|
||||
## Overview
|
||||
|
||||
A Playdate game has a single C entry point, `eventHandler`, which the
|
||||
firmware calls with a `PlaydateAPI*` and an event code. Export it with
|
||||
`@_cdecl`, call ``Playdate/initialize(with:)`` on the first event, and
|
||||
install an update callback:
|
||||
|
||||
```swift
|
||||
import CPlaydate
|
||||
import PlayDate
|
||||
|
||||
@_cdecl("eventHandler")
|
||||
func eventHandler(
|
||||
pointer: UnsafeMutableRawPointer,
|
||||
event: PDSystemEvent,
|
||||
argument: UInt32
|
||||
) -> Int32 {
|
||||
if case .initialize = SystemEvent(event: event, argument: argument) {
|
||||
Playdate.initialize(with: pointer) // must happen before anything else
|
||||
Game.shared.start()
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
final class Game {
|
||||
nonisolated(unsafe) static let shared = Game()
|
||||
|
||||
func start() {
|
||||
Display.setRefreshRate(50)
|
||||
|
||||
System.setUpdateCallback {
|
||||
self.update()
|
||||
return true // true = redraw the display this frame
|
||||
}
|
||||
}
|
||||
|
||||
func update() {
|
||||
let (_, pushed, _) = System.buttonState
|
||||
if pushed.contains(.a) {
|
||||
System.log("A pressed")
|
||||
}
|
||||
|
||||
Graphics.clear(color: .white)
|
||||
Graphics.drawText("Hello, Playdate", x: 8, y: 8)
|
||||
System.drawFPS()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Conventions to know
|
||||
|
||||
- **Initialization.** Calling any wrapper before
|
||||
``Playdate/initialize(with:)`` is a programmer error and will crash.
|
||||
- **Errors.** Fallible operations use typed throws — ``PlaydateError``
|
||||
generally, ``Network/NetError`` for network I/O.
|
||||
- **Ownership.** A wrapper that creates a C object frees it on `deinit`;
|
||||
keep the wrapper referenced for as long as you use it. Wrappers vending
|
||||
OS-owned objects don't free them — keep the owner alive instead, as
|
||||
documented on each API.
|
||||
- **Threading.** The Playdate runtime is single-threaded; don't call the
|
||||
API from other threads.
|
||||
@@ -0,0 +1,61 @@
|
||||
# ``PlayDate``
|
||||
|
||||
Swift bindings to the Playdate C API.
|
||||
|
||||
## Overview
|
||||
|
||||
The Playdate C API is delivered as a `PlaydateAPI*` struct of function
|
||||
pointers that the firmware hands to your game at launch. This module wraps
|
||||
that surface in idiomatic Swift: top-level namespaces per subsystem, wrapper
|
||||
types with ownership semantics, closures instead of function-pointer/userdata
|
||||
pairs, `OptionSet`s and `enum`s instead of raw constants, and typed `throws`
|
||||
for fallible calls.
|
||||
|
||||
Call ``Playdate/initialize(with:)`` from your game's `eventHandler` before
|
||||
using anything else — see <doc:GettingStarted>.
|
||||
|
||||
The bindings are written within the Embedded Swift subset, so the same code
|
||||
compiles for the Playdate Simulator and for the device
|
||||
(`armv7em-none-none-eabi`).
|
||||
|
||||
## Topics
|
||||
|
||||
### Essentials
|
||||
|
||||
- <doc:GettingStarted>
|
||||
- ``Playdate``
|
||||
- ``SystemEvent``
|
||||
- ``PlaydateError``
|
||||
|
||||
### System and display
|
||||
|
||||
- ``System``
|
||||
- ``Display``
|
||||
|
||||
### Drawing
|
||||
|
||||
- ``Graphics``
|
||||
- ``Rect``
|
||||
|
||||
### Sprites
|
||||
|
||||
- ``Sprite``
|
||||
|
||||
### Audio
|
||||
|
||||
- ``Sound``
|
||||
|
||||
### Storage
|
||||
|
||||
- ``File``
|
||||
- ``JSON``
|
||||
|
||||
### Connectivity
|
||||
|
||||
- ``Network``
|
||||
- ``Scoreboards``
|
||||
- ``AccessReply``
|
||||
|
||||
### Lua interop
|
||||
|
||||
- ``Lua``
|
||||
Reference in New Issue
Block a user