Index: src/Makefile ================================================================== --- src/Makefile +++ src/Makefile @@ -23,10 +23,11 @@ OFData+MessagePackParsing.m \ OFDate.m \ OFDictionary.m \ OFEnumerator.m \ OFFileManager.m \ + OFGameController.m \ OFGZIPStream.m \ OFHMAC.m \ OFINICategory.m \ OFINIFile.m \ OFIRI.m \ ADDED src/OFGameController.h Index: src/OFGameController.h ================================================================== --- /dev/null +++ src/OFGameController.h @@ -0,0 +1,81 @@ +/* + * 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 "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 +@property (class, readonly, nonatomic) size_t numControllers; +#endif + +/** + * @brief The buttons the controller has. + */ +@property (readonly, nonatomic) OFSet OF_GENERIC(OFString *) *buttons; + +/** + * @brief The currently pressed buttons on the controller. + */ +@property (readonly, nonatomic) OFSet OF_GENERIC(OFString *) *pressedButtons; + +/** + * @brief The number of axes the controller has. + */ +@property (readonly, nonatomic) size_t numAxes; + +/** + * @brief Returns the number of available controllers. + * + * @return The number of available controllers + */ ++ (size_t)numControllers; + +/** + * @brief Returns the specified controller. + * + * @param index The index of the controller to return + * @return The specified controller + */ ++ (OFGameController *)controllerWithIndex: (size_t)index; + +- (instancetype)init OF_UNAVAILABLE; + +/** + * @brief Returns the current position of the specified axis. + * + * The range is from (-1, -1) to (1, 1). + * + * @param index The index of the axis whose position to return + * @return The current position of the specified axis + */ +- (OFPoint)positionOfAxisWithIndex: (size_t)index; +@end + +OF_ASSUME_NONNULL_END ADDED src/OFGameController.m Index: src/OFGameController.m ================================================================== --- /dev/null +++ src/OFGameController.m @@ -0,0 +1,52 @@ +/* + * 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 "OFGameController.h" + +#import "OFOutOfRangeException.h" + +#ifdef OF_NINTENDO_3DS +# include "platform/3DS/OFGameController.m" +#else +@implementation OFGameController +@dynamic buttons, pressedButtons, numAxes; + ++ (size_t)numControllers +{ + return 0; +} + ++ (OFGameController *)controllerWithIndex: (size_t)index +{ + @throw [OFOutOfRangeException exception]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (OFPoint)positionOfAxisWithIndex: (size_t)index +{ + OF_UNRECOGNIZED_SELECTOR +} +@end +#endif Index: src/ObjFW.h ================================================================== --- src/ObjFW.h +++ src/ObjFW.h @@ -146,10 +146,11 @@ #import "OFOptionsParser.h" #import "OFTimer.h" #import "OFRunLoop.h" #import "OFMatrix4x4.h" +#import "OFGameController.h" #ifdef OF_WINDOWS # import "OFWindowsRegistryKey.h" #endif ADDED src/platform/3DS/OFGameController.m Index: src/platform/3DS/OFGameController.m ================================================================== --- /dev/null +++ src/platform/3DS/OFGameController.m @@ -0,0 +1,147 @@ +/* + * 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 "OFGameController.h" +#import "OFSet.h" + +#import "OFOutOfRangeException.h" + +#define id id_3ds +#include <3ds.h> +#undef id + +@interface OFGameController () +- (instancetype)of_init OF_METHOD_FAMILY(init); +@end + +static OFGameController *controller; + +static void +initController(void) +{ + controller = [[OFGameController alloc] of_init]; +} + +@implementation OFGameController ++ (size_t)numControllers +{ + return 1; +} + ++ (OFGameController *)controllerWithIndex: (size_t)index +{ + static OFOnceControl onceControl = OFOnceControlInitValue; + + if (index > 0) + @throw [OFOutOfRangeException exception]; + + OFOnce(&onceControl, initController); + + return [[controller retain] autorelease]; +} + +- (instancetype)init +{ + OF_INVALID_INIT_METHOD +} + +- (instancetype)of_init +{ + return [super init]; +} + +- (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]; +} + +- (OFSet *)pressedButtons +{ + OFMutableSet *pressedButtons = [OFMutableSet setWithCapacity: 18]; + u32 keys; + + hidScanInput(); + keys = hidKeysHeld(); + + if (keys & KEY_A) + [pressedButtons addObject: @"A"]; + if (keys & KEY_B) + [pressedButtons addObject: @"A"]; + if (keys & KEY_SELECT) + [pressedButtons addObject: @"Select"]; + if (keys & KEY_START) + [pressedButtons addObject: @"Start"]; + if (keys & KEY_DRIGHT) + [pressedButtons addObject: @"D-Pad Right"]; + if (keys & KEY_DLEFT) + [pressedButtons addObject: @"D-Pad Left"]; + if (keys & KEY_DUP) + [pressedButtons addObject: @"D-Pad Up"]; + if (keys & KEY_DDOWN) + [pressedButtons addObject: @"D-Pad Down"]; + if (keys & KEY_R) + [pressedButtons addObject: @"R"]; + if (keys & KEY_L) + [pressedButtons addObject: @"L"]; + if (keys & KEY_X) + [pressedButtons addObject: @"X"]; + if (keys & KEY_Y) + [pressedButtons addObject: @"Y"]; + if (keys & KEY_ZL) + [pressedButtons addObject: @"ZL"]; + if (keys & KEY_ZR) + [pressedButtons addObject: @"ZR"]; + if (keys & KEY_CSTICK_RIGHT) + [pressedButtons addObject: @"C-Stick Right"]; + if (keys & KEY_CSTICK_LEFT) + [pressedButtons addObject: @"C-Stick Left"]; + if (keys & KEY_CSTICK_UP) + [pressedButtons addObject: @"C-Stick Up"]; + if (keys & KEY_CSTICK_DOWN) + [pressedButtons addObject: @"C-Stick Down"]; + + [pressedButtons makeImmutable]; + + return pressedButtons; +} + +- (size_t)numAxes +{ + return 1; +} + +- (OFPoint)positionOfAxisWithIndex: (size_t)index +{ + circlePosition pos; + + 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)); +} +@end Index: src/test/OTAppDelegate.m ================================================================== --- src/test/OTAppDelegate.m +++ src/test/OTAppDelegate.m @@ -337,16 +337,19 @@ #elif defined(OF_NINTENDO_3DS) [OFStdOut setForegroundColor: [OFColor silver]]; [OFStdOut writeLine: @"Press A to continue"]; for (;;) { - hidScanInput(); + void *pool = objc_autoreleasePoolPush(); + OFGameController *controller = + [OFGameController controllerWithIndex: 0]; - if (hidKeysDown() & KEY_A) + if ([controller.pressedButtons containsObject: @"A"]) break; gspWaitForVBlank(); + objc_autoreleasePoolPop(pool); } #elif defined(OF_NINTENDO_SWITCH) [OFStdOut setForegroundColor: [OFColor silver]]; [OFStdOut writeLine: @"Press A to continue"];