Index: configure.ac ================================================================== --- configure.ac +++ configure.ac @@ -257,10 +257,11 @@ with_tls="no" check_pedantic="no" AC_DEFINE(OF_NINTENDO_3DS, 1, [Whether we are compiling for Nintendo 3DS]) + AC_SUBST(USE_SRCS_NINTENDO_3DS, '${SRCS_NINTENDO_3DS}') AC_SUBST(MAP_LDFLAGS, ['-Wl,-Map,$@.map']) ]) AC_ARG_WITH(nintendo-switch, AS_HELP_STRING([--with-nintendo-switch], [build for Nintendo Switch])) Index: extra.mk.in ================================================================== --- extra.mk.in +++ extra.mk.in @@ -98,10 +98,11 @@ USE_INCLUDES_ATOMIC = @USE_INCLUDES_ATOMIC@ USE_SRCS_APPLETALK = @USE_SRCS_APPLETALK@ USE_SRCS_EVDEV = @USE_SRCS_EVDEV@ USE_SRCS_FILES = @USE_SRCS_FILES@ USE_SRCS_IPX = @USE_SRCS_IPX@ +USE_SRCS_NINTENDO_3DS = @USE_SRCS_NINTENDO_3DS@ USE_SRCS_PLUGINS = @USE_SRCS_PLUGINS@ USE_SRCS_SCTP = @USE_SRCS_SCTP@ USE_SRCS_SOCKETS = @USE_SRCS_SOCKETS@ USE_SRCS_SUBPROCESSES = @USE_SRCS_SUBPROCESSES@ USE_SRCS_TAGGED_POINTERS = @USE_SRCS_TAGGED_POINTERS@ Index: src/hid/Makefile ================================================================== --- src/hid/Makefile +++ src/hid/Makefile @@ -15,13 +15,16 @@ OHGameControllerDirectionalPad.m \ OHGameControllerElement.m \ OHGameControllerProfile.m \ OHGamepad.m \ ${USE_SRCS_EVDEV} \ + ${USE_SRCS_NINTENDO_3DS} \ ${USE_SRCS_XINPUT} SRCS_EVDEV = OHEvdevGameController.m \ OHEvdevGamepad.m +SRCS_NINTENDO_3DS = OHNintendo3DSGameController.m \ + OHNintendo3DSGamepad.m SRCS_XINPUT = OHXInputGameController.m \ OHXInputGamepad.m INCLUDES := ${SRCS:.m=.h} \ ObjFWHID.h Index: src/hid/OHGameController.m ================================================================== --- src/hid/OHGameController.m +++ src/hid/OHGameController.m @@ -29,10 +29,13 @@ # import "OHEvdevGameController.h" #endif #ifdef OF_WINDOWS # import "OHXInputGameController.h" #endif +#ifdef OF_NINTENDO_3DS +# import "OHNintendo3DSGameController.h" +#endif @implementation OHGameController @dynamic name, rawProfile; + (OFArray OF_GENERIC(OHGameController *) *)controllers @@ -39,10 +42,12 @@ { #if defined(OF_LINUX) && defined(OF_HAVE_FILES) return [OHEvdevGameController controllers]; #elif defined(OF_WINDOWS) return [OHXInputGameController controllers]; +#elif defined(OF_NINTENDO_3DS) + return [OHNintendo3DSGameController controllers]; #else return [OFArray array]; #endif } Index: src/hid/OHGamepad.h ================================================================== --- src/hid/OHGamepad.h +++ src/hid/OHGamepad.h @@ -71,17 +71,23 @@ */ @property (readonly, nonatomic) OHGameControllerButton *rightTriggerButton; /** * @brief The left thumb stick button. + * + * This button is optional and may be `nil`. */ -@property (readonly, nonatomic) OHGameControllerButton *leftThumbstickButton; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) + OHGameControllerButton *leftThumbstickButton; /** * @brief The right thumb stick button. + * + * This button is optional and may be `nil`. */ -@property (readonly, nonatomic) OHGameControllerButton *rightThumbstickButton; +@property OF_NULLABLE_PROPERTY (readonly, nonatomic) + OHGameControllerButton *rightThumbstickButton; /** * @brief The menu button, sometimes also called start button. */ @property (readonly, nonatomic) OHGameControllerButton *menuButton; Index: src/hid/OHGamepad.m ================================================================== --- src/hid/OHGamepad.m +++ src/hid/OHGamepad.m @@ -22,8 +22,22 @@ #import "OHGamepad.h" @implementation OHGamepad @dynamic northButton, southButton, westButton, eastButton, leftShoulderButton; @dynamic rightShoulderButton, leftTriggerButton, rightTriggerButton; -@dynamic leftThumbstickButton, rightThumbstickButton, menuButton, optionsButton; -@dynamic homeButton, leftThumbstick, rightThumbstick, dPad; +@dynamic menuButton, optionsButton, leftThumbstick, rightThumbstick, dPad; + +- (OHGameControllerButton *)leftThumbstickButton +{ + return nil; +} + +- (OHGameControllerButton *)rightThumbstickButton +{ + return nil; +} + +- (OHGameControllerButton *)homeButton +{ + return nil; +} @end ADDED src/hid/OHNintendo3DSGameController.h Index: src/hid/OHNintendo3DSGameController.h ================================================================== --- /dev/null +++ src/hid/OHNintendo3DSGameController.h @@ -0,0 +1,32 @@ +/* + * 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 + * . + */ + +#import "OHGameController.h" + +OF_ASSUME_NONNULL_BEGIN + +@class OHNintendo3DSGamepad; + +@interface OHNintendo3DSGameController: OHGameController +{ + OHNintendo3DSGamepad *_gamepad; +} +@end + +OF_ASSUME_NONNULL_END ADDED src/hid/OHNintendo3DSGameController.m Index: src/hid/OHNintendo3DSGameController.m ================================================================== --- /dev/null +++ src/hid/OHNintendo3DSGameController.m @@ -0,0 +1,154 @@ +/* + * 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 "OHNintendo3DSGameController.h" +#import "OFArray.h" +#import "OFDictionary.h" +#import "OFNumber.h" +#import "OHGameControllerAxis.h" +#import "OHGameControllerButton.h" +#import "OHGameControllerDirectionalPad.h" +#import "OHNintendo3DSGamepad.h" + +#import "OFInitializationFailedException.h" +#import "OFReadFailedException.h" + +#define id id_3ds +#include <3ds.h> +#undef id + +static OFArray OF_GENERIC(OHGameController *) *controllers; + +/* Work around for the compiler getting confused. */ +static float +keyValue(u32 keys, u32 key) +{ + if (keys & key) + return 1; + else + return 0; +} + +@implementation OHNintendo3DSGameController +@synthesize gamepad = _gamepad; + ++ (void)initialize +{ + void *pool; + + if (self != [OHNintendo3DSGameController class]) + return; + + pool = objc_autoreleasePoolPush(); + controllers = [[OFArray alloc] initWithObject: + [[[OHNintendo3DSGameController alloc] init] autorelease]]; + objc_autoreleasePoolPop(pool); +} + ++ (OFArray OF_GENERIC(OHGameController *) *)controllers +{ + return controllers; +} + +- (instancetype)init +{ + self = [super init]; + + @try { + _gamepad = [[OHNintendo3DSGamepad alloc] init]; + + [self retrieveState]; + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (void)dealloc +{ + [_gamepad release]; + + [super dealloc]; +} + +- (void)retrieveState +{ + u32 keys; + circlePosition leftPos, rightPos; + + hidScanInput(); + + keys = hidKeysHeld(); + hidCircleRead(&leftPos); + hidCstickRead(&rightPos); + + _gamepad.northButton.value = keyValue(keys, KEY_X); + _gamepad.southButton.value = keyValue(keys, KEY_B); + _gamepad.westButton.value = keyValue(keys, KEY_Y); + _gamepad.eastButton.value = keyValue(keys, KEY_A); + _gamepad.leftShoulderButton.value = keyValue(keys, KEY_L); + _gamepad.rightShoulderButton.value = keyValue(keys, KEY_R); + _gamepad.leftTriggerButton.value = keyValue(keys, KEY_ZL); + _gamepad.rightTriggerButton.value = keyValue(keys, KEY_ZR); + _gamepad.menuButton.value = keyValue(keys, KEY_START); + _gamepad.optionsButton.value = keyValue(keys, KEY_SELECT); + + if (leftPos.dx > 150) + leftPos.dx = 150; + if (leftPos.dx < -150) + leftPos.dx = -150; + if (leftPos.dy > 150) + leftPos.dy = 150; + if (leftPos.dy < -150) + leftPos.dy = -150; + + if (rightPos.dx > 150) + rightPos.dx = 150; + if (rightPos.dx < -150) + rightPos.dx = -150; + if (rightPos.dy > 150) + rightPos.dy = 150; + if (rightPos.dy < -150) + rightPos.dy = -150; + + _gamepad.leftThumbstick.xAxis.value = (float)leftPos.dx / 150; + _gamepad.leftThumbstick.yAxis.value = -(float)leftPos.dy / 150; + _gamepad.rightThumbstick.xAxis.value = (float)rightPos.dx / 150; + _gamepad.rightThumbstick.yAxis.value = -(float)rightPos.dy / 150; + + _gamepad.dPad.up.value = keyValue(keys, KEY_DUP); + _gamepad.dPad.down.value = keyValue(keys, KEY_DDOWN); + _gamepad.dPad.left.value = keyValue(keys, KEY_DLEFT); + _gamepad.dPad.right.value = keyValue(keys, KEY_DRIGHT); +} + +- (OFString *)name +{ + return @"Nintendo 3DS"; +} + +- (OHGameControllerProfile *)rawProfile +{ + return _gamepad; +} +@end ADDED src/hid/OHNintendo3DSGamepad.h Index: src/hid/OHNintendo3DSGamepad.h ================================================================== --- /dev/null +++ src/hid/OHNintendo3DSGamepad.h @@ -0,0 +1,27 @@ +/* + * 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 + * . + */ + +#import "OHGamepad.h" + +OF_ASSUME_NONNULL_BEGIN + +@interface OHNintendo3DSGamepad: OHGamepad +@end + +OF_ASSUME_NONNULL_END ADDED src/hid/OHNintendo3DSGamepad.m Index: src/hid/OHNintendo3DSGamepad.m ================================================================== --- /dev/null +++ src/hid/OHNintendo3DSGamepad.m @@ -0,0 +1,181 @@ +/* + * 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 "OHNintendo3DSGamepad.h" +#import "OFDictionary.h" +#import "OHGameControllerAxis.h" +#import "OHGameControllerButton.h" +#import "OHGameControllerDirectionalPad.h" + +static OFString *const buttonNames[] = { + @"A", @"B", @"X", @"Y", @"L", @"R", @"ZL", @"ZR", @"Start", @"Select" +}; +static const size_t numButtons = sizeof(buttonNames) / sizeof(*buttonNames); + +@implementation OHNintendo3DSGamepad +- (instancetype)init +{ + self = [super init]; + + @try { + void *pool = objc_autoreleasePoolPush(); + OFMutableDictionary *buttons = + [OFMutableDictionary dictionaryWithCapacity: numButtons]; + OFMutableDictionary *directionalPads; + OHGameControllerAxis *xAxis, *yAxis; + OHGameControllerDirectionalPad *directionalPad; + OHGameControllerButton *up, *down, *left, *right; + + for (size_t i = 0; i < numButtons; i++) { + OHGameControllerButton *button; + + button = [[OHGameControllerButton alloc] + initWithName: buttonNames[i]]; + @try { + [buttons setObject: button + forKey: buttonNames[i]]; + } @finally { + [button release]; + } + } + [buttons makeImmutable]; + _buttons = [buttons retain]; + + _axes = [[OFDictionary alloc] init]; + + directionalPads = + [OFMutableDictionary dictionaryWithCapacity: 3]; + + xAxis = [[[OHGameControllerAxis alloc] + initWithName: @"X"] autorelease]; + yAxis = [[[OHGameControllerAxis alloc] + initWithName: @"Y"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"Circle Pad" + xAxis: xAxis + yAxis: yAxis] autorelease]; + [directionalPads setObject: directionalPad + forKey: @"Circle Pad"]; + + xAxis = [[[OHGameControllerAxis alloc] + initWithName: @"CX"] autorelease]; + yAxis = [[[OHGameControllerAxis alloc] + initWithName: @"CY"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"C-Stick" + xAxis: xAxis + yAxis: yAxis] autorelease]; + [directionalPads setObject: directionalPad + forKey: @"C-Stick"]; + + up = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Up"] autorelease]; + down = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Down"] autorelease]; + left = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Left"] autorelease]; + right = [[[OHGameControllerButton alloc] + initWithName: @"D-Pad Right"] autorelease]; + directionalPad = [[[OHGameControllerDirectionalPad alloc] + initWithName: @"D-Pad" + up: up + down: down + left: left + right: right] autorelease]; + [directionalPads setObject: directionalPad forKey: @"D-Pad"]; + + [directionalPads makeImmutable]; + _directionalPads = [directionalPads retain]; + + objc_autoreleasePoolPop(pool); + } @catch (id e) { + [self release]; + @throw e; + } + + return self; +} + +- (OHGameControllerButton *)northButton +{ + return [_buttons objectForKey: @"X"]; +} + +- (OHGameControllerButton *)southButton +{ + return [_buttons objectForKey: @"B"]; +} + +- (OHGameControllerButton *)westButton +{ + return [_buttons objectForKey: @"Y"]; +} + +- (OHGameControllerButton *)eastButton +{ + return [_buttons objectForKey: @"A"]; +} + +- (OHGameControllerButton *)leftShoulderButton +{ + return [_buttons objectForKey: @"L"]; +} + +- (OHGameControllerButton *)rightShoulderButton +{ + return [_buttons objectForKey: @"R"]; +} + +- (OHGameControllerButton *)leftTriggerButton +{ + return [_buttons objectForKey: @"ZL"]; +} + +- (OHGameControllerButton *)rightTriggerButton +{ + return [_buttons objectForKey: @"ZR"]; +} + +- (OHGameControllerButton *)menuButton +{ + return [_buttons objectForKey: @"Start"]; +} + +- (OHGameControllerButton *)optionsButton +{ + return [_buttons objectForKey: @"Select"]; +} + +- (OHGameControllerDirectionalPad *)leftThumbstick +{ + return [_directionalPads objectForKey: @"Circle Pad"]; +} + +- (OHGameControllerDirectionalPad *)rightThumbstick +{ + return [_directionalPads objectForKey: @"C-Stick"]; +} + +- (OHGameControllerDirectionalPad *)dPad +{ + return [_directionalPads objectForKey: @"D-Pad"]; +} +@end Index: src/hid/OHXInputGameController.h ================================================================== --- src/hid/OHXInputGameController.h +++ src/hid/OHXInputGameController.h @@ -42,6 +42,5 @@ #ifdef __cplusplus } #endif OF_ASSUME_NONNULL_END -