Index: Doxyfile ================================================================== --- Doxyfile +++ Doxyfile @@ -47,7 +47,7 @@ SIGHUP \ SIGUSR1 \ SIGUSR2 MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES -IGNORE_PREFIX = OF OF_ OT OT_ +IGNORE_PREFIX = HID OF OF_ OT OT_ EXTRACT_STATIC = yes Index: src/hid/HIDEvdevGameController.h ================================================================== --- src/hid/HIDEvdevGameController.h +++ src/hid/HIDEvdevGameController.h @@ -18,22 +18,22 @@ */ #import "HIDGameController.h" OF_ASSUME_NONNULL_BEGIN + +@class HIDGameControllerMapping; @interface HIDEvdevGameController: HIDGameController { OFString *_path; int _fd; bool _discardUntilReport; unsigned long *_evBits, *_keyBits, *_absBits; uint16_t _vendorID, _productID; OFString *_name; - OFDictionary OF_GENERIC(OFString *, HIDGameControllerButton *) - *_buttons; - OFDictionary OF_GENERIC(OFString *, HIDGameControllerAxis *) *_axes; + HIDGameControllerMapping *_mapping; } - (instancetype)of_initWithPath: (OFString *)path OF_METHOD_FAMILY(init); - (void)of_pollState; @end Index: src/hid/HIDEvdevGameController.m ================================================================== --- src/hid/HIDEvdevGameController.m +++ src/hid/HIDEvdevGameController.m @@ -22,17 +22,20 @@ #include #include #include #import "HIDEvdevGameController.h" + #import "OFArray.h" #import "OFDictionary.h" #import "OFFileManager.h" #import "OFLocale.h" #import "OFNumber.h" + #import "HIDGameControllerAxis.h" #import "HIDGameControllerButton.h" +#import "HIDGameControllerMapping.h" #include #include #import "OFInitializationFailedException.h" @@ -46,11 +49,13 @@ @public int32_t _minValue, _maxValue; } @end -@implementation HIDEvdevGameControllerAxis +@interface HIDEvdevGameControllerMapping: HIDGameControllerMapping +- (instancetype)of_initWithButtons: (OFDictionary *)buttons + axes: (OFDictionary *)axes OF_METHOD_FAMILY(init); @end static const uint16_t buttonIDs[] = { 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, @@ -260,11 +265,11 @@ return ((value - min) / (max - min) * 2) - 1; } @implementation HIDEvdevGameController -@synthesize name = _name, buttons = _buttons, axes = _axes; +@synthesize name = _name, unmappedMapping = _mapping; + (OFArray OF_GENERIC(HIDGameController *) *)controllers { OFMutableArray *controllers = [OFMutableArray array]; void *pool = objc_autoreleasePoolPush(); @@ -306,10 +311,11 @@ - (instancetype)of_initWithPath: (OFString *)path { self = [super init]; @try { + void *pool = objc_autoreleasePoolPush(); OFStringEncoding encoding = [OFLocale encoding]; struct input_id inputID; char name[128]; OFMutableDictionary *buttons, *axes; @@ -355,12 +361,11 @@ @throw [OFInitializationFailedException exception]; _name = [[OFString alloc] initWithCString: name encoding: encoding]; - buttons = [[OFMutableDictionary alloc] init]; - _buttons = buttons; + buttons = [OFMutableDictionary dictionary]; for (size_t i = 0; i < sizeof(buttonIDs) / sizeof(*buttonIDs); i++) { if (OFBitSetIsSet(_keyBits, buttonIDs[i])) { OFString *buttonName; HIDGameControllerButton *button; @@ -375,12 +380,11 @@ [buttons setObject: button forKey: buttonName]; } } [buttons makeImmutable]; - axes = [[OFMutableDictionary alloc] init]; - _axes = axes; + axes = [OFMutableDictionary dictionary]; if (OFBitSetIsSet(_evBits, EV_ABS)) { _absBits = OFAllocZeroedMemory(OFRoundUpToPowerOf2( OF_ULONG_BIT, ABS_MAX) / OF_ULONG_BIT, sizeof(unsigned long)); @@ -407,12 +411,18 @@ [axes setObject: axis forKey: axisName]; } } } [axes makeImmutable]; + + _mapping = [[HIDEvdevGameControllerMapping alloc] + of_initWithButtons: buttons + axes: axes]; [self of_pollState]; + + objc_autoreleasePoolPop(pool); } @catch (id e) { [self release]; @throw e; } @@ -429,12 +439,11 @@ OFFreeMemory(_evBits); OFFreeMemory(_keyBits); OFFreeMemory(_absBits); [_name release]; - [_buttons release]; - [_axes release]; + [_mapping release]; [super dealloc]; } - (OFNumber *)vendorID @@ -468,11 +477,11 @@ name = buttonToName(buttonIDs[i]); if (name == nil) continue; - button = [_buttons objectForKey: name]; + button = [_mapping.buttons objectForKey: name]; if (button == nil) continue; button.value = (OFBitSetIsSet(keyState, buttonIDs[i]) ? 1 : 0); } @@ -490,11 +499,11 @@ name = axisToName(axisIDs[i]); if (name == nil) continue; axis = (HIDEvdevGameControllerAxis *) - [_axes objectForKey: name]; + [_mapping.axes objectForKey: name]; if (axis == nil) continue; if (ioctl(_fd, EVIOCGABS(axisIDs[i]), &info) == -1) @throw [OFReadFailedException @@ -510,10 +519,11 @@ } } - (void)retrieveState { + void *pool = objc_autoreleasePoolPush(); struct input_event event; for (;;) { OFString *name; HIDGameControllerButton *button; @@ -550,11 +560,11 @@ case EV_KEY: name = buttonToName(event.code); if (name == nil) continue; - button = [_buttons objectForKey: name]; + button = [_mapping.buttons objectForKey: name]; if (button == nil) continue; button.value = (event.value ? 1 : 0); @@ -563,20 +573,22 @@ name = axisToName(event.code); if (name == nil) continue; axis = (HIDEvdevGameControllerAxis *) - [_axes objectForKey: name]; + [_mapping.axes objectForKey: name]; if (axis == nil) continue; axis.value = scale(event.value, axis->_minValue, axis->_maxValue); break; } } + + objc_autoreleasePoolPop(pool); } - (OFComparisonResult)compare: (HIDEvdevGameController *)otherController { unsigned long long selfIndex, otherIndex; @@ -594,5 +606,26 @@ return OFOrderedAscending; return OFOrderedSame; } @end + +@implementation HIDEvdevGameControllerAxis +@end + +@implementation HIDEvdevGameControllerMapping +- (instancetype)of_initWithButtons: (OFDictionary *)buttons + axes: (OFDictionary *)axes +{ + self = [super init]; + + @try { + _buttons = [buttons retain]; + _axes = [axes retain]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} +@end Index: src/hid/HIDGameController.h ================================================================== --- src/hid/HIDGameController.h +++ src/hid/HIDGameController.h @@ -29,17 +29,17 @@ # endif #endif OF_ASSUME_NONNULL_BEGIN -@class HIDGameControllerAxis; -@class HIDGameControllerButton; +@class HIDGameControllerMapping; @class OFArray OF_GENERIC(ObjectType); -@class OFDictionary OF_GENERIC(KeyType, ObjectType); @class OFNumber; /** + * @class HIDGameController HIDGameController.h ObjFWHID/HIDGameController.h + * * @brief A class for reading state from a game controller. */ @interface HIDGameController: OFObject #ifdef OF_HAVE_CLASS_PROPERTIES @property (class, readonly, nonatomic) @@ -60,20 +60,14 @@ * @brief The product ID of the controller or `nil` if unavailable. */ @property OF_NULLABLE_PROPERTY (readonly, nonatomic) OFNumber *productID; /** - * @brief A map of all button names to their @ref HIDGameControllerButton. - */ -@property (readonly, nonatomic) - OFDictionary OF_GENERIC(OFString *, HIDGameControllerButton *) *buttons; - -/** - * @brief A map of all axis names to their @ref HIDGameControllerAxis. - */ -@property (readonly, nonatomic) - OFDictionary OF_GENERIC(OFString *, HIDGameControllerAxis *) *axes; + * @brief An unmapped mapping for the game controller, meaning no remapping is + * being performed. + */ +@property (readonly, nonatomic) HIDGameControllerMapping *unmappedMapping; /** * @brief Returns the available controllers. * * @return The available controllers Index: src/hid/HIDGameController.m ================================================================== --- src/hid/HIDGameController.m +++ src/hid/HIDGameController.m @@ -27,11 +27,11 @@ #if defined(OF_LINUX) && defined(OF_HAVE_FILES) # include "HIDEvdevGameController.h" #endif @implementation HIDGameController -@dynamic name, buttons, axes; +@dynamic name, unmappedMapping; + (OFArray OF_GENERIC(HIDGameController *) *)controllers { #if defined(OF_LINUX) && defined(OF_HAVE_FILES) return [HIDEvdevGameController controllers]; Index: src/hid/HIDGameControllerAxis.h ================================================================== --- src/hid/HIDGameControllerAxis.h +++ src/hid/HIDGameControllerAxis.h @@ -20,11 +20,14 @@ #import "HIDGameControllerElement.h" OF_ASSUME_NONNULL_BEGIN /** - * @brief An button of a game controller. + * @class HIDGameControllerAxis \ + * HIDGameControllerAxis.h ObjFWHID/HIDGameControllerAxis.h + * + * @brief An axis of a game controller. */ @interface HIDGameControllerAxis: HIDGameControllerElement { float _value; OF_RESERVE_IVARS(HIDGameControllerButton, 4) Index: src/hid/HIDGameControllerButton.h ================================================================== --- src/hid/HIDGameControllerButton.h +++ src/hid/HIDGameControllerButton.h @@ -20,11 +20,14 @@ #import "HIDGameControllerElement.h" OF_ASSUME_NONNULL_BEGIN /** - * @brief An button of a game controller. + * @class HIDGameControllerButton \ + * HIDGameControllerButton.h ObjFWHID/HIDGameControllerButton.h + * + * @brief A button of a game controller. */ @interface HIDGameControllerButton: HIDGameControllerElement { float _value; OF_RESERVE_IVARS(HIDGameControllerButton, 4) Index: src/hid/HIDGameControllerDirectionalPad.h ================================================================== --- src/hid/HIDGameControllerDirectionalPad.h +++ src/hid/HIDGameControllerDirectionalPad.h @@ -22,10 +22,14 @@ #import "HIDGameControllerButton.h" OF_ASSUME_NONNULL_BEGIN /** + * @class HIDGameControllerDirectionalPad \ + * HIDGameControllerDirectionalPad.h \ + * ObjFWHID/HIDGameControllerDirectionalPad.h + * * @brief An directional pad or thumb stick of a game controller. */ OF_SUBCLASSING_RESTRICTED @interface HIDGameControllerDirectionalPad: HIDGameControllerElement { Index: src/hid/HIDGameControllerElement.h ================================================================== --- src/hid/HIDGameControllerElement.h +++ src/hid/HIDGameControllerElement.h @@ -30,10 +30,13 @@ #endif OF_ASSUME_NONNULL_BEGIN /** + * @class HIDGameControllerElement \ + * HIDGameControllerElement.h ObjFWHID/HIDGameControllerElement.h + * * @brief An element of a game controller, e.g. a button, an axis or a * directional pad. */ @interface HIDGameControllerElement: OFObject { ADDED src/hid/HIDGameControllerMapping.h Index: src/hid/HIDGameControllerMapping.h ================================================================== --- /dev/null +++ src/hid/HIDGameControllerMapping.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * 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 + * . + */ + +#ifdef OBJFWHID_LOCAL_INCLUDES +# import "OFObject.h" +# import "OFString.h" +#else +# if defined(__has_feature) && __has_feature(modules) +@import ObjFW; +# else +# import +# import +# endif +#endif + +OF_ASSUME_NONNULL_BEGIN + +@class HIDGameControllerAxis; +@class HIDGameControllerButton; +@class HIDGameControllerDirectionalPad; +@class OFDictionary OF_GENERIC(KeyType, ObjectType); + +/** + * @class HIDGameControllerMapping \ + * HIDGameControllerMapping.h ObjFWHID/HIDGameControllerMapping.h + * + * @brief A mapping for a @ref HIDGameController. + */ +@interface HIDGameControllerMapping: OFObject +{ + OFDictionary OF_GENERIC(OFString *, HIDGameControllerButton *) + *_buttons; + OFDictionary OF_GENERIC(OFString *, HIDGameControllerAxis *) *_axes; + OFDictionary OF_GENERIC(OFString *, HIDGameControllerDirectionalPad *) + *_directionalPads; + OF_RESERVE_IVARS(HIDGameControllerMapping, 4) +} + +/** + * @brief A map of all button names to their @ref HIDGameControllerButton. + */ +@property (readonly, nonatomic) + OFDictionary OF_GENERIC(OFString *, HIDGameControllerButton *) *buttons; + +/** + * @brief A map of all axis names to their @ref HIDGameControllerAxis. + */ +@property (readonly, nonatomic) + OFDictionary OF_GENERIC(OFString *, HIDGameControllerAxis *) *axes; + +/** + * @brief A map of all directional pads to their + * @ref HIDGameControllerDirectionalPad. + */ +@property (readonly, nonatomic) OFDictionary OF_GENERIC(OFString *, + HIDGameControllerDirectionalPad *) *directionalPads; +@end + +OF_ASSUME_NONNULL_END ADDED src/hid/HIDGameControllerMapping.m Index: src/hid/HIDGameControllerMapping.m ================================================================== --- /dev/null +++ src/hid/HIDGameControllerMapping.m @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2008-2024 Jonathan Schleifer + * + * 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 + * . + */ + +#include "config.h" + +#import "HIDGameControllerMapping.h" + +@implementation HIDGameControllerMapping: OFObject +@synthesize buttons = _buttons, axes = _axes; +@synthesize directionalPads = _directionalPads; + +- (void)dealloc +{ + [_buttons release]; + [_axes release]; + [_directionalPads release]; + + [super dealloc]; +} +@end Index: src/hid/Makefile ================================================================== --- src/hid/Makefile +++ src/hid/Makefile @@ -9,13 +9,14 @@ LIB_MINOR = ${OBJFWHID_LIB_MINOR} LIB_PATCH = ${OBJFWHID_LIB_PATCH} SRCS = HIDGameController.m \ HIDGameControllerAxis.m \ - HIDGameControllerButton.m \ - HIDGameControllerDirectionalPad.m \ - HIDGameControllerElement.m + HIDGameControllerButton.m \ + HIDGameControllerDirectionalPad.m \ + HIDGameControllerElement.m \ + HIDGameControllerMapping.m INCLUDES := ${SRCS:.m=.h} \ ObjFWHID.h SRCS += HIDGameControllerEmulatedAxis.m \ Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -30,10 +30,11 @@ #import "OTTestCase.h" #import "HIDGameController.h" #import "HIDGameControllerButton.h" +#import "HIDGameControllerMapping.h" #import "OTAssertionFailedException.h" #import "OTTestSkippedException.h" #ifdef OF_IOS @@ -285,11 +286,12 @@ for (;;) { void *pool = objc_autoreleasePoolPush(); HIDGameController *controller = [[HIDGameController controllers] objectAtIndex: 0]; HIDGameControllerButton *button = - [controller.buttons objectForKey: @"A"]; + [controller.unmappedMapping.buttons + objectForKey: @"A"]; [controller retrieveState]; if (button.pressed) break; @@ -549,13 +551,13 @@ void *pool = objc_autoreleasePoolPush(); HIDGameController *controller = [[HIDGameController controllers] objectAtIndex: 0]; HIDGameControllerButton *button = # ifdef OF_WII - [controller.buttons objectForKey: @"Home"]; + [controller.unmappedMapping.buttons objectForKey: @"Home"]; # else - [controller.buttons objectForKey: @"Start"]; + [controller.unmappedMapping.buttons objectForKey: @"Start"]; # endif [controller retrieveState]; if (button.pressed) Index: tests/gamecontroller/GameControllerTests.m ================================================================== --- tests/gamecontroller/GameControllerTests.m +++ tests/gamecontroller/GameControllerTests.m @@ -29,10 +29,11 @@ #import "OFThread.h" #import "HIDGameController.h" #import "HIDGameControllerAxis.h" #import "HIDGameControllerButton.h" +#import "HIDGameControllerMapping.h" #import "OFReadFailedException.h" #if defined(OF_NINTENDO_DS) static size_t buttonsPerLine = 2; @@ -80,15 +81,17 @@ } [OFStdOut setCursorPosition: OFMakePoint(0, 0)]; for (HIDGameController *controller in _controllers) { + HIDGameControllerMapping *mapping = + controller.unmappedMapping; OFArray OF_GENERIC(OFString *) *buttons = - controller.buttons.allKeys.sortedArray; + mapping.buttons.allKeys.sortedArray; OFArray OF_GENERIC(OFString *) *axes = - controller.axes.allKeys.sortedArray; - size_t i = 0; + mapping.axes.allKeys.sortedArray; + size_t i; [OFStdOut setForegroundColor: [OFColor green]]; [OFStdOut writeString: controller.description]; @try { @@ -97,13 +100,14 @@ [OFStdOut setForegroundColor: [OFColor red]]; [OFStdOut writeFormat: @"\n%@", e.description]; continue; } + i = 0; for (OFString *name in buttons) { HIDGameControllerButton *button = - [controller.buttons objectForKey: name]; + [mapping.buttons objectForKey: name]; if (i == 0) [OFStdOut writeString: @"\n"]; if (button.value == 1) @@ -127,16 +131,25 @@ [OFStdOut writeString: @" "]; } [OFStdOut setForegroundColor: [OFColor gray]]; [OFStdOut writeString: @"\n"]; + i = 0; for (OFString *name in axes) { HIDGameControllerAxis *axis = - [controller.axes objectForKey: name]; + [mapping.axes objectForKey: name]; + + if (i == 0) + [OFStdOut writeString: @"\n"]; [OFStdOut writeFormat: @"%@: %5.2f ", name, axis.value]; + + if (++i == buttonsPerLine) { + i = 0; + } else + [OFStdOut writeString: @" "]; } [OFStdOut writeString: @"\n"]; }