Overview
Comment: | OFGameController: Add support for Linux |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | gamecontroller |
Files: | files | file ages | folders |
SHA3-256: |
6f20cdbd5c0f186ea1efeca76fb7f4a4 |
User & Date: | js on 2024-05-09 12:57:21 |
Other Links: | branch diff | manifest | tags |
Context
2024-05-09
| ||
13:10 | OFGameController: Add missing import check-in: c5b37a9dd8 user: js tags: gamecontroller | |
12:57 | OFGameController: Add support for Linux check-in: 6f20cdbd5c user: js tags: gamecontroller | |
10:22 | Merge trunk into branch "gamecontroller" check-in: c0a80a897d user: js tags: gamecontroller | |
Changes
Modified src/OFGameController.h from [542208ba0a] to [64229ea449].
︙ | ︙ | |||
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN /** @file */ @class OFSet OF_GENERIC(ObjectType); /** * @brief A class for reading state from a game controller. */ OF_SUBCLASSING_RESTRICTED @interface OFGameController: OFObject #ifdef OF_HAVE_CLASS_PROPERTIES | > > > > > > > > > > > > > | > > > > > > | | > | | < | < < < < < < < | 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | #import "OFObject.h" #import "OFString.h" OF_ASSUME_NONNULL_BEGIN /** @file */ @class OFArray OF_GENERIC(ObjectType); @class OFMutableSet OF_GENERIC(ObjectType); @class OFSet OF_GENERIC(ObjectType); /** * @brief A class for reading state from a game controller. */ OF_SUBCLASSING_RESTRICTED @interface OFGameController: OFObject { #ifdef OF_LINUX OFString *_path; int _fd; OFString *_name; OFMutableSet *_buttons, *_pressedButtons; size_t _numAnalogSticks; OFPoint _analogStickPositions[2]; #endif } #ifdef OF_HAVE_CLASS_PROPERTIES @property (class, readonly, nonatomic) OFArray <OFGameController *> *controllers; #endif /** * @brief The name of the controller. */ @property (readonly, nonatomic, copy) OFString *name; /** * @brief The buttons the controller has. */ @property (readonly, nonatomic, copy) OFSet OF_GENERIC(OFString *) *buttons; /** * @brief The currently pressed buttons on the controller. */ @property (readonly, nonatomic, copy) OFSet OF_GENERIC(OFString *) *pressedButtons; /** * @brief The number of analog sticks the controller has. */ @property (readonly, nonatomic) size_t numAnalogSticks; /** * @brief Returns the available controllers. * * @return The available controllers */ + (OFArray OF_GENERIC(OFGameController *) *)controllers; - (instancetype)init OF_UNAVAILABLE; /** * @brief Returns the current position of the specified analog stick. * * The range is from (-1, -1) to (1, 1). |
︙ | ︙ |
Modified src/OFGameController.m from [8a9752888e] to [f307cfd03f].
︙ | ︙ | |||
19 20 21 22 23 24 25 | #include "config.h" #import "OFGameController.h" #import "OFOutOfRangeException.h" | > > | | | | < < < < < | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | #include "config.h" #import "OFGameController.h" #import "OFOutOfRangeException.h" #if defined(OF_LINUX) && defined(OF_HAVE_FILES) # include "platform/Linux/OFGameController.m" #elif defined(OF_NINTENDO_DS) # include "platform/NintendoDS/OFGameController.m" #elif defined(OF_NINTENDO_3DS) # include "platform/Nintendo3DS/OFGameController.m" #else @implementation OFGameController @dynamic name, buttons, pressedButtons, numAnalogSticks; + (OFArray OF_GENERIC(OFGameController *) *)controllers { return [OFArray array]; } - (instancetype)init { OF_INVALID_INIT_METHOD } |
︙ | ︙ |
Added src/platform/Linux/OFGameController.m version [ce46d32b19].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | /* * Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im> * * All rights reserved. * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License version 3.0 only, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * version 3.0 for more details. * * You should have received a copy of the GNU Lesser General Public License * version 3.0 along with this program. If not, see * <https://www.gnu.org/licenses/>. */ #include "config.h" #include <errno.h> #include <fcntl.h> #include <unistd.h> #import "OFGameController.h" #import "OFArray.h" #import "OFFileManager.h" #import "OFLocale.h" #import "OFSet.h" #include <sys/ioctl.h> #include <linux/input.h> #import "OFInitializationFailedException.h" #import "OFInvalidArgumentException.h" #import "OFOpenItemFailedException.h" #import "OFOutOfRangeException.h" #import "OFReadFailedException.h" @interface OFGameController () - (instancetype)of_initWithPath: (OFString *)path OF_METHOD_FAMILY(init); - (void)of_processEvents; @end static const uint16_t buttons[] = { BTN_A, BTN_B, BTN_C, BTN_X, BTN_Y, BTN_Z, BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, BTN_SELECT, BTN_START, BTN_MODE, BTN_THUMBL, BTN_THUMBR, BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT }; static OFString * buttonToName(uint16_t button) { switch (button) { case BTN_A: return @"A"; case BTN_B: return @"B"; case BTN_C: return @"C"; case BTN_X: return @"X"; case BTN_Y: return @"Y"; case BTN_Z: return @"Z"; case BTN_TL: return @"TL"; case BTN_TR: return @"TR"; case BTN_TL2: return @"TL2"; case BTN_TR2: return @"TR2"; case BTN_SELECT: return @"Select"; case BTN_START: return @"Start"; case BTN_MODE: return @"Mode"; case BTN_THUMBL: return @"Thumb L"; case BTN_THUMBR: return @"Thumb R"; case BTN_DPAD_UP: return @"D-Pad Up"; case BTN_DPAD_DOWN: return @"D-Pad Down"; case BTN_DPAD_LEFT: return @"D-Pad Left"; case BTN_DPAD_RIGHT: return @"D-Pad Right"; } return nil; } @implementation OFGameController @synthesize name = _name, buttons = _buttons; @synthesize numAnalogSticks = _numAnalogSticks; + (OFArray OF_GENERIC(OFGameController *) *)controllers { OFMutableArray *controllers = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); for (OFString *device in [[OFFileManager defaultManager] contentsOfDirectoryAtPath: @"/dev/input"]) { OFString *path; OFGameController *controller; if (![device hasPrefix: @"event"]) continue; path = [@"/dev/input" stringByAppendingPathComponent: device]; @try { controller = [[[OFGameController alloc] of_initWithPath: path] autorelease]; } @catch (OFOpenItemFailedException *e) { if (e.errNo == EACCES) continue; @throw e; } @catch (OFInvalidArgumentException *e) { /* Not a game controller. */ continue; } [controllers addObject: controller]; } [controllers sort]; [controllers makeImmutable]; objc_autoreleasePoolPop(pool); return controllers; } - (instancetype)init { OF_INVALID_INIT_METHOD } - (instancetype)of_initWithPath: (OFString *)path { self = [super init]; @try { OFStringEncoding encoding = [OFLocale encoding]; unsigned long evBits[OFRoundUpToPowerOf2(OF_ULONG_BIT, EV_MAX) / OF_ULONG_BIT] = { 0 }; unsigned long keyBits[OFRoundUpToPowerOf2(OF_ULONG_BIT, KEY_MAX) / OF_ULONG_BIT] = { 0 }; unsigned long absBits[OFRoundUpToPowerOf2(OF_ULONG_BIT, ABS_MAX) / OF_ULONG_BIT] = { 0 }; char name[128]; _path = [path copy]; if ((_fd = open([_path cStringWithEncoding: encoding], O_RDONLY | O_NONBLOCK)) == -1) @throw [OFOpenItemFailedException exceptionWithPath: _path mode: @"r" errNo: errno]; if (ioctl(_fd, EVIOCGBIT(0, sizeof(evBits)), evBits) == -1) @throw [OFInitializationFailedException exception]; if (!OFBitSetIsSet(evBits, EV_KEY)) @throw [OFInvalidArgumentException exception]; if (ioctl(_fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits) == -1) @throw [OFInitializationFailedException exception]; if (!OFBitSetIsSet(keyBits, BTN_GAMEPAD)) @throw [OFInvalidArgumentException exception]; if (ioctl(_fd, EVIOCGNAME(sizeof(name)), name) == -1) @throw [OFInitializationFailedException exception]; _name = [[OFString alloc] initWithCString: name encoding: encoding]; _buttons = [[OFMutableSet alloc] init]; for (size_t i = 0; i < sizeof(buttons) / sizeof(*buttons); i++) [_buttons addObject: buttonToName(buttons[i])]; _pressedButtons = [[OFMutableSet alloc] init]; if (OFBitSetIsSet(evBits, EV_ABS)) { if (ioctl(_fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits) == -1) @throw [OFInitializationFailedException exception]; if (OFBitSetIsSet(absBits, ABS_X) && OFBitSetIsSet(absBits, ABS_Y)) { _numAnalogSticks++; if (OFBitSetIsSet(absBits, ABS_RX) && OFBitSetIsSet(absBits, ABS_RY)) _numAnalogSticks++; } if (OFBitSetIsSet(absBits, ABS_HAT0X) && OFBitSetIsSet(absBits, ABS_HAT0Y)) { [_buttons addObject: @"D-Pad Left"]; [_buttons addObject: @"D-Pad Right"]; [_buttons addObject: @"D-Pad Up"]; [_buttons addObject: @"D-Pad Down"]; } } [_buttons makeImmutable]; } @catch (id e) { [self release]; @throw e; } return self; } - (void)dealloc { [_path release]; if (_fd != -1) close(_fd); [_name release]; [_buttons release]; [_pressedButtons release]; [super dealloc]; } - (void)of_processEvents { struct input_event event; for (;;) { errno = 0; if (read(_fd, &event, sizeof(event)) < (int)sizeof(event)) { if (errno == EWOULDBLOCK) return; @throw [OFReadFailedException exceptionWithObject: self requestedLength: sizeof(event) errNo: errno]; } switch (event.type) { case EV_KEY: if (event.value) [_pressedButtons addObject: buttonToName(event.code)]; else [_pressedButtons removeObject: buttonToName(event.code)]; break; case EV_ABS: switch (event.code) { case ABS_X: _analogStickPositions[0].x = (float)event.value / (event.value < 0 ? -INT16_MIN : INT16_MAX); break; case ABS_Y: _analogStickPositions[0].y = (float)event.value / (event.value < 0 ? -INT16_MIN : INT16_MAX); break; case ABS_RX: _analogStickPositions[1].x = (float)event.value / (event.value < 0 ? -INT16_MIN : INT16_MAX); break; case ABS_RY: _analogStickPositions[1].y = (float)event.value / (event.value < 0 ? -INT16_MIN : INT16_MAX); break; } break; } } } - (OFComparisonResult)compare: (OFGameController *)otherController { unsigned long long selfIndex, otherIndex; if (![otherController isKindOfClass: [OFGameController class]]) @throw [OFInvalidArgumentException exception]; selfIndex = [_path substringFromIndex: 16].unsignedLongLongValue; otherIndex = [otherController->_path substringFromIndex: 16] .unsignedLongLongValue; if (selfIndex > otherIndex) return OFOrderedDescending; if (selfIndex < otherIndex) return OFOrderedAscending; return OFOrderedSame; } - (OFSet *)pressedButtons { [self of_processEvents]; return [[_pressedButtons copy] autorelease]; } - (OFPoint)positionOfAnalogStickWithIndex: (size_t)index { if (index + 1 > _numAnalogSticks) @throw [OFOutOfRangeException exception]; [self of_processEvents]; return _analogStickPositions[index]; } - (OFString *)description { return [OFString stringWithFormat: @"<%@: %@>", self.class, self.name]; } @end |
Modified src/platform/Nintendo3DS/OFGameController.m from [5edf21e397] to [8b137a158f].
︙ | ︙ | |||
28 29 30 31 32 33 34 | #include <3ds.h> #undef id @interface OFGameController () - (instancetype)of_init OF_METHOD_FAMILY(init); @end | | | > > > | > > < < < < | < < < < | | > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #include <3ds.h> #undef id @interface OFGameController () - (instancetype)of_init OF_METHOD_FAMILY(init); @end static OFArray OF_GENERIC(OFGameController *) *controllers; static void initControllers(void) { void *pool = objc_autoreleasePoolPush(); controllers = [[OFArray alloc] initWithObject: [[[OFGameController alloc] of_init] autorelease]]; objc_autoreleasePoolPop(); } @implementation OFGameController + (OFArray OF_GENERIC(OFGameController *) *)controllers { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, initControllers); return [[controllers retain] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } - (instancetype)of_init { return [super init]; } - (OFString *)name { return @"Nintendo 3DS"; } - (OFSet *)buttons { return [OFSet setWithObjects: @"A", @"B", @"Select", @"Start", @"D-Pad Right", @"D-Pad Left", @"D-Pad Up", @"D-Pad Down", @"R", @"L", @"X", @"Y", @"ZL", @"ZR", @"C-Stick Right", @"C-Stick Left", @"C-Stick Up", @"C-Stick Down", nil]; |
︙ | ︙ | |||
137 138 139 140 141 142 143 | if (index > 0) @throw [OFOutOfRangeException exception]; hidCircleRead(&pos); return OFMakePoint( | | | > > > > > | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | if (index > 0) @throw [OFOutOfRangeException exception]; hidCircleRead(&pos); return OFMakePoint( (float)pos.dx / (pos.dx < 0 ? -INT16_MIN : INT16_MAX), (float)pos.dy / (pos.dy < 0 ? -INT16_MIN : INT16_MAX)); } - (OFString *)description { return [OFString stringWithFormat: @"<%@: %@>", self.class, self.name]; } @end |
Modified src/platform/NintendoDS/OFGameController.m from [30201f00f5] to [bd26d85f5f].
︙ | ︙ | |||
28 29 30 31 32 33 34 | #include <nds.h> #undef asm @interface OFGameController () - (instancetype)of_init OF_METHOD_FAMILY(init); @end | | | > > > | > > < < < < | < < < < | | > > > > > | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | #include <nds.h> #undef asm @interface OFGameController () - (instancetype)of_init OF_METHOD_FAMILY(init); @end static OFArray OF_GENERIC(OFGameController *) *controllers; static void initControllers(void) { void *pool = objc_autoreleasePoolPush(); controllers = [[OFArray alloc] initWithObject: [[[OFGameController alloc] of_init] autorelease]]; objc_autoreleasePoolPop(); } @implementation OFGameController + (OFArray OF_GENERIC(OFGameController *) *)controllers { static OFOnceControl onceControl = OFOnceControlInitValue; OFOnce(&onceControl, initControllers); return [[controllers retain] autorelease]; } - (instancetype)init { OF_INVALID_INIT_METHOD } - (instancetype)of_init { return [super init]; } - (OFString *)name { return @"Nintendo DS"; } - (OFSet *)buttons { return [OFSet setWithObjects: @"A", @"B", @"Select", @"Start", @"D-Pad Right", @"D-Pad Left", @"D-Pad Up", @"D-Pad Down", @"R", @"L", @"X", @"Y", nil]; } |
︙ | ︙ | |||
118 119 120 121 122 123 124 125 | return 0; } - (OFPoint)positionOfAnalogStickWithIndex: (size_t)index { @throw [OFOutOfRangeException exception]; } @end | > > > > > | 120 121 122 123 124 125 126 127 128 129 130 131 132 | return 0; } - (OFPoint)positionOfAnalogStickWithIndex: (size_t)index { @throw [OFOutOfRangeException exception]; } - (OFString *)description { return [OFString stringWithFormat: @"<%@: %@>", self.class, self.name]; } @end |
Modified src/test/OTAppDelegate.m from [2b12d71a47] to [bd3935ec7e].
︙ | ︙ | |||
326 327 328 329 330 331 332 | #elif defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFStdOut setForegroundColor: [OFColor silver]]; [OFStdOut writeLine: @"Press A to continue"]; for (;;) { void *pool = objc_autoreleasePoolPush(); OFGameController *controller = | | | 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 | #elif defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFStdOut setForegroundColor: [OFColor silver]]; [OFStdOut writeLine: @"Press A to continue"]; for (;;) { void *pool = objc_autoreleasePoolPush(); OFGameController *controller = [[OFGameController controllers] objectAtIndex: 0]; if ([controller.pressedButtons containsObject: @"A"]) break; # if defined(OF_NINTENDO_DS) swiWaitForVBlank(); # elif defined(OF_NINTENDO_3DS) |
︙ | ︙ |