Promote subsystem namespaces to the top level of the module
System, Display, Graphics, Sprite, Sound, File, JSON, Lua, Scoreboards, Network, Rect, SystemEvent, and AccessReply now live at module scope instead of being nested in the Playdate enum; consumers write System.log(...) instead of Playdate.System.log(...), and can qualify with the module name (PlayDate.System) on collision. The Playdate enum remains only as the raw C API bootstrap (initialize(with:), api, apiPointer), and Playdate.Error is renamed PlaydateError so no top-level type shadows Swift.Error. Breaking change for any existing consumers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,16 +15,16 @@ All ten C subsystems are covered:
|
|||||||
|
|
||||||
| Namespace | Wraps | Highlights |
|
| Namespace | Wraps | Highlights |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Playdate.System` | `playdate->system` | input, time, menu items, logging |
|
| `System` | `playdate->system` | input, time, menu items, logging |
|
||||||
| `Playdate.Display` | `playdate->display` | refresh rate, scale, mosaic, flip |
|
| `Display` | `playdate->display` | refresh rate, scale, mosaic, flip |
|
||||||
| `Playdate.Graphics` | `playdate->graphics` | drawing, `Bitmap`, `Font`, `TileMap`, video |
|
| `Graphics` | `playdate->graphics` | drawing, `Bitmap`, `Font`, `TileMap`, video |
|
||||||
| `Playdate.Sprite` | `playdate->sprite` | display list, collisions, custom draw |
|
| `Sprite` | `playdate->sprite` | display list, collisions, custom draw |
|
||||||
| `Playdate.Sound` | `playdate->sound` | players, synths, sequences, effects |
|
| `Sound` | `playdate->sound` | players, synths, sequences, effects |
|
||||||
| `Playdate.File` | `playdate->file` | `Handle`, directory operations |
|
| `File` | `playdate->file` | `Handle`, directory operations |
|
||||||
| `Playdate.JSON` | `playdate->json` | `Value` tree decode/encode |
|
| `JSON` | `playdate->json` | `Value` tree decode/encode |
|
||||||
| `Playdate.Lua` | `playdate->lua` | C functions, classes, stack access |
|
| `Lua` | `playdate->lua` | C functions, classes, stack access |
|
||||||
| `Playdate.Scoreboards` | `playdate->scoreboards` | online leaderboards |
|
| `Scoreboards` | `playdate->scoreboards` | online leaderboards |
|
||||||
| `Playdate.Network` | `playdate->network` | wifi, `HTTPConnection`, `TCPConnection` |
|
| `Network` | `playdate->network` | wifi, `HTTPConnection`, `TCPConnection` |
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ import PlayDate
|
|||||||
func eventHandler(pointer: UnsafeMutableRawPointer,
|
func eventHandler(pointer: UnsafeMutableRawPointer,
|
||||||
event: PDSystemEvent,
|
event: PDSystemEvent,
|
||||||
argument: UInt32) -> Int32 {
|
argument: UInt32) -> Int32 {
|
||||||
switch Playdate.SystemEvent(event: event, argument: argument) {
|
switch SystemEvent(event: event, argument: argument) {
|
||||||
case .initialize:
|
case .initialize:
|
||||||
Playdate.initialize(with: pointer) // must happen before anything else
|
Playdate.initialize(with: pointer) // must happen before anything else
|
||||||
Game.shared.start()
|
Game.shared.start()
|
||||||
@@ -97,25 +97,25 @@ func eventHandler(pointer: UnsafeMutableRawPointer,
|
|||||||
final class Game {
|
final class Game {
|
||||||
nonisolated(unsafe) static let shared = Game()
|
nonisolated(unsafe) static let shared = Game()
|
||||||
|
|
||||||
var player = Playdate.Sprite()
|
var player = Sprite()
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
Playdate.Display.setRefreshRate(50)
|
Display.setRefreshRate(50)
|
||||||
|
|
||||||
Playdate.System.setUpdateCallback {
|
System.setUpdateCallback {
|
||||||
self.update()
|
self.update()
|
||||||
return true // true = redraw the display this frame
|
return true // true = redraw the display this frame
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func update() {
|
func update() {
|
||||||
let (_, pushed, _) = Playdate.System.buttonState
|
let (_, pushed, _) = System.buttonState
|
||||||
if pushed.contains(.a) {
|
if pushed.contains(.a) {
|
||||||
Playdate.System.log("A pressed at \(Playdate.System.currentTimeMilliseconds)ms")
|
System.log("A pressed at \(System.currentTimeMilliseconds)ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
Playdate.Sprite.updateAndDrawAll()
|
Sprite.updateAndDrawAll()
|
||||||
Playdate.System.drawFPS()
|
System.drawFPS()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -130,68 +130,68 @@ a programmer error and will crash.
|
|||||||
|
|
||||||
```swift
|
```swift
|
||||||
// Buttons are an OptionSet: current (held), pushed and released this frame.
|
// Buttons are an OptionSet: current (held), pushed and released this frame.
|
||||||
let (current, pushed, released) = Playdate.System.buttonState
|
let (current, pushed, released) = System.buttonState
|
||||||
if current.contains([.b, .down]) { /* charge shot */ }
|
if current.contains([.b, .down]) { /* charge shot */ }
|
||||||
|
|
||||||
// Crank.
|
// Crank.
|
||||||
if !Playdate.System.isCrankDocked {
|
if !System.isCrankDocked {
|
||||||
aim(degrees: Playdate.System.crankAngle)
|
aim(degrees: System.crankAngle)
|
||||||
spin(by: Playdate.System.crankChange)
|
spin(by: System.crankChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accelerometer is a peripheral you enable first.
|
// Accelerometer is a peripheral you enable first.
|
||||||
Playdate.System.setPeripheralsEnabled(.accelerometer)
|
System.setPeripheralsEnabled(.accelerometer)
|
||||||
let (x, y, z) = Playdate.System.accelerometer
|
let (x, y, z) = System.accelerometer
|
||||||
|
|
||||||
// System menu items take closures; the binding keeps them alive until removed.
|
// System menu items take closures; the binding keeps them alive until removed.
|
||||||
Playdate.System.addCheckmarkMenuItem(title: "music", isChecked: true) { item in
|
System.addCheckmarkMenuItem(title: "music", isChecked: true) { item in
|
||||||
Audio.musicEnabled = item.isChecked
|
Audio.musicEnabled = item.isChecked
|
||||||
}
|
}
|
||||||
Playdate.System.addOptionsMenuItem(title: "mode", options: ["easy", "hard"]) { item in
|
System.addOptionsMenuItem(title: "mode", options: ["easy", "hard"]) { item in
|
||||||
Game.shared.difficulty = item.value
|
Game.shared.difficulty = item.value
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging goes to the simulator console or device serial.
|
// Logging goes to the simulator console or device serial.
|
||||||
Playdate.System.log("spawned \(count) enemies")
|
System.log("spawned \(count) enemies")
|
||||||
Playdate.System.error("unrecoverable") // stops execution
|
System.error("unrecoverable") // stops execution
|
||||||
```
|
```
|
||||||
|
|
||||||
### Graphics: drawing, bitmaps, fonts
|
### Graphics: drawing, bitmaps, fonts
|
||||||
|
|
||||||
Fallible loads (`Bitmap(path:)`, `Font(path:)`, …) throw `Playdate.Error`,
|
Fallible loads (`Bitmap(path:)`, `Font(path:)`, …) throw `PlaydateError`,
|
||||||
which carries the message produced by the OS:
|
which carries the message produced by the OS:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let font = try Playdate.Graphics.Font(path: "fonts/Asheville-Sans-14-Bold.pft")
|
let font = try Graphics.Font(path: "fonts/Asheville-Sans-14-Bold.pft")
|
||||||
Playdate.Graphics.setFont(font)
|
Graphics.setFont(font)
|
||||||
|
|
||||||
Playdate.Graphics.clear(color: .white)
|
Graphics.clear(color: .white)
|
||||||
Playdate.Graphics.fillRect(x: 0, y: 0, width: 400, height: 32, color: .black)
|
Graphics.fillRect(x: 0, y: 0, width: 400, height: 32, color: .black)
|
||||||
Playdate.Graphics.drawText("Hëllo, Playdate", x: 8, y: 8)
|
Graphics.drawText("Hëllo, Playdate", x: 8, y: 8)
|
||||||
|
|
||||||
// Colors are solid or 8×8 patterns.
|
// Colors are solid or 8×8 patterns.
|
||||||
let checker = Playdate.Graphics.Pattern(rows: (0xAA, 0x55, 0xAA, 0x55,
|
let checker = Graphics.Pattern(rows: (0xAA, 0x55, 0xAA, 0x55,
|
||||||
0xAA, 0x55, 0xAA, 0x55))
|
0xAA, 0x55, 0xAA, 0x55))
|
||||||
Playdate.Graphics.fillEllipse(x: 100, y: 100, width: 64, height: 64,
|
Graphics.fillEllipse(x: 100, y: 100, width: 64, height: 64,
|
||||||
color: .pattern(checker))
|
color: .pattern(checker))
|
||||||
|
|
||||||
// Bitmaps draw themselves; draw into one by pushing it as the context.
|
// Bitmaps draw themselves; draw into one by pushing it as the context.
|
||||||
let logo = try Playdate.Graphics.Bitmap(path: "images/logo")
|
let logo = try Graphics.Bitmap(path: "images/logo")
|
||||||
logo.draw(x: 168, y: 88)
|
logo.draw(x: 168, y: 88)
|
||||||
|
|
||||||
let canvas = Playdate.Graphics.Bitmap(width: 64, height: 64)
|
let canvas = Graphics.Bitmap(width: 64, height: 64)
|
||||||
Playdate.Graphics.pushContext(canvas)
|
Graphics.pushContext(canvas)
|
||||||
Playdate.Graphics.drawLine(x1: 0, y1: 0, x2: 63, y2: 63, width: 2, color: .black)
|
Graphics.drawLine(x1: 0, y1: 0, x2: 63, y2: 63, width: 2, color: .black)
|
||||||
Playdate.Graphics.popContext()
|
Graphics.popContext()
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sprites and collisions
|
### Sprites and collisions
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let ball = Playdate.Sprite()
|
let ball = Sprite()
|
||||||
ball.setImage(try Playdate.Graphics.Bitmap(path: "images/ball"))
|
ball.setImage(try Graphics.Bitmap(path: "images/ball"))
|
||||||
ball.moveTo(x: 200, y: 120)
|
ball.moveTo(x: 200, y: 120)
|
||||||
ball.collideRect = Playdate.Rect(x: 0, y: 0, width: 16, height: 16)
|
ball.collideRect = Rect(x: 0, y: 0, width: 16, height: 16)
|
||||||
ball.setCollisionResponseFunction { _, _ in .bounce }
|
ball.setCollisionResponseFunction { _, _ in .bounce }
|
||||||
ball.add() // adds to the display list; the binding keeps it alive while added
|
ball.add() // adds to the display list; the binding keeps it alive while added
|
||||||
|
|
||||||
@@ -211,29 +211,29 @@ sprite userdata slot is reserved by the binding for that recovery — use the
|
|||||||
|
|
||||||
```swift
|
```swift
|
||||||
// Stream music from disk.
|
// Stream music from disk.
|
||||||
let music = try Playdate.Sound.FilePlayer(path: "audio/theme")
|
let music = try Sound.FilePlayer(path: "audio/theme")
|
||||||
music.play(repeat: 0) // 0 = loop forever
|
music.play(repeat: 0) // 0 = loop forever
|
||||||
|
|
||||||
// Play short effects from memory.
|
// Play short effects from memory.
|
||||||
let blip = try Playdate.Sound.SamplePlayer(path: "audio/blip")
|
let blip = try Sound.SamplePlayer(path: "audio/blip")
|
||||||
blip.play()
|
blip.play()
|
||||||
|
|
||||||
// Synthesis.
|
// Synthesis.
|
||||||
let synth = Playdate.Sound.Synth(waveform: .square)
|
let synth = Sound.Synth(waveform: .square)
|
||||||
synth.setAttackTime(0.01)
|
synth.setAttackTime(0.01)
|
||||||
synth.setReleaseTime(0.2)
|
synth.setReleaseTime(0.2)
|
||||||
synth.playMIDINote(Playdate.Sound.noteC4, velocity: 0.8, length: 0.5)
|
synth.playMIDINote(Sound.noteC4, velocity: 0.8, length: 0.5)
|
||||||
|
|
||||||
// Channels mix sources and effects.
|
// Channels mix sources and effects.
|
||||||
let channel = Playdate.Sound.Channel()
|
let channel = Sound.Channel()
|
||||||
channel.add()
|
channel.add()
|
||||||
channel.addSource(synth)
|
channel.addSource(synth)
|
||||||
let filter = Playdate.Sound.TwoPoleFilter(kind: .lowPass)
|
let filter = Sound.TwoPoleFilter(kind: .lowPass)
|
||||||
filter.setFrequency(800)
|
filter.setFrequency(800)
|
||||||
channel.addEffect(filter)
|
channel.addEffect(filter)
|
||||||
|
|
||||||
// Anything that takes a modulator accepts any SignalValue (LFO, Envelope, …).
|
// Anything that takes a modulator accepts any SignalValue (LFO, Envelope, …).
|
||||||
let wobble = Playdate.Sound.LFO(shape: .sine)
|
let wobble = Sound.LFO(shape: .sine)
|
||||||
wobble.setRate(2)
|
wobble.setRate(2)
|
||||||
synth.frequencyModulator = wobble
|
synth.frequencyModulator = wobble
|
||||||
```
|
```
|
||||||
@@ -242,20 +242,20 @@ synth.frequencyModulator = wobble
|
|||||||
|
|
||||||
```swift
|
```swift
|
||||||
// Paths resolve against the game's Data directory and pdx per the open mode.
|
// Paths resolve against the game's Data directory and pdx per the open mode.
|
||||||
let save = try Playdate.File.Handle(path: "save.json", mode: .write)
|
let save = try File.Handle(path: "save.json", mode: .write)
|
||||||
try save.write(Playdate.JSON.encode(.table([
|
try save.write(JSON.encode(.table([
|
||||||
"level": .int(3),
|
"level": .int(3),
|
||||||
"name": .string("Röck"),
|
"name": .string("Röck"),
|
||||||
])))
|
])))
|
||||||
try save.close()
|
try save.close()
|
||||||
|
|
||||||
let loaded = try Playdate.JSON.decodeFile(at: "save.json")
|
let loaded = try JSON.decodeFile(at: "save.json")
|
||||||
if case .table(let entries) = loaded, case .int(let level)? = entries["level"] {
|
if case .table(let entries) = loaded, case .int(let level)? = entries["level"] {
|
||||||
Game.shared.level = level
|
Game.shared.level = level
|
||||||
}
|
}
|
||||||
|
|
||||||
try Playdate.File.listFiles(at: "replays") { name in
|
try File.listFiles(at: "replays") { name in
|
||||||
Playdate.System.log("found \(name)")
|
System.log("found \(name)")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -264,14 +264,14 @@ try Playdate.File.listFiles(at: "replays") { name in
|
|||||||
Network access requires user permission per server:
|
Network access requires user permission per server:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let reply = Playdate.Network.HTTPConnection.requestAccess(
|
let reply = Network.HTTPConnection.requestAccess(
|
||||||
server: "example.com", purpose: "Fetching daily puzzles") { allowed in
|
server: "example.com", purpose: "Fetching daily puzzles") { allowed in
|
||||||
guard allowed else { return }
|
guard allowed else { return }
|
||||||
Puzzles.fetch()
|
Puzzles.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetch() {
|
func fetch() {
|
||||||
guard let connection = Playdate.Network.HTTPConnection(server: "example.com") else { return }
|
guard let connection = Network.HTTPConnection(server: "example.com") else { return }
|
||||||
connection.setRequestCompleteCallback { connection in
|
connection.setRequestCompleteCallback { connection in
|
||||||
let body = try? connection.read(length: connection.bytesAvailable)
|
let body = try? connection.read(length: connection.bytesAvailable)
|
||||||
// … keep `connection` referenced somewhere until this fires …
|
// … keep `connection` referenced somewhere until this fires …
|
||||||
@@ -286,19 +286,22 @@ Lua callbacks are C function pointers with no context, so they must be
|
|||||||
`@convention(c)` functions rather than capturing closures:
|
`@convention(c)` functions rather than capturing closures:
|
||||||
|
|
||||||
```swift
|
```swift
|
||||||
let double: Playdate.Lua.CFunction = { _ in
|
let double: Lua.CFunction = { _ in
|
||||||
Playdate.Lua.push(Playdate.Lua.intArgument(at: 1) * 2)
|
Lua.push(Lua.intArgument(at: 1) * 2)
|
||||||
return 1 // number of return values pushed
|
return 1 // number of return values pushed
|
||||||
}
|
}
|
||||||
try Playdate.Lua.addFunction(double, name: "mylib.double")
|
try Lua.addFunction(double, name: "mylib.double")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Conventions
|
## Conventions
|
||||||
|
|
||||||
- **Namespaces.** Everything lives under `Playdate`. Games that find that
|
- **Namespaces.** The subsystem namespaces (`System`, `Graphics`, `Sound`,
|
||||||
verbose can alias: `typealias Graphics = Playdate.Graphics`.
|
…) live at the top level of the module; only the raw C API bootstrap
|
||||||
- **Errors.** Fallible operations use typed throws — `throws(Playdate.Error)`
|
stays under `Playdate` (`Playdate.initialize(with:)`, `Playdate.api`).
|
||||||
generally, `throws(Playdate.Network.NetError)` for network I/O — so `catch`
|
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.
|
gives you a concrete type, and no `any Error` existentials are needed.
|
||||||
- **Ownership.** A wrapper that *creates* a C object frees it on `deinit`;
|
- **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
|
keep the wrapper referenced for as long as you use it. Wrappers vending
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ import CPlaydate
|
|||||||
import PlayDate
|
import PlayDate
|
||||||
|
|
||||||
// Touch a type from each module to prove both import and link.
|
// Touch a type from each module to prove both import and link.
|
||||||
let event = Playdate.SystemEvent(event: kEventInit, argument: 0)
|
let event = SystemEvent(event: kEventInit, argument: 0)
|
||||||
let buttons: Playdate.System.Buttons = [.a, .up]
|
let buttons: System.Buttons = [.a, .up]
|
||||||
print(event != nil && buttons.contains(.a) ? "ok" : "broken")
|
print(event != nil && buttons.contains(.a) ? "ok" : "broken")
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,10 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate {
|
/// The display API: resolution, refresh rate, scaling, and effects.
|
||||||
/// The display API: resolution, refresh rate, scaling, and effects.
|
public enum Display {}
|
||||||
public enum Display {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.Display {
|
extension Display {
|
||||||
private static var api: playdate_display { Playdate.api.display.pointee }
|
private static var api: playdate_display { Playdate.api.display.pointee }
|
||||||
|
|
||||||
/// The display width in pixels, taking the current scale into account.
|
/// The display width in pixels, taking the current scale into account.
|
||||||
|
|||||||
+22
-24
@@ -11,16 +11,14 @@ internal import CPlaydate
|
|||||||
private var fileAPI: playdate_file { Playdate.api.file.pointee }
|
private var fileAPI: playdate_file { Playdate.api.file.pointee }
|
||||||
|
|
||||||
/// The most recent file system error as a thrown error.
|
/// The most recent file system error as a thrown error.
|
||||||
private func lastFileError() -> Playdate.Error {
|
private func lastFileError() -> PlaydateError {
|
||||||
Playdate.Error(cString: fileAPI.geterr.unsafelyUnwrapped())
|
PlaydateError(cString: fileAPI.geterr.unsafelyUnwrapped())
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Playdate {
|
/// The file API: access to the game's Data directory and pdx contents.
|
||||||
/// The file API: access to the game's Data directory and pdx contents.
|
public enum File {}
|
||||||
public enum File {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.File {
|
extension File {
|
||||||
// MARK: - Types
|
// MARK: - Types
|
||||||
|
|
||||||
/// How to open a file.
|
/// How to open a file.
|
||||||
@@ -44,7 +42,7 @@ extension Playdate.File {
|
|||||||
public struct Stat: Sendable {
|
public struct Stat: Sendable {
|
||||||
public let isDirectory: Bool
|
public let isDirectory: Bool
|
||||||
public let size: UInt32
|
public let size: UInt32
|
||||||
public let modified: Playdate.System.DateTime
|
public let modified: System.DateTime
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The origin used by `Handle.seek(to:from:)`.
|
/// The origin used by `Handle.seek(to:from:)`.
|
||||||
@@ -59,7 +57,7 @@ extension Playdate.File {
|
|||||||
/// Calls `each` with the name of every file in `path`. Subdirectory names
|
/// Calls `each` with the name of every file in `path`. Subdirectory names
|
||||||
/// end in a slash. Throws if the directory does not exist.
|
/// end in a slash. Throws if the directory does not exist.
|
||||||
public static func listFiles(at path: String, showHidden: Bool = false,
|
public static func listFiles(at path: String, showHidden: Bool = false,
|
||||||
_ each: (String) -> Void) throws(Playdate.Error) {
|
_ each: (String) -> Void) throws(PlaydateError) {
|
||||||
let result = withoutActuallyEscaping(each) { each in
|
let result = withoutActuallyEscaping(each) { each in
|
||||||
var callback = each
|
var callback = each
|
||||||
return path.withPlaydateCString { cPath in
|
return path.withPlaydateCString { cPath in
|
||||||
@@ -76,27 +74,27 @@ extension Playdate.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Information about the file or directory at `path`.
|
/// Information about the file or directory at `path`.
|
||||||
public static func stat(_ path: String) throws(Playdate.Error) -> Stat {
|
public static func stat(_ path: String) throws(PlaydateError) -> Stat {
|
||||||
var stat = FileStat()
|
var stat = FileStat()
|
||||||
let result = path.withPlaydateCString { fileAPI.stat.unsafelyUnwrapped($0, &stat) }
|
let result = path.withPlaydateCString { fileAPI.stat.unsafelyUnwrapped($0, &stat) }
|
||||||
if result != 0 { throw lastFileError() }
|
if result != 0 { throw lastFileError() }
|
||||||
return Stat(
|
return Stat(
|
||||||
isDirectory: stat.isdir != 0,
|
isDirectory: stat.isdir != 0,
|
||||||
size: stat.size,
|
size: stat.size,
|
||||||
modified: Playdate.System.DateTime(
|
modified: System.DateTime(
|
||||||
year: UInt16(stat.m_year), month: UInt8(stat.m_month), day: UInt8(stat.m_day),
|
year: UInt16(stat.m_year), month: UInt8(stat.m_month), day: UInt8(stat.m_day),
|
||||||
hour: UInt8(stat.m_hour), minute: UInt8(stat.m_minute), second: UInt8(stat.m_second)))
|
hour: UInt8(stat.m_hour), minute: UInt8(stat.m_minute), second: UInt8(stat.m_second)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a directory (and intermediate directories) in the Data directory.
|
/// Creates a directory (and intermediate directories) in the Data directory.
|
||||||
public static func mkdir(_ path: String) throws(Playdate.Error) {
|
public static func mkdir(_ path: String) throws(PlaydateError) {
|
||||||
let result = path.withPlaydateCString { fileAPI.mkdir.unsafelyUnwrapped($0) }
|
let result = path.withPlaydateCString { fileAPI.mkdir.unsafelyUnwrapped($0) }
|
||||||
if result != 0 { throw lastFileError() }
|
if result != 0 { throw lastFileError() }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Deletes the file or directory at `path`. Directories require
|
/// Deletes the file or directory at `path`. Directories require
|
||||||
/// `recursive` to be deleted with their contents.
|
/// `recursive` to be deleted with their contents.
|
||||||
public static func unlink(_ path: String, recursive: Bool = false) throws(Playdate.Error) {
|
public static func unlink(_ path: String, recursive: Bool = false) throws(PlaydateError) {
|
||||||
let result = path.withPlaydateCString {
|
let result = path.withPlaydateCString {
|
||||||
fileAPI.unlink.unsafelyUnwrapped($0, recursive ? 1 : 0)
|
fileAPI.unlink.unsafelyUnwrapped($0, recursive ? 1 : 0)
|
||||||
}
|
}
|
||||||
@@ -105,7 +103,7 @@ extension Playdate.File {
|
|||||||
|
|
||||||
/// Renames (moves) a file in the Data directory, overwriting any existing
|
/// Renames (moves) a file in the Data directory, overwriting any existing
|
||||||
/// file at the destination.
|
/// file at the destination.
|
||||||
public static func rename(from: String, to: String) throws(Playdate.Error) {
|
public static func rename(from: String, to: String) throws(PlaydateError) {
|
||||||
let result = from.withPlaydateCString { cFrom in
|
let result = from.withPlaydateCString { cFrom in
|
||||||
to.withPlaydateCString { cTo in
|
to.withPlaydateCString { cTo in
|
||||||
fileAPI.rename.unsafelyUnwrapped(cFrom, cTo)
|
fileAPI.rename.unsafelyUnwrapped(cFrom, cTo)
|
||||||
@@ -123,7 +121,7 @@ extension Playdate.File {
|
|||||||
private var isClosed = false
|
private var isClosed = false
|
||||||
|
|
||||||
/// Opens the file at `path`.
|
/// Opens the file at `path`.
|
||||||
public init(path: String, mode: Options) throws(Playdate.Error) {
|
public init(path: String, mode: Options) throws(PlaydateError) {
|
||||||
let pointer = path.withPlaydateCString {
|
let pointer = path.withPlaydateCString {
|
||||||
fileAPI.open.unsafelyUnwrapped($0, mode.cValue)
|
fileAPI.open.unsafelyUnwrapped($0, mode.cValue)
|
||||||
}
|
}
|
||||||
@@ -138,7 +136,7 @@ extension Playdate.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the file. Further operations are invalid.
|
/// Closes the file. Further operations are invalid.
|
||||||
public func close() throws(Playdate.Error) {
|
public func close() throws(PlaydateError) {
|
||||||
guard !isClosed else { return }
|
guard !isClosed else { return }
|
||||||
isClosed = true
|
isClosed = true
|
||||||
if fileAPI.close.unsafelyUnwrapped(pointer) != 0 { throw lastFileError() }
|
if fileAPI.close.unsafelyUnwrapped(pointer) != 0 { throw lastFileError() }
|
||||||
@@ -146,7 +144,7 @@ extension Playdate.File {
|
|||||||
|
|
||||||
/// Reads up to `buffer.count` bytes into `buffer`. Returns the number
|
/// Reads up to `buffer.count` bytes into `buffer`. Returns the number
|
||||||
/// of bytes read; 0 indicates end of file.
|
/// of bytes read; 0 indicates end of file.
|
||||||
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(Playdate.Error) -> Int {
|
public func read(into buffer: UnsafeMutableRawBufferPointer) throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.read.unsafelyUnwrapped(
|
let result = fileAPI.read.unsafelyUnwrapped(
|
||||||
pointer, buffer.baseAddress, UInt32(buffer.count))
|
pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
@@ -154,7 +152,7 @@ extension Playdate.File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reads up to `length` bytes and returns them.
|
/// Reads up to `length` bytes and returns them.
|
||||||
public func read(length: Int) throws(Playdate.Error) -> [UInt8] {
|
public func read(length: Int) throws(PlaydateError) -> [UInt8] {
|
||||||
var bytes = [UInt8](repeating: 0, count: length)
|
var bytes = [UInt8](repeating: 0, count: length)
|
||||||
let result = bytes.withUnsafeMutableBytes { buffer in
|
let result = bytes.withUnsafeMutableBytes { buffer in
|
||||||
fileAPI.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
fileAPI.read.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
@@ -166,7 +164,7 @@ extension Playdate.File {
|
|||||||
|
|
||||||
/// Writes the buffer to the file. Returns the number of bytes written.
|
/// Writes the buffer to the file. Returns the number of bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ buffer: UnsafeRawBufferPointer) throws(Playdate.Error) -> Int {
|
public func write(_ buffer: UnsafeRawBufferPointer) throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.write.unsafelyUnwrapped(
|
let result = fileAPI.write.unsafelyUnwrapped(
|
||||||
pointer, buffer.baseAddress, UInt32(buffer.count))
|
pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
@@ -175,7 +173,7 @@ extension Playdate.File {
|
|||||||
|
|
||||||
/// Writes the bytes to the file. Returns the number of bytes written.
|
/// Writes the bytes to the file. Returns the number of bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ bytes: [UInt8]) throws(Playdate.Error) -> Int {
|
public func write(_ bytes: [UInt8]) throws(PlaydateError) -> Int {
|
||||||
let result = bytes.withUnsafeBytes { buffer in
|
let result = bytes.withUnsafeBytes { buffer in
|
||||||
fileAPI.write.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
fileAPI.write.unsafelyUnwrapped(pointer, buffer.baseAddress, UInt32(buffer.count))
|
||||||
}
|
}
|
||||||
@@ -185,27 +183,27 @@ extension Playdate.File {
|
|||||||
|
|
||||||
/// Writes the string's UTF-8 to the file. Returns the bytes written.
|
/// Writes the string's UTF-8 to the file. Returns the bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func write(_ string: String) throws(Playdate.Error) -> Int {
|
public func write(_ string: String) throws(PlaydateError) -> Int {
|
||||||
try write(Array(string.utf8))
|
try write(Array(string.utf8))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Flushes buffered writes to disk. Returns the bytes written.
|
/// Flushes buffered writes to disk. Returns the bytes written.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public func flush() throws(Playdate.Error) -> Int {
|
public func flush() throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.flush.unsafelyUnwrapped(pointer)
|
let result = fileAPI.flush.unsafelyUnwrapped(pointer)
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
return Int(result)
|
return Int(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The current read/write offset.
|
/// The current read/write offset.
|
||||||
public func tell() throws(Playdate.Error) -> Int {
|
public func tell() throws(PlaydateError) -> Int {
|
||||||
let result = fileAPI.tell.unsafelyUnwrapped(pointer)
|
let result = fileAPI.tell.unsafelyUnwrapped(pointer)
|
||||||
if result < 0 { throw lastFileError() }
|
if result < 0 { throw lastFileError() }
|
||||||
return Int(result)
|
return Int(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Moves the read/write offset to `offset` relative to `origin`.
|
/// Moves the read/write offset to `offset` relative to `origin`.
|
||||||
public func seek(to offset: Int, from origin: SeekOrigin = .start) throws(Playdate.Error) {
|
public func seek(to offset: Int, from origin: SeekOrigin = .start) throws(PlaydateError) {
|
||||||
if fileAPI.seek.unsafelyUnwrapped(pointer, Int32(offset), origin.rawValue) != 0 {
|
if fileAPI.seek.unsafelyUnwrapped(pointer, Int32(offset), origin.rawValue) != 0 {
|
||||||
throw lastFileError()
|
throw lastFileError()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,12 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate {
|
/// The graphics API: drawing, bitmaps, fonts, tilemaps, and video.
|
||||||
/// The graphics API: drawing, bitmaps, fonts, tilemaps, and video.
|
public enum Graphics {}
|
||||||
public enum Graphics {}
|
|
||||||
}
|
|
||||||
|
|
||||||
var gfx: playdate_graphics { Playdate.api.graphics.pointee }
|
var gfx: playdate_graphics { Playdate.api.graphics.pointee }
|
||||||
|
|
||||||
extension Playdate.Graphics {
|
extension Graphics {
|
||||||
// MARK: - Screen constants
|
// MARK: - Screen constants
|
||||||
|
|
||||||
/// The width of the screen in pixels (`LCD_COLUMNS`).
|
/// The width of the screen in pixels (`LCD_COLUMNS`).
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate.Graphics {
|
extension Graphics {
|
||||||
/// An image that can be drawn to the screen or used as a drawing target.
|
/// An image that can be drawn to the screen or used as a drawing target.
|
||||||
/// Wraps `LCDBitmap`.
|
/// Wraps `LCDBitmap`.
|
||||||
public final class Bitmap {
|
public final class Bitmap {
|
||||||
@@ -29,10 +29,10 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a bitmap from a file in the game's pdx or Data directory.
|
/// Loads a bitmap from a file in the game's pdx or Data directory.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
let pointer = path.withPlaydateCString { gfx.loadBitmap.unsafelyUnwrapped($0, &error) }
|
let pointer = path.withPlaydateCString { gfx.loadBitmap.unsafelyUnwrapped($0, &error) }
|
||||||
guard let pointer else { throw Playdate.Error(cString: error) }
|
guard let pointer else { throw PlaydateError(cString: error) }
|
||||||
self.init(pointer: pointer, isOwned: true)
|
self.init(pointer: pointer, isOwned: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,10 +74,10 @@ extension Playdate.Graphics {
|
|||||||
// MARK: Operations
|
// MARK: Operations
|
||||||
|
|
||||||
/// Replaces the bitmap's contents with the image at `path`.
|
/// Replaces the bitmap's contents with the image at `path`.
|
||||||
public func load(path: String) throws(Playdate.Error) {
|
public func load(path: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
path.withPlaydateCString { gfx.loadIntoBitmap.unsafelyUnwrapped($0, pointer, &error) }
|
path.withPlaydateCString { gfx.loadIntoBitmap.unsafelyUnwrapped($0, pointer, &error) }
|
||||||
if let error { throw Playdate.Error(cString: error) }
|
if let error { throw PlaydateError(cString: error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fills the bitmap with `color`.
|
/// Fills the bitmap with `color`.
|
||||||
@@ -166,10 +166,10 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads an image table from a file.
|
/// Loads an image table from a file.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
let pointer = path.withPlaydateCString { gfx.loadBitmapTable.unsafelyUnwrapped($0, &error) }
|
let pointer = path.withPlaydateCString { gfx.loadBitmapTable.unsafelyUnwrapped($0, &error) }
|
||||||
guard let pointer else { throw Playdate.Error(cString: error) }
|
guard let pointer else { throw PlaydateError(cString: error) }
|
||||||
self.init(pointer: pointer)
|
self.init(pointer: pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,10 +178,10 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Replaces the table's contents with the image table at `path`.
|
/// Replaces the table's contents with the image table at `path`.
|
||||||
public func load(path: String) throws(Playdate.Error) {
|
public func load(path: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
path.withPlaydateCString { gfx.loadIntoBitmapTable.unsafelyUnwrapped($0, pointer, &error) }
|
path.withPlaydateCString { gfx.loadIntoBitmapTable.unsafelyUnwrapped($0, pointer, &error) }
|
||||||
if let error { throw Playdate.Error(cString: error) }
|
if let error { throw PlaydateError(cString: error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The bitmap at `index`, or `nil` if out of range. The bitmap
|
/// The bitmap at `index`, or `nil` if out of range. The bitmap
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate.Graphics {
|
extension Graphics {
|
||||||
/// A font loaded from a .pft file. Wraps `LCDFont`.
|
/// A font loaded from a .pft file. Wraps `LCDFont`.
|
||||||
public final class Font {
|
public final class Font {
|
||||||
let pointer: OpaquePointer
|
let pointer: OpaquePointer
|
||||||
@@ -19,10 +19,10 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads a font from a file.
|
/// Loads a font from a file.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
let pointer = path.withPlaydateCString { gfx.loadFont.unsafelyUnwrapped($0, &error) }
|
let pointer = path.withPlaydateCString { gfx.loadFont.unsafelyUnwrapped($0, &error) }
|
||||||
guard let pointer else { throw Playdate.Error(cString: error) }
|
guard let pointer else { throw PlaydateError(cString: error) }
|
||||||
self.init(pointer: pointer)
|
self.init(pointer: pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ extension Playdate.Graphics {
|
|||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
// Per the C API docs, fonts are freed with the system allocator.
|
// Per the C API docs, fonts are freed with the system allocator.
|
||||||
Playdate.System.systemFree(UnsafeMutableRawPointer(pointer))
|
System.systemFree(UnsafeMutableRawPointer(pointer))
|
||||||
retainedData?.deallocate()
|
retainedData?.deallocate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ internal import CPlaydate
|
|||||||
|
|
||||||
private var tilemapAPI: playdate_tilemap { gfx.tilemap.pointee }
|
private var tilemapAPI: playdate_tilemap { gfx.tilemap.pointee }
|
||||||
|
|
||||||
extension Playdate.Graphics {
|
extension Graphics {
|
||||||
/// A grid of tiles drawn from a bitmap table. Wraps `LCDTileMap`.
|
/// A grid of tiles drawn from a bitmap table. Wraps `LCDTileMap`.
|
||||||
public final class TileMap {
|
public final class TileMap {
|
||||||
let pointer: OpaquePointer
|
let pointer: OpaquePointer
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ internal import CPlaydate
|
|||||||
private var videoAPI: playdate_video { gfx.video.pointee }
|
private var videoAPI: playdate_video { gfx.video.pointee }
|
||||||
private var streamAPI: playdate_videostream { gfx.videostream.pointee }
|
private var streamAPI: playdate_videostream { gfx.videostream.pointee }
|
||||||
|
|
||||||
extension Playdate.Graphics {
|
extension Graphics {
|
||||||
/// Plays .pdv video files. Wraps `LCDVideoPlayer`.
|
/// Plays .pdv video files. Wraps `LCDVideoPlayer`.
|
||||||
public final class VideoPlayer {
|
public final class VideoPlayer {
|
||||||
let pointer: OpaquePointer
|
let pointer: OpaquePointer
|
||||||
@@ -23,10 +23,10 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Opens the .pdv file at `path`.
|
/// Opens the .pdv file at `path`.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
let pointer = path.withPlaydateCString { videoAPI.loadVideo.unsafelyUnwrapped($0) }
|
let pointer = path.withPlaydateCString { videoAPI.loadVideo.unsafelyUnwrapped($0) }
|
||||||
guard let pointer else {
|
guard let pointer else {
|
||||||
throw Playdate.Error(message: "unable to load video: \(path)")
|
throw PlaydateError(message: "unable to load video: \(path)")
|
||||||
}
|
}
|
||||||
self.init(pointer: pointer, isOwned: true)
|
self.init(pointer: pointer, isOwned: true)
|
||||||
}
|
}
|
||||||
@@ -38,9 +38,9 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Sets the bitmap the video renders into.
|
/// Sets the bitmap the video renders into.
|
||||||
public func setContext(_ context: Bitmap) throws(Playdate.Error) {
|
public func setContext(_ context: Bitmap) throws(PlaydateError) {
|
||||||
guard videoAPI.setContext.unsafelyUnwrapped(pointer, context.pointer) != 0 else {
|
guard videoAPI.setContext.unsafelyUnwrapped(pointer, context.pointer) != 0 else {
|
||||||
throw Playdate.Error(message: error ?? "unable to set video context")
|
throw PlaydateError(message: error ?? "unable to set video context")
|
||||||
}
|
}
|
||||||
retainedContext = context
|
retainedContext = context
|
||||||
}
|
}
|
||||||
@@ -58,9 +58,9 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Renders frame `frame` into the current context.
|
/// Renders frame `frame` into the current context.
|
||||||
public func renderFrame(_ frame: Int) throws(Playdate.Error) {
|
public func renderFrame(_ frame: Int) throws(PlaydateError) {
|
||||||
guard videoAPI.renderFrame.unsafelyUnwrapped(pointer, Int32(frame)) != 0 else {
|
guard videoAPI.renderFrame.unsafelyUnwrapped(pointer, Int32(frame)) != 0 else {
|
||||||
throw Playdate.Error(message: error ?? "unable to render frame \(frame)")
|
throw PlaydateError(message: error ?? "unable to render frame \(frame)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,27 +100,27 @@ extension Playdate.Graphics {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Streams from an open file.
|
/// Streams from an open file.
|
||||||
public func setFile(_ file: Playdate.File.Handle) {
|
public func setFile(_ file: File.Handle) {
|
||||||
retainedSource = file
|
retainedSource = file
|
||||||
streamAPI.setFile.unsafelyUnwrapped(pointer, file.pointer)
|
streamAPI.setFile.unsafelyUnwrapped(pointer, file.pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Streams from an HTTP connection.
|
/// Streams from an HTTP connection.
|
||||||
public func setHTTPConnection(_ connection: Playdate.Network.HTTPConnection) {
|
public func setHTTPConnection(_ connection: Network.HTTPConnection) {
|
||||||
retainedSource = connection
|
retainedSource = connection
|
||||||
streamAPI.setHTTPConnection.unsafelyUnwrapped(pointer, connection.pointer)
|
streamAPI.setHTTPConnection.unsafelyUnwrapped(pointer, connection.pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Streams from a TCP connection.
|
/// Streams from a TCP connection.
|
||||||
public func setTCPConnection(_ connection: Playdate.Network.TCPConnection) {
|
public func setTCPConnection(_ connection: Network.TCPConnection) {
|
||||||
retainedSource = connection
|
retainedSource = connection
|
||||||
streamAPI.setTCPConnection.unsafelyUnwrapped(pointer, connection.pointer)
|
streamAPI.setTCPConnection.unsafelyUnwrapped(pointer, connection.pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The player used for the stream's audio track. Owned by the stream.
|
/// The player used for the stream's audio track. Owned by the stream.
|
||||||
public var filePlayer: Playdate.Sound.FilePlayer? {
|
public var filePlayer: Sound.FilePlayer? {
|
||||||
guard let player = streamAPI.getFilePlayer.unsafelyUnwrapped(pointer) else { return nil }
|
guard let player = streamAPI.getFilePlayer.unsafelyUnwrapped(pointer) else { return nil }
|
||||||
return Playdate.Sound.FilePlayer(pointer: player, isOwned: false)
|
return Sound.FilePlayer(pointer: player, isOwned: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The player used for the stream's video track. Owned by the stream.
|
/// The player used for the stream's video track. Owned by the stream.
|
||||||
|
|||||||
+12
-14
@@ -11,12 +11,10 @@ internal import CPlaydate
|
|||||||
|
|
||||||
private var jsonAPI: playdate_json { Playdate.api.json.pointee }
|
private var jsonAPI: playdate_json { Playdate.api.json.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// The JSON API: decoding to and encoding from a `Value` tree.
|
||||||
/// The JSON API: decoding to and encoding from a `Value` tree.
|
public enum JSON {}
|
||||||
public enum JSON {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.JSON {
|
extension JSON {
|
||||||
/// A decoded JSON value.
|
/// A decoded JSON value.
|
||||||
public indirect enum Value {
|
public indirect enum Value {
|
||||||
case null
|
case null
|
||||||
@@ -88,12 +86,12 @@ extension Playdate.JSON {
|
|||||||
decoder.didDecodeTableValue = { decoder, key, value in
|
decoder.didDecodeTableValue = { decoder, key, value in
|
||||||
guard let userdata = decoder?.pointee.userdata else { return }
|
guard let userdata = decoder?.pointee.userdata else { return }
|
||||||
let context = Unmanaged<DecodeContext>.fromOpaque(userdata).takeUnretainedValue()
|
let context = Unmanaged<DecodeContext>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
context.append(Playdate.JSON.convert(value), key: String(playdateCString: key))
|
context.append(JSON.convert(value), key: String(playdateCString: key))
|
||||||
}
|
}
|
||||||
decoder.didDecodeArrayValue = { decoder, _, value in
|
decoder.didDecodeArrayValue = { decoder, _, value in
|
||||||
guard let userdata = decoder?.pointee.userdata else { return }
|
guard let userdata = decoder?.pointee.userdata else { return }
|
||||||
let context = Unmanaged<DecodeContext>.fromOpaque(userdata).takeUnretainedValue()
|
let context = Unmanaged<DecodeContext>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
context.append(Playdate.JSON.convert(value), key: nil)
|
context.append(JSON.convert(value), key: nil)
|
||||||
}
|
}
|
||||||
decoder.didDecodeSublist = { decoder, _, _ in
|
decoder.didDecodeSublist = { decoder, _, _ in
|
||||||
guard let userdata = decoder?.pointee.userdata else { return nil }
|
guard let userdata = decoder?.pointee.userdata else { return nil }
|
||||||
@@ -107,7 +105,7 @@ extension Playdate.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes a JSON string into a `Value` tree.
|
/// Decodes a JSON string into a `Value` tree.
|
||||||
public static func decode(_ jsonString: String) throws(Playdate.Error) -> Value {
|
public static func decode(_ jsonString: String) throws(PlaydateError) -> Value {
|
||||||
let context = DecodeContext()
|
let context = DecodeContext()
|
||||||
let unmanaged = Unmanaged.passUnretained(context)
|
let unmanaged = Unmanaged.passUnretained(context)
|
||||||
var decoder = makeDecoder(context: unmanaged)
|
var decoder = makeDecoder(context: unmanaged)
|
||||||
@@ -124,14 +122,14 @@ extension Playdate.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Decodes JSON read from an open file into a `Value` tree.
|
/// Decodes JSON read from an open file into a `Value` tree.
|
||||||
public static func decode(file: Playdate.File.Handle) throws(Playdate.Error) -> Value {
|
public static func decode(file: File.Handle) throws(PlaydateError) -> Value {
|
||||||
let context = DecodeContext()
|
let context = DecodeContext()
|
||||||
var decoder = makeDecoder(context: Unmanaged.passUnretained(context))
|
var decoder = makeDecoder(context: Unmanaged.passUnretained(context))
|
||||||
var reader = json_reader()
|
var reader = json_reader()
|
||||||
reader.userdata = Unmanaged.passUnretained(file).toOpaque()
|
reader.userdata = Unmanaged.passUnretained(file).toOpaque()
|
||||||
reader.read = { userdata, buffer, size in
|
reader.read = { userdata, buffer, size in
|
||||||
guard let userdata, let buffer else { return -1 }
|
guard let userdata, let buffer else { return -1 }
|
||||||
let file = Unmanaged<Playdate.File.Handle>.fromOpaque(userdata).takeUnretainedValue()
|
let file = Unmanaged<File.Handle>.fromOpaque(userdata).takeUnretainedValue()
|
||||||
let destination = UnsafeMutableRawBufferPointer(start: buffer, count: Int(size))
|
let destination = UnsafeMutableRawBufferPointer(start: buffer, count: Int(size))
|
||||||
do {
|
do {
|
||||||
let count = try file.read(into: destination)
|
let count = try file.read(into: destination)
|
||||||
@@ -153,13 +151,13 @@ extension Playdate.JSON {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Opens and decodes the JSON file at `path`.
|
/// Opens and decodes the JSON file at `path`.
|
||||||
public static func decodeFile(at path: String) throws(Playdate.Error) -> Value {
|
public static func decodeFile(at path: String) throws(PlaydateError) -> Value {
|
||||||
let file = try Playdate.File.Handle(path: path, mode: [.read, .readData])
|
let file = try File.Handle(path: path, mode: [.read, .readData])
|
||||||
return try decode(file: file)
|
return try decode(file: file)
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func decodeError(_ context: DecodeContext) -> Playdate.Error {
|
private static func decodeError(_ context: DecodeContext) -> PlaydateError {
|
||||||
Playdate.Error(message: "\(context.errorMessage ?? "JSON decode failed") (line \(context.errorLine))")
|
PlaydateError(message: "\(context.errorMessage ?? "JSON decode failed") (line \(context.errorLine))")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Encoding
|
// MARK: - Encoding
|
||||||
|
|||||||
+16
-18
@@ -11,13 +11,11 @@ public import CPlaydate
|
|||||||
|
|
||||||
private var luaAPI: playdate_lua { Playdate.api.lua.pointee }
|
private var luaAPI: playdate_lua { Playdate.api.lua.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// The Lua bridge: registering C functions and classes, and exchanging
|
||||||
/// The Lua bridge: registering C functions and classes, and exchanging
|
/// values with Lua code.
|
||||||
/// values with Lua code.
|
public enum Lua {}
|
||||||
public enum Lua {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.Lua {
|
extension Lua {
|
||||||
/// A function callable from Lua. Returns the number of values it pushed
|
/// A function callable from Lua. Returns the number of values it pushed
|
||||||
/// onto the stack.
|
/// onto the stack.
|
||||||
public typealias CFunction = lua_CFunction
|
public typealias CFunction = lua_CFunction
|
||||||
@@ -58,12 +56,12 @@ extension Playdate.Lua {
|
|||||||
|
|
||||||
/// Makes `function` callable from Lua as `name` (which may contain dots
|
/// Makes `function` callable from Lua as `name` (which may contain dots
|
||||||
/// for namespacing, e.g. "mylib.myfunc").
|
/// for namespacing, e.g. "mylib.myfunc").
|
||||||
public static func addFunction(_ function: CFunction, name: String) throws(Playdate.Error) {
|
public static func addFunction(_ function: CFunction, name: String) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
let ok = name.withPlaydateCString {
|
let ok = name.withPlaydateCString {
|
||||||
luaAPI.addFunction.unsafelyUnwrapped(function, $0, &error) != 0
|
luaAPI.addFunction.unsafelyUnwrapped(function, $0, &error) != 0
|
||||||
}
|
}
|
||||||
if !ok { throw Playdate.Error(cString: error) }
|
if !ok { throw PlaydateError(cString: error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Registers a Lua class named `name` with the given methods and
|
/// Registers a Lua class named `name` with the given methods and
|
||||||
@@ -72,7 +70,7 @@ extension Playdate.Lua {
|
|||||||
public static func registerClass(name: String,
|
public static func registerClass(name: String,
|
||||||
functions: [(name: String, function: CFunction)],
|
functions: [(name: String, function: CFunction)],
|
||||||
values: [ClassValue] = [],
|
values: [ClassValue] = [],
|
||||||
isStatic: Bool = false) throws(Playdate.Error) {
|
isStatic: Bool = false) throws(PlaydateError) {
|
||||||
// The registration tables are kept alive permanently: the OS
|
// The registration tables are kept alive permanently: the OS
|
||||||
// documents no copying guarantees for them.
|
// documents no copying guarantees for them.
|
||||||
var registrations: [lua_reg] = functions.map { entry in
|
var registrations: [lua_reg] = functions.map { entry in
|
||||||
@@ -107,7 +105,7 @@ extension Playdate.Lua {
|
|||||||
values.isEmpty ? nil : constantsBuffer,
|
values.isEmpty ? nil : constantsBuffer,
|
||||||
isStatic ? 1 : 0, &error) != 0
|
isStatic ? 1 : 0, &error) != 0
|
||||||
}
|
}
|
||||||
if !ok { throw Playdate.Error(cString: error) }
|
if !ok { throw PlaydateError(cString: error) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pushes a function onto the stack, e.g. for `setUserValue`.
|
/// Pushes a function onto the stack, e.g. for `setUserValue`.
|
||||||
@@ -189,15 +187,15 @@ extension Playdate.Lua {
|
|||||||
|
|
||||||
/// The argument as a bitmap. References an object owned by Lua; retain
|
/// The argument as a bitmap. References an object owned by Lua; retain
|
||||||
/// the Lua value while using it.
|
/// the Lua value while using it.
|
||||||
public static func bitmapArgument(at position: Int) -> Playdate.Graphics.Bitmap? {
|
public static func bitmapArgument(at position: Int) -> Graphics.Bitmap? {
|
||||||
guard let bitmap = luaAPI.getBitmap.unsafelyUnwrapped(Int32(position)) else { return nil }
|
guard let bitmap = luaAPI.getBitmap.unsafelyUnwrapped(Int32(position)) else { return nil }
|
||||||
return Playdate.Graphics.Bitmap(pointer: bitmap, isOwned: false)
|
return Graphics.Bitmap(pointer: bitmap, isOwned: false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The argument as a sprite.
|
/// The argument as a sprite.
|
||||||
public static func spriteArgument(at position: Int) -> Playdate.Sprite? {
|
public static func spriteArgument(at position: Int) -> Sprite? {
|
||||||
guard let sprite = luaAPI.getSprite.unsafelyUnwrapped(Int32(position)) else { return nil }
|
guard let sprite = luaAPI.getSprite.unsafelyUnwrapped(Int32(position)) else { return nil }
|
||||||
return Playdate.Sprite.wrapper(for: sprite)
|
return Sprite.wrapper(for: sprite)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Return values
|
// MARK: - Return values
|
||||||
@@ -229,11 +227,11 @@ extension Playdate.Lua {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func push(_ bitmap: Playdate.Graphics.Bitmap) {
|
public static func push(_ bitmap: Graphics.Bitmap) {
|
||||||
luaAPI.pushBitmap.unsafelyUnwrapped(bitmap.pointer)
|
luaAPI.pushBitmap.unsafelyUnwrapped(bitmap.pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func push(_ sprite: Playdate.Sprite) {
|
public static func push(_ sprite: Sprite) {
|
||||||
luaAPI.pushSprite.unsafelyUnwrapped(sprite.pointer)
|
luaAPI.pushSprite.unsafelyUnwrapped(sprite.pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,11 +281,11 @@ extension Playdate.Lua {
|
|||||||
|
|
||||||
/// Calls the Lua function `name`. Push the arguments onto the stack
|
/// Calls the Lua function `name`. Push the arguments onto the stack
|
||||||
/// first. Calling Lua from Swift has overhead; use sparingly.
|
/// first. Calling Lua from Swift has overhead; use sparingly.
|
||||||
public static func callFunction(_ name: String, argumentCount: Int = 0) throws(Playdate.Error) {
|
public static func callFunction(_ name: String, argumentCount: Int = 0) throws(PlaydateError) {
|
||||||
var error: UnsafePointer<CChar>?
|
var error: UnsafePointer<CChar>?
|
||||||
let ok = name.withPlaydateCString {
|
let ok = name.withPlaydateCString {
|
||||||
luaAPI.callFunction.unsafelyUnwrapped($0, Int32(argumentCount), &error) != 0
|
luaAPI.callFunction.unsafelyUnwrapped($0, Int32(argumentCount), &error) != 0
|
||||||
}
|
}
|
||||||
if !ok { throw Playdate.Error(cString: error) }
|
if !ok { throw PlaydateError(cString: error) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,10 @@ private var networkAPI: playdate_network { Playdate.api.network.pointee }
|
|||||||
private var httpAPI: playdate_http { networkAPI.http.pointee }
|
private var httpAPI: playdate_http { networkAPI.http.pointee }
|
||||||
private var tcpAPI: playdate_tcp { networkAPI.tcp.pointee }
|
private var tcpAPI: playdate_tcp { networkAPI.tcp.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// The network API: wifi status, HTTP, and TCP.
|
||||||
/// The network API: wifi status, HTTP, and TCP.
|
public enum Network {}
|
||||||
public enum Network {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.Network {
|
|
||||||
public typealias AccessReply = Playdate.AccessReply
|
|
||||||
|
|
||||||
|
extension Network {
|
||||||
/// A network error code (`PDNetErr`).
|
/// A network error code (`PDNetErr`).
|
||||||
public enum NetError: Int32, Swift.Error, Sendable {
|
public enum NetError: Int32, Swift.Error, Sendable {
|
||||||
case noDevice = -1
|
case noDevice = -1
|
||||||
@@ -79,9 +75,9 @@ extension Playdate.Network {
|
|||||||
setEnabledCompletion = completion
|
setEnabledCompletion = completion
|
||||||
if completion != nil {
|
if completion != nil {
|
||||||
networkAPI.setEnabled.unsafelyUnwrapped(enabled, { error in
|
networkAPI.setEnabled.unsafelyUnwrapped(enabled, { error in
|
||||||
let completion = Playdate.Network.setEnabledCompletion
|
let completion = Network.setEnabledCompletion
|
||||||
Playdate.Network.setEnabledCompletion = nil
|
Network.setEnabledCompletion = nil
|
||||||
completion?(Playdate.Network.optionalError(error))
|
completion?(Network.optionalError(error))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
networkAPI.setEnabled.unsafelyUnwrapped(enabled, nil)
|
networkAPI.setEnabled.unsafelyUnwrapped(enabled, nil)
|
||||||
@@ -140,7 +136,7 @@ extension Playdate.Network {
|
|||||||
public static func requestAccess(server: String, port: Int = 443, useSSL: Bool = true,
|
public static func requestAccess(server: String, port: Int = 443, useSSL: Bool = true,
|
||||||
purpose: String? = nil,
|
purpose: String? = nil,
|
||||||
completion: @escaping (Bool) -> Void) -> AccessReply {
|
completion: @escaping (Bool) -> Void) -> AccessReply {
|
||||||
Playdate.Network.requestAccess(
|
Network.requestAccess(
|
||||||
rawRequest: { httpAPI.requestAccess.unsafelyUnwrapped($0, $1, $2, $3, $4, $5) },
|
rawRequest: { httpAPI.requestAccess.unsafelyUnwrapped($0, $1, $2, $3, $4, $5) },
|
||||||
server: server, port: port, useSSL: useSSL, purpose: purpose,
|
server: server, port: port, useSSL: useSSL, purpose: purpose,
|
||||||
completion: completion)
|
completion: completion)
|
||||||
@@ -205,7 +201,7 @@ extension Playdate.Network {
|
|||||||
httpAPI.get.unsafelyUnwrapped(pointer, cPath, cHeaders, headers.utf8.count)
|
httpAPI.get.unsafelyUnwrapped(pointer, cPath, cHeaders, headers.utf8.count)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try Playdate.Network.check(error)
|
try Network.check(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a POST request for `path` with the given body.
|
/// Sends a POST request for `path` with the given body.
|
||||||
@@ -220,7 +216,7 @@ extension Playdate.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try Playdate.Network.check(error)
|
try Network.check(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a request with an arbitrary HTTP method.
|
/// Sends a request with an arbitrary HTTP method.
|
||||||
@@ -238,14 +234,14 @@ extension Playdate.Network {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try Playdate.Network.check(error)
|
try Network.check(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Response
|
// MARK: Response
|
||||||
|
|
||||||
/// The last error on the connection, if any.
|
/// The last error on the connection, if any.
|
||||||
public var error: NetError? {
|
public var error: NetError? {
|
||||||
Playdate.Network.optionalError(httpAPI.getError.unsafelyUnwrapped(pointer))
|
Network.optionalError(httpAPI.getError.unsafelyUnwrapped(pointer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The number of bytes read of the current response, and the total
|
/// The number of bytes read of the current response, and the total
|
||||||
@@ -380,7 +376,7 @@ extension Playdate.Network {
|
|||||||
public static func requestAccess(server: String, port: Int, useSSL: Bool = true,
|
public static func requestAccess(server: String, port: Int, useSSL: Bool = true,
|
||||||
purpose: String? = nil,
|
purpose: String? = nil,
|
||||||
completion: @escaping (Bool) -> Void) -> AccessReply {
|
completion: @escaping (Bool) -> Void) -> AccessReply {
|
||||||
Playdate.Network.requestAccess(
|
Network.requestAccess(
|
||||||
rawRequest: { tcpAPI.requestAccess.unsafelyUnwrapped($0, $1, $2, $3, $4, $5) },
|
rawRequest: { tcpAPI.requestAccess.unsafelyUnwrapped($0, $1, $2, $3, $4, $5) },
|
||||||
server: server, port: port, useSSL: useSSL, purpose: purpose,
|
server: server, port: port, useSSL: useSSL, purpose: purpose,
|
||||||
completion: completion)
|
completion: completion)
|
||||||
@@ -410,7 +406,7 @@ extension Playdate.Network {
|
|||||||
|
|
||||||
/// The last error on the connection, if any.
|
/// The last error on the connection, if any.
|
||||||
public var error: NetError? {
|
public var error: NetError? {
|
||||||
Playdate.Network.optionalError(tcpAPI.getError.unsafelyUnwrapped(pointer))
|
Network.optionalError(tcpAPI.getError.unsafelyUnwrapped(pointer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The time to wait for the connection to open, in milliseconds.
|
/// The time to wait for the connection to open, in milliseconds.
|
||||||
@@ -425,14 +421,14 @@ extension Playdate.Network {
|
|||||||
guard let wrapper = TCPConnection.wrapper(for: connection) else { return }
|
guard let wrapper = TCPConnection.wrapper(for: connection) else { return }
|
||||||
let completion = wrapper.openCompletion
|
let completion = wrapper.openCompletion
|
||||||
wrapper.openCompletion = nil
|
wrapper.openCompletion = nil
|
||||||
completion?(wrapper, Playdate.Network.optionalError(error))
|
completion?(wrapper, Network.optionalError(error))
|
||||||
}, nil)
|
}, nil)
|
||||||
try Playdate.Network.check(error)
|
try Network.check(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closes the connection.
|
/// Closes the connection.
|
||||||
public func close() throws(NetError) {
|
public func close() throws(NetError) {
|
||||||
try Playdate.Network.check(tcpAPI.close.unsafelyUnwrapped(pointer))
|
try Network.check(tcpAPI.close.unsafelyUnwrapped(pointer))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called when the connection closes, with the reason if it closed
|
/// Called when the connection closes, with the reason if it closed
|
||||||
@@ -442,7 +438,7 @@ extension Playdate.Network {
|
|||||||
if callback != nil {
|
if callback != nil {
|
||||||
tcpAPI.setConnectionClosedCallback.unsafelyUnwrapped(pointer, { connection, error in
|
tcpAPI.setConnectionClosedCallback.unsafelyUnwrapped(pointer, { connection, error in
|
||||||
guard let wrapper = TCPConnection.wrapper(for: connection) else { return }
|
guard let wrapper = TCPConnection.wrapper(for: connection) else { return }
|
||||||
wrapper.connectionClosedCallback?(wrapper, Playdate.Network.optionalError(error))
|
wrapper.connectionClosedCallback?(wrapper, Network.optionalError(error))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
tcpAPI.setConnectionClosedCallback.unsafelyUnwrapped(pointer, nil)
|
tcpAPI.setConnectionClosedCallback.unsafelyUnwrapped(pointer, nil)
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
|
|
||||||
public import CPlaydate
|
public import CPlaydate
|
||||||
|
|
||||||
|
/// The raw C API bootstrap. Everything else in this module (System,
|
||||||
|
/// Graphics, Sprite, Sound, ...) lives at the top level of the `PlayDate`
|
||||||
|
/// module and requires `initialize(with:)` to have been called first.
|
||||||
public enum Playdate {
|
public enum Playdate {
|
||||||
/// The raw C API. Populated by `initialize(with:)`.
|
/// The raw C API. Populated by `initialize(with:)`.
|
||||||
///
|
///
|
||||||
@@ -29,9 +32,10 @@ public enum Playdate {
|
|||||||
apiPointer = pointer.assumingMemoryBound(to: PlaydateAPI.self)
|
apiPointer = pointer.assumingMemoryBound(to: PlaydateAPI.self)
|
||||||
api = apiPointer.pointee
|
api = apiPointer.pointee
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// An error reported by the Playdate OS.
|
/// An error reported by the Playdate OS.
|
||||||
public struct Error: Swift.Error, Sendable {
|
public struct PlaydateError: Swift.Error, Sendable {
|
||||||
public let message: String
|
public let message: String
|
||||||
|
|
||||||
init(message: String) {
|
init(message: String) {
|
||||||
@@ -41,18 +45,18 @@ public enum Playdate {
|
|||||||
init(cString: UnsafePointer<CChar>?) {
|
init(cString: UnsafePointer<CChar>?) {
|
||||||
self.init(message: String(playdateCString: cString) ?? "unknown error")
|
self.init(message: String(playdateCString: cString) ?? "unknown error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The user's answer to a permission request (microphone, network).
|
/// The user's answer to a permission request (microphone, network).
|
||||||
public enum AccessReply: UInt32, Sendable {
|
public enum AccessReply: UInt32, Sendable {
|
||||||
case ask = 0
|
case ask = 0
|
||||||
case deny = 1
|
case deny = 1
|
||||||
case allow = 2
|
case allow = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Swift view of `PDSystemEvent` with the key code folded into the
|
/// A Swift view of `PDSystemEvent` with the key code folded into the
|
||||||
/// key events.
|
/// key events.
|
||||||
public enum SystemEvent {
|
public enum SystemEvent {
|
||||||
case initialize
|
case initialize
|
||||||
case initializeLua
|
case initializeLua
|
||||||
case lock
|
case lock
|
||||||
@@ -83,5 +87,4 @@ public enum Playdate {
|
|||||||
default: return nil
|
default: return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ internal import CPlaydate
|
|||||||
|
|
||||||
private var scoreboardsAPI: playdate_scoreboards { Playdate.api.scoreboards.pointee }
|
private var scoreboardsAPI: playdate_scoreboards { Playdate.api.scoreboards.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// The scoreboards API for games with online leaderboards.
|
||||||
/// The scoreboards API for games with online leaderboards.
|
public enum Scoreboards {}
|
||||||
public enum Scoreboards {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.Scoreboards {
|
extension Scoreboards {
|
||||||
/// A score on a board.
|
/// A score on a board.
|
||||||
public struct Score {
|
public struct Score {
|
||||||
public let rank: UInt32
|
public let rank: UInt32
|
||||||
@@ -92,22 +90,22 @@ extension Playdate.Scoreboards {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nonisolated(unsafe) private static var addScoreCompletion: ((Result<Score, Playdate.Error>) -> Void)?
|
nonisolated(unsafe) private static var addScoreCompletion: ((Result<Score, PlaydateError>) -> Void)?
|
||||||
nonisolated(unsafe) private static var personalBestCompletion: ((Result<Score, Playdate.Error>) -> Void)?
|
nonisolated(unsafe) private static var personalBestCompletion: ((Result<Score, PlaydateError>) -> Void)?
|
||||||
nonisolated(unsafe) private static var boardsCompletion: ((Result<BoardsList, Playdate.Error>) -> Void)?
|
nonisolated(unsafe) private static var boardsCompletion: ((Result<BoardsList, PlaydateError>) -> Void)?
|
||||||
nonisolated(unsafe) private static var scoresCompletion: ((Result<ScoresList, Playdate.Error>) -> Void)?
|
nonisolated(unsafe) private static var scoresCompletion: ((Result<ScoresList, PlaydateError>) -> Void)?
|
||||||
|
|
||||||
/// Submits a score to the board. Returns `false` if the request could
|
/// Submits a score to the board. Returns `false` if the request could
|
||||||
/// not be started.
|
/// not be started.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func addScore(boardID: String, value: UInt32,
|
public static func addScore(boardID: String, value: UInt32,
|
||||||
completion: @escaping (Result<Score, Playdate.Error>) -> Void) -> Bool {
|
completion: @escaping (Result<Score, PlaydateError>) -> Void) -> Bool {
|
||||||
addScoreCompletion = completion
|
addScoreCompletion = completion
|
||||||
return boardID.withPlaydateCString { cBoardID in
|
return boardID.withPlaydateCString { cBoardID in
|
||||||
scoreboardsAPI.addScore.unsafelyUnwrapped(cBoardID, value, { score, errorMessage in
|
scoreboardsAPI.addScore.unsafelyUnwrapped(cBoardID, value, { score, errorMessage in
|
||||||
let completion = Playdate.Scoreboards.addScoreCompletion
|
let completion = Scoreboards.addScoreCompletion
|
||||||
Playdate.Scoreboards.addScoreCompletion = nil
|
Scoreboards.addScoreCompletion = nil
|
||||||
completion?(Playdate.Scoreboards.result(score, errorMessage))
|
completion?(Scoreboards.result(score, errorMessage))
|
||||||
}) != 0
|
}) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,26 +113,26 @@ extension Playdate.Scoreboards {
|
|||||||
/// Fetches the current player's best score on the board.
|
/// Fetches the current player's best score on the board.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func getPersonalBest(boardID: String,
|
public static func getPersonalBest(boardID: String,
|
||||||
completion: @escaping (Result<Score, Playdate.Error>) -> Void) -> Bool {
|
completion: @escaping (Result<Score, PlaydateError>) -> Void) -> Bool {
|
||||||
personalBestCompletion = completion
|
personalBestCompletion = completion
|
||||||
return boardID.withPlaydateCString { cBoardID in
|
return boardID.withPlaydateCString { cBoardID in
|
||||||
scoreboardsAPI.getPersonalBest.unsafelyUnwrapped(cBoardID, { score, errorMessage in
|
scoreboardsAPI.getPersonalBest.unsafelyUnwrapped(cBoardID, { score, errorMessage in
|
||||||
let completion = Playdate.Scoreboards.personalBestCompletion
|
let completion = Scoreboards.personalBestCompletion
|
||||||
Playdate.Scoreboards.personalBestCompletion = nil
|
Scoreboards.personalBestCompletion = nil
|
||||||
completion?(Playdate.Scoreboards.result(score, errorMessage))
|
completion?(Scoreboards.result(score, errorMessage))
|
||||||
}) != 0
|
}) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the list of the game's boards.
|
/// Fetches the list of the game's boards.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func getScoreboards(completion: @escaping (Result<BoardsList, Playdate.Error>) -> Void) -> Bool {
|
public static func getScoreboards(completion: @escaping (Result<BoardsList, PlaydateError>) -> Void) -> Bool {
|
||||||
boardsCompletion = completion
|
boardsCompletion = completion
|
||||||
return scoreboardsAPI.getScoreboards.unsafelyUnwrapped({ boards, errorMessage in
|
return scoreboardsAPI.getScoreboards.unsafelyUnwrapped({ boards, errorMessage in
|
||||||
let completion = Playdate.Scoreboards.boardsCompletion
|
let completion = Scoreboards.boardsCompletion
|
||||||
Playdate.Scoreboards.boardsCompletion = nil
|
Scoreboards.boardsCompletion = nil
|
||||||
guard let boards else {
|
guard let boards else {
|
||||||
completion?(.failure(Playdate.Error(cString: errorMessage)))
|
completion?(.failure(PlaydateError(cString: errorMessage)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let list = BoardsList(boards.pointee)
|
let list = BoardsList(boards.pointee)
|
||||||
@@ -146,14 +144,14 @@ extension Playdate.Scoreboards {
|
|||||||
/// Fetches the scores on the board.
|
/// Fetches the scores on the board.
|
||||||
@discardableResult
|
@discardableResult
|
||||||
public static func getScores(boardID: String,
|
public static func getScores(boardID: String,
|
||||||
completion: @escaping (Result<ScoresList, Playdate.Error>) -> Void) -> Bool {
|
completion: @escaping (Result<ScoresList, PlaydateError>) -> Void) -> Bool {
|
||||||
scoresCompletion = completion
|
scoresCompletion = completion
|
||||||
return boardID.withPlaydateCString { cBoardID in
|
return boardID.withPlaydateCString { cBoardID in
|
||||||
scoreboardsAPI.getScores.unsafelyUnwrapped(cBoardID, { scores, errorMessage in
|
scoreboardsAPI.getScores.unsafelyUnwrapped(cBoardID, { scores, errorMessage in
|
||||||
let completion = Playdate.Scoreboards.scoresCompletion
|
let completion = Scoreboards.scoresCompletion
|
||||||
Playdate.Scoreboards.scoresCompletion = nil
|
Scoreboards.scoresCompletion = nil
|
||||||
guard let scores else {
|
guard let scores else {
|
||||||
completion?(.failure(Playdate.Error(cString: errorMessage)))
|
completion?(.failure(PlaydateError(cString: errorMessage)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let list = ScoresList(scores.pointee)
|
let list = ScoresList(scores.pointee)
|
||||||
@@ -164,9 +162,9 @@ extension Playdate.Scoreboards {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static func result(_ score: UnsafeMutablePointer<PDScore>?,
|
private static func result(_ score: UnsafeMutablePointer<PDScore>?,
|
||||||
_ errorMessage: UnsafePointer<CChar>?) -> Result<Score, Playdate.Error> {
|
_ errorMessage: UnsafePointer<CChar>?) -> Result<Score, PlaydateError> {
|
||||||
guard let score else {
|
guard let score else {
|
||||||
return .failure(Playdate.Error(cString: errorMessage))
|
return .failure(PlaydateError(cString: errorMessage))
|
||||||
}
|
}
|
||||||
let value = Score(score.pointee)
|
let value = Score(score.pointee)
|
||||||
scoreboardsAPI.freeScore.unsafelyUnwrapped(score)
|
scoreboardsAPI.freeScore.unsafelyUnwrapped(score)
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ internal import CPlaydate
|
|||||||
|
|
||||||
var snd: playdate_sound { Playdate.api.sound.pointee }
|
var snd: playdate_sound { Playdate.api.sound.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// The sound API: channels, players, synths, sequences, and effects.
|
||||||
/// The sound API: channels, players, synths, sequences, and effects.
|
public enum Sound {}
|
||||||
public enum Sound {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.Sound {
|
extension Sound {
|
||||||
/// A note as a MIDI note number, where 60 is middle C. Fractional values
|
/// A note as a MIDI note number, where 60 is middle C. Fractional values
|
||||||
/// are valid.
|
/// are valid.
|
||||||
public typealias MIDINote = Float
|
public typealias MIDINote = Float
|
||||||
@@ -60,12 +58,9 @@ extension Playdate.Sound {
|
|||||||
case headset = 2
|
case headset = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The user's answer to a permission request.
|
|
||||||
public typealias AccessReply = Playdate.AccessReply
|
|
||||||
|
|
||||||
/// The most recent sound error as a thrown error.
|
/// The most recent sound error as a thrown error.
|
||||||
static func lastError() -> Playdate.Error {
|
static func lastError() -> PlaydateError {
|
||||||
Playdate.Error(cString: snd.getError.unsafelyUnwrapped())
|
PlaydateError(cString: snd.getError.unsafelyUnwrapped())
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Top-level functions
|
// MARK: - Top-level functions
|
||||||
@@ -96,7 +91,7 @@ extension Playdate.Sound {
|
|||||||
if callback != nil {
|
if callback != nil {
|
||||||
return snd.setMicCallback.unsafelyUnwrapped({ _, buffer, length in
|
return snd.setMicCallback.unsafelyUnwrapped({ _, buffer, length in
|
||||||
let samples = UnsafeMutableBufferPointer(start: buffer, count: Int(length))
|
let samples = UnsafeMutableBufferPointer(start: buffer, count: Int(length))
|
||||||
return Playdate.Sound.micCallback?(samples) == true ? 1 : 0
|
return Sound.micCallback?(samples) == true ? 1 : 0
|
||||||
}, nil, CPlaydate.MicSource(source.rawValue)) != 0
|
}, nil, CPlaydate.MicSource(source.rawValue)) != 0
|
||||||
} else {
|
} else {
|
||||||
return snd.setMicCallback.unsafelyUnwrapped(nil, nil, CPlaydate.MicSource(source.rawValue)) != 0
|
return snd.setMicCallback.unsafelyUnwrapped(nil, nil, CPlaydate.MicSource(source.rawValue)) != 0
|
||||||
@@ -147,7 +142,7 @@ extension Playdate.Sound {
|
|||||||
headphoneChangeCallback = callback
|
headphoneChangeCallback = callback
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
snd.getHeadphoneState.unsafelyUnwrapped(nil, nil, { headphone, mic in
|
snd.getHeadphoneState.unsafelyUnwrapped(nil, nil, { headphone, mic in
|
||||||
Playdate.Sound.headphoneChangeCallback?(headphone != 0, mic != 0)
|
Sound.headphoneChangeCallback?(headphone != 0, mic != 0)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
snd.getHeadphoneState.unsafelyUnwrapped(nil, nil, nil)
|
snd.getHeadphoneState.unsafelyUnwrapped(nil, nil, nil)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ internal import CPlaydate
|
|||||||
|
|
||||||
private var effectAPI: playdate_sound_effect { snd.effect.pointee }
|
private var effectAPI: playdate_sound_effect { snd.effect.pointee }
|
||||||
|
|
||||||
extension Playdate.Sound {
|
extension Sound {
|
||||||
/// An effect that processes a channel's audio: the base class of the
|
/// An effect that processes a channel's audio: the base class of the
|
||||||
/// built-in effects. Wraps `SoundEffect`.
|
/// built-in effects. Wraps `SoundEffect`.
|
||||||
public class Effect {
|
public class Effect {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate.Sound {
|
extension Sound {
|
||||||
/// A value that can modulate a parameter. The base class of `Signal`,
|
/// A value that can modulate a parameter. The base class of `Signal`,
|
||||||
/// `LFO`, `Envelope`, and `ControlSignal`. Wraps `PDSynthSignalValue`.
|
/// `LFO`, `Envelope`, and `ControlSignal`. Wraps `PDSynthSignalValue`.
|
||||||
public class SignalValue {
|
public class SignalValue {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate.Sound {
|
extension Sound {
|
||||||
/// A source of audio: the base class of `FilePlayer`, `SamplePlayer`,
|
/// A source of audio: the base class of `FilePlayer`, `SamplePlayer`,
|
||||||
/// `Synth`, `DelayLineTap`, and `CallbackSource`. Wraps `SoundSource`.
|
/// `Synth`, `DelayLineTap`, and `CallbackSource`. Wraps `SoundSource`.
|
||||||
public class Source {
|
public class Source {
|
||||||
@@ -116,7 +116,7 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a player and loads the audio file at `path`.
|
/// Creates a player and loads the audio file at `path`.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
self.init()
|
self.init()
|
||||||
try load(path: path)
|
try load(path: path)
|
||||||
}
|
}
|
||||||
@@ -128,12 +128,12 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares the player to stream the file at `path`.
|
/// Prepares the player to stream the file at `path`.
|
||||||
public func load(path: String) throws(Playdate.Error) {
|
public func load(path: String) throws(PlaydateError) {
|
||||||
let loaded = path.withPlaydateCString {
|
let loaded = path.withPlaydateCString {
|
||||||
FilePlayer.api.loadIntoPlayer.unsafelyUnwrapped(pointer, $0) != 0
|
FilePlayer.api.loadIntoPlayer.unsafelyUnwrapped(pointer, $0) != 0
|
||||||
}
|
}
|
||||||
if !loaded {
|
if !loaded {
|
||||||
throw Playdate.Error(message: "unable to load audio file: \(path)")
|
throw PlaydateError(message: "unable to load audio file: \(path)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -266,10 +266,10 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the wav or aiff file at `path`.
|
/// Loads the wav or aiff file at `path`.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
let pointer = path.withPlaydateCString { AudioSample.api.load.unsafelyUnwrapped($0) }
|
let pointer = path.withPlaydateCString { AudioSample.api.load.unsafelyUnwrapped($0) }
|
||||||
guard let pointer else {
|
guard let pointer else {
|
||||||
throw Playdate.Error(message: "unable to load sample: \(path)")
|
throw PlaydateError(message: "unable to load sample: \(path)")
|
||||||
}
|
}
|
||||||
self.init(pointer: pointer, isOwned: true)
|
self.init(pointer: pointer, isOwned: true)
|
||||||
}
|
}
|
||||||
@@ -294,12 +294,12 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the file at `path` into this sample's buffer.
|
/// Loads the file at `path` into this sample's buffer.
|
||||||
public func load(path: String) throws(Playdate.Error) {
|
public func load(path: String) throws(PlaydateError) {
|
||||||
let loaded = path.withPlaydateCString {
|
let loaded = path.withPlaydateCString {
|
||||||
AudioSample.api.loadIntoSample.unsafelyUnwrapped(pointer, $0) != 0
|
AudioSample.api.loadIntoSample.unsafelyUnwrapped(pointer, $0) != 0
|
||||||
}
|
}
|
||||||
if !loaded {
|
if !loaded {
|
||||||
throw Playdate.Error(message: "unable to load sample: \(path)")
|
throw PlaydateError(message: "unable to load sample: \(path)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,7 +346,7 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a player for the sample at `path`.
|
/// Creates a player for the sample at `path`.
|
||||||
public convenience init(path: String) throws(Playdate.Error) {
|
public convenience init(path: String) throws(PlaydateError) {
|
||||||
self.init()
|
self.init()
|
||||||
sample = try AudioSample(path: path)
|
sample = try AudioSample(path: path)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate.Sound {
|
extension Sound {
|
||||||
/// A synthesizer voice. Wraps `PDSynth`.
|
/// A synthesizer voice. Wraps `PDSynth`.
|
||||||
public final class Synth: Source {
|
public final class Synth: Source {
|
||||||
private static var api: playdate_sound_synth { snd.synth.pointee }
|
private static var api: playdate_sound_synth { snd.synth.pointee }
|
||||||
@@ -109,11 +109,11 @@ extension Playdate.Sound {
|
|||||||
/// Uses a wavetable for the synth. `log2size` is the base-2 log of
|
/// Uses a wavetable for the synth. `log2size` is the base-2 log of
|
||||||
/// each waveform's size (e.g. 8 for 256 samples).
|
/// each waveform's size (e.g. 8 for 256 samples).
|
||||||
public func setWavetable(_ sample: AudioSample, log2size: Int,
|
public func setWavetable(_ sample: AudioSample, log2size: Int,
|
||||||
columns: Int, rows: Int) throws(Playdate.Error) {
|
columns: Int, rows: Int) throws(PlaydateError) {
|
||||||
retainedSample = sample
|
retainedSample = sample
|
||||||
guard Synth.api.setWavetable.unsafelyUnwrapped(
|
guard Synth.api.setWavetable.unsafelyUnwrapped(
|
||||||
pointer, sample.pointer, Int32(log2size), Int32(columns), Int32(rows)) != 0 else {
|
pointer, sample.pointer, Int32(log2size), Int32(columns), Int32(rows)) != 0 else {
|
||||||
throw Playdate.Error(message: "invalid wavetable dimensions")
|
throw PlaydateError(message: "invalid wavetable dimensions")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -497,7 +497,7 @@ extension Playdate.Sound {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a sequence and loads the MIDI file at `path`.
|
/// Creates a sequence and loads the MIDI file at `path`.
|
||||||
public convenience init(midiFilePath: String) throws(Playdate.Error) {
|
public convenience init(midiFilePath: String) throws(PlaydateError) {
|
||||||
self.init()
|
self.init()
|
||||||
try loadMIDIFile(path: midiFilePath)
|
try loadMIDIFile(path: midiFilePath)
|
||||||
}
|
}
|
||||||
@@ -506,12 +506,12 @@ extension Playdate.Sound {
|
|||||||
Sequence.api.freeSequence.unsafelyUnwrapped(pointer)
|
Sequence.api.freeSequence.unsafelyUnwrapped(pointer)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func loadMIDIFile(path: String) throws(Playdate.Error) {
|
public func loadMIDIFile(path: String) throws(PlaydateError) {
|
||||||
let loaded = path.withPlaydateCString {
|
let loaded = path.withPlaydateCString {
|
||||||
Sequence.api.loadMIDIFile.unsafelyUnwrapped(pointer, $0) != 0
|
Sequence.api.loadMIDIFile.unsafelyUnwrapped(pointer, $0) != 0
|
||||||
}
|
}
|
||||||
if !loaded {
|
if !loaded {
|
||||||
throw Playdate.Error(message: "unable to load MIDI file: \(path)")
|
throw PlaydateError(message: "unable to load MIDI file: \(path)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ internal import CPlaydate
|
|||||||
|
|
||||||
private var spriteAPI: playdate_sprite { Playdate.api.sprite.pointee }
|
private var spriteAPI: playdate_sprite { Playdate.api.sprite.pointee }
|
||||||
|
|
||||||
extension Playdate {
|
/// A floating-point rectangle mirroring `PDRect`.
|
||||||
/// A floating-point rectangle mirroring `PDRect`.
|
public struct Rect: Sendable {
|
||||||
public struct Rect: Sendable {
|
|
||||||
public var x: Float
|
public var x: Float
|
||||||
public var y: Float
|
public var y: Float
|
||||||
public var width: Float
|
public var width: Float
|
||||||
@@ -32,14 +31,12 @@ extension Playdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var cValue: PDRect { PDRect(x: x, y: y, width: width, height: height) }
|
var cValue: PDRect { PDRect(x: x, y: y, width: width, height: height) }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Playdate {
|
/// A sprite: a drawable object with position, z-order, and collision
|
||||||
/// A sprite: a drawable object with position, z-order, and collision
|
/// support. Wraps `LCDSprite`. Static members wrap the global sprite
|
||||||
/// support. Wraps `LCDSprite`. Static members wrap the global sprite
|
/// system functions.
|
||||||
/// system functions.
|
public final class Sprite {
|
||||||
public final class Sprite {
|
|
||||||
let pointer: OpaquePointer
|
let pointer: OpaquePointer
|
||||||
let isOwned: Bool
|
let isOwned: Bool
|
||||||
|
|
||||||
@@ -131,9 +128,9 @@ extension Playdate {
|
|||||||
/// Where the sprite started touching `other`.
|
/// Where the sprite started touching `other`.
|
||||||
public let touch: (x: Float, y: Float)
|
public let touch: (x: Float, y: Float)
|
||||||
/// The sprite's rect at the moment of the touch.
|
/// The sprite's rect at the moment of the touch.
|
||||||
public let spriteRect: Playdate.Rect
|
public let spriteRect: Rect
|
||||||
/// `other`'s rect at the moment of the touch.
|
/// `other`'s rect at the moment of the touch.
|
||||||
public let otherRect: Playdate.Rect
|
public let otherRect: Rect
|
||||||
|
|
||||||
init(_ info: SpriteCollisionInfo) {
|
init(_ info: SpriteCollisionInfo) {
|
||||||
sprite = Sprite.wrapper(for: info.sprite)
|
sprite = Sprite.wrapper(for: info.sprite)
|
||||||
@@ -144,8 +141,8 @@ extension Playdate {
|
|||||||
move = (info.move.x, info.move.y)
|
move = (info.move.x, info.move.y)
|
||||||
normal = (Int(info.normal.x), Int(info.normal.y))
|
normal = (Int(info.normal.x), Int(info.normal.y))
|
||||||
touch = (info.touch.x, info.touch.y)
|
touch = (info.touch.x, info.touch.y)
|
||||||
spriteRect = Playdate.Rect(info.spriteRect)
|
spriteRect = Rect(info.spriteRect)
|
||||||
otherRect = Playdate.Rect(info.otherRect)
|
otherRect = Rect(info.otherRect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,8 +225,8 @@ extension Playdate {
|
|||||||
// MARK: - Geometry
|
// MARK: - Geometry
|
||||||
|
|
||||||
/// The sprite's bounds. Setting this positions and sizes the sprite.
|
/// The sprite's bounds. Setting this positions and sizes the sprite.
|
||||||
public var bounds: Playdate.Rect {
|
public var bounds: Rect {
|
||||||
get { Playdate.Rect(spriteAPI.getBounds.unsafelyUnwrapped(pointer)) }
|
get { Rect(spriteAPI.getBounds.unsafelyUnwrapped(pointer)) }
|
||||||
set { spriteAPI.setBounds.unsafelyUnwrapped(pointer, newValue.cValue) }
|
set { spriteAPI.setBounds.unsafelyUnwrapped(pointer, newValue.cValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,7 +373,7 @@ extension Playdate {
|
|||||||
|
|
||||||
/// Marks part of the sprite (in sprite-local coordinates) as needing
|
/// Marks part of the sprite (in sprite-local coordinates) as needing
|
||||||
/// a redraw.
|
/// a redraw.
|
||||||
public func markDirty(rect: Playdate.Rect) {
|
public func markDirty(rect: Rect) {
|
||||||
spriteAPI.markDirtyRect.unsafelyUnwrapped(pointer, rect.cValue)
|
spriteAPI.markDirtyRect.unsafelyUnwrapped(pointer, rect.cValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,13 +408,13 @@ extension Playdate {
|
|||||||
/// Sets a custom draw function, called when the sprite needs to draw.
|
/// Sets a custom draw function, called when the sprite needs to draw.
|
||||||
/// `bounds` is the sprite's bounds; `drawRect` is the region that
|
/// `bounds` is the sprite's bounds; `drawRect` is the region that
|
||||||
/// needs redrawing.
|
/// needs redrawing.
|
||||||
public func setDrawFunction(_ draw: ((Sprite, _ bounds: Playdate.Rect, _ drawRect: Playdate.Rect) -> Void)?) {
|
public func setDrawFunction(_ draw: ((Sprite, _ bounds: Rect, _ drawRect: Rect) -> Void)?) {
|
||||||
drawFunction = draw
|
drawFunction = draw
|
||||||
if draw != nil {
|
if draw != nil {
|
||||||
spriteAPI.setDrawFunction.unsafelyUnwrapped(pointer, { spritePointer, bounds, drawRect in
|
spriteAPI.setDrawFunction.unsafelyUnwrapped(pointer, { spritePointer, bounds, drawRect in
|
||||||
guard let spritePointer else { return }
|
guard let spritePointer else { return }
|
||||||
let sprite = Sprite.wrapper(for: spritePointer)
|
let sprite = Sprite.wrapper(for: spritePointer)
|
||||||
sprite.drawFunction?(sprite, Playdate.Rect(bounds), Playdate.Rect(drawRect))
|
sprite.drawFunction?(sprite, Rect(bounds), Rect(drawRect))
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
spriteAPI.setDrawFunction.unsafelyUnwrapped(pointer, nil)
|
spriteAPI.setDrawFunction.unsafelyUnwrapped(pointer, nil)
|
||||||
@@ -432,8 +429,8 @@ extension Playdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The rect (in sprite-local coordinates) used for collisions.
|
/// The rect (in sprite-local coordinates) used for collisions.
|
||||||
public var collideRect: Playdate.Rect {
|
public var collideRect: Rect {
|
||||||
get { Playdate.Rect(spriteAPI.getCollideRect.unsafelyUnwrapped(pointer)) }
|
get { Rect(spriteAPI.getCollideRect.unsafelyUnwrapped(pointer)) }
|
||||||
set { spriteAPI.setCollideRect.unsafelyUnwrapped(pointer, newValue.cValue) }
|
set { spriteAPI.setCollideRect.unsafelyUnwrapped(pointer, newValue.cValue) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -466,7 +463,7 @@ extension Playdate {
|
|||||||
for index in 0..<Int(count) {
|
for index in 0..<Int(count) {
|
||||||
infos.append(CollisionInfo(pointer[index]))
|
infos.append(CollisionInfo(pointer[index]))
|
||||||
}
|
}
|
||||||
Playdate.System.systemFree(pointer)
|
System.systemFree(pointer)
|
||||||
return infos
|
return infos
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -502,7 +499,7 @@ extension Playdate {
|
|||||||
sprites.append(wrapper(for: spritePointer))
|
sprites.append(wrapper(for: spritePointer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Playdate.System.systemFree(pointer)
|
System.systemFree(pointer)
|
||||||
return sprites
|
return sprites
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -538,7 +535,7 @@ extension Playdate {
|
|||||||
for index in 0..<Int(count) {
|
for index in 0..<Int(count) {
|
||||||
infos.append(QueryInfo(result[index]))
|
infos.append(QueryInfo(result[index]))
|
||||||
}
|
}
|
||||||
Playdate.System.systemFree(result)
|
System.systemFree(result)
|
||||||
return infos
|
return infos
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -555,5 +552,4 @@ extension Playdate {
|
|||||||
let result = spriteAPI.allOverlappingSprites.unsafelyUnwrapped(&count)
|
let result = spriteAPI.allOverlappingSprites.unsafelyUnwrapped(&count)
|
||||||
return sprites(result, count: count)
|
return sprites(result, count: count)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,10 @@
|
|||||||
|
|
||||||
internal import CPlaydate
|
internal import CPlaydate
|
||||||
|
|
||||||
extension Playdate {
|
/// The system API: logging, input, time, menu items, and device state.
|
||||||
/// The system API: logging, input, time, menu items, and device state.
|
public enum System {}
|
||||||
public enum System {}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension Playdate.System {
|
extension System {
|
||||||
private static var api: playdate_sys { Playdate.api.system.pointee }
|
private static var api: playdate_sys { Playdate.api.system.pointee }
|
||||||
|
|
||||||
// MARK: - Types
|
// MARK: - Types
|
||||||
@@ -189,8 +187,8 @@ extension Playdate.System {
|
|||||||
public static func getServerTime(_ completion: @escaping (_ time: String?, _ error: String?) -> Void) {
|
public static func getServerTime(_ completion: @escaping (_ time: String?, _ error: String?) -> Void) {
|
||||||
serverTimeCompletion = completion
|
serverTimeCompletion = completion
|
||||||
api.getServerTime.unsafelyUnwrapped { time, error in
|
api.getServerTime.unsafelyUnwrapped { time, error in
|
||||||
let completion = Playdate.System.serverTimeCompletion
|
let completion = System.serverTimeCompletion
|
||||||
Playdate.System.serverTimeCompletion = nil
|
System.serverTimeCompletion = nil
|
||||||
completion?(String(playdateCString: time), String(playdateCString: error))
|
completion?(String(playdateCString: time), String(playdateCString: error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,7 +201,7 @@ extension Playdate.System {
|
|||||||
public static func setUpdateCallback(_ callback: @escaping () -> Bool) {
|
public static func setUpdateCallback(_ callback: @escaping () -> Bool) {
|
||||||
updateCallback = callback
|
updateCallback = callback
|
||||||
api.setUpdateCallback.unsafelyUnwrapped({ _ in
|
api.setUpdateCallback.unsafelyUnwrapped({ _ in
|
||||||
Playdate.System.updateCallback?() == true ? 1 : 0
|
System.updateCallback?() == true ? 1 : 0
|
||||||
}, nil)
|
}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,7 +229,7 @@ extension Playdate.System {
|
|||||||
buttonCallback = callback
|
buttonCallback = callback
|
||||||
if callback != nil {
|
if callback != nil {
|
||||||
api.setButtonCallback.unsafelyUnwrapped({ button, down, when, _ in
|
api.setButtonCallback.unsafelyUnwrapped({ button, down, when, _ in
|
||||||
Playdate.System.buttonCallback?(Buttons(button), down != 0, when) ?? 0
|
System.buttonCallback?(Buttons(button), down != 0, when) ?? 0
|
||||||
}, nil, Int32(queueSize))
|
}, nil, Int32(queueSize))
|
||||||
} else {
|
} else {
|
||||||
api.setButtonCallback.unsafelyUnwrapped(nil, nil, Int32(queueSize))
|
api.setButtonCallback.unsafelyUnwrapped(nil, nil, Int32(queueSize))
|
||||||
@@ -280,7 +278,7 @@ extension Playdate.System {
|
|||||||
if callback != nil {
|
if callback != nil {
|
||||||
api.setSerialMessageCallback.unsafelyUnwrapped { data in
|
api.setSerialMessageCallback.unsafelyUnwrapped { data in
|
||||||
guard let message = String(playdateCString: data) else { return }
|
guard let message = String(playdateCString: data) else { return }
|
||||||
Playdate.System.serialMessageCallback?(message)
|
System.serialMessageCallback?(message)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
api.setSerialMessageCallback.unsafelyUnwrapped(nil)
|
api.setSerialMessageCallback.unsafelyUnwrapped(nil)
|
||||||
@@ -415,7 +413,7 @@ extension Playdate.System {
|
|||||||
|
|
||||||
/// Sets a custom image for the pause menu, optionally shifted left by
|
/// Sets a custom image for the pause menu, optionally shifted left by
|
||||||
/// `xOffset` (0...200).
|
/// `xOffset` (0...200).
|
||||||
public static func setMenuImage(_ bitmap: Playdate.Graphics.Bitmap?, xOffset: Int = 0) {
|
public static func setMenuImage(_ bitmap: Graphics.Bitmap?, xOffset: Int = 0) {
|
||||||
api.setMenuImage.unsafelyUnwrapped(bitmap?.pointer, Int32(xOffset))
|
api.setMenuImage.unsafelyUnwrapped(bitmap?.pointer, Int32(xOffset))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,20 +6,20 @@ import Testing
|
|||||||
// inline C helpers that work without it.
|
// inline C helpers that work without it.
|
||||||
|
|
||||||
@Test func buttonsOptionSetMatchesCMasks() {
|
@Test func buttonsOptionSetMatchesCMasks() {
|
||||||
#expect(Playdate.System.Buttons.left.rawValue == 1 << 0)
|
#expect(System.Buttons.left.rawValue == 1 << 0)
|
||||||
#expect(Playdate.System.Buttons.right.rawValue == 1 << 1)
|
#expect(System.Buttons.right.rawValue == 1 << 1)
|
||||||
#expect(Playdate.System.Buttons.up.rawValue == 1 << 2)
|
#expect(System.Buttons.up.rawValue == 1 << 2)
|
||||||
#expect(Playdate.System.Buttons.down.rawValue == 1 << 3)
|
#expect(System.Buttons.down.rawValue == 1 << 3)
|
||||||
#expect(Playdate.System.Buttons.b.rawValue == 1 << 4)
|
#expect(System.Buttons.b.rawValue == 1 << 4)
|
||||||
#expect(Playdate.System.Buttons.a.rawValue == 1 << 5)
|
#expect(System.Buttons.a.rawValue == 1 << 5)
|
||||||
|
|
||||||
let combined: Playdate.System.Buttons = [.a, .up]
|
let combined: System.Buttons = [.a, .up]
|
||||||
#expect(combined.contains(.a))
|
#expect(combined.contains(.a))
|
||||||
#expect(!combined.contains(.b))
|
#expect(!combined.contains(.b))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func graphicsRectConvertsBetweenOriginSizeAndEdges() {
|
@Test func graphicsRectConvertsBetweenOriginSizeAndEdges() {
|
||||||
let rect = Playdate.Graphics.Rect(x: 10, y: 20, width: 30, height: 40)
|
let rect = Graphics.Rect(x: 10, y: 20, width: 30, height: 40)
|
||||||
#expect(rect.left == 10)
|
#expect(rect.left == 10)
|
||||||
#expect(rect.right == 40)
|
#expect(rect.right == 40)
|
||||||
#expect(rect.top == 20)
|
#expect(rect.top == 20)
|
||||||
@@ -29,13 +29,13 @@ import Testing
|
|||||||
#expect(translated.left == 15)
|
#expect(translated.left == 15)
|
||||||
#expect(translated.top == 15)
|
#expect(translated.top == 15)
|
||||||
|
|
||||||
let roundTripped = Playdate.Graphics.Rect(rect.cValue)
|
let roundTripped = Graphics.Rect(rect.cValue)
|
||||||
#expect(roundTripped.left == rect.left && roundTripped.bottom == rect.bottom)
|
#expect(roundTripped.left == rect.left && roundTripped.bottom == rect.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func spriteRectRoundTripsThroughC() {
|
@Test func spriteRectRoundTripsThroughC() {
|
||||||
let rect = Playdate.Rect(x: 1.5, y: 2.5, width: 3, height: 4)
|
let rect = Rect(x: 1.5, y: 2.5, width: 3, height: 4)
|
||||||
let roundTripped = Playdate.Rect(rect.cValue)
|
let roundTripped = Rect(rect.cValue)
|
||||||
#expect(roundTripped.x == 1.5)
|
#expect(roundTripped.x == 1.5)
|
||||||
#expect(roundTripped.y == 2.5)
|
#expect(roundTripped.y == 2.5)
|
||||||
#expect(roundTripped.width == 3)
|
#expect(roundTripped.width == 3)
|
||||||
@@ -43,23 +43,23 @@ import Testing
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test func soundFormatPropertiesMatchCMacros() {
|
@Test func soundFormatPropertiesMatchCMacros() {
|
||||||
#expect(!Playdate.Sound.Format.mono8bit.isStereo)
|
#expect(!Sound.Format.mono8bit.isStereo)
|
||||||
#expect(Playdate.Sound.Format.stereo16bit.isStereo)
|
#expect(Sound.Format.stereo16bit.isStereo)
|
||||||
#expect(Playdate.Sound.Format.mono16bit.is16bit)
|
#expect(Sound.Format.mono16bit.is16bit)
|
||||||
#expect(!Playdate.Sound.Format.monoADPCM.is16bit)
|
#expect(!Sound.Format.monoADPCM.is16bit)
|
||||||
|
|
||||||
#expect(Playdate.Sound.Format.mono8bit.bytesPerFrame == 1)
|
#expect(Sound.Format.mono8bit.bytesPerFrame == 1)
|
||||||
#expect(Playdate.Sound.Format.stereo8bit.bytesPerFrame == 2)
|
#expect(Sound.Format.stereo8bit.bytesPerFrame == 2)
|
||||||
#expect(Playdate.Sound.Format.mono16bit.bytesPerFrame == 2)
|
#expect(Sound.Format.mono16bit.bytesPerFrame == 2)
|
||||||
#expect(Playdate.Sound.Format.stereo16bit.bytesPerFrame == 4)
|
#expect(Sound.Format.stereo16bit.bytesPerFrame == 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test func midiNoteFrequencyConversionRoundTrips() {
|
@Test func midiNoteFrequencyConversionRoundTrips() {
|
||||||
// A4 (MIDI 69) is 440 Hz.
|
// A4 (MIDI 69) is 440 Hz.
|
||||||
let a4 = Playdate.Sound.frequency(forNote: 69)
|
let a4 = Sound.frequency(forNote: 69)
|
||||||
#expect(abs(a4 - 440) < 0.01)
|
#expect(abs(a4 - 440) < 0.01)
|
||||||
|
|
||||||
let note = Playdate.Sound.note(forFrequency: 440)
|
let note = Sound.note(forFrequency: 440)
|
||||||
#expect(abs(note - 69) < 0.001)
|
#expect(abs(note - 69) < 0.001)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,9 +74,9 @@ import Testing
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test func dateTimeMirrorsCStruct() {
|
@Test func dateTimeMirrorsCStruct() {
|
||||||
let dateTime = Playdate.System.DateTime(year: 2026, month: 7, day: 24, weekday: 5,
|
let dateTime = System.DateTime(year: 2026, month: 7, day: 24, weekday: 5,
|
||||||
hour: 12, minute: 34, second: 56)
|
hour: 12, minute: 34, second: 56)
|
||||||
let roundTripped = Playdate.System.DateTime(dateTime.cValue)
|
let roundTripped = System.DateTime(dateTime.cValue)
|
||||||
#expect(roundTripped.year == 2026)
|
#expect(roundTripped.year == 2026)
|
||||||
#expect(roundTripped.month == 7)
|
#expect(roundTripped.month == 7)
|
||||||
#expect(roundTripped.day == 24)
|
#expect(roundTripped.day == 24)
|
||||||
|
|||||||
Reference in New Issue
Block a user