diff --git a/Sources/PlaydateKit/Display/Display.swift b/Sources/PlaydateKit/Display/Display.swift index e1381db..6e76406 100644 --- a/Sources/PlaydateKit/Display/Display.swift +++ b/Sources/PlaydateKit/Display/Display.swift @@ -4,6 +4,7 @@ internal import CPlaydate public enum Display {} extension Display { + /// The cached `playdate->display` C API table. private static var api: UnsafePointer { Playdate.displayAPI.unsafelyUnwrapped } /// The display width in pixels, taking the current scale into account. diff --git a/Sources/PlaydateKit/File/Enumerations/SeekOrigin.swift b/Sources/PlaydateKit/File/Enumerations/SeekOrigin.swift index eb46596..0b3fe94 100644 --- a/Sources/PlaydateKit/File/Enumerations/SeekOrigin.swift +++ b/Sources/PlaydateKit/File/Enumerations/SeekOrigin.swift @@ -1,8 +1,11 @@ extension File { /// The origin used by `Handle.seek(to:from:)`. public enum SeekOrigin: Int32, Sendable { + /// Relative to the beginning of the file. case start = 0 + /// Relative to the current offset. case current = 1 + /// Relative to the end of the file. case end = 2 } } diff --git a/Sources/PlaydateKit/File/File.swift b/Sources/PlaydateKit/File/File.swift index 41b4f19..b6a6380 100644 --- a/Sources/PlaydateKit/File/File.swift +++ b/Sources/PlaydateKit/File/File.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->file` C API table. var fileAPI: UnsafePointer { Playdate.fileAPI.unsafelyUnwrapped } /// The most recent file system error as a thrown error. @@ -8,6 +9,9 @@ func lastFileError() -> PlaydateError { } /// The file API: access to the game's Data directory and pdx contents. +/// +/// Paths are relative to the game's Data directory (read/write) or the +/// game's pdx (read-only), depending on the mode used to open them. public enum File {} extension File { diff --git a/Sources/PlaydateKit/File/Structures/Stat.swift b/Sources/PlaydateKit/File/Structures/Stat.swift index 3e484e4..58ad59a 100644 --- a/Sources/PlaydateKit/File/Structures/Stat.swift +++ b/Sources/PlaydateKit/File/Structures/Stat.swift @@ -1,8 +1,11 @@ extension File { /// Information about a file or directory, mirroring `FileStat`. public struct Stat: Sendable { + /// Whether the path is a directory. public let isDirectory: Bool + /// The file's size, in bytes. public let size: UInt32 + /// The time the file was last modified. public let modified: System.DateTime } } diff --git a/Sources/PlaydateKit/Graphics/Classes/Bitmap.swift b/Sources/PlaydateKit/Graphics/Classes/Bitmap.swift index fab07b4..e40a8a8 100644 --- a/Sources/PlaydateKit/Graphics/Classes/Bitmap.swift +++ b/Sources/PlaydateKit/Graphics/Classes/Bitmap.swift @@ -39,6 +39,7 @@ extension Graphics { // MARK: Properties + /// The bitmap's dimensions, row stride, and raw storage. public var data: Data { var width: Int32 = 0, height: Int32 = 0, rowBytes: Int32 = 0 var mask: UnsafeMutablePointer? @@ -61,7 +62,9 @@ extension Graphics { return size } + /// The bitmap's width, in pixels. public var width: Int { size.width } + /// The bitmap's height, in pixels. public var height: Int { size.height } /// The color of the pixel at (x, y). @@ -84,6 +87,7 @@ extension Graphics { color.withLCDColor { gfx.pointee.clearBitmap.unsafelyUnwrapped(pointer, $0) } } + /// Returns a new copy of the bitmap. public func copy() -> Bitmap { Bitmap(pointer: gfx.pointee.copyBitmap.unsafelyUnwrapped(pointer).unsafelyUnwrapped, isOwned: true) } diff --git a/Sources/PlaydateKit/Graphics/Classes/BitmapTable.swift b/Sources/PlaydateKit/Graphics/Classes/BitmapTable.swift index cc7c73e..792f4a2 100644 --- a/Sources/PlaydateKit/Graphics/Classes/BitmapTable.swift +++ b/Sources/PlaydateKit/Graphics/Classes/BitmapTable.swift @@ -52,6 +52,7 @@ extension Graphics { return (Int(count), Int(width)) } + /// The number of bitmaps in the table. public var count: Int { info.count } } } diff --git a/Sources/PlaydateKit/Graphics/Classes/StreamPlayer.swift b/Sources/PlaydateKit/Graphics/Classes/StreamPlayer.swift index 007e6ae..46bf11e 100644 --- a/Sources/PlaydateKit/Graphics/Classes/StreamPlayer.swift +++ b/Sources/PlaydateKit/Graphics/Classes/StreamPlayer.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->graphics->videostream` C API table. private var streamAPI: UnsafePointer { Playdate.videoStreamAPI.unsafelyUnwrapped } extension Graphics { diff --git a/Sources/PlaydateKit/Graphics/Classes/TileMap.swift b/Sources/PlaydateKit/Graphics/Classes/TileMap.swift index ef04c4a..daa1003 100644 --- a/Sources/PlaydateKit/Graphics/Classes/TileMap.swift +++ b/Sources/PlaydateKit/Graphics/Classes/TileMap.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->graphics->tilemap` C API table. private var tilemapAPI: UnsafePointer { Playdate.tilemapAPI.unsafelyUnwrapped } extension Graphics { diff --git a/Sources/PlaydateKit/Graphics/Classes/VideoPlayer.swift b/Sources/PlaydateKit/Graphics/Classes/VideoPlayer.swift index 48896a7..919c6ca 100644 --- a/Sources/PlaydateKit/Graphics/Classes/VideoPlayer.swift +++ b/Sources/PlaydateKit/Graphics/Classes/VideoPlayer.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->graphics->video` C API table. private var videoAPI: UnsafePointer { Playdate.videoAPI.unsafelyUnwrapped } extension Graphics { diff --git a/Sources/PlaydateKit/Graphics/Enumerations/Color.swift b/Sources/PlaydateKit/Graphics/Enumerations/Color.swift index 5512494..798ddd6 100644 --- a/Sources/PlaydateKit/Graphics/Enumerations/Color.swift +++ b/Sources/PlaydateKit/Graphics/Enumerations/Color.swift @@ -3,10 +3,15 @@ internal import CPlaydate extension Graphics { /// A drawing color: solid or an 8×8 pattern. public enum Color: Sendable { + /// Solid black. case black + /// Solid white. case white + /// Transparent; leaves the destination unchanged. case clear + /// Inverts the destination pixels. case xor + /// An 8×8 two-color pattern. case pattern(Pattern) /// Materializes the `LCDColor` for the duration of `body`. Pattern diff --git a/Sources/PlaydateKit/Graphics/Enumerations/DrawMode.swift b/Sources/PlaydateKit/Graphics/Enumerations/DrawMode.swift index 92dfe4b..6f951ef 100644 --- a/Sources/PlaydateKit/Graphics/Enumerations/DrawMode.swift +++ b/Sources/PlaydateKit/Graphics/Enumerations/DrawMode.swift @@ -3,13 +3,21 @@ internal import CPlaydate extension Graphics { /// How source pixels combine with the destination when drawing. public enum DrawMode: UInt32, Sendable { + /// Source pixels replace the destination. case copy = 0 + /// White source pixels are treated as transparent. case whiteTransparent = 1 + /// Black source pixels are treated as transparent. case blackTransparent = 2 + /// Opaque source pixels draw white. case fillWhite = 3 + /// Opaque source pixels draw black. case fillBlack = 4 + /// Source pixels are XORed with the destination. case xor = 5 + /// The inverse of `xor`. case nxor = 6 + /// Source pixels draw inverted. case inverted = 7 init(_ mode: LCDBitmapDrawMode) { self = DrawMode(rawValue: UInt32(mode.rawValue)) ?? .copy } diff --git a/Sources/PlaydateKit/Graphics/Graphics.swift b/Sources/PlaydateKit/Graphics/Graphics.swift index c361738..f1fa54a 100644 --- a/Sources/PlaydateKit/Graphics/Graphics.swift +++ b/Sources/PlaydateKit/Graphics/Graphics.swift @@ -3,6 +3,7 @@ internal import CPlaydate /// The graphics API: drawing, bitmaps, fonts, tilemaps, and video. public enum Graphics {} +/// The cached `playdate->graphics` C API table. var gfx: UnsafePointer { Playdate.graphicsAPI.unsafelyUnwrapped } extension Graphics { @@ -50,10 +51,12 @@ extension Graphics { gfx.pointee.setScreenClipRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height)) } + /// Clears the current clip rect. public static func clearClipRect() { gfx.pointee.clearClipRect.unsafelyUnwrapped() } + /// Sets the end cap style used by subsequent line drawing. public static func setLineCapStyle(_ style: LineCapStyle) { gfx.pointee.setLineCapStyle.unsafelyUnwrapped(style.cValue) } @@ -71,18 +74,21 @@ extension Graphics { gfx.pointee.pushContext.unsafelyUnwrapped(target?.pointer) } + /// Pops the top drawing context off the stack. public static func popContext() { gfx.pointee.popContext.unsafelyUnwrapped() } // MARK: - Shapes + /// Draws a line from (x1, y1) to (x2, y2) with the given stroke width. public static func drawLine(x1: Int, y1: Int, x2: Int, y2: Int, width: Int, color: Color) { color.withLCDColor { gfx.pointee.drawLine.unsafelyUnwrapped(Int32(x1), Int32(y1), Int32(x2), Int32(y2), Int32(width), $0) } } + /// Fills the triangle with vertices (x1, y1), (x2, y2), and (x3, y3). public static func fillTriangle(x1: Int, y1: Int, x2: Int, y2: Int, x3: Int, y3: Int, color: Color) { color.withLCDColor { gfx.pointee.fillTriangle.unsafelyUnwrapped(Int32(x1), Int32(y1), Int32(x2), Int32(y2), @@ -90,18 +96,22 @@ extension Graphics { } } + /// Draws the outline of a rectangle, stroked inside its frame. public static func drawRect(x: Int, y: Int, width: Int, height: Int, color: Color) { color.withLCDColor { gfx.pointee.drawRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height), $0) } } + /// Fills the rectangle with `color`. public static func fillRect(x: Int, y: Int, width: Int, height: Int, color: Color) { color.withLCDColor { gfx.pointee.fillRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height), $0) } } + /// Draws the outline of a rectangle with rounded corners, stroked with + /// `lineWidth`. public static func drawRoundRect(x: Int, y: Int, width: Int, height: Int, radius: Int, lineWidth: Int, color: Color) { color.withLCDColor { @@ -110,6 +120,7 @@ extension Graphics { } } + /// Fills a rectangle with rounded corners. public static func fillRoundRect(x: Int, y: Int, width: Int, height: Int, radius: Int, color: Color) { color.withLCDColor { gfx.pointee.fillRoundRect.unsafelyUnwrapped(Int32(x), Int32(y), Int32(width), Int32(height), @@ -127,6 +138,8 @@ extension Graphics { } } + /// Fills an ellipse inside the rect. If the angles differ, fills the + /// wedge from `startAngle` to `endAngle` (clockwise degrees, 0 at top). public static func fillEllipse(x: Int, y: Int, width: Int, height: Int, startAngle: Float = 0, endAngle: Float = 0, color: Color) { color.withLCDColor { @@ -202,6 +215,7 @@ extension Graphics { gfx.pointee.setTextTracking.unsafelyUnwrapped(Int32(tracking)) } + /// The extra space currently added between letters, in pixels. public static var textTracking: Int { Int(gfx.pointee.getTextTracking.unsafelyUnwrapped()) } diff --git a/Sources/PlaydateKit/Graphics/Structures/Bitmap.Data.swift b/Sources/PlaydateKit/Graphics/Structures/Bitmap.Data.swift index 9d99caf..7efc480 100644 --- a/Sources/PlaydateKit/Graphics/Structures/Bitmap.Data.swift +++ b/Sources/PlaydateKit/Graphics/Structures/Bitmap.Data.swift @@ -2,10 +2,17 @@ extension Graphics.Bitmap { /// The bitmap's dimensions, row stride, and raw pixel/mask storage. /// The pointers are owned by the bitmap. public struct Data { + /// The bitmap's width, in pixels. public let width: Int + /// The bitmap's height, in pixels. public let height: Int + /// The stride of one row of pixel data, in bytes. public let rowBytes: Int + /// The bitmap's mask data, or `nil` if it has no mask. One bit per + /// pixel; rows are `rowBytes` wide. public let mask: UnsafeMutablePointer? + /// The bitmap's pixel data. One bit per pixel; rows are `rowBytes` + /// wide. public let data: UnsafeMutablePointer? } } diff --git a/Sources/PlaydateKit/Graphics/Structures/Graphics.Rect.swift b/Sources/PlaydateKit/Graphics/Structures/Graphics.Rect.swift index 3d6d75b..1748ddf 100644 --- a/Sources/PlaydateKit/Graphics/Structures/Graphics.Rect.swift +++ b/Sources/PlaydateKit/Graphics/Structures/Graphics.Rect.swift @@ -9,6 +9,8 @@ extension Graphics { public var top: Int public var bottom: Int + /// Creates a rect from its edges. `right` and `bottom` are not + /// inclusive. public init(left: Int, right: Int, top: Int, bottom: Int) { self.left = left self.right = right @@ -16,6 +18,7 @@ extension Graphics { self.bottom = bottom } + /// Creates a rect from an origin and size. public init(x: Int, y: Int, width: Int, height: Int) { self.init(left: x, right: x + width, top: y, bottom: y + height) } @@ -30,6 +33,7 @@ extension Graphics { top: Int32(top), bottom: Int32(bottom)) } + /// Returns the rect offset by (dx, dy). public func translated(dx: Int, dy: Int) -> Rect { Rect(left: left + dx, right: right + dx, top: top + dy, bottom: bottom + dy) } diff --git a/Sources/PlaydateKit/Graphics/Structures/Pattern.swift b/Sources/PlaydateKit/Graphics/Structures/Pattern.swift index 84ddf49..32b6815 100644 --- a/Sources/PlaydateKit/Graphics/Structures/Pattern.swift +++ b/Sources/PlaydateKit/Graphics/Structures/Pattern.swift @@ -1,9 +1,12 @@ extension Graphics { /// An 8×8 two-color pattern: 8 rows of image data followed by 8 rows of mask. public struct Pattern: Sendable { + /// The pattern's 8 rows of image data followed by 8 rows of mask, + /// one byte per row. public var bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) + /// Creates a pattern from 8 rows of image data and 8 rows of mask. public init(bytes: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8)) { self.bytes = bytes diff --git a/Sources/PlaydateKit/JSON/Classes/Encoder.swift b/Sources/PlaydateKit/JSON/Classes/Encoder.swift index 2aef1e5..505bcbb 100644 --- a/Sources/PlaydateKit/JSON/Classes/Encoder.swift +++ b/Sources/PlaydateKit/JSON/Classes/Encoder.swift @@ -22,6 +22,7 @@ extension JSON { /// The JSON produced so far. public var json: String { output.text } + /// Starts a JSON array. public func startArray() { withUnsafeMutablePointer(to: &encoder) { $0.pointee.startArray.unsafelyUnwrapped($0) } } @@ -31,10 +32,12 @@ extension JSON { withUnsafeMutablePointer(to: &encoder) { $0.pointee.addArrayMember.unsafelyUnwrapped($0) } } + /// Ends the current array. public func endArray() { withUnsafeMutablePointer(to: &encoder) { $0.pointee.endArray.unsafelyUnwrapped($0) } } + /// Starts a JSON object. public func startTable() { withUnsafeMutablePointer(to: &encoder) { $0.pointee.startTable.unsafelyUnwrapped($0) } } @@ -48,28 +51,34 @@ extension JSON { } } + /// Ends the current object. public func endTable() { withUnsafeMutablePointer(to: &encoder) { $0.pointee.endTable.unsafelyUnwrapped($0) } } + /// Writes a `null` value. public func writeNull() { withUnsafeMutablePointer(to: &encoder) { $0.pointee.writeNull.unsafelyUnwrapped($0) } } + /// Writes a boolean value. public func writeBool(_ value: Bool) { withUnsafeMutablePointer(to: &encoder) { (value ? $0.pointee.writeTrue : $0.pointee.writeFalse).unsafelyUnwrapped($0) } } + /// Writes an integer value. public func writeInt(_ value: Int) { withUnsafeMutablePointer(to: &encoder) { $0.pointee.writeInt.unsafelyUnwrapped($0, Int32(value)) } } + /// Writes a floating-point value. public func writeDouble(_ value: Double) { withUnsafeMutablePointer(to: &encoder) { $0.pointee.writeDouble.unsafelyUnwrapped($0, value) } } + /// Writes a string value. public func writeString(_ value: String) { value.withPlaydateCString { cString in withUnsafeMutablePointer(to: &encoder) { diff --git a/Sources/PlaydateKit/JSON/Enumerations/Value.swift b/Sources/PlaydateKit/JSON/Enumerations/Value.swift index 7938136..10bed5e 100644 --- a/Sources/PlaydateKit/JSON/Enumerations/Value.swift +++ b/Sources/PlaydateKit/JSON/Enumerations/Value.swift @@ -1,12 +1,19 @@ extension JSON { /// A decoded JSON value. public indirect enum Value { + /// A JSON `null`. case null + /// A JSON `true` or `false`. case bool(Bool) + /// A JSON number without a fractional part. case int(Int) + /// A JSON number with a fractional part. case float(Float) + /// A JSON string. case string(String) + /// A JSON array. case array([Value]) + /// A JSON object. case table([String: Value]) } } diff --git a/Sources/PlaydateKit/JSON/JSON.swift b/Sources/PlaydateKit/JSON/JSON.swift index c24323a..9b3a42f 100644 --- a/Sources/PlaydateKit/JSON/JSON.swift +++ b/Sources/PlaydateKit/JSON/JSON.swift @@ -1,8 +1,13 @@ internal import CPlaydate +/// The cached `playdate->json` C API table. var jsonAPI: UnsafePointer { Playdate.jsonAPI.unsafelyUnwrapped } /// The JSON API: decoding to and encoding from a `Value` tree. +/// +/// The C decoder is callback-based; this wrapper drives it to build a +/// complete `Value` tree. The encoder is exposed both as a streaming +/// `Encoder` and as a one-shot `encode(_:)` of a `Value`. public enum JSON {} extension JSON { diff --git a/Sources/PlaydateKit/Lua/Enumerations/ClassValue.swift b/Sources/PlaydateKit/Lua/Enumerations/ClassValue.swift index 2db98f7..5c97985 100644 --- a/Sources/PlaydateKit/Lua/Enumerations/ClassValue.swift +++ b/Sources/PlaydateKit/Lua/Enumerations/ClassValue.swift @@ -1,8 +1,11 @@ extension Lua { /// A constant published on a registered class. public enum ClassValue { + /// An integer constant. case int(name: String, value: UInt32) + /// A floating-point constant. case float(name: String, value: Float) + /// A string constant. case string(name: String, value: String) } } diff --git a/Sources/PlaydateKit/Lua/Lua.swift b/Sources/PlaydateKit/Lua/Lua.swift index 7cdcc49..7f73b57 100644 --- a/Sources/PlaydateKit/Lua/Lua.swift +++ b/Sources/PlaydateKit/Lua/Lua.swift @@ -1,9 +1,14 @@ internal import CPlaydate +/// The cached `playdate->lua` C API table. var luaAPI: UnsafePointer { Playdate.luaAPI.unsafelyUnwrapped } /// The Lua bridge: registering C functions and classes, and exchanging /// values with Lua code. +/// +/// Lua callbacks are C function pointers without userdata, so functions +/// registered here must be `@convention(c)` (the `CFunction` typealias), +/// not capturing closures. public enum Lua {} extension Lua { @@ -168,26 +173,33 @@ extension Lua { // MARK: - Return values + /// Pushes nil onto the stack. public static func pushNil() { luaAPI.pointee.pushNil.unsafelyUnwrapped() } + /// Pushes a boolean onto the stack. public static func push(_ value: Bool) { luaAPI.pointee.pushBool.unsafelyUnwrapped(value ? 1 : 0) } + /// Pushes an integer onto the stack. public static func push(_ value: Int) { luaAPI.pointee.pushInt.unsafelyUnwrapped(Int32(value)) } + /// Pushes a float onto the stack. public static func push(_ value: Float) { luaAPI.pointee.pushFloat.unsafelyUnwrapped(value) } + /// Pushes a string onto the stack. public static func push(_ value: String) { value.withPlaydateCString { luaAPI.pointee.pushString.unsafelyUnwrapped($0) } } + /// Pushes raw bytes (which may contain embedded zeros) onto the stack + /// as a Lua string. public static func push(bytes: [UInt8]) { bytes.withUnsafeBytes { buffer in luaAPI.pointee.pushBytes.unsafelyUnwrapped( @@ -195,10 +207,12 @@ extension Lua { } } + /// Pushes a bitmap onto the stack. public static func push(_ bitmap: Graphics.Bitmap) { luaAPI.pointee.pushBitmap.unsafelyUnwrapped(bitmap.pointer) } + /// Pushes a sprite onto the stack. public static func push(_ sprite: Sprite) { luaAPI.pointee.pushSprite.unsafelyUnwrapped(sprite.pointer) } diff --git a/Sources/PlaydateKit/Lua/Structures/UDObject.swift b/Sources/PlaydateKit/Lua/Structures/UDObject.swift index afd73a7..0a3ec0b 100644 --- a/Sources/PlaydateKit/Lua/Structures/UDObject.swift +++ b/Sources/PlaydateKit/Lua/Structures/UDObject.swift @@ -11,6 +11,8 @@ extension Lua { UDObject(pointer: luaAPI.pointee.retainObject.unsafelyUnwrapped(pointer).unsafelyUnwrapped) } + /// Balances a `retain()`, allowing the object to be + /// garbage-collected again. public func release() { luaAPI.pointee.releaseObject.unsafelyUnwrapped(pointer) } diff --git a/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift b/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift index cf31564..1281d7c 100644 --- a/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift +++ b/Sources/PlaydateKit/Network/Classes/HTTPConnection.swift @@ -1,9 +1,14 @@ internal import CPlaydate +/// The cached `playdate->network->http` C API table. private var httpAPI: UnsafePointer { Playdate.httpAPI.unsafelyUnwrapped } extension Network { /// An HTTP connection to a server. Wraps `HTTPConnection`. + /// + /// The binding stores a back-reference to each wrapper in the + /// underlying object's userdata slot so callbacks can recover the + /// wrapper; the C userdata slot is therefore reserved by the binding. public final class HTTPConnection { let pointer: OpaquePointer diff --git a/Sources/PlaydateKit/Network/Classes/TCPConnection.swift b/Sources/PlaydateKit/Network/Classes/TCPConnection.swift index 999c1c5..21e122b 100644 --- a/Sources/PlaydateKit/Network/Classes/TCPConnection.swift +++ b/Sources/PlaydateKit/Network/Classes/TCPConnection.swift @@ -1,9 +1,14 @@ internal import CPlaydate +/// The cached `playdate->network->tcp` C API table. private var tcpAPI: UnsafePointer { Playdate.tcpAPI.unsafelyUnwrapped } extension Network { /// A TCP connection to a server. Wraps `TCPConnection`. + /// + /// The binding stores a back-reference to each wrapper in the + /// underlying object's userdata slot so callbacks can recover the + /// wrapper; the C userdata slot is therefore reserved by the binding. public final class TCPConnection { let pointer: OpaquePointer diff --git a/Sources/PlaydateKit/Network/Enumerations/NetError.swift b/Sources/PlaydateKit/Network/Enumerations/NetError.swift index 6307d90..f80137e 100644 --- a/Sources/PlaydateKit/Network/Enumerations/NetError.swift +++ b/Sources/PlaydateKit/Network/Enumerations/NetError.swift @@ -21,8 +21,11 @@ extension Network { case notConnectedToAP = -16 case notImplemented = -17 case connectionClosed = -18 + /// An error code not covered by `PDNetErr`. case unknown = 1 + /// Creates an error from the C code, or `.unknown` for + /// unrecognized codes. init(_ error: PDNetErr) { self = NetError(rawValue: Int32(error.rawValue)) ?? .unknown } diff --git a/Sources/PlaydateKit/Network/Network.swift b/Sources/PlaydateKit/Network/Network.swift index 2318e0c..7f67483 100644 --- a/Sources/PlaydateKit/Network/Network.swift +++ b/Sources/PlaydateKit/Network/Network.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->network` C API table. private var networkAPI: UnsafePointer { Playdate.networkAPI.unsafelyUnwrapped } /// The network API: wifi status, HTTP, and TCP. @@ -18,6 +19,7 @@ extension Network { error == NET_OK ? nil : NetError(error) } + /// The device's current wifi status. public static var status: WifiStatus { WifiStatus(rawValue: UInt32(networkAPI.pointee.getStatus.unsafelyUnwrapped().rawValue)) ?? .notConnected } diff --git a/Sources/PlaydateKit/Playdate/Enumerations/AccessReply.swift b/Sources/PlaydateKit/Playdate/Enumerations/AccessReply.swift index 7eca6bd..57b2778 100644 --- a/Sources/PlaydateKit/Playdate/Enumerations/AccessReply.swift +++ b/Sources/PlaydateKit/Playdate/Enumerations/AccessReply.swift @@ -1,6 +1,10 @@ /// The user's answer to a permission request (microphone, network). public enum AccessReply: UInt32, Sendable { + /// The user has not answered yet; the request's completion delivers + /// the answer later. case ask = 0 + /// The user has already denied access; the completion is not called. case deny = 1 + /// The user has already granted access; the completion is not called. case allow = 2 } diff --git a/Sources/PlaydateKit/Playdate/Enumerations/SystemEvent.swift b/Sources/PlaydateKit/Playdate/Enumerations/SystemEvent.swift index a74bcbd..71b689c 100644 --- a/Sources/PlaydateKit/Playdate/Enumerations/SystemEvent.swift +++ b/Sources/PlaydateKit/Playdate/Enumerations/SystemEvent.swift @@ -3,19 +3,34 @@ public import CPlaydate /// A Swift view of `PDSystemEvent` with the key code folded into the /// key events. public enum SystemEvent { + /// Sent once at startup, before the first update. case initialize + /// Sent when the Lua runtime is ready, for registering custom + /// functions and classes. case initializeLua + /// The device was locked. case lock + /// The device was unlocked. case unlock + /// The game was paused (e.g. the system menu opened). case pause + /// The game resumed after a pause. case resume + /// The game is about to be terminated. case terminate + /// A simulator key was pressed. case keyPressed(keyCode: UInt32) + /// A simulator key was released. case keyReleased(keyCode: UInt32) + /// The device is about to power down because the battery is low. case lowPower + /// A Mirror session started. case mirrorStarted + /// A Mirror session ended. case mirrorEnded + /// Creates an event from the C event and its argument, or `nil` for + /// events unknown to this binding. public init?(event: PDSystemEvent, argument: UInt32) { switch event { case kEventInit: self = .initialize diff --git a/Sources/PlaydateKit/Playdate/Playdate.swift b/Sources/PlaydateKit/Playdate/Playdate.swift index 4d18dd3..9d528a7 100644 --- a/Sources/PlaydateKit/Playdate/Playdate.swift +++ b/Sources/PlaydateKit/Playdate/Playdate.swift @@ -1,8 +1,12 @@ public import CPlaydate -/// The raw C API bootstrap. Everything else in this module (System, -/// Graphics, Sprite, Sound, ...) lives at the top level of the `PlaydateKit` -/// module and requires `initialize(with:)` to have been called first. +/// The raw C API bootstrap. +/// +/// The C API is delivered as a `PlaydateAPI` struct of function pointers +/// that the firmware hands to the game's `eventHandler` entry point. Call +/// `initialize(with:)` from that entry point before using any other API in +/// this module. Everything else (System, Graphics, Sprite, Sound, ...) +/// lives at the top level of the `PlaydateKit` module. public enum Playdate { /// The raw C API. Populated by `initialize(with:)`. /// diff --git a/Sources/PlaydateKit/Playdate/Structures/PlaydateError.swift b/Sources/PlaydateKit/Playdate/Structures/PlaydateError.swift index d1a720a..6692e22 100644 --- a/Sources/PlaydateKit/Playdate/Structures/PlaydateError.swift +++ b/Sources/PlaydateKit/Playdate/Structures/PlaydateError.swift @@ -1,11 +1,15 @@ /// An error reported by the Playdate OS. public struct PlaydateError: Swift.Error, Sendable { + /// The message reported by the OS, or a description of the failure. public let message: String + /// Creates an error with the given message. init(message: String) { self.message = message } + /// Creates an error by copying an OS-provided C string; a nil pointer + /// produces "unknown error". init(cString: UnsafePointer?) { self.init(message: String(playdateCString: cString) ?? "unknown error") } diff --git a/Sources/PlaydateKit/Scoreboards/Scoreboards.swift b/Sources/PlaydateKit/Scoreboards/Scoreboards.swift index 8ebdbdb..8026588 100644 --- a/Sources/PlaydateKit/Scoreboards/Scoreboards.swift +++ b/Sources/PlaydateKit/Scoreboards/Scoreboards.swift @@ -1,8 +1,13 @@ internal import CPlaydate +/// The cached `playdate->scoreboards` C API table. var scoreboardsAPI: UnsafePointer { Playdate.scoreboardsAPI.unsafelyUnwrapped } /// The scoreboards API for games with online leaderboards. +/// +/// The C callbacks carry no userdata, so one completion per operation kind +/// is tracked at a time; starting a second request of the same kind before +/// the first completes replaces the stored completion. public enum Scoreboards {} extension Scoreboards { diff --git a/Sources/PlaydateKit/Scoreboards/Structures/Board.swift b/Sources/PlaydateKit/Scoreboards/Structures/Board.swift index c1461b9..e602429 100644 --- a/Sources/PlaydateKit/Scoreboards/Structures/Board.swift +++ b/Sources/PlaydateKit/Scoreboards/Structures/Board.swift @@ -3,7 +3,9 @@ internal import CPlaydate extension Scoreboards { /// A board belonging to the game. public struct Board { + /// The board's identifier, used in the other scoreboard calls. public let boardID: String + /// The board's display name. public let name: String init(_ board: PDBoard) { diff --git a/Sources/PlaydateKit/Scoreboards/Structures/BoardsList.swift b/Sources/PlaydateKit/Scoreboards/Structures/BoardsList.swift index 3ac0515..46f7027 100644 --- a/Sources/PlaydateKit/Scoreboards/Structures/BoardsList.swift +++ b/Sources/PlaydateKit/Scoreboards/Structures/BoardsList.swift @@ -3,7 +3,9 @@ internal import CPlaydate extension Scoreboards { /// The game's boards. public struct BoardsList { + /// When the list was last updated, in seconds since the epoch. public let lastUpdated: UInt32 + /// The game's boards. public let boards: [Board] init(_ list: PDBoardsList) { diff --git a/Sources/PlaydateKit/Scoreboards/Structures/Score.swift b/Sources/PlaydateKit/Scoreboards/Structures/Score.swift index 43c6f06..cd2533d 100644 --- a/Sources/PlaydateKit/Scoreboards/Structures/Score.swift +++ b/Sources/PlaydateKit/Scoreboards/Structures/Score.swift @@ -3,9 +3,13 @@ internal import CPlaydate extension Scoreboards { /// A score on a board. public struct Score { + /// The score's position on the board, starting at 1. public let rank: UInt32 + /// The score's value. public let value: UInt32 + /// The name of the player who posted the score. public let player: String + /// The board the score belongs to, when known. public let boardID: String? init(_ score: PDScore) { diff --git a/Sources/PlaydateKit/Scoreboards/Structures/ScoresList.swift b/Sources/PlaydateKit/Scoreboards/Structures/ScoresList.swift index 0045e07..c957e84 100644 --- a/Sources/PlaydateKit/Scoreboards/Structures/ScoresList.swift +++ b/Sources/PlaydateKit/Scoreboards/Structures/ScoresList.swift @@ -3,10 +3,15 @@ internal import CPlaydate extension Scoreboards { /// The scores on a board. public struct ScoresList { + /// The board the scores belong to. public let boardID: String + /// When the list was last updated, in seconds since the epoch. public let lastUpdated: UInt32 + /// Whether the current player's score is included in the list. public let playerIncluded: Bool + /// The maximum number of scores the list can hold. public let limit: UInt32 + /// The scores, ordered by rank. public let scores: [Score] init(_ list: PDScoresList) { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/BitCrusher.swift b/Sources/PlaydateKit/Sound/Effect/Classes/BitCrusher.swift index bb14230..c3f29a1 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/BitCrusher.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/BitCrusher.swift @@ -28,6 +28,7 @@ extension Sound { BitCrusher.api.pointee.setDepth.unsafelyUnwrapped(pointer, depth) } + /// Modulates the crush depth. public var depthModulator: SignalValue? { get { SignalValue.wrap(BitCrusher.api.pointee.getDepthModulator.unsafelyUnwrapped(pointer)) } set { @@ -41,6 +42,7 @@ extension Sound { BitCrusher.api.pointee.setDownsampling.unsafelyUnwrapped(pointer, downsampling) } + /// Modulates the downsampling amount. public var downsamplingModulator: SignalValue? { get { SignalValue.wrap(BitCrusher.api.pointee.getDownsamplingModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/DelayLineTap.swift b/Sources/PlaydateKit/Sound/Effect/Classes/DelayLineTap.swift index b272e04..290ffc3 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/DelayLineTap.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/DelayLineTap.swift @@ -24,6 +24,7 @@ extension Sound { DelayLineTap.api.pointee.setTapDelay.unsafelyUnwrapped(pointer, Int32(frames)) } + /// Modulates the tap's delay. public var delayModulator: SignalValue? { get { SignalValue.wrap(DelayLineTap.api.pointee.getTapDelayModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/Effect.swift b/Sources/PlaydateKit/Sound/Effect/Classes/Effect.swift index 3df534d..a76e784 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/Effect.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/Effect.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->sound->effect` C API table. private var effectAPI: UnsafePointer { Playdate.effectAPI.unsafelyUnwrapped } extension Sound { @@ -48,6 +49,7 @@ extension Sound { effectAPI.pointee.setMix.unsafelyUnwrapped(pointer, level) } + /// Modulates the wet/dry mix. public var mixModulator: SignalValue? { get { SignalValue.wrap(effectAPI.pointee.getMixModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/OnePoleFilter.swift b/Sources/PlaydateKit/Sound/Effect/Classes/OnePoleFilter.swift index 11187b4..2228e7a 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/OnePoleFilter.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/OnePoleFilter.swift @@ -24,6 +24,7 @@ extension Sound { OnePoleFilter.api.pointee.setParameter.unsafelyUnwrapped(pointer, parameter) } + /// Modulates the filter's cutoff parameter. public var parameterModulator: SignalValue? { get { SignalValue.wrap(OnePoleFilter.api.pointee.getParameterModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/Overdrive.swift b/Sources/PlaydateKit/Sound/Effect/Classes/Overdrive.swift index c887166..3997c48 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/Overdrive.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/Overdrive.swift @@ -28,6 +28,7 @@ extension Sound { Overdrive.api.pointee.setLimit.unsafelyUnwrapped(pointer, limit) } + /// Modulates the clipping limit. public var limitModulator: SignalValue? { get { SignalValue.wrap(Overdrive.api.pointee.getLimitModulator.unsafelyUnwrapped(pointer)) } set { @@ -41,6 +42,7 @@ extension Sound { Overdrive.api.pointee.setOffset.unsafelyUnwrapped(pointer, offset) } + /// Modulates the DC offset. public var offsetModulator: SignalValue? { get { SignalValue.wrap(Overdrive.api.pointee.getOffsetModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/RingModulator.swift b/Sources/PlaydateKit/Sound/Effect/Classes/RingModulator.swift index dcaac75..39bf760 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/RingModulator.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/RingModulator.swift @@ -23,6 +23,7 @@ extension Sound { RingModulator.api.pointee.setFrequency.unsafelyUnwrapped(pointer, frequency) } + /// Modulates the modulation frequency. public var frequencyModulator: SignalValue? { get { SignalValue.wrap(RingModulator.api.pointee.getFrequencyModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Classes/TwoPoleFilter.swift b/Sources/PlaydateKit/Sound/Effect/Classes/TwoPoleFilter.swift index 4216e97..0f7dfca 100644 --- a/Sources/PlaydateKit/Sound/Effect/Classes/TwoPoleFilter.swift +++ b/Sources/PlaydateKit/Sound/Effect/Classes/TwoPoleFilter.swift @@ -29,6 +29,7 @@ extension Sound { TwoPoleFilter.api.pointee.setFrequency.unsafelyUnwrapped(pointer, frequency) } + /// Modulates the filter's frequency. public var frequencyModulator: SignalValue? { get { SignalValue.wrap(TwoPoleFilter.api.pointee.getFrequencyModulator.unsafelyUnwrapped(pointer)) } set { @@ -46,6 +47,7 @@ extension Sound { TwoPoleFilter.api.pointee.setResonance.unsafelyUnwrapped(pointer, resonance) } + /// Modulates the filter's resonance. public var resonanceModulator: SignalValue? { get { SignalValue.wrap(TwoPoleFilter.api.pointee.getResonanceModulator.unsafelyUnwrapped(pointer)) } set { diff --git a/Sources/PlaydateKit/Sound/Effect/Enumerations/TwoPoleFilter.Kind.swift b/Sources/PlaydateKit/Sound/Effect/Enumerations/TwoPoleFilter.Kind.swift index 96b4362..166b57a 100644 --- a/Sources/PlaydateKit/Sound/Effect/Enumerations/TwoPoleFilter.Kind.swift +++ b/Sources/PlaydateKit/Sound/Effect/Enumerations/TwoPoleFilter.Kind.swift @@ -1,11 +1,13 @@ internal import CPlaydate extension Sound.TwoPoleFilter { + /// The filter's response type. public enum Kind: UInt32, Sendable { case lowPass = 0 case highPass = 1 case bandPass = 2 case notch = 3 + /// A parametric EQ filter. case peq = 4 case lowShelf = 5 case highShelf = 6 diff --git a/Sources/PlaydateKit/Sound/Enumerations/Format.swift b/Sources/PlaydateKit/Sound/Enumerations/Format.swift index 787dcb3..50b3d5e 100644 --- a/Sources/PlaydateKit/Sound/Enumerations/Format.swift +++ b/Sources/PlaydateKit/Sound/Enumerations/Format.swift @@ -13,8 +13,11 @@ extension Sound { init(_ format: SoundFormat) { self = Format(rawValue: UInt32(format.rawValue)) ?? .mono16bit } var cValue: SoundFormat { SoundFormat(SoundFormat.RawValue(rawValue)) } + /// Whether the format has two channels. public var isStereo: Bool { rawValue & 1 != 0 } + /// Whether samples are 16-bit (rather than 8-bit or ADPCM). public var is16bit: Bool { rawValue >= 2 && rawValue < 4 } + /// The size of one sample frame, in bytes. public var bytesPerFrame: Int { Int(SoundFormat_bytesPerFrame(cValue)) } } } diff --git a/Sources/PlaydateKit/Sound/Enumerations/MicSource.swift b/Sources/PlaydateKit/Sound/Enumerations/MicSource.swift index b6c03c9..55e6e94 100644 --- a/Sources/PlaydateKit/Sound/Enumerations/MicSource.swift +++ b/Sources/PlaydateKit/Sound/Enumerations/MicSource.swift @@ -1,8 +1,12 @@ extension Sound { /// The microphone used when recording. public enum MicSource: UInt32, Sendable { + /// Use the headset microphone if one is connected, otherwise the + /// built-in microphone. case autodetect = 0 + /// Always use the built-in microphone. case internalMic = 1 + /// Always use the headset microphone. case headset = 2 } } diff --git a/Sources/PlaydateKit/Sound/Signal/Classes/ControlSignal.swift b/Sources/PlaydateKit/Sound/Signal/Classes/ControlSignal.swift index 27b58b1..8d7c077 100644 --- a/Sources/PlaydateKit/Sound/Signal/Classes/ControlSignal.swift +++ b/Sources/PlaydateKit/Sound/Signal/Classes/ControlSignal.swift @@ -21,6 +21,7 @@ extension Sound { } } + /// Removes all events from the signal's timeline. public func clearEvents() { ControlSignal.api.pointee.clearEvents.unsafelyUnwrapped(pointer) } @@ -32,6 +33,7 @@ extension Sound { interpolate ? 1 : 0) } + /// Removes the event at `step`, if any. public func removeEvent(step: Int) { ControlSignal.api.pointee.removeEvent.unsafelyUnwrapped(pointer, Int32(step)) } diff --git a/Sources/PlaydateKit/Sound/Signal/Classes/Envelope.swift b/Sources/PlaydateKit/Sound/Signal/Classes/Envelope.swift index 09be698..53d0bf3 100644 --- a/Sources/PlaydateKit/Sound/Signal/Classes/Envelope.swift +++ b/Sources/PlaydateKit/Sound/Signal/Classes/Envelope.swift @@ -22,18 +22,22 @@ extension Sound { } } + /// The attack time, in seconds. public func setAttack(_ attack: Float) { Envelope.api.pointee.setAttack.unsafelyUnwrapped(pointer, attack) } + /// The decay time, in seconds. public func setDecay(_ decay: Float) { Envelope.api.pointee.setDecay.unsafelyUnwrapped(pointer, decay) } + /// The sustain level, 0...1. public func setSustain(_ sustain: Float) { Envelope.api.pointee.setSustain.unsafelyUnwrapped(pointer, sustain) } + /// The release time, in seconds. public func setRelease(_ release: Float) { Envelope.api.pointee.setRelease.unsafelyUnwrapped(pointer, release) } @@ -66,6 +70,7 @@ extension Sound { Envelope.api.pointee.setRateScaling.unsafelyUnwrapped(pointer, scaling, start, end) } + /// The envelope's current value. public var value: Float { Envelope.api.pointee.getValue.unsafelyUnwrapped(pointer) } diff --git a/Sources/PlaydateKit/Sound/Signal/Classes/LFO.swift b/Sources/PlaydateKit/Sound/Signal/Classes/LFO.swift index eaeddba..61f170e 100644 --- a/Sources/PlaydateKit/Sound/Signal/Classes/LFO.swift +++ b/Sources/PlaydateKit/Sound/Signal/Classes/LFO.swift @@ -89,6 +89,7 @@ extension Sound { LFO.api.pointee.setRandomSeed.unsafelyUnwrapped(pointer, seed) } + /// The LFO's current value. public var value: Float { LFO.api.pointee.getValue.unsafelyUnwrapped(pointer) } diff --git a/Sources/PlaydateKit/Sound/Sound.swift b/Sources/PlaydateKit/Sound/Sound.swift index 1dc274b..658af3d 100644 --- a/Sources/PlaydateKit/Sound/Sound.swift +++ b/Sources/PlaydateKit/Sound/Sound.swift @@ -1,5 +1,6 @@ internal import CPlaydate +/// The cached `playdate->sound` C API table. var snd: UnsafePointer { Playdate.soundAPI.unsafelyUnwrapped } /// The sound API: channels, players, synths, sequences, and effects. diff --git a/Sources/PlaydateKit/Sound/Source/Classes/FilePlayer.swift b/Sources/PlaydateKit/Sound/Source/Classes/FilePlayer.swift index 5bc36a0..b41dd42 100644 --- a/Sources/PlaydateKit/Sound/Source/Classes/FilePlayer.swift +++ b/Sources/PlaydateKit/Sound/Source/Classes/FilePlayer.swift @@ -52,10 +52,12 @@ extension Sound { FilePlayer.api.pointee.play.unsafelyUnwrapped(pointer, Int32(repeatCount)) != 0 } + /// Pauses playback. public func pause() { FilePlayer.api.pointee.pause.unsafelyUnwrapped(pointer) } + /// Stops playback. public func stop() { FilePlayer.api.pointee.stop.unsafelyUnwrapped(pointer) } diff --git a/Sources/PlaydateKit/Sound/Source/Classes/SamplePlayer.swift b/Sources/PlaydateKit/Sound/Source/Classes/SamplePlayer.swift index 2a4a758..305a3e4 100644 --- a/Sources/PlaydateKit/Sound/Source/Classes/SamplePlayer.swift +++ b/Sources/PlaydateKit/Sound/Source/Classes/SamplePlayer.swift @@ -46,10 +46,12 @@ extension Sound { SamplePlayer.api.pointee.play.unsafelyUnwrapped(pointer, Int32(repeatCount), rate) != 0 } + /// Stops playback. public func stop() { SamplePlayer.api.pointee.stop.unsafelyUnwrapped(pointer) } + /// Pauses or resumes playback. public func setPaused(_ paused: Bool) { SamplePlayer.api.pointee.setPaused.unsafelyUnwrapped(pointer, paused ? 1 : 0) } diff --git a/Sources/PlaydateKit/Sound/Synth/Classes/Instrument.swift b/Sources/PlaydateKit/Sound/Synth/Classes/Instrument.swift index e63ee14..b940bcc 100644 --- a/Sources/PlaydateKit/Sound/Synth/Classes/Instrument.swift +++ b/Sources/PlaydateKit/Sound/Synth/Classes/Instrument.swift @@ -72,10 +72,13 @@ extension Sound { Instrument.api.pointee.setPitchBend.unsafelyUnwrapped(pointer, bend) } + /// The range of `setPitchBend(_:)`, in half-steps. public func setPitchBendRange(halfSteps: Float) { Instrument.api.pointee.setPitchBendRange.unsafelyUnwrapped(pointer, halfSteps) } + /// Transposes played notes by `halfSteps` (fractional values + /// allowed). public func setTranspose(halfSteps: Float) { Instrument.api.pointee.setTranspose.unsafelyUnwrapped(pointer, halfSteps) } @@ -85,20 +88,24 @@ extension Sound { Instrument.api.pointee.noteOff.unsafelyUnwrapped(pointer, note, when) } + /// Releases every playing voice at time `when` (0 = now). public func allNotesOff(when: UInt32 = 0) { Instrument.api.pointee.allNotesOff.unsafelyUnwrapped(pointer, when) } + /// Sets the volume of the left and right channels, 0...1. public func setVolume(left: Float, right: Float) { Instrument.api.pointee.setVolume.unsafelyUnwrapped(pointer, left, right) } + /// The volume of the left and right channels. public var volume: (left: Float, right: Float) { var left: Float = 0, right: Float = 0 Instrument.api.pointee.getVolume.unsafelyUnwrapped(pointer, &left, &right) return (left, right) } + /// The number of voices currently playing. public var activeVoiceCount: Int { Int(Instrument.api.pointee.activeVoiceCount.unsafelyUnwrapped(pointer)) } diff --git a/Sources/PlaydateKit/Sound/Synth/Classes/Sequence.swift b/Sources/PlaydateKit/Sound/Synth/Classes/Sequence.swift index f29e57f..b21779d 100644 --- a/Sources/PlaydateKit/Sound/Synth/Classes/Sequence.swift +++ b/Sources/PlaydateKit/Sound/Synth/Classes/Sequence.swift @@ -47,10 +47,12 @@ extension Sound { } } + /// Stops playback. public func stop() { Sequence.api.pointee.stop.unsafelyUnwrapped(pointer) } + /// Whether the sequence is playing. public var isPlaying: Bool { Sequence.api.pointee.isPlaying.unsafelyUnwrapped(pointer) != 0 } diff --git a/Sources/PlaydateKit/Sound/Synth/Classes/SequenceTrack.swift b/Sources/PlaydateKit/Sound/Synth/Classes/SequenceTrack.swift index 07826e0..b08ff88 100644 --- a/Sources/PlaydateKit/Sound/Synth/Classes/SequenceTrack.swift +++ b/Sources/PlaydateKit/Sound/Synth/Classes/SequenceTrack.swift @@ -45,10 +45,12 @@ extension Sound { SequenceTrack.api.pointee.addNoteEvent.unsafelyUnwrapped(pointer, step, length, note, velocity) } + /// Removes the note at `step`, if any. public func removeNote(step: UInt32, note: MIDINote) { SequenceTrack.api.pointee.removeNoteEvent.unsafelyUnwrapped(pointer, step, note) } + /// Removes all notes from the track. public func clearNotes() { SequenceTrack.api.pointee.clearNotes.unsafelyUnwrapped(pointer) } @@ -94,6 +96,7 @@ extension Sound { return ControlSignal(pointer: signal, isOwned: false) } + /// Removes all control signal events from the track. public func clearControlEvents() { SequenceTrack.api.pointee.clearControlEvents.unsafelyUnwrapped(pointer) } @@ -103,10 +106,12 @@ extension Sound { Int(SequenceTrack.api.pointee.getPolyphony.unsafelyUnwrapped(pointer)) } + /// The number of notes currently playing. public var activeVoiceCount: Int { Int(SequenceTrack.api.pointee.activeVoiceCount.unsafelyUnwrapped(pointer)) } + /// Mutes or unmutes the track. public func setMuted(_ muted: Bool) { SequenceTrack.api.pointee.setMuted.unsafelyUnwrapped(pointer, muted ? 1 : 0) } diff --git a/Sources/PlaydateKit/Sound/Synth/Classes/Synth.swift b/Sources/PlaydateKit/Sound/Synth/Classes/Synth.swift index 2c3ef26..5b3b6d8 100644 --- a/Sources/PlaydateKit/Sound/Synth/Classes/Synth.swift +++ b/Sources/PlaydateKit/Sound/Synth/Classes/Synth.swift @@ -108,18 +108,22 @@ extension Sound { // MARK: Envelope + /// The envelope's attack time, in seconds. public func setAttackTime(_ attack: Float) { Synth.api.pointee.setAttackTime.unsafelyUnwrapped(pointer, attack) } + /// The envelope's decay time, in seconds. public func setDecayTime(_ decay: Float) { Synth.api.pointee.setDecayTime.unsafelyUnwrapped(pointer, decay) } + /// The envelope's sustain level, 0...1. public func setSustainLevel(_ sustain: Float) { Synth.api.pointee.setSustainLevel.unsafelyUnwrapped(pointer, sustain) } + /// The envelope's release time, in seconds. public func setReleaseTime(_ release: Float) { Synth.api.pointee.setReleaseTime.unsafelyUnwrapped(pointer, release) } @@ -142,6 +146,7 @@ extension Sound { Synth.api.pointee.setTranspose.unsafelyUnwrapped(pointer, halfSteps) } + /// Modulates the synth's frequency. public var frequencyModulator: SignalValue? { get { SignalValue.wrap(Synth.api.pointee.getFrequencyModulator.unsafelyUnwrapped(pointer)) } set { @@ -150,6 +155,7 @@ extension Sound { } } + /// Modulates the synth's amplitude. public var amplitudeModulator: SignalValue? { get { SignalValue.wrap(Synth.api.pointee.getAmplitudeModulator.unsafelyUnwrapped(pointer)) } set { @@ -170,12 +176,14 @@ extension Sound { Synth.api.pointee.setParameter.unsafelyUnwrapped(pointer, Int32(parameter), value) != 0 } + /// Modulates a generator parameter. public func setParameterModulator(_ parameter: Int, _ modulator: SignalValue?) { retain(modulator) Synth.api.pointee.setParameterModulator.unsafelyUnwrapped(pointer, Int32(parameter), modulator?.pointer) } + /// The modulator installed on a generator parameter, if any. public func parameterModulator(_ parameter: Int) -> SignalValue? { SignalValue.wrap(Synth.api.pointee.getParameterModulator.unsafelyUnwrapped(pointer, Int32(parameter))) } diff --git a/Sources/PlaydateKit/Sound/Synth/Enumerations/Synth.Waveform.swift b/Sources/PlaydateKit/Sound/Synth/Enumerations/Synth.Waveform.swift index e78929f..7de492a 100644 --- a/Sources/PlaydateKit/Sound/Synth/Enumerations/Synth.Waveform.swift +++ b/Sources/PlaydateKit/Sound/Synth/Enumerations/Synth.Waveform.swift @@ -8,8 +8,11 @@ extension Sound.Synth { case sine = 2 case noise = 3 case sawtooth = 4 + /// A Pocket Operator-style phase-distortion waveform. case poPhase = 5 + /// A Pocket Operator-style digital waveform. case poDigital = 6 + /// A Pocket Operator-style VOSIM (voice simulation) waveform. case poVosim = 7 var cValue: SoundWaveform { SoundWaveform(SoundWaveform.RawValue(rawValue)) } diff --git a/Sources/PlaydateKit/Sprite/Classes/Sprite.swift b/Sources/PlaydateKit/Sprite/Classes/Sprite.swift index aad29cd..2089237 100644 --- a/Sources/PlaydateKit/Sprite/Classes/Sprite.swift +++ b/Sources/PlaydateKit/Sprite/Classes/Sprite.swift @@ -1,10 +1,16 @@ internal import CPlaydate +/// The cached `playdate->sprite` C API table. private var spriteAPI: UnsafePointer { Playdate.spriteAPI.unsafelyUnwrapped } /// A sprite: a drawable object with position, z-order, and collision /// support. Wraps `LCDSprite`. Static members wrap the global sprite /// system functions. +/// +/// The binding stores a back-reference to each `Sprite` wrapper in the +/// underlying `LCDSprite`'s userdata slot, so callbacks and queries can +/// recover the wrapper. Do not mix these wrappers with C code that sets +/// its own sprite userdata; use `userdata` for per-sprite storage instead. public final class Sprite { let pointer: OpaquePointer let isOwned: Bool @@ -269,11 +275,13 @@ public final class Sprite { set { spriteAPI.pointee.setUpdatesEnabled.unsafelyUnwrapped(pointer, newValue ? 1 : 0) } } + /// Whether the sprite participates in collisions. public var collisionsEnabled: Bool { get { spriteAPI.pointee.collisionsEnabled.unsafelyUnwrapped(pointer) != 0 } set { spriteAPI.pointee.setCollisionsEnabled.unsafelyUnwrapped(pointer, newValue ? 1 : 0) } } + /// Whether the sprite is drawn. public var isVisible: Bool { get { spriteAPI.pointee.isVisible.unsafelyUnwrapped(pointer) != 0 } set { spriteAPI.pointee.setVisible.unsafelyUnwrapped(pointer, newValue ? 1 : 0) } diff --git a/Sources/PlaydateKit/Sprite/Enumerations/CollisionResponse.swift b/Sources/PlaydateKit/Sprite/Enumerations/CollisionResponse.swift index a0ec242..bb2d95d 100644 --- a/Sources/PlaydateKit/Sprite/Enumerations/CollisionResponse.swift +++ b/Sources/PlaydateKit/Sprite/Enumerations/CollisionResponse.swift @@ -3,9 +3,13 @@ internal import CPlaydate extension Sprite { /// How a sprite reacts when a collision occurs. public enum CollisionResponse: UInt32, Sendable { + /// The sprite slides along the edge of the other sprite. case slide = 0 + /// The sprite stops at the point of collision. case freeze = 1 + /// The sprite passes through, still reporting the collision. case overlap = 2 + /// The sprite bounces off the other sprite. case bounce = 3 init(_ response: SpriteCollisionResponseType) { diff --git a/Sources/PlaydateKit/Sprite/Structures/Rect.swift b/Sources/PlaydateKit/Sprite/Structures/Rect.swift index d01f8b0..deaa8b0 100644 --- a/Sources/PlaydateKit/Sprite/Structures/Rect.swift +++ b/Sources/PlaydateKit/Sprite/Structures/Rect.swift @@ -7,6 +7,7 @@ public struct Rect: Sendable { public var width: Float public var height: Float + /// Creates a rect from an origin and size. public init(x: Float, y: Float, width: Float, height: Float) { self.x = x self.y = y diff --git a/Sources/PlaydateKit/Support.swift b/Sources/PlaydateKit/Support.swift index 3e074a5..7098972 100644 --- a/Sources/PlaydateKit/Support.swift +++ b/Sources/PlaydateKit/Support.swift @@ -1,5 +1,10 @@ internal import CPlaydate +/// Internal C-string helpers shared by the wrappers. +/// +/// The conversions are implemented manually (rather than with +/// `String(cString:)` / `withCString`) so the module stays within the +/// Embedded Swift subset used for device builds. extension String { /// Creates a string by copying a null-terminated UTF-8 C string. init(playdateCString pointer: UnsafePointer) { diff --git a/Sources/PlaydateKit/System/Classes/MenuItem.swift b/Sources/PlaydateKit/System/Classes/MenuItem.swift index 98a3113..48dcbe0 100644 --- a/Sources/PlaydateKit/System/Classes/MenuItem.swift +++ b/Sources/PlaydateKit/System/Classes/MenuItem.swift @@ -8,6 +8,8 @@ extension System { /// Retains C strings passed to the OS for option titles. private var retainedOptionTitles: [UnsafeMutablePointer] = [] + /// Wraps the C menu item; fails (and frees the retained titles) if + /// `pointer` is nil. init?(pointer: OpaquePointer?, retainedOptionTitles: [UnsafeMutablePointer] = [], onSelect: @escaping (MenuItem) -> Void) { diff --git a/Sources/PlaydateKit/System/Structures/Info.swift b/Sources/PlaydateKit/System/Structures/Info.swift index ce0aa1b..a5ea6fc 100644 --- a/Sources/PlaydateKit/System/Structures/Info.swift +++ b/Sources/PlaydateKit/System/Structures/Info.swift @@ -1,8 +1,11 @@ extension System { /// OS, language, and pdx version information, mirroring `PDInfo`. public struct Info: Sendable { + /// The Playdate OS version. public let osVersion: UInt32 + /// The system language. public let language: Language + /// The version of the game's pdx. public let pdxVersion: UInt32 } } diff --git a/Sources/PlaydateKit/System/Structures/PowerStatus.swift b/Sources/PlaydateKit/System/Structures/PowerStatus.swift index 1ddb178..5abf7e9 100644 --- a/Sources/PlaydateKit/System/Structures/PowerStatus.swift +++ b/Sources/PlaydateKit/System/Structures/PowerStatus.swift @@ -6,8 +6,11 @@ extension System { public let rawValue: UInt32 public init(rawValue: UInt32) { self.rawValue = rawValue } + /// The battery is charging. public static let charging = PowerStatus(rawValue: UInt32(kPDPowerStatusCharging.rawValue)) + /// Power is supplied over USB. public static let usb = PowerStatus(rawValue: UInt32(kPDPowerStatusUsb.rawValue)) + /// Power is supplied through the accessory screw terminals. public static let screws = PowerStatus(rawValue: UInt32(kPDPowerStatusScrews.rawValue)) } } diff --git a/Sources/PlaydateKit/System/System.swift b/Sources/PlaydateKit/System/System.swift index a3d9989..3b687ce 100644 --- a/Sources/PlaydateKit/System/System.swift +++ b/Sources/PlaydateKit/System/System.swift @@ -4,6 +4,7 @@ internal import CPlaydate public enum System {} extension System { + /// The cached `playdate->system` C API table. private static var api: UnsafePointer { Playdate.systemAPI.unsafelyUnwrapped } // MARK: - Memory @@ -34,6 +35,7 @@ extension System { // MARK: - Time + /// The system language setting. public static var language: Language { Language(api.pointee.getLanguage.unsafelyUnwrapped()) } /// Milliseconds since the game launched. Wraps around after about 49 days. @@ -53,21 +55,25 @@ extension System { /// High-resolution timer value, in seconds. public static var elapsedTime: Float { api.pointee.getElapsedTime.unsafelyUnwrapped() } + /// Resets the high-resolution timer to zero. public static func resetElapsedTime() { api.pointee.resetElapsedTime.unsafelyUnwrapped() } /// Offset from UTC of the user-set timezone, in seconds. public static var timezoneOffset: Int32 { api.pointee.getTimezoneOffset.unsafelyUnwrapped() } + /// Whether the user prefers 24-hour time display. public static var shouldDisplay24HourTime: Bool { api.pointee.shouldDisplay24HourTime.unsafelyUnwrapped() != 0 } + /// Converts seconds since the 2000-01-01 epoch to a calendar date. public static func convertEpochToDateTime(_ epoch: UInt32) -> DateTime { var dateTime = PDDateTime() api.pointee.convertEpochToDateTime.unsafelyUnwrapped(epoch, &dateTime) return DateTime(dateTime) } + /// Converts a calendar date to seconds since the 2000-01-01 epoch. public static func convertDateTimeToEpoch(_ dateTime: DateTime) -> UInt32 { var cValue = dateTime.cValue return api.pointee.convertDateTimeToEpoch.unsafelyUnwrapped(&cValue) @@ -135,6 +141,8 @@ extension System { nonisolated(unsafe) private static var buttonCallback: ((Buttons, Bool, UInt32) -> Int32)? + /// Enables the given peripherals (e.g. the accelerometer), disabling + /// the rest. public static func setPeripheralsEnabled(_ peripherals: Peripherals) { api.pointee.setPeripheralsEnabled.unsafelyUnwrapped(PDPeripherals(PDPeripherals.RawValue(peripherals.rawValue))) } @@ -153,6 +161,7 @@ extension System { /// The crank position in degrees; 0 points along the +Y axis. public static var crankAngle: Float { api.pointee.getCrankAngle.unsafelyUnwrapped() } + /// Whether the crank is folded into the device. public static var isCrankDocked: Bool { api.pointee.isCrankDocked.unsafelyUnwrapped() != 0 } /// Disables or enables the crank dock/undock sounds. Returns the previous setting. @@ -164,6 +173,7 @@ extension System { /// Whether the user has the "flipped" system setting enabled. public static var isFlipped: Bool { api.pointee.getFlipped.unsafelyUnwrapped() != 0 } + /// Disables or re-enables the automatic screen lock. public static func setAutoLockDisabled(_ disabled: Bool) { api.pointee.setAutoLockDisabled.unsafelyUnwrapped(disabled ? 1 : 0) } @@ -272,6 +282,7 @@ extension System { /// Battery charge, 0...100. public static var batteryPercentage: Float { api.pointee.getBatteryPercentage.unsafelyUnwrapped() } + /// The battery voltage, in volts. public static var batteryVoltage: Float { api.pointee.getBatteryVoltage.unsafelyUnwrapped() } /// Flushes the CPU instruction cache after loading code at runtime. @@ -323,6 +334,7 @@ extension System { /// The system volume, 0...1. public static var volume: Float { api.pointee.getVolume.unsafelyUnwrapped() } + /// The battery and power supply state. public static var powerStatus: PowerStatus { PowerStatus(rawValue: UInt32(api.pointee.getPowerStatus.unsafelyUnwrapped().rawValue)) }