/* * Copyright (c) 2008-2025 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 "OFApplication.h" #import "OFArray.h" #import "OFColor.h" #import "OFDate.h" #import "OFDictionary.h" #import "OFNumber.h" #import "OFStdIOStream.h" #import "OFThread.h" #import "OFTimer.h" #import "OHExtendedGamepad.h" #import "OHGameController.h" #import "OHGameControllerAxis.h" #import "OHGameControllerButton.h" #import "OHGameControllerDirectionalPad.h" #import "OHGameControllerProfile.h" #import "OHGamepad.h" #import "OHJoyConPair.h" #import "OHLeftJoyCon.h" #import "OHRightJoyCon.h" #import "OFReadFailedException.h" #if defined(OF_NINTENDO_DS) static size_t buttonsPerLine = 2; #elif defined(OF_NINTENDO_3DS) static size_t buttonsPerLine = 3; #else static size_t buttonsPerLine = 5; #endif #if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) || \ defined(OF_NINTENDO_SWITCH) # define red maroon # define yellow olive # define gray silver #endif #ifdef OF_NINTENDO_SWITCH # define id nx_id # include # undef id #endif @interface GameControllerTests: OFObject { OFArray OF_GENERIC(OHGameController *) *_controllers; OFDate *_lastControllersUpdate; OHJoyConPair *_joyConPair; } @end OF_APPLICATION_DELEGATE(GameControllerTests) static void printProfile(id profile) { OFArray OF_GENERIC(OFString *) *buttons = profile.buttons.allKeys.sortedArray; OFArray OF_GENERIC(OFString *) *axes = profile.axes.allKeys.sortedArray; OFArray OF_GENERIC(OFString *) *directionalPads = profile.directionalPads.allKeys.sortedArray; size_t i; i = 0; for (OFString *name in buttons) { OHGameControllerButton *button = [profile.buttons objectForKey: name]; if (i++ == buttonsPerLine) { [OFStdOut writeString: @"\n"]; i = 0; } if (OFStdOut.colors >= 256) { float red, green, blue; if (button.value < 0.25f) { red = 0.5f - 2.0f * button.value; green = 0.5f + 2.0f * button.value; blue = 0.5f - 2.0f * button.value; } else if (button.value < 0.5f) { red = 2.0f * button.value; green = 1.0f; blue = 0.0f; } else { red = 1.0f; green = 1.0f - 2.0f * (button.value - 0.5f); blue = 0.0f; } OFStdOut.foregroundColor = [OFColor colorWithRed: red green: green blue: blue alpha: 1.0f]; } else { if (button.value == 1.0f) OFStdOut.foregroundColor = [OFColor red]; else if (button.value > 0.5f) OFStdOut.foregroundColor = [OFColor yellow]; else if (button.value > 0.0f) OFStdOut.foregroundColor = [OFColor green]; else OFStdOut.foregroundColor = [OFColor gray]; } [OFStdOut writeFormat: @"[%@] ", name]; } if (OFStdOut.colors >= 256) OFStdOut.foregroundColor = [OFColor colorWithRed: 0.5f green: 0.5f blue: 0.5f alpha: 1.0f]; else OFStdOut.foregroundColor = [OFColor gray]; [OFStdOut writeString: @"\n"]; i = 0; for (OFString *name in axes) { OHGameControllerAxis *axis = [profile.axes objectForKey: name]; if (i++ == buttonsPerLine) { [OFStdOut writeString: @"\n"]; i = 0; } [OFStdOut writeFormat: @"%@: %5.2f ", name, axis.value]; } if (axes.count > 0) [OFStdOut writeString: @"\n"]; i = 0; for (OFString *name in directionalPads) { OHGameControllerDirectionalPad *directionalPad = [profile.directionalPads objectForKey: name]; if (i++ == 2) { [OFStdOut writeString: @"\n"]; i = 0; } if (OFStdOut.colors >= 256) { float red = 0.5f + directionalPad.xAxis.value / 2.0f; float blue = 0.5f + directionalPad.yAxis.value / 2.0f; float green = 1.0f - (red / 2.0f + blue / 2.0f); OFStdOut.foregroundColor = [OFColor colorWithRed: red green: green blue: blue alpha: 1.0f]; } [OFStdOut writeFormat: @"%@: (%5.2f, %5.2f) ", name, directionalPad.xAxis.value, directionalPad.yAxis.value]; } if (OFStdOut.colors >= 256) OFStdOut.foregroundColor = [OFColor colorWithRed: 0.5f green: 0.5f blue: 0.5f alpha: 1.0f]; if (directionalPads.count > 0) [OFStdOut writeString: @"\n"]; if ([profile conformsToProtocol: @protocol(OHGamepad)]) { id gamepad = (id )profile; [OFStdOut writeFormat: @"[Map] North: %@ South: %@ West: %@ East: %@\n", gamepad.northButton.name, gamepad.southButton.name, gamepad.westButton.name, gamepad.eastButton.name]; [OFStdOut writeFormat: @"[Map] Left Shoulder: %@ Right Shoulder: %@\n", gamepad.leftShoulderButton.name, gamepad.rightShoulderButton.name]; } if ([profile conformsToProtocol: @protocol(OHExtendedGamepad)]) { id extendedGamepad = (id )profile; [OFStdOut writeFormat: @"[Map] Left Trigger: %@ Right Trigger: %@\n", extendedGamepad.leftTriggerButton.name, extendedGamepad.rightTriggerButton.name]; [OFStdOut writeFormat: @"[Map] Left Thumbstick: %@ Right Thumbstick: %@\n", extendedGamepad.leftThumbstickButton.name, extendedGamepad.rightThumbstickButton.name]; } if ([profile conformsToProtocol: @protocol(OHGamepad)]) { id gamepad = (id )profile; [OFStdOut writeFormat: @"[Map] Menu: %@ Options: %@", gamepad.menuButton.name, gamepad.optionsButton.name]; } if ([profile conformsToProtocol: @protocol(OHExtendedGamepad)]) { id extendedGamepad = (id )profile; [OFStdOut writeFormat: @" Home: %@", extendedGamepad.homeButton.name]; } if ([profile conformsToProtocol: @protocol(OHGamepad)]) [OFStdOut writeString: @"\n"]; } @implementation GameControllerTests - (void)updateOutput { OHLeftJoyCon *leftJoyCon = nil; OHRightJoyCon *rightJoyCon = nil; if (_lastControllersUpdate == nil || -[_lastControllersUpdate timeIntervalSinceNow] > 1) { [_joyConPair release]; [_controllers release]; [_lastControllersUpdate release]; _joyConPair = nil; _controllers = [[OHGameController controllers] retain]; _lastControllersUpdate = [[OFDate alloc] init]; [OFStdOut clear]; } [OFStdOut setCursorPosition: OFMakePoint(0, 0)]; for (OHGameController *controller in _controllers) { id profile = controller.profile; OFStdOut.foregroundColor = [OFColor green]; [OFStdOut writeLine: controller.description]; OFStdOut.foregroundColor = [OFColor teal]; [OFStdOut writeFormat: @"Profile: %@\n", profile]; @try { [controller updateState]; } @catch (OFReadFailedException *e) { OFStdOut.foregroundColor = [OFColor red]; [OFStdOut writeString: e.description]; continue; } printProfile(profile); if ([profile isKindOfClass: [OHLeftJoyCon class]]) leftJoyCon = (OHLeftJoyCon *)profile; else if ([profile isKindOfClass: [OHRightJoyCon class]]) rightJoyCon = (OHRightJoyCon *)profile; if (_joyConPair == nil && leftJoyCon != nil && rightJoyCon != nil) _joyConPair = [[OHJoyConPair gamepadWithLeftJoyCon: leftJoyCon rightJoyCon: rightJoyCon] retain]; } if (_joyConPair) { OFStdOut.foregroundColor = [OFColor green]; [OFStdOut writeLine: @"Joy-Con Pair"]; printProfile(_joyConPair); } #if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFThread waitForVerticalBlank]; #elif defined(OF_NINTENDO_SWITCH) consoleUpdate(NULL); #endif } - (void)applicationDidFinishLaunching: (OFNotification *)notification { #if defined(OF_WII) || defined(OF_NINTENDO_DS) || defined(OF_NINTENDO_3DS) [OFStdIOStream setUpConsole]; #endif #if defined(OF_NINTENDO_SWITCH) consoleInit(NULL); while (appletMainLoop()) { void *pool = objc_autoreleasePoolPush(); [self updateOutput]; objc_autoreleasePoolPop(pool); } #else [OFTimer scheduledTimerWithTimeInterval: 1.f / 60.f target: self selector: @selector(updateOutput) repeats: true]; #endif } @end