├── .gitignore ├── JoyConSwift.podspec ├── JoyConSwift.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcshareddata │ └── xcschemes │ └── JoyConSwiftStatic.xcscheme ├── LICENSE ├── README.md ├── Sample ├── JoyConSwiftSample.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── JoyConSwiftSample.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── JoyConSwiftSample │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ └── Main.storyboard │ ├── Info.plist │ ├── JoyConSwiftSample.entitlements │ ├── ViewController+NSTableViewDelegate.swift │ └── ViewController.swift ├── Source ├── Controller.swift ├── HomeLEDPattern.swift ├── Info.plist ├── JoyCon.swift ├── JoyConManager.swift ├── JoyConSwift.h ├── Rumble.swift ├── Subcommand.swift ├── Utils.swift └── controllers │ ├── FamicomController1.swift │ ├── FamicomController2.swift │ ├── JoyConL.swift │ ├── JoyConR.swift │ ├── ProController.swift │ └── SNESController.swift ├── docs ├── Controller │ └── index.html ├── FamicomController1 │ └── index.html ├── FamicomController2 │ └── index.html ├── HomeLEDPattern │ └── index.html ├── JoyCon │ └── index.html ├── JoyConL │ └── index.html ├── JoyConManager │ └── index.html ├── JoyConR │ └── index.html ├── JoyCon_BatteryStatus │ └── index.html ├── JoyCon_Button │ └── index.html ├── JoyCon_ControllerType │ └── index.html ├── JoyCon_HCIState │ └── index.html ├── JoyCon_InputMode │ └── index.html ├── JoyCon_OutputType │ └── index.html ├── JoyCon_PlayerLightPattern │ └── index.html ├── JoyCon_StickDirection │ └── index.html ├── ProController │ └── index.html ├── Rumble │ └── index.html ├── Rumble_HighFrequency │ └── index.html ├── Rumble_LowFrequency │ └── index.html ├── SNESController │ └── index.html └── index.html └── generate_docs.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | *.DS_Store 4 | build/ 5 | *.pbxuser 6 | !default.pbxuser 7 | *.mode1v3 8 | !default.mode1v3 9 | *.mode2v3 10 | !default.mode2v3 11 | *.perspectivev3 12 | !default.perspectivev3 13 | xcuserdata 14 | *.xccheckout 15 | *.moved-aside 16 | DerivedData 17 | *.hmap 18 | *.ipa 19 | *.xcuserstate 20 | 21 | # CocoaPods 22 | # 23 | # We recommend against adding the Pods directory to your .gitignore. However 24 | # you should judge for yourself, the pros and cons are mentioned at: 25 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 26 | # 27 | Pods/ 28 | 29 | # Carthage 30 | # 31 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 32 | # Carthage/Checkouts 33 | Carthage/Build 34 | 35 | # etc 36 | Framework/ 37 | *~ 38 | *.swp 39 | 40 | -------------------------------------------------------------------------------- /JoyConSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "JoyConSwift" 3 | s.version = "0.2.1" 4 | s.summary = "IOKit wrapper for Nintendo Joy-Con and ProController (macOS, Swift)" 5 | s.homepage = "https://github.com/magicien/JoyConSwift" 6 | s.license = "MIT" 7 | s.author = { "magicien" => "magicien.du.ballon@gmail.com" } 8 | s.platform = :osx, "10.14" 9 | s.source = { :git => "https://github.com/magicien/JoyConSwift.git", :tag => "v#{s.version}" } 10 | s.source_files = "Source/**/*.{swift,h}" 11 | s.swift_version = "5.0" 12 | end 13 | -------------------------------------------------------------------------------- /JoyConSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DD8D413E22B616A500C729D9 /* JoyConSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = DD8D413022B616A500C729D9 /* JoyConSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 11 | DD8D414A22B616E800C729D9 /* Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8D414722B616E800C729D9 /* Controller.swift */; }; 12 | DD8D414C22B616E800C729D9 /* JoyConManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8D414922B616E800C729D9 /* JoyConManager.swift */; }; 13 | DD8D52C022BF796000C729D9 /* JoyCon.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8D52BF22BF796000C729D9 /* JoyCon.swift */; }; 14 | DD8D52C222BF7B9600C729D9 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD8D52C122BF7B9600C729D9 /* Utils.swift */; }; 15 | DDA27BEC22C616C700AFB876 /* Subcommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDA27BEB22C616C700AFB876 /* Subcommand.swift */; }; 16 | DDEADDA524A9CEBC00A61D12 /* Rumble.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEADDA424A9CEBC00A61D12 /* Rumble.swift */; }; 17 | DDEADDB224ABABDC00A61D12 /* HomeLEDPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDEADDB124ABABDC00A61D12 /* HomeLEDPattern.swift */; }; 18 | DDF3BF8124E004EA00132D61 /* ProController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7A24E004BD00132D61 /* ProController.swift */; }; 19 | DDF3BF8224E004EC00132D61 /* SNESController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7B24E004BD00132D61 /* SNESController.swift */; }; 20 | DDF3BF8324E004EF00132D61 /* FamicomController1.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7C24E004BD00132D61 /* FamicomController1.swift */; }; 21 | DDF3BF8424E004FE00132D61 /* FamicomController2.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7F24E004BD00132D61 /* FamicomController2.swift */; }; 22 | DDF3BF8524E0050400132D61 /* JoyConL.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7D24E004BD00132D61 /* JoyConL.swift */; }; 23 | DDF3BF8624E0050700132D61 /* JoyConR.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDF3BF7E24E004BD00132D61 /* JoyConR.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | DD8D412D22B616A500C729D9 /* JoyConSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JoyConSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | DD8D413022B616A500C729D9 /* JoyConSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JoyConSwift.h; sourceTree = ""; }; 29 | DD8D413122B616A500C729D9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 30 | DD8D414722B616E800C729D9 /* Controller.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Controller.swift; sourceTree = ""; }; 31 | DD8D414922B616E800C729D9 /* JoyConManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JoyConManager.swift; sourceTree = ""; }; 32 | DD8D52BF22BF796000C729D9 /* JoyCon.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoyCon.swift; sourceTree = ""; }; 33 | DD8D52C122BF7B9600C729D9 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = ""; }; 34 | DDA27BEB22C616C700AFB876 /* Subcommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Subcommand.swift; sourceTree = ""; }; 35 | DDEADDA424A9CEBC00A61D12 /* Rumble.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Rumble.swift; sourceTree = ""; }; 36 | DDEADDB124ABABDC00A61D12 /* HomeLEDPattern.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeLEDPattern.swift; sourceTree = ""; }; 37 | DDF3BF7A24E004BD00132D61 /* ProController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProController.swift; sourceTree = ""; }; 38 | DDF3BF7B24E004BD00132D61 /* SNESController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SNESController.swift; sourceTree = ""; }; 39 | DDF3BF7C24E004BD00132D61 /* FamicomController1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FamicomController1.swift; sourceTree = ""; }; 40 | DDF3BF7D24E004BD00132D61 /* JoyConL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoyConL.swift; sourceTree = ""; }; 41 | DDF3BF7E24E004BD00132D61 /* JoyConR.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoyConR.swift; sourceTree = ""; }; 42 | DDF3BF7F24E004BD00132D61 /* FamicomController2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FamicomController2.swift; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | DD8D412A22B616A500C729D9 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | ); 51 | runOnlyForDeploymentPostprocessing = 0; 52 | }; 53 | /* End PBXFrameworksBuildPhase section */ 54 | 55 | /* Begin PBXGroup section */ 56 | DD8D412322B616A500C729D9 = { 57 | isa = PBXGroup; 58 | children = ( 59 | DD8D412F22B616A500C729D9 /* Source */, 60 | DD8D412E22B616A500C729D9 /* Products */, 61 | ); 62 | sourceTree = ""; 63 | }; 64 | DD8D412E22B616A500C729D9 /* Products */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | DD8D412D22B616A500C729D9 /* JoyConSwift.framework */, 68 | ); 69 | name = Products; 70 | sourceTree = ""; 71 | }; 72 | DD8D412F22B616A500C729D9 /* Source */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | DD8D413022B616A500C729D9 /* JoyConSwift.h */, 76 | DD8D52BF22BF796000C729D9 /* JoyCon.swift */, 77 | DD8D414922B616E800C729D9 /* JoyConManager.swift */, 78 | DD8D414722B616E800C729D9 /* Controller.swift */, 79 | DDEADDB124ABABDC00A61D12 /* HomeLEDPattern.swift */, 80 | DDEADDA424A9CEBC00A61D12 /* Rumble.swift */, 81 | DDA27BEB22C616C700AFB876 /* Subcommand.swift */, 82 | DD8D52C122BF7B9600C729D9 /* Utils.swift */, 83 | DDF3BF8024E004BD00132D61 /* controllers */, 84 | DD8D413122B616A500C729D9 /* Info.plist */, 85 | ); 86 | path = Source; 87 | sourceTree = ""; 88 | }; 89 | DDF3BF8024E004BD00132D61 /* controllers */ = { 90 | isa = PBXGroup; 91 | children = ( 92 | DDF3BF7C24E004BD00132D61 /* FamicomController1.swift */, 93 | DDF3BF7F24E004BD00132D61 /* FamicomController2.swift */, 94 | DDF3BF7D24E004BD00132D61 /* JoyConL.swift */, 95 | DDF3BF7E24E004BD00132D61 /* JoyConR.swift */, 96 | DDF3BF7A24E004BD00132D61 /* ProController.swift */, 97 | DDF3BF7B24E004BD00132D61 /* SNESController.swift */, 98 | ); 99 | name = controllers; 100 | path = /Users/ohno/Documents/Development/JoyConSwift/Source/controllers; 101 | sourceTree = ""; 102 | }; 103 | /* End PBXGroup section */ 104 | 105 | /* Begin PBXHeadersBuildPhase section */ 106 | DD8D412822B616A500C729D9 /* Headers */ = { 107 | isa = PBXHeadersBuildPhase; 108 | buildActionMask = 2147483647; 109 | files = ( 110 | DD8D413E22B616A500C729D9 /* JoyConSwift.h in Headers */, 111 | ); 112 | runOnlyForDeploymentPostprocessing = 0; 113 | }; 114 | /* End PBXHeadersBuildPhase section */ 115 | 116 | /* Begin PBXNativeTarget section */ 117 | DD8D412C22B616A500C729D9 /* JoyConSwift */ = { 118 | isa = PBXNativeTarget; 119 | buildConfigurationList = DD8D414122B616A500C729D9 /* Build configuration list for PBXNativeTarget "JoyConSwift" */; 120 | buildPhases = ( 121 | DD8D412822B616A500C729D9 /* Headers */, 122 | DD8D412922B616A500C729D9 /* Sources */, 123 | DD8D412A22B616A500C729D9 /* Frameworks */, 124 | DD8D412B22B616A500C729D9 /* Resources */, 125 | ); 126 | buildRules = ( 127 | ); 128 | dependencies = ( 129 | ); 130 | name = JoyConSwift; 131 | productName = JoyConSwift; 132 | productReference = DD8D412D22B616A500C729D9 /* JoyConSwift.framework */; 133 | productType = "com.apple.product-type.framework"; 134 | }; 135 | /* End PBXNativeTarget section */ 136 | 137 | /* Begin PBXProject section */ 138 | DD8D412422B616A500C729D9 /* Project object */ = { 139 | isa = PBXProject; 140 | attributes = { 141 | LastSwiftUpdateCheck = 1020; 142 | LastUpgradeCheck = 1020; 143 | ORGANIZATIONNAME = DarkHorse; 144 | TargetAttributes = { 145 | DD8D412C22B616A500C729D9 = { 146 | CreatedOnToolsVersion = 10.2.1; 147 | LastSwiftMigration = 1020; 148 | }; 149 | }; 150 | }; 151 | buildConfigurationList = DD8D412722B616A500C729D9 /* Build configuration list for PBXProject "JoyConSwift" */; 152 | compatibilityVersion = "Xcode 9.3"; 153 | developmentRegion = en; 154 | hasScannedForEncodings = 0; 155 | knownRegions = ( 156 | en, 157 | ); 158 | mainGroup = DD8D412322B616A500C729D9; 159 | productRefGroup = DD8D412E22B616A500C729D9 /* Products */; 160 | projectDirPath = ""; 161 | projectRoot = ""; 162 | targets = ( 163 | DD8D412C22B616A500C729D9 /* JoyConSwift */, 164 | ); 165 | }; 166 | /* End PBXProject section */ 167 | 168 | /* Begin PBXResourcesBuildPhase section */ 169 | DD8D412B22B616A500C729D9 /* Resources */ = { 170 | isa = PBXResourcesBuildPhase; 171 | buildActionMask = 2147483647; 172 | files = ( 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | }; 176 | /* End PBXResourcesBuildPhase section */ 177 | 178 | /* Begin PBXSourcesBuildPhase section */ 179 | DD8D412922B616A500C729D9 /* Sources */ = { 180 | isa = PBXSourcesBuildPhase; 181 | buildActionMask = 2147483647; 182 | files = ( 183 | DD8D52C022BF796000C729D9 /* JoyCon.swift in Sources */, 184 | DDEADDA524A9CEBC00A61D12 /* Rumble.swift in Sources */, 185 | DD8D414C22B616E800C729D9 /* JoyConManager.swift in Sources */, 186 | DDEADDB224ABABDC00A61D12 /* HomeLEDPattern.swift in Sources */, 187 | DD8D414A22B616E800C729D9 /* Controller.swift in Sources */, 188 | DDA27BEC22C616C700AFB876 /* Subcommand.swift in Sources */, 189 | DD8D52C222BF7B9600C729D9 /* Utils.swift in Sources */, 190 | DDF3BF8324E004EF00132D61 /* FamicomController1.swift in Sources */, 191 | DDF3BF8424E004FE00132D61 /* FamicomController2.swift in Sources */, 192 | DDF3BF8524E0050400132D61 /* JoyConL.swift in Sources */, 193 | DDF3BF8624E0050700132D61 /* JoyConR.swift in Sources */, 194 | DDF3BF8124E004EA00132D61 /* ProController.swift in Sources */, 195 | DDF3BF8224E004EC00132D61 /* SNESController.swift in Sources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXSourcesBuildPhase section */ 200 | 201 | /* Begin XCBuildConfiguration section */ 202 | DD8D413F22B616A500C729D9 /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | ALWAYS_SEARCH_USER_PATHS = NO; 206 | CLANG_ANALYZER_NONNULL = YES; 207 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_ENABLE_OBJC_WEAK = YES; 213 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 214 | CLANG_WARN_BOOL_CONVERSION = YES; 215 | CLANG_WARN_COMMA = YES; 216 | CLANG_WARN_CONSTANT_CONVERSION = YES; 217 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 218 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 219 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 220 | CLANG_WARN_EMPTY_BODY = YES; 221 | CLANG_WARN_ENUM_CONVERSION = YES; 222 | CLANG_WARN_INFINITE_RECURSION = YES; 223 | CLANG_WARN_INT_CONVERSION = YES; 224 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 225 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 226 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 227 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 228 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 229 | CLANG_WARN_STRICT_PROTOTYPES = YES; 230 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 231 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 232 | CLANG_WARN_UNREACHABLE_CODE = YES; 233 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 234 | CODE_SIGN_IDENTITY = "Mac Developer"; 235 | COPY_PHASE_STRIP = NO; 236 | CURRENT_PROJECT_VERSION = 1; 237 | DEBUG_INFORMATION_FORMAT = dwarf; 238 | ENABLE_STRICT_OBJC_MSGSEND = YES; 239 | ENABLE_TESTABILITY = YES; 240 | GCC_C_LANGUAGE_STANDARD = gnu11; 241 | GCC_DYNAMIC_NO_PIC = NO; 242 | GCC_NO_COMMON_BLOCKS = YES; 243 | GCC_OPTIMIZATION_LEVEL = 0; 244 | GCC_PREPROCESSOR_DEFINITIONS = ( 245 | "DEBUG=1", 246 | "$(inherited)", 247 | ); 248 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 250 | GCC_WARN_UNDECLARED_SELECTOR = YES; 251 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 252 | GCC_WARN_UNUSED_FUNCTION = YES; 253 | GCC_WARN_UNUSED_VARIABLE = YES; 254 | MACOSX_DEPLOYMENT_TARGET = 10.14; 255 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 256 | MTL_FAST_MATH = YES; 257 | ONLY_ACTIVE_ARCH = YES; 258 | SDKROOT = macosx; 259 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 260 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 261 | VERSIONING_SYSTEM = "apple-generic"; 262 | VERSION_INFO_PREFIX = ""; 263 | }; 264 | name = Debug; 265 | }; 266 | DD8D414022B616A500C729D9 /* Release */ = { 267 | isa = XCBuildConfiguration; 268 | buildSettings = { 269 | ALWAYS_SEARCH_USER_PATHS = NO; 270 | CLANG_ANALYZER_NONNULL = YES; 271 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 272 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 273 | CLANG_CXX_LIBRARY = "libc++"; 274 | CLANG_ENABLE_MODULES = YES; 275 | CLANG_ENABLE_OBJC_ARC = YES; 276 | CLANG_ENABLE_OBJC_WEAK = YES; 277 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 278 | CLANG_WARN_BOOL_CONVERSION = YES; 279 | CLANG_WARN_COMMA = YES; 280 | CLANG_WARN_CONSTANT_CONVERSION = YES; 281 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 282 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 283 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 284 | CLANG_WARN_EMPTY_BODY = YES; 285 | CLANG_WARN_ENUM_CONVERSION = YES; 286 | CLANG_WARN_INFINITE_RECURSION = YES; 287 | CLANG_WARN_INT_CONVERSION = YES; 288 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 289 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 290 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 291 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 292 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 293 | CLANG_WARN_STRICT_PROTOTYPES = YES; 294 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 295 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 296 | CLANG_WARN_UNREACHABLE_CODE = YES; 297 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 298 | CODE_SIGN_IDENTITY = "Mac Developer"; 299 | COPY_PHASE_STRIP = NO; 300 | CURRENT_PROJECT_VERSION = 1; 301 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 302 | ENABLE_NS_ASSERTIONS = NO; 303 | ENABLE_STRICT_OBJC_MSGSEND = YES; 304 | GCC_C_LANGUAGE_STANDARD = gnu11; 305 | GCC_NO_COMMON_BLOCKS = YES; 306 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 307 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 308 | GCC_WARN_UNDECLARED_SELECTOR = YES; 309 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 310 | GCC_WARN_UNUSED_FUNCTION = YES; 311 | GCC_WARN_UNUSED_VARIABLE = YES; 312 | MACOSX_DEPLOYMENT_TARGET = 10.14; 313 | MTL_ENABLE_DEBUG_INFO = NO; 314 | MTL_FAST_MATH = YES; 315 | SDKROOT = macosx; 316 | SWIFT_COMPILATION_MODE = wholemodule; 317 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 318 | VERSIONING_SYSTEM = "apple-generic"; 319 | VERSION_INFO_PREFIX = ""; 320 | }; 321 | name = Release; 322 | }; 323 | DD8D414222B616A500C729D9 /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | buildSettings = { 326 | CLANG_ENABLE_MODULES = YES; 327 | CODE_SIGN_IDENTITY = ""; 328 | CODE_SIGN_STYLE = Automatic; 329 | COMBINE_HIDPI_IMAGES = YES; 330 | DEFINES_MODULE = YES; 331 | DEVELOPMENT_TEAM = EF37D6UQZ9; 332 | DYLIB_COMPATIBILITY_VERSION = 1; 333 | DYLIB_CURRENT_VERSION = 1; 334 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 335 | FRAMEWORK_VERSION = A; 336 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 337 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 338 | LD_RUNPATH_SEARCH_PATHS = ( 339 | "$(inherited)", 340 | "@executable_path/../Frameworks", 341 | "@loader_path/Frameworks", 342 | ); 343 | MARKETING_VERSION = 0.2.1; 344 | PRODUCT_BUNDLE_IDENTIFIER = jp.0spec.JoyConSwift; 345 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 346 | SKIP_INSTALL = YES; 347 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 348 | SWIFT_VERSION = 5.0; 349 | }; 350 | name = Debug; 351 | }; 352 | DD8D414322B616A500C729D9 /* Release */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | CLANG_ENABLE_MODULES = YES; 356 | CODE_SIGN_IDENTITY = ""; 357 | CODE_SIGN_STYLE = Automatic; 358 | COMBINE_HIDPI_IMAGES = YES; 359 | DEFINES_MODULE = YES; 360 | DEVELOPMENT_TEAM = EF37D6UQZ9; 361 | DYLIB_COMPATIBILITY_VERSION = 1; 362 | DYLIB_CURRENT_VERSION = 1; 363 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 364 | FRAMEWORK_VERSION = A; 365 | INFOPLIST_FILE = "$(SRCROOT)/Source/Info.plist"; 366 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 367 | LD_RUNPATH_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "@executable_path/../Frameworks", 370 | "@loader_path/Frameworks", 371 | ); 372 | MARKETING_VERSION = 0.2.1; 373 | PRODUCT_BUNDLE_IDENTIFIER = jp.0spec.JoyConSwift; 374 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 375 | SKIP_INSTALL = YES; 376 | SWIFT_VERSION = 5.0; 377 | }; 378 | name = Release; 379 | }; 380 | /* End XCBuildConfiguration section */ 381 | 382 | /* Begin XCConfigurationList section */ 383 | DD8D412722B616A500C729D9 /* Build configuration list for PBXProject "JoyConSwift" */ = { 384 | isa = XCConfigurationList; 385 | buildConfigurations = ( 386 | DD8D413F22B616A500C729D9 /* Debug */, 387 | DD8D414022B616A500C729D9 /* Release */, 388 | ); 389 | defaultConfigurationIsVisible = 0; 390 | defaultConfigurationName = Release; 391 | }; 392 | DD8D414122B616A500C729D9 /* Build configuration list for PBXNativeTarget "JoyConSwift" */ = { 393 | isa = XCConfigurationList; 394 | buildConfigurations = ( 395 | DD8D414222B616A500C729D9 /* Debug */, 396 | DD8D414322B616A500C729D9 /* Release */, 397 | ); 398 | defaultConfigurationIsVisible = 0; 399 | defaultConfigurationName = Release; 400 | }; 401 | /* End XCConfigurationList section */ 402 | }; 403 | rootObject = DD8D412422B616A500C729D9 /* Project object */; 404 | } 405 | -------------------------------------------------------------------------------- /JoyConSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /JoyConSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JoyConSwift.xcodeproj/xcshareddata/xcschemes/JoyConSwiftStatic.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 magicien 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JoyConSwift 2 | IOKit wrapper for Nintendo Joy-Con and ProController (macOS, Swift) 3 | 4 | ## Installation 5 | 6 | ### Using [CocoaPods](http://cocoapods.org/) 7 | 8 | Add the following to your [Podfile](http://guides.cocoapods.org/using/the-podfile.html): 9 | 10 | ```rb 11 | pod 'JoyConSwift' 12 | ``` 13 | 14 | ## Set USB Capability 15 | 16 | To use controllers, you need to check `Signing & Capabilities` > `App SandBox` > `USB` in your Xcode project. 17 | 18 | usb_capability 19 | 20 | ## Usage 21 | 22 | ```swift 23 | import JoyConSwift 24 | 25 | // Initialize the manager 26 | let manager = JoyConManager() 27 | 28 | // Set connection event callbacks 29 | manager.connectHandler = { controller in 30 | // Do something with the controller 31 | controller.setPlayerLights(l1: .on, l2: .off, l3: .off, l4: .off) 32 | controller.enableIMU(enable: true) 33 | controller.setInputMode(mode: .standardFull) 34 | controller.buttonPressHandler = { button in 35 | if button == .A { 36 | // Do something with the A button 37 | } 38 | } 39 | } 40 | manager.disconnectHandler = { controller in 41 | // Clean the controller data 42 | } 43 | 44 | // Start waiting for the connection events 45 | manager.runAsync() 46 | ``` 47 | 48 | ## Documentation 49 | 50 | See [JoyConSwift Documentation](https://magicien.github.io/JoyConSwift/) 51 | 52 | ## Thanks to 53 | 54 | [dekuNukem/Nintendo_Switch_Reverse_Engineering](https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering) - A look at inner workings of Joycon and Nintendo Switch 55 | 56 | ## See also 57 | 58 | [JoyKeyMapper](https://github.com/magicien/JoyKeyMapper) - Nintendo Joy-Con/ProController Key mapper for macOS 59 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | DD0ADFC922D1D11C00B68F05 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0ADFC822D1D11C00B68F05 /* AppDelegate.swift */; }; 11 | DD0ADFCB22D1D11C00B68F05 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0ADFCA22D1D11C00B68F05 /* ViewController.swift */; }; 12 | DD0ADFCD22D1D11E00B68F05 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DD0ADFCC22D1D11E00B68F05 /* Assets.xcassets */; }; 13 | DD0ADFD022D1D11E00B68F05 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DD0ADFCE22D1D11E00B68F05 /* Main.storyboard */; }; 14 | DD0ADFEB22D1D1BC00B68F05 /* JoyConSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD0ADFEA22D1D1BC00B68F05 /* JoyConSwift.framework */; }; 15 | DD0ADFEC22D1D1BC00B68F05 /* JoyConSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = DD0ADFEA22D1D1BC00B68F05 /* JoyConSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | DD0ADFEF22D1D77300B68F05 /* ViewController+NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD0ADFEE22D1D77300B68F05 /* ViewController+NSTableViewDelegate.swift */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | DD0ADFED22D1D1BC00B68F05 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | DD0ADFEC22D1D1BC00B68F05 /* JoyConSwift.framework in Embed Frameworks */, 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | DD0ADFC522D1D11C00B68F05 /* JoyConSwiftSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JoyConSwiftSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 35 | DD0ADFC822D1D11C00B68F05 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 36 | DD0ADFCA22D1D11C00B68F05 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 37 | DD0ADFCC22D1D11E00B68F05 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 38 | DD0ADFCF22D1D11E00B68F05 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 39 | DD0ADFD122D1D11E00B68F05 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 40 | DD0ADFD222D1D11E00B68F05 /* JoyConSwiftSample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = JoyConSwiftSample.entitlements; sourceTree = ""; }; 41 | DD0ADFEA22D1D1BC00B68F05 /* JoyConSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = JoyConSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | DD0ADFEE22D1D77300B68F05 /* ViewController+NSTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController+NSTableViewDelegate.swift"; sourceTree = ""; }; 43 | /* End PBXFileReference section */ 44 | 45 | /* Begin PBXFrameworksBuildPhase section */ 46 | DD0ADFC222D1D11C00B68F05 /* Frameworks */ = { 47 | isa = PBXFrameworksBuildPhase; 48 | buildActionMask = 2147483647; 49 | files = ( 50 | DD0ADFEB22D1D1BC00B68F05 /* JoyConSwift.framework in Frameworks */, 51 | ); 52 | runOnlyForDeploymentPostprocessing = 0; 53 | }; 54 | /* End PBXFrameworksBuildPhase section */ 55 | 56 | /* Begin PBXGroup section */ 57 | DD0ADFBC22D1D11C00B68F05 = { 58 | isa = PBXGroup; 59 | children = ( 60 | DD0ADFEA22D1D1BC00B68F05 /* JoyConSwift.framework */, 61 | DD0ADFC722D1D11C00B68F05 /* JoyConSwiftSample */, 62 | DD0ADFC622D1D11C00B68F05 /* Products */, 63 | ); 64 | sourceTree = ""; 65 | }; 66 | DD0ADFC622D1D11C00B68F05 /* Products */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | DD0ADFC522D1D11C00B68F05 /* JoyConSwiftSample.app */, 70 | ); 71 | name = Products; 72 | sourceTree = ""; 73 | }; 74 | DD0ADFC722D1D11C00B68F05 /* JoyConSwiftSample */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | DD0ADFC822D1D11C00B68F05 /* AppDelegate.swift */, 78 | DD0ADFCA22D1D11C00B68F05 /* ViewController.swift */, 79 | DD0ADFEE22D1D77300B68F05 /* ViewController+NSTableViewDelegate.swift */, 80 | DD0ADFCC22D1D11E00B68F05 /* Assets.xcassets */, 81 | DD0ADFCE22D1D11E00B68F05 /* Main.storyboard */, 82 | DD0ADFD122D1D11E00B68F05 /* Info.plist */, 83 | DD0ADFD222D1D11E00B68F05 /* JoyConSwiftSample.entitlements */, 84 | ); 85 | path = JoyConSwiftSample; 86 | sourceTree = ""; 87 | }; 88 | /* End PBXGroup section */ 89 | 90 | /* Begin PBXNativeTarget section */ 91 | DD0ADFC422D1D11C00B68F05 /* JoyConSwiftSample */ = { 92 | isa = PBXNativeTarget; 93 | buildConfigurationList = DD0ADFD522D1D11E00B68F05 /* Build configuration list for PBXNativeTarget "JoyConSwiftSample" */; 94 | buildPhases = ( 95 | DD0ADFC122D1D11C00B68F05 /* Sources */, 96 | DD0ADFC222D1D11C00B68F05 /* Frameworks */, 97 | DD0ADFC322D1D11C00B68F05 /* Resources */, 98 | DD0ADFED22D1D1BC00B68F05 /* Embed Frameworks */, 99 | ); 100 | buildRules = ( 101 | ); 102 | dependencies = ( 103 | ); 104 | name = JoyConSwiftSample; 105 | productName = JoyConSwiftSample; 106 | productReference = DD0ADFC522D1D11C00B68F05 /* JoyConSwiftSample.app */; 107 | productType = "com.apple.product-type.application"; 108 | }; 109 | /* End PBXNativeTarget section */ 110 | 111 | /* Begin PBXProject section */ 112 | DD0ADFBD22D1D11C00B68F05 /* Project object */ = { 113 | isa = PBXProject; 114 | attributes = { 115 | LastSwiftUpdateCheck = 1020; 116 | LastUpgradeCheck = 1020; 117 | ORGANIZATIONNAME = DarkHorse; 118 | TargetAttributes = { 119 | DD0ADFC422D1D11C00B68F05 = { 120 | CreatedOnToolsVersion = 10.2.1; 121 | SystemCapabilities = { 122 | com.apple.Sandbox = { 123 | enabled = 1; 124 | }; 125 | }; 126 | }; 127 | }; 128 | }; 129 | buildConfigurationList = DD0ADFC022D1D11C00B68F05 /* Build configuration list for PBXProject "JoyConSwiftSample" */; 130 | compatibilityVersion = "Xcode 9.3"; 131 | developmentRegion = en; 132 | hasScannedForEncodings = 0; 133 | knownRegions = ( 134 | en, 135 | Base, 136 | ); 137 | mainGroup = DD0ADFBC22D1D11C00B68F05; 138 | productRefGroup = DD0ADFC622D1D11C00B68F05 /* Products */; 139 | projectDirPath = ""; 140 | projectRoot = ""; 141 | targets = ( 142 | DD0ADFC422D1D11C00B68F05 /* JoyConSwiftSample */, 143 | ); 144 | }; 145 | /* End PBXProject section */ 146 | 147 | /* Begin PBXResourcesBuildPhase section */ 148 | DD0ADFC322D1D11C00B68F05 /* Resources */ = { 149 | isa = PBXResourcesBuildPhase; 150 | buildActionMask = 2147483647; 151 | files = ( 152 | DD0ADFCD22D1D11E00B68F05 /* Assets.xcassets in Resources */, 153 | DD0ADFD022D1D11E00B68F05 /* Main.storyboard in Resources */, 154 | ); 155 | runOnlyForDeploymentPostprocessing = 0; 156 | }; 157 | /* End PBXResourcesBuildPhase section */ 158 | 159 | /* Begin PBXSourcesBuildPhase section */ 160 | DD0ADFC122D1D11C00B68F05 /* Sources */ = { 161 | isa = PBXSourcesBuildPhase; 162 | buildActionMask = 2147483647; 163 | files = ( 164 | DD0ADFCB22D1D11C00B68F05 /* ViewController.swift in Sources */, 165 | DD0ADFEF22D1D77300B68F05 /* ViewController+NSTableViewDelegate.swift in Sources */, 166 | DD0ADFC922D1D11C00B68F05 /* AppDelegate.swift in Sources */, 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | /* End PBXSourcesBuildPhase section */ 171 | 172 | /* Begin PBXVariantGroup section */ 173 | DD0ADFCE22D1D11E00B68F05 /* Main.storyboard */ = { 174 | isa = PBXVariantGroup; 175 | children = ( 176 | DD0ADFCF22D1D11E00B68F05 /* Base */, 177 | ); 178 | name = Main.storyboard; 179 | sourceTree = ""; 180 | }; 181 | /* End PBXVariantGroup section */ 182 | 183 | /* Begin XCBuildConfiguration section */ 184 | DD0ADFD322D1D11E00B68F05 /* Debug */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_ANALYZER_NONNULL = YES; 189 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 190 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 191 | CLANG_CXX_LIBRARY = "libc++"; 192 | CLANG_ENABLE_MODULES = YES; 193 | CLANG_ENABLE_OBJC_ARC = YES; 194 | CLANG_ENABLE_OBJC_WEAK = YES; 195 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 196 | CLANG_WARN_BOOL_CONVERSION = YES; 197 | CLANG_WARN_COMMA = YES; 198 | CLANG_WARN_CONSTANT_CONVERSION = YES; 199 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 202 | CLANG_WARN_EMPTY_BODY = YES; 203 | CLANG_WARN_ENUM_CONVERSION = YES; 204 | CLANG_WARN_INFINITE_RECURSION = YES; 205 | CLANG_WARN_INT_CONVERSION = YES; 206 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 207 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 208 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 209 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 210 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 211 | CLANG_WARN_STRICT_PROTOTYPES = YES; 212 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 213 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 214 | CLANG_WARN_UNREACHABLE_CODE = YES; 215 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 216 | CODE_SIGN_IDENTITY = "Mac Developer"; 217 | COPY_PHASE_STRIP = NO; 218 | DEBUG_INFORMATION_FORMAT = dwarf; 219 | ENABLE_STRICT_OBJC_MSGSEND = YES; 220 | ENABLE_TESTABILITY = YES; 221 | GCC_C_LANGUAGE_STANDARD = gnu11; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_NO_COMMON_BLOCKS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | MACOSX_DEPLOYMENT_TARGET = 10.14; 236 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 237 | MTL_FAST_MATH = YES; 238 | ONLY_ACTIVE_ARCH = YES; 239 | SDKROOT = macosx; 240 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 241 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 242 | }; 243 | name = Debug; 244 | }; 245 | DD0ADFD422D1D11E00B68F05 /* Release */ = { 246 | isa = XCBuildConfiguration; 247 | buildSettings = { 248 | ALWAYS_SEARCH_USER_PATHS = NO; 249 | CLANG_ANALYZER_NONNULL = YES; 250 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 251 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 252 | CLANG_CXX_LIBRARY = "libc++"; 253 | CLANG_ENABLE_MODULES = YES; 254 | CLANG_ENABLE_OBJC_ARC = YES; 255 | CLANG_ENABLE_OBJC_WEAK = YES; 256 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 257 | CLANG_WARN_BOOL_CONVERSION = YES; 258 | CLANG_WARN_COMMA = YES; 259 | CLANG_WARN_CONSTANT_CONVERSION = YES; 260 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 261 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 262 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 263 | CLANG_WARN_EMPTY_BODY = YES; 264 | CLANG_WARN_ENUM_CONVERSION = YES; 265 | CLANG_WARN_INFINITE_RECURSION = YES; 266 | CLANG_WARN_INT_CONVERSION = YES; 267 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 268 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 269 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 270 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 271 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 272 | CLANG_WARN_STRICT_PROTOTYPES = YES; 273 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 274 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 275 | CLANG_WARN_UNREACHABLE_CODE = YES; 276 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 277 | CODE_SIGN_IDENTITY = "Mac Developer"; 278 | COPY_PHASE_STRIP = NO; 279 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 280 | ENABLE_NS_ASSERTIONS = NO; 281 | ENABLE_STRICT_OBJC_MSGSEND = YES; 282 | GCC_C_LANGUAGE_STANDARD = gnu11; 283 | GCC_NO_COMMON_BLOCKS = YES; 284 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 285 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 286 | GCC_WARN_UNDECLARED_SELECTOR = YES; 287 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 288 | GCC_WARN_UNUSED_FUNCTION = YES; 289 | GCC_WARN_UNUSED_VARIABLE = YES; 290 | MACOSX_DEPLOYMENT_TARGET = 10.14; 291 | MTL_ENABLE_DEBUG_INFO = NO; 292 | MTL_FAST_MATH = YES; 293 | SDKROOT = macosx; 294 | SWIFT_COMPILATION_MODE = wholemodule; 295 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 296 | }; 297 | name = Release; 298 | }; 299 | DD0ADFD622D1D11E00B68F05 /* Debug */ = { 300 | isa = XCBuildConfiguration; 301 | buildSettings = { 302 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 303 | CODE_SIGN_ENTITLEMENTS = JoyConSwiftSample/JoyConSwiftSample.entitlements; 304 | CODE_SIGN_STYLE = Automatic; 305 | COMBINE_HIDPI_IMAGES = YES; 306 | DEVELOPMENT_TEAM = EF37D6UQZ9; 307 | INFOPLIST_FILE = JoyConSwiftSample/Info.plist; 308 | LD_RUNPATH_SEARCH_PATHS = ( 309 | "$(inherited)", 310 | "@executable_path/../Frameworks", 311 | ); 312 | PRODUCT_BUNDLE_IDENTIFIER = jp.0spec.JoyConSwiftSample; 313 | PRODUCT_NAME = "$(TARGET_NAME)"; 314 | SWIFT_VERSION = 5.0; 315 | }; 316 | name = Debug; 317 | }; 318 | DD0ADFD722D1D11E00B68F05 /* Release */ = { 319 | isa = XCBuildConfiguration; 320 | buildSettings = { 321 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 322 | CODE_SIGN_ENTITLEMENTS = JoyConSwiftSample/JoyConSwiftSample.entitlements; 323 | CODE_SIGN_STYLE = Automatic; 324 | COMBINE_HIDPI_IMAGES = YES; 325 | DEVELOPMENT_TEAM = EF37D6UQZ9; 326 | INFOPLIST_FILE = JoyConSwiftSample/Info.plist; 327 | LD_RUNPATH_SEARCH_PATHS = ( 328 | "$(inherited)", 329 | "@executable_path/../Frameworks", 330 | ); 331 | PRODUCT_BUNDLE_IDENTIFIER = jp.0spec.JoyConSwiftSample; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SWIFT_VERSION = 5.0; 334 | }; 335 | name = Release; 336 | }; 337 | /* End XCBuildConfiguration section */ 338 | 339 | /* Begin XCConfigurationList section */ 340 | DD0ADFC022D1D11C00B68F05 /* Build configuration list for PBXProject "JoyConSwiftSample" */ = { 341 | isa = XCConfigurationList; 342 | buildConfigurations = ( 343 | DD0ADFD322D1D11E00B68F05 /* Debug */, 344 | DD0ADFD422D1D11E00B68F05 /* Release */, 345 | ); 346 | defaultConfigurationIsVisible = 0; 347 | defaultConfigurationName = Release; 348 | }; 349 | DD0ADFD522D1D11E00B68F05 /* Build configuration list for PBXNativeTarget "JoyConSwiftSample" */ = { 350 | isa = XCConfigurationList; 351 | buildConfigurations = ( 352 | DD0ADFD622D1D11E00B68F05 /* Debug */, 353 | DD0ADFD722D1D11E00B68F05 /* Release */, 354 | ); 355 | defaultConfigurationIsVisible = 0; 356 | defaultConfigurationName = Release; 357 | }; 358 | /* End XCConfigurationList section */ 359 | }; 360 | rootObject = DD0ADFBD22D1D11C00B68F05 /* Project object */; 361 | } 362 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // JoyConSwiftSample 4 | // 5 | // Created by magicien on 2019/07/07. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import JoyConSwift 11 | 12 | @NSApplicationMain 13 | class AppDelegate: NSObject, NSApplicationDelegate { 14 | func applicationDidFinishLaunching(_ aNotification: Notification) { 15 | 16 | } 17 | 18 | func applicationWillTerminate(_ aNotification: Notification) { 19 | // Insert code here to tear down your application 20 | } 21 | 22 | func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 23 | return true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2019 DarkHorse. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/JoyConSwiftSample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.device.usb 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/ViewController+NSTableViewDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController+NSTableViewDelegate.swift 3 | // JoyConSwiftSample 4 | // 5 | // Created by magicien on 2019/07/07. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import JoyConSwift 11 | 12 | let buttonNames: [JoyCon.Button: String] = [ 13 | .Up: "Up", 14 | .Right: "Right", 15 | .Down: "Down", 16 | .Left: "Left", 17 | .A: "A", 18 | .B: "B", 19 | .X: "X", 20 | .Y: "Y", 21 | .L: "L", 22 | .ZL: "ZL", 23 | .R: "R", 24 | .ZR: "ZR", 25 | .Minus: "Minus", 26 | .Plus: "Plus", 27 | .Capture: "Capture", 28 | .Home: "Home", 29 | .LStick: "Left Stick", 30 | .RStick: "Right Stick", 31 | .LeftSL: "SL", 32 | .LeftSR: "SR", 33 | .RightSL: "SL", 34 | .RightSR: "SR" 35 | ] 36 | let controllerButtons: [JoyCon.ControllerType: [JoyCon.Button]] = [ 37 | .JoyConL: [.Up, .Right, .Down, .Left, .LeftSL, .LeftSR, .L, .ZL, .Minus, .Capture, .LStick], 38 | .JoyConR: [.A, .B, .X, .Y, .RightSL, .RightSR, .R, .ZR, .Plus, .Home, .RStick], 39 | .ProController: [.A, .B, .X, .Y, .L, .ZL, .R, .ZR, .Up, .Right, .Down, .Left, .Minus, .Plus, .Capture, .Home, .LStick, .RStick] 40 | ] 41 | 42 | let buttonNameColumnID = "buttonName" 43 | let buttonStateColumnID = "buttonState" 44 | 45 | extension ViewController: NSTableViewDelegate, NSTableViewDataSource { 46 | func numberOfRows(in tableView: NSTableView) -> Int { 47 | guard let controller = selectedController else { return 0 } 48 | guard let buttons = controllerButtons[controller.type] else { return 0 } 49 | return buttons.count 50 | } 51 | 52 | func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? { 53 | guard let column = tableColumn else { return nil } 54 | let newView = tableView.makeView(withIdentifier: column.identifier, owner: self) 55 | guard let cellView = newView as? NSTableCellView else { return nil } 56 | guard let controller = selectedController else { return nil } 57 | guard let button = controllerButtons[controller.type]?[row] else { return nil } 58 | 59 | if column.identifier.rawValue == buttonNameColumnID { 60 | cellView.textField?.stringValue = buttonNames[button] ?? "" 61 | } else if column.identifier.rawValue == buttonStateColumnID { 62 | if let isPushed = self.selectedController?.buttonState[button] { 63 | cellView.textField?.stringValue = isPushed ? "on" : "off" 64 | } else { 65 | cellView.textField?.stringValue = "" 66 | } 67 | } 68 | 69 | return cellView 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Sample/JoyConSwiftSample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // JoyConSwiftSample 4 | // 5 | // Created by magicien on 2019/07/07. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import JoyConSwift 11 | 12 | class ViewController: NSViewController { 13 | @IBOutlet weak var deviceSelect: NSPopUpButton! 14 | @IBOutlet weak var buttonTable: NSTableView! 15 | @IBOutlet weak var leftStickX: NSTextField! 16 | @IBOutlet weak var leftStickY: NSTextField! 17 | @IBOutlet weak var rightStickX: NSTextField! 18 | @IBOutlet weak var rightStickY: NSTextField! 19 | @IBOutlet weak var accelX: NSTextField! 20 | @IBOutlet weak var accelY: NSTextField! 21 | @IBOutlet weak var accelZ: NSTextField! 22 | @IBOutlet weak var disconnectButton: NSButton! 23 | 24 | var manager: JoyConManager = JoyConManager() 25 | var controllers: [JoyConSwift.Controller] = [] 26 | var selectedController: JoyConSwift.Controller? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | 31 | self.buttonTable.delegate = self 32 | self.buttonTable.dataSource = self 33 | 34 | self.manager.connectHandler = { [weak self] controller in 35 | self?.addController(controller) 36 | } 37 | self.manager.disconnectHandler = { [weak self] controller in 38 | self?.removeController(controller) 39 | } 40 | 41 | _ = self.manager.runAsync() 42 | } 43 | 44 | override func viewDidDisappear() { 45 | self.controllers.forEach { controller in 46 | controller.setHCIState(state: .disconnect) 47 | } 48 | } 49 | 50 | override var representedObject: Any? { 51 | didSet { 52 | // Update the view, if already loaded. 53 | } 54 | } 55 | 56 | @IBAction func selectController(_ sender: NSPopUpButton) { 57 | let index = sender.indexOfSelectedItem 58 | guard self.controllers.count > index else { return } 59 | 60 | if index < 0 { 61 | self.selectedController = nil 62 | return 63 | } 64 | self.selectedController = self.controllers[index] 65 | } 66 | 67 | @IBAction func pressDisconnect(_ sender: NSButton) { 68 | self.selectedController?.setHCIState(state: .disconnect) 69 | } 70 | 71 | func addController(_ controller: JoyConSwift.Controller) { 72 | controller.setPlayerLights(l1: .on, l2: .off, l3: .off, l4: .off) 73 | controller.enableIMU(enable: true) 74 | controller.setInputMode(mode: .standardFull) 75 | if self.controllers.first(where: { controller === $0 }) == nil { 76 | self.controllers.append(controller) 77 | self.refreshDeviceSelect() 78 | } 79 | controller.buttonPressHandler = { [weak self] _ in 80 | if self?.selectedController === controller { 81 | DispatchQueue.main.async { 82 | self?.buttonTable.reloadData() 83 | } 84 | } 85 | } 86 | controller.buttonReleaseHandler = { [weak self] _ in 87 | if self?.selectedController === controller { 88 | DispatchQueue.main.async { 89 | self?.buttonTable.reloadData() 90 | } 91 | } 92 | } 93 | controller.leftStickPosHandler = { pos in 94 | DispatchQueue.main.async { [weak self] in 95 | self?.leftStickX.stringValue = String(format: "%.2f", pos.x) 96 | self?.leftStickY.stringValue = String(format: "%.2f", pos.y) 97 | } 98 | } 99 | controller.rightStickPosHandler = { pos in 100 | DispatchQueue.main.async { [weak self] in 101 | self?.rightStickX.stringValue = String(format: "%.2f", pos.x) 102 | self?.rightStickY.stringValue = String(format: "%.2f", pos.y) 103 | } 104 | } 105 | controller.sensorHandler = { 106 | DispatchQueue.main.async { [weak self] in 107 | self?.accelX.stringValue = String(format: "%.2f", controller.acceleration.x) 108 | self?.accelY.stringValue = String(format: "%.2f", controller.acceleration.y) 109 | self?.accelZ.stringValue = String(format: "%.2f", controller.acceleration.z) 110 | } 111 | } 112 | } 113 | 114 | func removeController(_ controller: JoyConSwift.Controller) { 115 | self.controllers.removeAll(where: { controller === $0 }) 116 | self.refreshDeviceSelect() 117 | } 118 | 119 | func refreshDeviceSelect() { 120 | DispatchQueue.main.async { [weak self] in 121 | guard let strongSelf = self else { return } 122 | let selectedTitle = strongSelf.deviceSelect.selectedItem?.title 123 | strongSelf.deviceSelect.removeAllItems() 124 | strongSelf.controllers.forEach { controller in 125 | let title = "\(controller.type) (\(controller.serialID))" 126 | strongSelf.deviceSelect.addItem(withTitle: title) 127 | } 128 | 129 | if selectedTitle != nil { 130 | let selectedIndex = strongSelf.deviceSelect.indexOfItem(withTitle: selectedTitle!) 131 | if selectedIndex < 0 { 132 | strongSelf.selectController(strongSelf.deviceSelect) 133 | strongSelf.resetData() 134 | } else { 135 | strongSelf.deviceSelect.selectItem(at: selectedIndex) 136 | } 137 | } else if strongSelf.deviceSelect.numberOfItems > 0 { 138 | strongSelf.deviceSelect.selectItem(at: 0) 139 | strongSelf.selectController(strongSelf.deviceSelect) 140 | strongSelf.resetData() 141 | } else { 142 | strongSelf.selectController(strongSelf.deviceSelect) 143 | strongSelf.resetData() 144 | } 145 | 146 | strongSelf.disconnectButton.isEnabled = (strongSelf.selectedController != nil) 147 | } 148 | } 149 | 150 | func resetData() { 151 | self.buttonTable.reloadData() 152 | self.leftStickX.stringValue = "" 153 | self.leftStickY.stringValue = "" 154 | self.rightStickX.stringValue = "" 155 | self.rightStickY.stringValue = "" 156 | self.accelX.stringValue = "" 157 | self.accelY.stringValue = "" 158 | self.accelZ.stringValue = "" 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /Source/HomeLEDPattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeLEDPattern.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2020/06/30. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Home LED pattern data for setHomeLight function 11 | public struct HomeLEDPattern { 12 | /// LED intensity 13 | var intensity: UInt8 14 | 15 | /// Fading transition duration to this cycle 16 | var fadeDuration: UInt8 17 | 18 | /// LED duration of this cycle 19 | var duration: UInt8 20 | 21 | public init(intensity: UInt8, fadeDuration: UInt8, duration: UInt8) { 22 | self.intensity = intensity 23 | self.fadeDuration = fadeDuration 24 | self.duration = duration 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Source/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2019 DarkHorse. All rights reserved. 23 | 24 | 25 | -------------------------------------------------------------------------------- /Source/JoyCon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoyCon.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// enum values for JoyCon 12 | public enum JoyCon { 13 | 14 | /// Controller types 15 | public enum ControllerType: String { 16 | case FamicomController1 = "Famicom Controller 1" 17 | case FamicomController2 = "Famicom Controller 2" 18 | case JoyConL = "Joy-Con (L)" 19 | case JoyConR = "Joy-Con (R)" 20 | case ProController = "Pro Controller" 21 | case SNESController = "SNES Controlller" 22 | case unknown = "unknown" 23 | } 24 | 25 | /// Types of the output report 26 | public enum OutputType: UInt8 { 27 | case subcommand = 0x01 28 | case firmwareUpdate = 0x03 29 | case rumble = 0x10 30 | case nfcIR = 0x11 31 | } 32 | 33 | /// HCI states which are used for a "Set HCI state" subcommand 34 | public enum HCIState: UInt8 { 35 | case disconnect = 0x00 36 | case rebootAndReconnect = 0x01 37 | case rebootAndParing = 0x02 38 | case rebootAndReconnectHome = 0x04 39 | } 40 | 41 | /// Input report modes 42 | public enum InputMode: UInt8 { 43 | case pollingNFCIR = 0x00 44 | case pollingNFCIRConfig = 0x01 45 | case pollingNFCIRData = 0x02 46 | case pollingIRCamera = 0x03 47 | case standardFull = 0x30 48 | case nfcIR = 0x31 49 | case simple = 0x3F 50 | } 51 | 52 | /// Player light patterns 53 | public enum PlayerLightPattern { 54 | case on 55 | case off 56 | case flash 57 | } 58 | 59 | /// Battery status 60 | public enum BatteryStatus: UInt8 { 61 | case full 62 | case medium 63 | case low 64 | case critical 65 | case empty 66 | case unknown 67 | } 68 | 69 | /// Buttons 70 | public enum Button { 71 | // JoyCon (L) 72 | case Minus 73 | case Capture 74 | case LStick 75 | case Down 76 | case Up 77 | case Right 78 | case Left 79 | /// SR button of Joy-Con (L) 80 | case LeftSR 81 | /// SL button of Joy-Con (L) 82 | case LeftSL 83 | case L 84 | case ZL 85 | 86 | // JoyCon (R) 87 | case Plus 88 | case Home 89 | case RStick 90 | case X 91 | case Y 92 | case B 93 | case A 94 | /// SR button of Joy-Con (R) 95 | case RightSR 96 | /// SL button of Joy-Con (R) 97 | case RightSL 98 | case R 99 | case ZR 100 | 101 | // Famicom Controller 102 | case Start 103 | case Select 104 | } 105 | 106 | /// Stick directions 107 | public enum StickDirection { 108 | case Up 109 | case UpRight 110 | case Right 111 | case DownRight 112 | case Down 113 | case DownLeft 114 | case Left 115 | case UpLeft 116 | case Neutral 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Source/JoyConManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoyConManager.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import IOKit 11 | import IOKit.hid 12 | 13 | let controllerTypeOutputReport: [UInt8] = [ 14 | JoyCon.OutputType.subcommand.rawValue, // type 15 | 0x0f, // packet counter 16 | 0x00, 0x01, 0x00, 0x40, 0x00, 0x01, 0x00, 0x40, // rumble data 17 | Subcommand.CommandType.getSPIFlash.rawValue, // subcommand type 18 | 0x12, 0x60, 0x00, 0x00, // address 19 | 0x01, // data length 20 | ] 21 | 22 | /// The manager class to handle controller connection/disconnection events 23 | public class JoyConManager { 24 | static let vendorID: Int32 = 0x057E 25 | static let joyConLID: Int32 = 0x2006 // Joy-Con (L) 26 | static let joyConRID: Int32 = 0x2007 // Joy-Con (R), Famicom Controller 1&2 27 | static let proConID: Int32 = 0x2009 // Pro Controller 28 | static let snesConID: Int32 = 0x2017 // SNES Controller 29 | 30 | static let joyConLType: UInt8 = 0x01 31 | static let joyConRType: UInt8 = 0x02 32 | static let proConType: UInt8 = 0x03 33 | static let famicomCon1Type: UInt8 = 0x07 34 | static let famicomCon2Type: UInt8 = 0x08 35 | static let snesConType: UInt8 = 0x0B 36 | 37 | private let manager: IOHIDManager = IOHIDManagerCreate(kCFAllocatorDefault, IOOptionBits(kIOHIDOptionsTypeNone)) 38 | private var matchingControllers: [IOHIDDevice] = [] 39 | private var controllers: [IOHIDDevice: Controller] = [:] 40 | private var runLoop: RunLoop? = nil 41 | 42 | /// Handler for a controller connection event 43 | public var connectHandler: ((_ controller: Controller) -> Void)? = nil 44 | /// Handler for a controller disconnection event 45 | public var disconnectHandler: ((_ controller: Controller) -> Void)? = nil 46 | 47 | /// Initialize a manager 48 | public init() {} 49 | 50 | let handleMatchCallback: IOHIDDeviceCallback = { (context, result, sender, device) in 51 | let manager: JoyConManager = unsafeBitCast(context, to: JoyConManager.self) 52 | manager.handleMatch(result: result, sender: sender, device: device) 53 | } 54 | 55 | let handleInputCallback: IOHIDValueCallback = { (context, result, sender, value) in 56 | let manager: JoyConManager = unsafeBitCast(context, to: JoyConManager.self) 57 | manager.handleInput(result: result, sender: sender, value: value) 58 | } 59 | 60 | let handleRemoveCallback: IOHIDDeviceCallback = { (context, result, sender, device) in 61 | let manager: JoyConManager = unsafeBitCast(context, to: JoyConManager.self) 62 | manager.handleRemove(result: result, sender: sender, device: device) 63 | } 64 | 65 | func handleMatch(result: IOReturn, sender: UnsafeMutableRawPointer?, device: IOHIDDevice) { 66 | if (self.controllers.contains { (dev, ctrl) in dev == device }) { 67 | return 68 | } 69 | 70 | self.matchingControllers.append(device) 71 | let result = IOHIDDeviceSetReport(device, kIOHIDReportTypeOutput, CFIndex(0x01), controllerTypeOutputReport, controllerTypeOutputReport.count); 72 | if (result != kIOReturnSuccess) { 73 | print(String(format: "IOHIDDeviceSetReport error: %d", result)) 74 | return 75 | } 76 | } 77 | 78 | func handleControllerType(device: IOHIDDevice, result: IOReturn, value: IOHIDValue) { 79 | guard self.matchingControllers.contains(device) else { return } 80 | let ptr = IOHIDValueGetBytePtr(value) 81 | let address = ReadUInt32(from: ptr+14) 82 | let length = Int((ptr+18).pointee) 83 | guard address == 0x6012, length == 1 else { return } 84 | let buffer = UnsafeBufferPointer(start: ptr+19, count: length) 85 | let data = Array(buffer) 86 | 87 | var _controller: Controller? = nil 88 | switch data[0] { 89 | case JoyConManager.joyConLType: 90 | _controller = JoyConL(device: device) 91 | break 92 | case JoyConManager.joyConRType: 93 | _controller = JoyConR(device: device) 94 | break 95 | case JoyConManager.proConType: 96 | _controller = ProController(device: device) 97 | break 98 | case JoyConManager.famicomCon1Type: 99 | _controller = FamicomController1(device: device) 100 | break 101 | case JoyConManager.famicomCon2Type: 102 | _controller = FamicomController2(device: device) 103 | break 104 | case JoyConManager.snesConType: 105 | _controller = SNESController(device: device) 106 | break 107 | default: 108 | break 109 | } 110 | 111 | guard let controller = _controller else { return } 112 | self.matchingControllers.removeAll { $0 == device } 113 | self.controllers[device] = controller 114 | controller.isConnected = true 115 | controller.readInitializeData { [weak self] in 116 | self?.connectHandler?(controller) 117 | } 118 | } 119 | 120 | func handleInput(result: IOReturn, sender: UnsafeMutableRawPointer?, value: IOHIDValue) { 121 | guard let sender = sender else { return } 122 | let device = Unmanaged.fromOpaque(sender).takeUnretainedValue(); 123 | 124 | if self.matchingControllers.contains(device) { 125 | self.handleControllerType(device: device, result: result, value: value) 126 | return 127 | } 128 | 129 | guard let controller = self.controllers[device] else { return } 130 | if (result == kIOReturnSuccess) { 131 | controller.handleInput(value: value) 132 | } else { 133 | controller.handleError(result: result, value: value) 134 | } 135 | } 136 | 137 | func handleRemove(result: IOReturn, sender: UnsafeMutableRawPointer?, device: IOHIDDevice) { 138 | guard let controller = self.controllers[device] else { return } 139 | controller.isConnected = false 140 | 141 | self.controllers.removeValue(forKey: device) 142 | controller.cleanUp() 143 | 144 | self.disconnectHandler?(controller) 145 | } 146 | 147 | private func registerDeviceCallback() { 148 | IOHIDManagerRegisterDeviceMatchingCallback(self.manager, self.handleMatchCallback, unsafeBitCast(self, to: UnsafeMutableRawPointer.self)) 149 | IOHIDManagerRegisterDeviceRemovalCallback(self.manager, self.handleRemoveCallback, unsafeBitCast(self, to: UnsafeMutableRawPointer.self)) 150 | IOHIDManagerRegisterInputValueCallback(self.manager, self.handleInputCallback, unsafeBitCast(self, to: UnsafeMutableRawPointer.self)) 151 | } 152 | 153 | private func unregisterDeviceCallback() { 154 | IOHIDManagerRegisterDeviceMatchingCallback(self.manager, nil, nil) 155 | IOHIDManagerRegisterDeviceRemovalCallback(self.manager, nil, nil) 156 | IOHIDManagerRegisterInputValueCallback(self.manager, nil, nil) 157 | } 158 | 159 | private func cleanUp() { 160 | self.controllers.values.forEach { controller in 161 | controller.cleanUp() 162 | } 163 | self.controllers.removeAll() 164 | } 165 | 166 | /// Start waiting for controller connection/disconnection events in the current thread. 167 | /// If you don't want to stop the current thread, use `runAsync()` instead. 168 | /// - Returns: kIOReturnSuccess if succeeded. IOReturn error value if failed. 169 | public func run() -> IOReturn { 170 | let joyConLCriteria: [String: Any] = [ 171 | kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop, 172 | kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad, 173 | kIOHIDVendorIDKey: JoyConManager.vendorID, 174 | kIOHIDProductIDKey: JoyConManager.joyConLID, 175 | ] 176 | let joyConRCriteria: [String: Any] = [ 177 | kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop, 178 | kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad, 179 | kIOHIDVendorIDKey: JoyConManager.vendorID, 180 | kIOHIDProductIDKey: JoyConManager.joyConRID, 181 | ] 182 | let proConCriteria: [String: Any] = [ 183 | kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop, 184 | kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad, 185 | kIOHIDVendorIDKey: JoyConManager.vendorID, 186 | kIOHIDProductIDKey: JoyConManager.proConID, 187 | ] 188 | let snesConCriteria: [String: Any] = [ 189 | kIOHIDDeviceUsagePageKey: kHIDPage_GenericDesktop, 190 | kIOHIDDeviceUsageKey: kHIDUsage_GD_GamePad, 191 | kIOHIDVendorIDKey: JoyConManager.vendorID, 192 | kIOHIDProductIDKey: JoyConManager.snesConID, 193 | ] 194 | let criteria = [joyConLCriteria, joyConRCriteria, proConCriteria, snesConCriteria] 195 | 196 | let runLoop = RunLoop.current 197 | 198 | IOHIDManagerSetDeviceMatchingMultiple(self.manager, criteria as CFArray) 199 | IOHIDManagerScheduleWithRunLoop(self.manager, runLoop.getCFRunLoop(), CFRunLoopMode.defaultMode.rawValue) 200 | let ret = IOHIDManagerOpen(self.manager, IOOptionBits(kIOHIDOptionsTypeSeizeDevice)) 201 | if (ret != kIOReturnSuccess) { 202 | print("Failed to open HID manager") 203 | return ret 204 | } 205 | 206 | self.registerDeviceCallback() 207 | 208 | self.runLoop = runLoop 209 | self.runLoop?.run() 210 | 211 | IOHIDManagerClose(self.manager, IOOptionBits(kIOHIDOptionsTypeSeizeDevice)) 212 | IOHIDManagerUnscheduleFromRunLoop(self.manager, runLoop.getCFRunLoop(), CFRunLoopMode.defaultMode.rawValue) 213 | 214 | return kIOReturnSuccess 215 | } 216 | 217 | /// Start waiting for controller connection/disconnection events in a new thread. 218 | /// If you want to wait for the events synchronously, use `run()` instead. 219 | /// - Returns: kIOReturnSuccess if succeeded. IOReturn error value if failed. 220 | public func runAsync() -> IOReturn { 221 | DispatchQueue.global().async { [weak self] in 222 | _ = self?.run() 223 | } 224 | return kIOReturnSuccess 225 | } 226 | 227 | /// Stop waiting for controller connection/disconnection events 228 | public func stop() { 229 | if let currentLoop = self.runLoop?.getCFRunLoop() { 230 | CFRunLoopStop(currentLoop) 231 | } 232 | 233 | self.unregisterDeviceCallback() 234 | self.cleanUp() 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /Source/JoyConSwift.h: -------------------------------------------------------------------------------- 1 | // 2 | // JoyConSwift.h 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for JoyConSwift. 12 | FOUNDATION_EXPORT double JoyConSwiftVersionNumber; 13 | 14 | //! Project version string for JoyConSwift. 15 | FOUNDATION_EXPORT const unsigned char JoyConSwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | -------------------------------------------------------------------------------- /Source/Rumble.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Rumble.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2020/06/19. 6 | // 7 | 8 | import Foundation 9 | 10 | // Assuming A4 is 442Hz 11 | public enum Rumble { 12 | // 40.88 - 626.29 (E1 - D#5) 13 | public enum LowFrequency: UInt8 { 14 | case E1 = 0x02 // 41.39 => 41.77 15 | case F1 = 0x04 // 43.85 => 43.62 16 | case Fs1 = 0x07 // 46.46 => 46.55 17 | case G1 = 0x0a // 49.22 => 49.67 18 | case Gs1 = 0x0c // 52.15 => 51.87 19 | case A1 = 0x0f // 55.25 => 55.36 20 | case As1 = 0x12 // 58.54 => 59.07 21 | case B1 = 0x14 // 62.02 => 61.69 22 | case C2 = 0x17 // 65.70 => 65.83 23 | case Cs2 = 0x1a // 69.61 => 70.25 24 | case D2 = 0x1c // 73.75 => 73.36 25 | case Ds2 = 0x1f // 78.14 => 78.29 26 | case E2 = 0x22 // 82.78 => 83.54 27 | case F2 = 0x24 // 87.70 => 87.24 28 | case Fs2 = 0x27 // 92.92 => 93.10 29 | case G2 = 0x2a // 98.44 => 99.35 30 | case Gs2 = 0x2c // 104.30 => 103.75 31 | case A2 = 0x2f // 110.50 => 110.71 32 | case As2 = 0x32 // 117.07 => 118.14 33 | case B2 = 0x34 // 124.03 => 123.38 34 | case C3 = 0x37 // 131.41 => 131.66 35 | case Cs3 = 0x3a // 139.22 => 140.50 36 | case D3 = 0x3c // 147.50 => 146.72 37 | case Ds3 = 0x3f // 156.27 => 156.57 38 | case E3 = 0x42 // 165.56 => 167.08 39 | case F3 = 0x44 // 175.41 => 174.48 40 | case Fs3 = 0x47 // 185.84 => 186.20 41 | case G3 = 0x4a // 196.89 => 198.70 42 | case Gs3 = 0x4c // 208.60 => 207.49 43 | case A3 = 0x4f // 221.00 => 221.43 44 | case As3 = 0x52 // 234.14 => 236.29 45 | case B3 = 0x54 // 248.06 => 246.75 46 | case C4 = 0x57 // 262.81 => 262.32 47 | case Cs4 = 0x5a // 278.44 => 281.00 48 | case D4 = 0x5c // 295.00 => 293.44 49 | case Ds4 = 0x5f // 312.54 => 313.14 50 | case E4 = 0x62 // 331.13 => 334.17 51 | case F4 = 0x64 // 350.82 => 348.96 52 | case Fs4 = 0x67 // 371.68 => 372.39 53 | case G4 = 0x6a // 393.78 => 397.39 54 | case Gs4 = 0x6c // 417.19 => 414.99 55 | case A4 = 0x6f // 442.00 => 442.85 56 | case As4 = 0x72 // 468.28 => 472.58 57 | case B4 = 0x74 // 496.13 => 493.51 58 | case C5 = 0x77 // 525.63 => 526.64 59 | case Cs5 = 0x7a // 556.89 => 562.00 60 | case D5 = 0x7c // 590.00 => 586.88 61 | case Ds5 = 0x7f // 625.08 => 626.29 62 | } 63 | 64 | // 81.75 - 1252.57 Hz (E2 - D#6) 65 | public enum HighFrequency: UInt8 { 66 | case E2 = 0x02 // 82.78 => 83.54 67 | case F2 = 0x04 // 87.70 => 87.24 68 | case Fs2 = 0x07 // 92.92 => 93.10 69 | case G2 = 0x0a // 98.44 => 99.35 70 | case Gs2 = 0x0c // 104.30 => 103.75 71 | case A2 = 0x0f // 110.50 => 110.71 72 | case As2 = 0x12 // 117.07 => 118.14 73 | case B2 = 0x14 // 124.03 => 123.38 74 | case C3 = 0x17 // 131.41 => 131.66 75 | case Cs3 = 0x1a // 139.22 => 140.50 76 | case D3 = 0x1c // 147.50 => 146.72 77 | case Ds3 = 0x1f // 156.27 => 156.57 78 | case E3 = 0x22 // 165.56 => 167.08 79 | case F3 = 0x24 // 175.41 => 174.48 80 | case Fs3 = 0x27 // 185.84 => 186.20 81 | case G3 = 0x2a // 196.89 => 198.70 82 | case Gs3 = 0x2c // 208.60 => 207.49 83 | case A3 = 0x2f // 221.00 => 221.43 84 | case As3 = 0x32 // 234.14 => 236.29 85 | case B3 = 0x34 // 248.06 => 246.75 86 | case C4 = 0x37 // 262.81 => 262.32 87 | case Cs4 = 0x3a // 278.44 => 281.00 88 | case D4 = 0x3c // 295.00 => 293.44 89 | case Ds4 = 0x3f // 312.54 => 313.14 90 | case E4 = 0x42 // 331.13 => 334.17 91 | case F4 = 0x44 // 350.82 => 348.96 92 | case Fs4 = 0x47 // 371.68 => 372.39 93 | case G4 = 0x4a // 393.78 => 397.39 94 | case Gs4 = 0x4c // 417.19 => 414.99 95 | case A4 = 0x4f // 442.00 => 442.85 96 | case As4 = 0x52 // 468.28 => 472.58 97 | case B4 = 0x54 // 496.13 => 493.51 98 | case C5 = 0x57 // 525.63 => 526.64 99 | case Cs5 = 0x5a // 556.89 => 562.00 100 | case D5 = 0x5c // 590.00 => 586.88 101 | case Ds5 = 0x5f // 625.08 => 626.29 102 | case E5 = 0x62 // 662.25 => 668.34 103 | case F5 = 0x64 // 701.63 => 697.92 104 | case Fs5 = 0x67 // 743.35 => 744.78 105 | case G5 = 0x6a // 787.55 => 794.79 106 | case Gs5 = 0x6c // 834.38 => 829.98 107 | case A5 = 0x6f // 884.00 => 885.70 108 | case As5 = 0x72 // 936.57 => 945.17 109 | case B5 = 0x74 // 992.26 => 987.01 110 | case C6 = 0x77 // 1051.26 => 1053.28 111 | case Cs6 = 0x7a // 1113.77 => 1124.00 112 | case D6 = 0x7c // 1180.00 => 1173.77 113 | case Ds6 = 0x7f // 1250.16 => 1252.57 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /Source/Subcommand.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubCommand.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/28. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Bluetooth HID subcommands 12 | class Subcommand { 13 | enum CommandType: UInt8 { 14 | case getControllerState = 0x00 15 | case manualPairing = 0x01 16 | case getDeviceInfo = 0x02 17 | case setInputMode = 0x03 18 | case getTriggerTime = 0x04 19 | case getPageListState = 0x05 20 | case setHCIState = 0x06 21 | case resetParingInfo = 0x07 22 | case setLowPowerState = 0x08 23 | case getSPIFlash = 0x10 24 | case setSPIFlash = 0x11 25 | case eraseSPISector = 0x12 26 | case resetNFCIR = 0x20 27 | case setNFCIRConfig = 0x21 28 | case setNFCIRState = 0x22 29 | case setPlayerLights = 0x30 30 | case getPlayerLights = 0x31 31 | case setHomeLight = 0x38 32 | case enableIMU = 0x40 33 | case setIMUSensitivity = 0x41 34 | case setIMURegisters = 0x42 35 | case getIMURegisters = 0x43 36 | case enableVibration = 0x48 37 | case getRegulatedVoltage = 0x50 38 | case setGPIOValue = 0x51 39 | case getGPIOValue = 0x52 40 | } 41 | 42 | let type: CommandType 43 | let data: [UInt8] 44 | var responseHandler: ((_ value: IOHIDValue?) -> Void)? 45 | var timer: Timer? 46 | 47 | init(type: CommandType, data: [UInt8], responseHandler: ((_ value: IOHIDValue?) -> Void)?) { 48 | self.type = type 49 | self.data = data 50 | self.responseHandler = responseHandler 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Source/Utils.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utils.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | func ReadInt16(from ptr: UnsafePointer) -> Int16 { 12 | return ptr.withMemoryRebound(to: Int16.self, capacity: 1) { $0.pointee } 13 | } 14 | 15 | func ReadUInt16(from ptr: UnsafePointer) -> UInt16 { 16 | return ptr.withMemoryRebound(to: UInt16.self, capacity: 1) { $0.pointee } 17 | } 18 | 19 | func ReadInt32(from ptr: UnsafePointer) -> Int32 { 20 | return ptr.withMemoryRebound(to: Int32.self, capacity: 1) { $0.pointee } 21 | } 22 | 23 | func ReadUInt32(from ptr: UnsafePointer) -> UInt32 { 24 | return ptr.withMemoryRebound(to: UInt32.self, capacity: 1) { $0.pointee } 25 | } 26 | -------------------------------------------------------------------------------- /Source/controllers/FamicomController1.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FamicomController1.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2020/08/06. 6 | // Copyright © 2020 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// Famicom Controller for Player 1 class 13 | public class FamicomController1: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .B, 16 | 2: .A, 17 | 5: .L, 18 | 6: .R, 19 | 9: .Select, 20 | 10: .Start 21 | ] 22 | 23 | public override var type: JoyCon.ControllerType { 24 | return .FamicomController1 25 | } 26 | 27 | override func readSimpleState(value: IOHIDValue) { 28 | super.readSimpleState(value: value) 29 | 30 | let ptr = IOHIDValueGetBytePtr(value) 31 | let element = IOHIDValueGetElement(value) 32 | let buttonNo = IOHIDElementGetUsage(element) 33 | let buttonState = ptr.pointee 34 | 35 | if buttonNo == 57 { 36 | // D-Pad 37 | let up = (buttonState == 7 || buttonState == 0 || buttonState == 1) 38 | let right = (buttonState == 1 || buttonState == 2 || buttonState == 3) 39 | let down = (buttonState == 3 || buttonState == 4 || buttonState == 5) 40 | let left = (buttonState == 5 || buttonState == 6 || buttonState == 7) 41 | 42 | self.setButtonState(state: [ 43 | .Up: up, 44 | .Right: right, 45 | .Down: down, 46 | .Left: left 47 | ]) 48 | } else if let button = FamicomController1.buttonMap[buttonNo] { 49 | let isPushed = (buttonState != 0) 50 | self.setButtonState(state: [button: isPushed]) 51 | } 52 | } 53 | 54 | override func readStandardState(value: IOHIDValue) { 55 | super.readStandardState(value: value) 56 | 57 | let ptr = IOHIDValueGetBytePtr(value) 58 | let button1 = (ptr+2).pointee 59 | let button2 = (ptr+3).pointee 60 | let button3 = (ptr+4).pointee 61 | 62 | let b = button1 & 0x04 == 0x04 63 | let a = button1 & 0x08 == 0x08 64 | let r = button1 & 0x40 == 0x40 65 | let select = button2 & 0x01 == 0x01 66 | let start = button2 & 0x02 == 0x02 67 | let down = button3 & 0x01 == 0x01 68 | let up = button3 & 0x02 == 0x02 69 | let right = button3 & 0x04 == 0x04 70 | let left = button3 & 0x08 == 0x08 71 | let l = button3 & 0x40 == 0x40 72 | 73 | let newState: [JoyCon.Button: Bool] = [ 74 | .B: b, 75 | .A: a, 76 | .R: r, 77 | .Select: select, 78 | .Start: start, 79 | .Down: down, 80 | .Up: up, 81 | .Right: right, 82 | .Left: left, 83 | .L: l 84 | ] 85 | self.setButtonState(state: newState) 86 | } 87 | 88 | override public func readCalibration() {} 89 | } 90 | -------------------------------------------------------------------------------- /Source/controllers/FamicomController2.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FamicomController2.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2020/08/06. 6 | // Copyright © 2020 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// Famicom Controller for Player 2 class 13 | public class FamicomController2: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .B, 16 | 2: .A, 17 | 5: .L, 18 | 6: .R 19 | ] 20 | 21 | public override var type: JoyCon.ControllerType { 22 | return .FamicomController2 23 | } 24 | 25 | override func readSimpleState(value: IOHIDValue) { 26 | super.readSimpleState(value: value) 27 | 28 | let ptr = IOHIDValueGetBytePtr(value) 29 | let element = IOHIDValueGetElement(value) 30 | let buttonNo = IOHIDElementGetUsage(element) 31 | let buttonState = ptr.pointee 32 | 33 | if buttonNo == 57 { 34 | // D-Pad 35 | let up = (buttonState == 7 || buttonState == 0 || buttonState == 1) 36 | let right = (buttonState == 1 || buttonState == 2 || buttonState == 3) 37 | let down = (buttonState == 3 || buttonState == 4 || buttonState == 5) 38 | let left = (buttonState == 5 || buttonState == 6 || buttonState == 7) 39 | 40 | self.setButtonState(state: [ 41 | .Up: up, 42 | .Right: right, 43 | .Down: down, 44 | .Left: left 45 | ]) 46 | } else if let button = FamicomController2.buttonMap[buttonNo] { 47 | let isPushed = (buttonState != 0) 48 | self.setButtonState(state: [button: isPushed]) 49 | } 50 | } 51 | 52 | override func readStandardState(value: IOHIDValue) { 53 | super.readStandardState(value: value) 54 | 55 | let ptr = IOHIDValueGetBytePtr(value) 56 | let button1 = (ptr+2).pointee 57 | let button3 = (ptr+4).pointee 58 | 59 | let b = button1 & 0x04 == 0x04 60 | let a = button1 & 0x08 == 0x08 61 | let r = button1 & 0x40 == 0x40 62 | let down = button3 & 0x01 == 0x01 63 | let up = button3 & 0x02 == 0x02 64 | let right = button3 & 0x04 == 0x04 65 | let left = button3 & 0x08 == 0x08 66 | let l = button3 & 0x40 == 0x40 67 | 68 | let newState: [JoyCon.Button: Bool] = [ 69 | .B: b, 70 | .A: a, 71 | .R: r, 72 | .Down: down, 73 | .Up: up, 74 | .Right: right, 75 | .Left: left, 76 | .L: l 77 | ] 78 | self.setButtonState(state: newState) 79 | } 80 | 81 | override public func readCalibration() {} 82 | } 83 | -------------------------------------------------------------------------------- /Source/controllers/JoyConL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoyConL.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// Joy-Con (L) class 13 | public class JoyConL: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .Left, 16 | 2: .Down, 17 | 3: .Up, 18 | 4: .Right, 19 | 5: .LeftSL, 20 | 6: .LeftSR, 21 | 9: .Minus, 22 | 11: .LStick, 23 | 14: .Capture, 24 | 15: .L, 25 | 16: .ZL, 26 | ] 27 | 28 | static let stickDirection: [UInt8: JoyCon.StickDirection] = [ 29 | 0: .Up, 30 | 1: .UpRight, 31 | 2: .Right, 32 | 3: .DownRight, 33 | 4: .Down, 34 | 5: .DownLeft, 35 | 6: .Left, 36 | 7: .UpLeft, 37 | 8: .Neutral 38 | ] 39 | 40 | public override var type: JoyCon.ControllerType { 41 | return .JoyConL 42 | } 43 | 44 | override func readSimpleState(value: IOHIDValue) { 45 | super.readSimpleState(value: value) 46 | 47 | let ptr = IOHIDValueGetBytePtr(value) 48 | let element = IOHIDValueGetElement(value) 49 | let buttonNo = IOHIDElementGetUsage(element) 50 | let buttonState = ptr.pointee 51 | 52 | if buttonNo == 57 { 53 | // Stick 54 | if let direction = JoyConL.stickDirection[buttonState] { 55 | self.setLeftStickDirection(direction: direction) 56 | } 57 | } else if let button = JoyConL.buttonMap[buttonNo] { 58 | let isPushed = (buttonState != 0) 59 | self.setButtonState(state: [button: isPushed]) 60 | } 61 | } 62 | 63 | override func readStandardState(value: IOHIDValue) { 64 | super.readStandardState(value: value) 65 | 66 | let ptr = IOHIDValueGetBytePtr(value) 67 | let button1 = (ptr+3).pointee 68 | let button2 = (ptr+4).pointee 69 | 70 | let minus = (button1 & 0x01) == 0x01 71 | let capture = button1 & 0x20 == 0x20 72 | let stick = button1 & 0x08 == 0x08 73 | let down = button2 & 0x01 == 0x01 74 | let up = button2 & 0x02 == 0x02 75 | let right = button2 & 0x04 == 0x04 76 | let left = button2 & 0x08 == 0x08 77 | let sr = button2 & 0x10 == 0x10 78 | let sl = button2 & 0x20 == 0x20 79 | let l = button2 & 0x40 == 0x40 80 | let zl = button2 & 0x80 == 0x80 81 | 82 | let newState: [JoyCon.Button: Bool] = [ 83 | .Minus: minus, 84 | .Capture: capture, 85 | .LStick: stick, 86 | .Down: down, 87 | .Up: up, 88 | .Right: right, 89 | .Left: left, 90 | .LeftSR: sr, 91 | .LeftSL: sl, 92 | .L: l, 93 | .ZL: zl 94 | ] 95 | self.setButtonState(state: newState) 96 | 97 | self.readLStickData(value: value) 98 | } 99 | 100 | override public func readCalibration() { 101 | self.readLStickCalibration() 102 | self.readSensorCalibration() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/controllers/JoyConR.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoyConR.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// Joy-Con (R) class 13 | public class JoyConR: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .A, 16 | 2: .X, 17 | 3: .B, 18 | 4: .Y, 19 | 5: .RightSL, 20 | 6: .RightSR, 21 | 10: .Plus, 22 | 12: .RStick, 23 | 13: .Home, 24 | 15: .R, 25 | 16: .ZR 26 | ] 27 | 28 | static let stickDirection: [UInt8: JoyCon.StickDirection] = [ 29 | 0: .Left, 30 | 1: .UpLeft, 31 | 2: .Up, 32 | 3: .UpRight, 33 | 4: .Right, 34 | 5: .DownRight, 35 | 6: .Down, 36 | 7: .DownLeft, 37 | 8: .Neutral 38 | ] 39 | 40 | public override var type: JoyCon.ControllerType { 41 | return .JoyConR 42 | } 43 | 44 | override func readSimpleState(value: IOHIDValue) { 45 | super.readSimpleState(value: value) 46 | 47 | let ptr = IOHIDValueGetBytePtr(value) 48 | let element = IOHIDValueGetElement(value) 49 | let buttonNo = IOHIDElementGetUsage(element) 50 | let buttonState = ptr.pointee 51 | 52 | if buttonNo == 57 { 53 | // Stick 54 | if let direction = JoyConR.stickDirection[buttonState] { 55 | self.setRightStickDirection(direction: direction) 56 | } 57 | } else if let button = JoyConR.buttonMap[buttonNo] { 58 | let isPushed = (buttonState != 0) 59 | self.setButtonState(state: [button: isPushed]) 60 | } 61 | } 62 | 63 | override func readStandardState(value: IOHIDValue) { 64 | super.readStandardState(value: value) 65 | 66 | let ptr = IOHIDValueGetBytePtr(value) 67 | let button1 = (ptr+2).pointee 68 | let button2 = (ptr+3).pointee 69 | 70 | let y = button1 & 0x01 == 0x01 71 | let x = button1 & 0x02 == 0x02 72 | let b = button1 & 0x04 == 0x04 73 | let a = button1 & 0x08 == 0x08 74 | let sr = button1 & 0x10 == 0x10 75 | let sl = button1 & 0x20 == 0x20 76 | let r = button1 & 0x40 == 0x40 77 | let zr = button1 & 0x80 == 0x80 78 | let plus = button2 & 0x02 == 0x02 79 | let stick = button2 & 0x04 == 0x04 80 | let home = button2 & 0x10 == 0x10 81 | 82 | let newState: [JoyCon.Button: Bool] = [ 83 | .Y: y, 84 | .X: x, 85 | .B: b, 86 | .A: a, 87 | .RightSR: sr, 88 | .RightSL: sl, 89 | .R: r, 90 | .ZR: zr, 91 | .Plus: plus, 92 | .Home: home, 93 | .RStick: stick 94 | ] 95 | self.setButtonState(state: newState) 96 | 97 | self.readRStickData(value: value) 98 | } 99 | 100 | override public func readCalibration() { 101 | self.readRStickCalibration() 102 | self.readSensorCalibration() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/controllers/ProController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JoyConR.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2019/06/16. 6 | // Copyright © 2019 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// Pro Controller class 13 | public class ProController: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .B, 16 | 2: .A, 17 | 3: .Y, 18 | 4: .X, 19 | 5: .L, 20 | 6: .R, 21 | 7: .ZL, 22 | 8: .ZR, 23 | 9: .Minus, 24 | 10: .Plus, 25 | 11: .LStick, 26 | 12: .RStick, 27 | 13: .Home, 28 | 14: .Capture, 29 | ] 30 | 31 | static let stickDirection: [UInt8: JoyCon.StickDirection] = [ 32 | 0: .Left, 33 | 1: .UpLeft, 34 | 2: .Up, 35 | 3: .UpRight, 36 | 4: .Right, 37 | 5: .DownRight, 38 | 6: .Down, 39 | 7: .DownLeft, 40 | 8: .Neutral 41 | ] 42 | 43 | public override var type: JoyCon.ControllerType { 44 | return .ProController 45 | } 46 | 47 | override func readSimpleState(value: IOHIDValue) { 48 | super.readSimpleState(value: value) 49 | 50 | let ptr = IOHIDValueGetBytePtr(value) 51 | let element = IOHIDValueGetElement(value) 52 | let buttonNo = IOHIDElementGetUsage(element) 53 | let buttonState = ptr.pointee 54 | 55 | if buttonNo == 57 { 56 | // D-Pad 57 | let up = (buttonState == 7 || buttonState == 0 || buttonState == 1) 58 | let right = (buttonState == 1 || buttonState == 2 || buttonState == 3) 59 | let down = (buttonState == 3 || buttonState == 4 || buttonState == 5) 60 | let left = (buttonState == 5 || buttonState == 6 || buttonState == 7) 61 | 62 | self.setButtonState(state: [ 63 | .Up: up, 64 | .Right: right, 65 | .Down: down, 66 | .Left: left 67 | ]) 68 | } else if let button = ProController.buttonMap[buttonNo] { 69 | let isPushed = (buttonState != 0) 70 | self.setButtonState(state: [button: isPushed]) 71 | } else { 72 | // buttonNo: 48-52 ??? 73 | } 74 | } 75 | 76 | override func readStandardState(value: IOHIDValue) { 77 | super.readStandardState(value: value) 78 | 79 | let ptr = IOHIDValueGetBytePtr(value) 80 | let button1 = (ptr+2).pointee 81 | let button2 = (ptr+3).pointee 82 | let button3 = (ptr+4).pointee 83 | 84 | let y = button1 & 0x01 == 0x01 85 | let x = button1 & 0x02 == 0x02 86 | let b = button1 & 0x04 == 0x04 87 | let a = button1 & 0x08 == 0x08 88 | let r = button1 & 0x40 == 0x40 89 | let zr = button1 & 0x80 == 0x80 90 | let minus = button2 & 0x01 == 0x01 91 | let plus = button2 & 0x02 == 0x02 92 | let rStick = button2 & 0x04 == 0x04 93 | let lStick = button2 & 0x08 == 0x08 94 | let home = button2 & 0x10 == 0x10 95 | let capture = button2 & 0x20 == 0x20 96 | let down = button3 & 0x01 == 0x01 97 | let up = button3 & 0x02 == 0x02 98 | let right = button3 & 0x04 == 0x04 99 | let left = button3 & 0x08 == 0x08 100 | let l = button3 & 0x40 == 0x40 101 | let zl = button3 & 0x80 == 0x80 102 | 103 | let newState: [JoyCon.Button: Bool] = [ 104 | .Y: y, 105 | .X: x, 106 | .B: b, 107 | .A: a, 108 | .R: r, 109 | .ZR: zr, 110 | .Minus: minus, 111 | .Plus: plus, 112 | .LStick: lStick, 113 | .RStick: rStick, 114 | .Home: home, 115 | .Capture: capture, 116 | .Down: down, 117 | .Up: up, 118 | .Right: right, 119 | .Left: left, 120 | .L: l, 121 | .ZL: zl 122 | ] 123 | self.setButtonState(state: newState) 124 | 125 | self.readLStickData(value: value) 126 | self.readRStickData(value: value) 127 | } 128 | 129 | override public func readCalibration() { 130 | self.readLStickCalibration() 131 | self.readRStickCalibration() 132 | self.readSensorCalibration() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Source/controllers/SNESController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SNESController.swift 3 | // JoyConSwift 4 | // 5 | // Created by magicien on 2020/08/08. 6 | // Copyright © 2020 DarkHorse. All rights reserved. 7 | // 8 | 9 | import IOKit 10 | import IOKit.hid 11 | 12 | /// SNES Controller class 13 | public class SNESController: Controller { 14 | static let buttonMap: [UInt32: JoyCon.Button] = [ 15 | 1: .B, 16 | 2: .A, 17 | 3: .Y, 18 | 4: .X, 19 | 5: .L, 20 | 6: .R, 21 | 7: .ZL, 22 | 9: .Select, 23 | 10: .Start, 24 | 16: .ZR 25 | ] 26 | 27 | public override var type: JoyCon.ControllerType { 28 | return .SNESController 29 | } 30 | 31 | override func readSimpleState(value: IOHIDValue) { 32 | super.readSimpleState(value: value) 33 | 34 | let ptr = IOHIDValueGetBytePtr(value) 35 | let element = IOHIDValueGetElement(value) 36 | let buttonNo = IOHIDElementGetUsage(element) 37 | let buttonState = ptr.pointee 38 | 39 | if buttonNo == 57 { 40 | // D-Pad 41 | let up = (buttonState == 7 || buttonState == 0 || buttonState == 1) 42 | let right = (buttonState == 1 || buttonState == 2 || buttonState == 3) 43 | let down = (buttonState == 3 || buttonState == 4 || buttonState == 5) 44 | let left = (buttonState == 5 || buttonState == 6 || buttonState == 7) 45 | 46 | self.setButtonState(state: [ 47 | .Up: up, 48 | .Right: right, 49 | .Down: down, 50 | .Left: left 51 | ]) 52 | } else if let button = SNESController.buttonMap[buttonNo] { 53 | let isPushed = (buttonState != 0) 54 | self.setButtonState(state: [button: isPushed]) 55 | } 56 | } 57 | 58 | override func readStandardState(value: IOHIDValue) { 59 | super.readStandardState(value: value) 60 | 61 | let ptr = IOHIDValueGetBytePtr(value) 62 | let button1 = (ptr+2).pointee 63 | let button2 = (ptr+3).pointee 64 | let button3 = (ptr+4).pointee 65 | 66 | let y = button1 & 0x01 == 0x01 67 | let x = button1 & 0x02 == 0x02 68 | let b = button1 & 0x04 == 0x04 69 | let a = button1 & 0x08 == 0x08 70 | let r = button1 & 0x40 == 0x40 71 | let zr = button1 & 0x80 == 0x80 72 | let select = button2 & 0x01 == 0x01 73 | let start = button2 & 0x02 == 0x02 74 | let down = button3 & 0x01 == 0x01 75 | let up = button3 & 0x02 == 0x02 76 | let right = button3 & 0x04 == 0x04 77 | let left = button3 & 0x08 == 0x08 78 | let l = button3 & 0x40 == 0x40 79 | let zl = button3 & 0x80 == 0x80 80 | 81 | let newState: [JoyCon.Button: Bool] = [ 82 | .Y: y, 83 | .X: x, 84 | .B: b, 85 | .A: a, 86 | .R: r, 87 | .ZR: zr, 88 | .Select: select, 89 | .Start: start, 90 | .Down: down, 91 | .Up: up, 92 | .Right: right, 93 | .Left: left, 94 | .L: l, 95 | .ZL: zl 96 | ] 97 | self.setButtonState(state: newState) 98 | } 99 | 100 | override public func readCalibration() {} 101 | } 102 | -------------------------------------------------------------------------------- /docs/HomeLEDPattern/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JoyConSwift - HomeLEDPattern 7 | 1179 | 1180 | 1181 |
1182 | 1183 | 1184 | JoyConSwift 1185 | 1186 | Documentation 1187 | 1188 | Beta 1189 |
1190 | 1191 | 1196 | 1197 | 1203 | 1204 |
1205 |
1206 |

1207 | Structure 1208 | Home​LEDPattern 1209 |

1210 | 1211 |
public struct HomeLEDPattern
1212 |
1213 |

Home LED pattern data for setHomeLight function

1214 | 1215 |
1216 | 1217 |
1218 |

Initializers

1219 | 1220 |
1221 |

1222 | init(intensity:​fade​Duration:​duration:​) 1223 |

1224 |
public init(intensity: UInt8, fadeDuration: UInt8, duration: UInt8)
1225 |
1226 |
1227 | 1228 | 1229 | 1230 |
1231 |
1232 | 1233 |
1234 |

1235 | Generated on using swift-doc. 1236 |

1237 |
1238 | 1239 | 1240 | -------------------------------------------------------------------------------- /docs/JoyCon/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JoyConSwift - JoyCon 7 | 1179 | 1180 | 1181 |
1182 | 1183 | 1184 | JoyConSwift 1185 | 1186 | Documentation 1187 | 1188 | Beta 1189 |
1190 | 1191 | 1196 | 1197 | 1203 | 1204 |
1205 |
1206 |

1207 | Enumeration 1208 | Joy​Con 1209 |

1210 | 1211 |
public enum JoyCon
1212 |
1213 |

enum values for JoyCon

1214 | 1215 |
1216 | 1217 | 1218 | 1219 | 1220 | 1221 |
1222 |
1223 | 1224 |
1225 |

1226 | Generated on using swift-doc. 1227 |

1228 |
1229 | 1230 | 1231 | -------------------------------------------------------------------------------- /docs/Rumble/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | JoyConSwift - Rumble 7 | 1179 | 1180 | 1181 |
1182 | 1183 | 1184 | JoyConSwift 1185 | 1186 | Documentation 1187 | 1188 | Beta 1189 |
1190 | 1191 | 1196 | 1197 | 1203 | 1204 |
1205 |
1206 |

1207 | Enumeration 1208 | Rumble 1209 |

1210 | 1211 |
public enum Rumble
1212 | 1213 | 1214 | 1215 | 1216 | 1217 |
1218 |
1219 | 1220 | 1225 | 1226 | 1227 | -------------------------------------------------------------------------------- /generate_docs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -r ./docs 4 | swift doc generate Source/ --module-name JoyConSwift --output ./docs --format html 5 | 6 | FILES=`find ./docs -name *.html` 7 | for FILE in ${FILES}; do 8 | sed -i "" -e "s/href=\"\//href=\"\/JoyConSwift\//g" ${FILE} 9 | done 10 | --------------------------------------------------------------------------------