Skip to content

Commit 3aa05f5

Browse files
committed
Add GameController
1 parent 304c095 commit 3aa05f5

1 file changed

Lines changed: 164 additions & 0 deletions

File tree

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
//
2+
// GameController.swift
3+
// SwiftAndroid
4+
//
5+
// Created by Alsey Coleman Miller on 2/27/26.
6+
//
7+
8+
#if os(Android)
9+
import Android
10+
import AndroidNDK
11+
#endif
12+
13+
/// Swift wrapper for the Android Game Controller (Paddleboat) C API.
14+
@MainActor
15+
public struct GameController: ~Copyable {
16+
17+
let environment: JNIEnvironment
18+
19+
public init(context: jobject, environment: JNIEnvironment) {
20+
let result = Paddleboat_init(environment, context)
21+
guard result == 0 else {
22+
throw Error(rawValue: result) ?? .notInitialized
23+
}
24+
guard Paddleboat_isInitialized() else {
25+
throw .notInitialized
26+
}
27+
self.environment = environment
28+
}
29+
30+
deinit {
31+
Paddleboat_destroy(environment)
32+
}
33+
34+
public static func update() {
35+
Paddleboat_update(environment)
36+
}
37+
38+
// MARK: - Back button
39+
public static func setBackButtonConsumed(_ consume: Bool) {
40+
Paddleboat_setBackButtonConsumed(consume)
41+
}
42+
43+
public static func isBackButtonConsumed() -> Bool {
44+
Paddleboat_getBackButtonConsumed()
45+
}
46+
47+
// MARK: - Controller Info / Data
48+
public static func getControllerStatus(index: Int32) -> ControllerStatus {
49+
ControllerStatus(rawValue: Paddleboat_getControllerStatus(index)) ?? .inactive
50+
}
51+
52+
public static func getControllerName(index: Int32, bufferSize: Int = 128) -> (ErrorCode, String) {
53+
var buffer = [CChar](repeating: 0, count: bufferSize)
54+
let err = Paddleboat_getControllerName(index, buffer.count, &buffer)
55+
let code = ErrorCode(rawValue: err) ?? .noError
56+
let name = String(cString: buffer)
57+
return (code, name)
58+
}
59+
60+
// MARK: - Lights / Vibration
61+
@discardableResult
62+
public static func setControllerLight(index: Int32, type: LightType, data: UInt32) -> ErrorCode {
63+
guard let env = currentJNIEnv() else { return .notInitialized }
64+
let err = Paddleboat_setControllerLight(index, type.rawValue, data, env)
65+
return ErrorCode(rawValue: err) ?? .noError
66+
}
67+
68+
@discardableResult
69+
public static func setControllerVibration(index: Int32, vibration: VibrationData) -> ErrorCode {
70+
guard let env = currentJNIEnv() else { return .notInitialized }
71+
var cData = Paddleboat_Vibration_Data(duration_ms: vibration.durationMs,
72+
left_motor_intensity: vibration.intensityLeft,
73+
right_motor_intensity: vibration.intensityRight)
74+
let err = Paddleboat_setControllerVibrationData(index, &cData, env)
75+
return ErrorCode(rawValue: err) ?? .noError
76+
}
77+
78+
// MARK: - Motion
79+
public static func getIntegratedMotionSensorFlags() -> IntegratedMotionSensorFlags {
80+
IntegratedMotionSensorFlags(rawValue: Paddleboat_getIntegratedMotionSensorFlags())
81+
}
82+
}
83+
84+
// MARK: - Supporting Types
85+
86+
public extension GameController {
87+
88+
// MARK: - Version / Constants
89+
public static var maxControllers: Int32 { 8 }
90+
91+
// MARK: - Error
92+
public enum Error: Int32, Swift.Error {
93+
case alreadyInitialized = -2000
94+
case notInitialized = -2001
95+
case invalidParameter = -2002
96+
case invalidControllerIndex = -2003
97+
case noController = -2004
98+
case featureNotSupported = -2005
99+
case fileIO = -2006
100+
case incompatibleMappingData = -2007
101+
case invalidMappingData = -2008
102+
case noMouse = -2009
103+
case initGCMFailure = -2010
104+
}
105+
106+
public enum Status: Int32 {
107+
case inactive = 0
108+
case active = 1
109+
case justConnected = 2
110+
case justDisconnected = 3
111+
}
112+
113+
public struct Buttons: OptionSet {
114+
public let rawValue: UInt32
115+
public init(rawValue: UInt32) { self.rawValue = rawValue }
116+
public static let a = Buttons(rawValue: 1 << 0)
117+
public static let b = Buttons(rawValue: 1 << 1)
118+
public static let x = Buttons(rawValue: 1 << 2)
119+
public static let y = Buttons(rawValue: 1 << 3)
120+
public static let l1 = Buttons(rawValue: 1 << 4)
121+
public static let r1 = Buttons(rawValue: 1 << 5)
122+
public static let l2 = Buttons(rawValue: 1 << 6)
123+
public static let r2 = Buttons(rawValue: 1 << 7)
124+
public static let l3 = Buttons(rawValue: 1 << 8)
125+
public static let r3 = Buttons(rawValue: 1 << 9)
126+
public static let dpadUp = Buttons(rawValue: 1 << 10)
127+
public static let dpadDown = Buttons(rawValue: 1 << 11)
128+
public static let dpadLeft = Buttons(rawValue: 1 << 12)
129+
public static let dpadRight = Buttons(rawValue: 1 << 13)
130+
public static let start = Buttons(rawValue: 1 << 14)
131+
public static let select = Buttons(rawValue: 1 << 15)
132+
public static let system = Buttons(rawValue: 1 << 16)
133+
public static let touchpad = Buttons(rawValue: 1 << 17)
134+
public static let aux1 = Buttons(rawValue: 1 << 18)
135+
public static let aux2 = Buttons(rawValue: 1 << 19)
136+
public static let aux3 = Buttons(rawValue: 1 << 20)
137+
public static let aux4 = Buttons(rawValue: 1 << 21)
138+
}
139+
140+
public enum LightType: Int32 {
141+
case playerNumber = 0
142+
case rgb = 1
143+
}
144+
145+
public struct IntegratedMotionSensorFlags: OptionSet {
146+
public let rawValue: UInt32
147+
public init(rawValue: UInt32) { self.rawValue = rawValue }
148+
public static let none = IntegratedMotionSensorFlags([])
149+
public static let accelerometer = IntegratedMotionSensorFlags(rawValue: 0x00000001)
150+
public static let gyroscope = IntegratedMotionSensorFlags(rawValue: 0x00000002)
151+
public static let indexFlag = IntegratedMotionSensorFlags(rawValue: 0x40000000)
152+
}
153+
154+
public struct VibrationData {
155+
public var durationMs: Int32
156+
public var intensityLeft: Float
157+
public var intensityRight: Float
158+
public init(durationMs: Int32, intensityLeft: Float, intensityRight: Float) {
159+
self.durationMs = durationMs
160+
self.intensityLeft = intensityLeft
161+
self.intensityRight = intensityRight
162+
}
163+
}
164+
}

0 commit comments

Comments
 (0)