From 228895cedc09a9203ea930959541aefb268987ac Mon Sep 17 00:00:00 2001 From: fuzzard Date: Wed, 4 Nov 2020 15:47:04 +1000 Subject: [Darwin][Input] Extend GCController support to osx --- cmake/scripts/osx/ArchSetup.cmake | 3 +- cmake/treedata/darwin_embedded/subdirs.txt | 2 +- cmake/treedata/osx/subdirs.txt | 1 + xbmc/peripherals/PeripheralTypes.h | 16 +- xbmc/peripherals/Peripherals.cpp | 10 +- .../darwin/ios-common/peripherals/CMakeLists.txt | 10 - .../darwin/ios-common/peripherals/InputKey.h | 105 --- .../ios-common/peripherals/Input_Gamecontroller.h | 24 - .../ios-common/peripherals/Input_Gamecontroller.mm | 986 --------------------- .../peripherals/PeripheralBusDarwinEmbedded.h | 62 -- .../peripherals/PeripheralBusDarwinEmbedded.mm | 225 ----- .../PeripheralBusDarwinEmbeddedManager.h | 38 - .../PeripheralBusDarwinEmbeddedManager.mm | 183 ---- xbmc/platform/darwin/peripherals/CMakeLists.txt | 10 + xbmc/platform/darwin/peripherals/InputKey.h | 105 +++ .../darwin/peripherals/Input_Gamecontroller.h | 24 + .../darwin/peripherals/Input_Gamecontroller.mm | 986 +++++++++++++++++++++ .../darwin/peripherals/PeripheralBusGCController.h | 62 ++ .../peripherals/PeripheralBusGCController.mm | 225 +++++ .../peripherals/PeripheralBusGCControllerManager.h | 38 + .../PeripheralBusGCControllerManager.mm | 183 ++++ 21 files changed, 1649 insertions(+), 1649 deletions(-) delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/CMakeLists.txt delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/InputKey.h delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.h delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.mm delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.mm delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h delete mode 100644 xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.mm create mode 100644 xbmc/platform/darwin/peripherals/CMakeLists.txt create mode 100644 xbmc/platform/darwin/peripherals/InputKey.h create mode 100644 xbmc/platform/darwin/peripherals/Input_Gamecontroller.h create mode 100644 xbmc/platform/darwin/peripherals/Input_Gamecontroller.mm create mode 100644 xbmc/platform/darwin/peripherals/PeripheralBusGCController.h create mode 100644 xbmc/platform/darwin/peripherals/PeripheralBusGCController.mm create mode 100644 xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.h create mode 100644 xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.mm diff --git a/cmake/scripts/osx/ArchSetup.cmake b/cmake/scripts/osx/ArchSetup.cmake index f723aa24c4..8dc3907eac 100644 --- a/cmake/scripts/osx/ArchSetup.cmake +++ b/cmake/scripts/osx/ArchSetup.cmake @@ -35,7 +35,8 @@ list(APPEND DEPLIBS "-framework DiskArbitration" "-framework IOKit" "-framework ApplicationServices" "-framework AppKit" "-framework CoreAudio" "-framework AudioToolbox" "-framework CoreGraphics" "-framework CoreMedia" - "-framework VideoToolbox" "-framework Security") + "-framework VideoToolbox" "-framework Security" + "-framework GameController") set(CMAKE_OSX_DEPLOYMENT_TARGET 10.13) set(CMAKE_XCODE_ATTRIBUTE_CLANG_LINK_OBJC_RUNTIME OFF) diff --git a/cmake/treedata/darwin_embedded/subdirs.txt b/cmake/treedata/darwin_embedded/subdirs.txt index 718c5c59bc..20bfa02a70 100644 --- a/cmake/treedata/darwin_embedded/subdirs.txt +++ b/cmake/treedata/darwin_embedded/subdirs.txt @@ -5,8 +5,8 @@ xbmc/input/touch/generic input/touch/generic xbmc/platform/darwin platform/darwin xbmc/platform/darwin/ios-common platform/ios-common xbmc/platform/darwin/ios-common/network platform/ios-common/network -xbmc/platform/darwin/ios-common/peripherals platform/ios-common/peripherals xbmc/platform/darwin/ios-common/storage platform/ios-common/storage +xbmc/platform/darwin/peripherals platform/darwin/peripherals xbmc/platform/darwin/network platform/darwin/network xbmc/platform/darwin/utils platform/darwin/utils xbmc/platform/posix posix diff --git a/cmake/treedata/osx/subdirs.txt b/cmake/treedata/osx/subdirs.txt index fa28aa0efb..fea7db61a4 100644 --- a/cmake/treedata/osx/subdirs.txt +++ b/cmake/treedata/osx/subdirs.txt @@ -7,6 +7,7 @@ xbmc/platform/darwin/osx/network platform/darwin/osx/network xbmc/platform/darwin/osx/peripherals platform/osx/peripherals xbmc/platform/darwin/osx/powermanagement platform/darwin/osx/powermanagement xbmc/platform/darwin/osx/storage platform/osx/storage +xbmc/platform/darwin/peripherals platform/darwin/peripherals xbmc/platform/darwin/utils platform/darwin/utils xbmc/platform/posix posix xbmc/platform/posix/filesystem platform/posix/filesystem diff --git a/xbmc/peripherals/PeripheralTypes.h b/xbmc/peripherals/PeripheralTypes.h index 74994774b2..425ac0f930 100644 --- a/xbmc/peripherals/PeripheralTypes.h +++ b/xbmc/peripherals/PeripheralTypes.h @@ -33,8 +33,8 @@ enum PeripheralBusType #ifdef TARGET_ANDROID PERIPHERAL_BUS_ANDROID, #endif -#if defined(TARGET_DARWIN_EMBEDDED) - PERIPHERAL_BUS_DARWINEMBEDDED, +#if defined(TARGET_DARWIN) + PERIPHERAL_BUS_GCCONTROLLER, #endif PERIPHERAL_BUS_APPLICATION, }; @@ -190,9 +190,9 @@ public: case PERIPHERAL_BUS_ANDROID: return "android"; #endif -#if defined(TARGET_DARWIN_EMBEDDED) - case PERIPHERAL_BUS_DARWINEMBEDDED: - return "darwin_embedded"; +#if defined(TARGET_DARWIN) + case PERIPHERAL_BUS_GCCONTROLLER: + return "darwin_gccontroller"; #endif case PERIPHERAL_BUS_APPLICATION: return "application"; @@ -218,9 +218,9 @@ public: else if (strTypeLowerCase == "android") return PERIPHERAL_BUS_ANDROID; #endif -#if defined(TARGET_DARWIN_EMBEDDED) - else if (strTypeLowerCase == "darwin_embedded") - return PERIPHERAL_BUS_DARWINEMBEDDED; +#if defined(TARGET_DARWIN) + else if (strTypeLowerCase == "darwin_gccontroller") + return PERIPHERAL_BUS_GCCONTROLLER; #endif else if (strTypeLowerCase == "application") return PERIPHERAL_BUS_APPLICATION; diff --git a/xbmc/peripherals/Peripherals.cpp b/xbmc/peripherals/Peripherals.cpp index 7062abfe8f..8465f20429 100644 --- a/xbmc/peripherals/Peripherals.cpp +++ b/xbmc/peripherals/Peripherals.cpp @@ -20,8 +20,8 @@ #include #if defined(TARGET_ANDROID) #include "platform/android/peripherals/PeripheralBusAndroid.h" -#elif defined(TARGET_DARWIN_EMBEDDED) -#include "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h" +#elif defined(TARGET_DARWIN) +#include "platform/darwin/peripherals/PeripheralBusGCController.h" #endif #include "FileItem.h" #include "GUIUserMessages.h" @@ -100,7 +100,6 @@ void CPeripherals::Initialise() { Clear(); -#if !defined(TARGET_DARWIN_TVOS) CDirectory::Create("special://profile/peripheral_data"); /* load mappings from peripherals.xml */ @@ -117,8 +116,8 @@ void CPeripherals::Initialise() busses.push_back(std::make_shared(*this)); #if defined(TARGET_ANDROID) busses.push_back(std::make_shared(*this)); -#elif defined(TARGET_DARWIN_EMBEDDED) - busses.push_back(std::make_shared(*this)); +#elif defined(TARGET_DARWIN) + busses.push_back(std::make_shared(*this)); #endif busses.push_back(std::make_shared(*this)); @@ -135,7 +134,6 @@ void CPeripherals::Initialise() MESSAGING::CApplicationMessenger::GetInstance().RegisterReceiver(this); CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this); -#endif } void CPeripherals::Clear() diff --git a/xbmc/platform/darwin/ios-common/peripherals/CMakeLists.txt b/xbmc/platform/darwin/ios-common/peripherals/CMakeLists.txt deleted file mode 100644 index f9adab7de1..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -set(SOURCES Input_Gamecontroller.mm - PeripheralBusDarwinEmbedded.mm - PeripheralBusDarwinEmbeddedManager.mm) - -set(HEADERS Input_Gamecontroller.h - InputKey.h - PeripheralBusDarwinEmbedded.h - PeripheralBusDarwinEmbeddedManager.h) - -core_add_library(platform_darwinembedded_peripherals) diff --git a/xbmc/platform/darwin/ios-common/peripherals/InputKey.h b/xbmc/platform/darwin/ios-common/peripherals/InputKey.h deleted file mode 100644 index 9afaa661a7..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/InputKey.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#pragma once - -enum class GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON -{ - UP = 0, - DOWN = 1, - LEFT = 2, - RIGHT = 3, - A = 4, - B = 5, - X = 6, - Y = 7, - LEFTSHOULDER = 8, - LEFTTRIGGER = 9, - RIGHTSHOULDER = 10, - RIGHTTRIGGER = 11, - MENU = 12, - OPTION = 13, - LEFTTHUMBSTICKBUTTON = 14, - RIGHTTHUMBSTICKBUTTON = 15, - UNUSED = 99 -}; - -enum class GCCONTROLLER_EXTENDED_GAMEPAD_AXIS -{ - // Thumbstick Axis - LEFTTHUMB_X = 0, - LEFTTHUMB_Y = 1, - RIGHTTHUMB_X = 2, - RIGHTTHUMB_Y = 3, - // Thumbstick - LEFT = 90, - RIGHT = 91, - UNUSED = 99 -}; - -enum class GCCONTROLLER_MICRO_GAMEPAD_BUTTON -{ - UP = 0, - DOWN = 1, - LEFT = 2, - RIGHT = 3, - MENU = 4, - A = 5, - X = 6, - UNUSED = 99 -}; - -enum class GCCONTROLLER_TYPE -{ - UNKNOWN = 0, - EXTENDED = 1, - MICRO = 2, - NOTFOUND = 98, - UNUSED = 99 -}; - -struct InputValueInfo -{ - GCCONTROLLER_TYPE controllerType; - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON extendedButton; - GCCONTROLLER_EXTENDED_GAMEPAD_AXIS extendedAxis; - GCCONTROLLER_MICRO_GAMEPAD_BUTTON microButton; - - InputValueInfo(GCCONTROLLER_TYPE controllerType) - : controllerType(controllerType), - extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), - extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), - microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) - { - } - - InputValueInfo(GCCONTROLLER_TYPE controllerType, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON extendedButton) - : controllerType(controllerType), - extendedButton(extendedButton), - extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), - microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) - { - } - - InputValueInfo(GCCONTROLLER_TYPE controllerType, GCCONTROLLER_MICRO_GAMEPAD_BUTTON microButton) - : controllerType(controllerType), - extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), - extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), - microButton(microButton) - { - } - - InputValueInfo(GCCONTROLLER_TYPE controllerType, GCCONTROLLER_EXTENDED_GAMEPAD_AXIS extendedAxis) - : controllerType(controllerType), - extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), - extendedAxis(extendedAxis), - microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) - { - } -}; diff --git a/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.h b/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.h deleted file mode 100644 index 8ca0407f24..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#include "peripherals/PeripheralTypes.h" -#include "threads/CriticalSection.h" - -#import - -enum class GCCONTROLLER_TYPE; -@class CBPeripheralBusDarwinEmbeddedManager; - -@interface Input_IOSGamecontroller : NSObject - -- (instancetype)initWithName:(CBPeripheralBusDarwinEmbeddedManager*)callbackManager; -- (PERIPHERALS::PeripheralScanResults)GetGCDevices; -- (GCCONTROLLER_TYPE)GetGCControllerType:(int)deviceID; -- (int)checkOptionalButtons:(int)deviceID; - -@end diff --git a/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.mm b/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.mm deleted file mode 100644 index 2f389fcdb5..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/Input_Gamecontroller.mm +++ /dev/null @@ -1,986 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#import "Input_Gamecontroller.h" - -#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" -#include "threads/CriticalSection.h" -#include "threads/SingleLock.h" -#include "utils/log.h" - -#import "platform/darwin/ios-common/peripherals/InputKey.h" -#import "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h" - -#import -#import - -// TODO: replace all `respondsToSelector:` checks with @available after switching to 13 SDK - -struct PlayerControllerState -{ - BOOL dpadLeftPressed; - BOOL dpadRightPressed; - BOOL dpadUpPressed; - BOOL dpadDownPressed; - BOOL LeftThumbLeftPressed; - BOOL LeftThumbRightPressed; - BOOL LeftThumbUpPressed; - BOOL LeftThumbDownPressed; - BOOL RightThumbLeftPressed; - BOOL RightThumbRightPressed; - BOOL RightThumbUpPressed; - BOOL RightThumbDownPressed; -}; - -@implementation Input_IOSGamecontroller -{ - NSMutableArray* controllerArray; - // State for each controller - struct PlayerControllerState controllerState[4]; - CBPeripheralBusDarwinEmbeddedManager* cbmanager; - CCriticalSection m_GCMutex; - CCriticalSection m_controllerMutex; -} - -#pragma mark - Notificaton Observer - -- (void)addModeSwitchObserver -{ - // notifications for controller (dis)connect - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(controllerWasConnected:) - name:GCControllerDidConnectNotification - object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self - selector:@selector(controllerWasDisconnected:) - name:GCControllerDidDisconnectNotification - object:nil]; -} - -#pragma mark - Controller connection - -- (void)controllerWasConnected:(NSNotification*)notification -{ - GCController* controller = (GCController*)notification.object; - - [self controllerConnection:controller]; -} - -- (GCControllerPlayerIndex)getAvailablePlayerIndex -{ - // No known controllers - if (!controllerArray) - return GCControllerPlayerIndex1; - - bool player1 = false; - bool player2 = false; - bool player3 = false; - bool player4 = false; - - for (GCController* controller in controllerArray) - { - switch (controller.playerIndex) - { - case GCControllerPlayerIndex1: - player1 = true; - break; - case GCControllerPlayerIndex2: - player2 = true; - break; - case GCControllerPlayerIndex3: - player3 = true; - break; - case GCControllerPlayerIndex4: - player4 = true; - break; - default: - break; - } - } - - if (!player1) - return GCControllerPlayerIndex1; - else if (!player2) - return GCControllerPlayerIndex2; - else if (!player3) - return GCControllerPlayerIndex3; - else if (!player4) - return GCControllerPlayerIndex4; - else - return GCControllerPlayerIndexUnset; -} - -- (void)controllerConnection:(GCController*)controller -{ - // Lock so add/remove events are serialised - CSingleLock lock(m_controllerMutex); - - if ([controllerArray containsObject:controller]) - { - CLog::Log(LOGINFO, "INPUT - GAMECONTROLLER: ignoring input device with ID {} already known", - [controller.vendorName UTF8String]); - return; - } - - controller.playerIndex = [self getAvailablePlayerIndex]; - - // set microgamepad to absolute values for dpad (ie center touchpad is 0,0) - if (controller.microGamepad) - controller.microGamepad.reportsAbsoluteDpadValues = YES; - - CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: input device with ID {} playerIndex {} added ", - [controller.vendorName UTF8String], static_cast(controller.playerIndex)); - [controllerArray addObject:controller]; - - [cbmanager DeviceAdded:static_cast(controller.playerIndex)]; - - [self registerChangeHandler:controller]; -} - -- (void)registerChangeHandler:(GCController*)controller -{ - if (controller.extendedGamepad) - { - CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: extendedGamepad changehandler added"); - // register block for input change detection - [self extendedValueChangeHandler:controller]; - } - else if (controller.microGamepad) - { - CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: microGamepad changehandler added"); - [self microValueChangeHandler:controller]; - } - if (@available(iOS 13.0, tvOS 13.0, *)) - { - // Do Nothing - Cant negate @available - } - else - { - // pausevaluechangehandler only required for <= *os12 - CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: <= *OS12 pauseValueChangeHandler added"); - [self pauseValueChangeHandler:controller]; - } -} - -#pragma mark - Controller disconnection - -- (void)controllerWasDisconnected:(NSNotification*)notification -{ - // Lock so add/remove events are serialised - CSingleLock lock(m_controllerMutex); - // a controller was disconnected - GCController* controller = (GCController*)notification.object; - if (!controllerArray) - return; - - // Reset controller state to ensure default state for next time playerIndex - controllerState[static_cast(controller.playerIndex)] = {}; - - auto i = [controllerArray indexOfObject:controller]; - - if (i == NSNotFound) - { - CLog::Log(LOGWARNING, "INPUT - GAMECONTROLLER: failed to remove input device {} Not Found ", - [controller.vendorName UTF8String]); - return; - } - - CLog::Log(LOGINFO, "INPUT - GAMECONTROLLER: input device \"{}\" removed", - [controller.vendorName UTF8String]); - - [controllerArray removeObjectAtIndex:i]; - [cbmanager DeviceRemoved:static_cast(controller.playerIndex)]; -} - -#pragma mark - GCMicroGamepad valueChangeHandler - -- (void)microValueChangeHandler:(GCController*)controller -{ - controller.microGamepad.valueChangedHandler = - ^(GCMicroGamepad* gamepad, GCControllerElement* element) { - NSString* message; - - kodi::addon::PeripheralEvent newEvent; - newEvent.SetPeripheralIndex(static_cast(gamepad.controller.playerIndex)); - - CSingleLock lock(m_GCMutex); - - // A button - if (gamepad.buttonA == element) - { - message = [self setButtonState:gamepad.buttonA - withEvent:&newEvent - withMessage:@"A Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, - GCCONTROLLER_MICRO_GAMEPAD_BUTTON::A}]; - } - // X button - else if (gamepad.buttonX == element) - { - message = [self setButtonState:gamepad.buttonX - withEvent:&newEvent - withMessage:@"X Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, - GCCONTROLLER_MICRO_GAMEPAD_BUTTON::X}]; - } - - // buttonMenu - else if ([gamepad respondsToSelector:@selector(buttonMenu)] && - [gamepad performSelector:@selector(buttonMenu)] == element) - { - message = [self setButtonState:static_cast(element) - withEvent:&newEvent - withMessage:@"Menu Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, - GCCONTROLLER_MICRO_GAMEPAD_BUTTON::MENU}]; - } - // d-pad - else if (gamepad.dpad == element) - { - message = [self checkdpad:gamepad.dpad - withEvent:&newEvent - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO} - withplayerIndex:static_cast(controller.playerIndex)]; - } - - [cbmanager SetDigitalEvent:newEvent]; - //! @todo Debug Purposes only - excessive log spam - // utilise spdlog for input compononent logging - // [cbmanager displayMessage:message controllerID:static_cast(controller.playerIndex)]; - }; -} - -#pragma mark - GCExtendedGamepad valueChangeHandler - -- (void)extendedValueChangeHandler:(GCController*)controller -{ - controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad* gamepad, - GCControllerElement* element) { - NSString* message; - - kodi::addon::PeripheralEvent newEvent; - kodi::addon::PeripheralEvent axisEvent; - auto playerIndex = static_cast(gamepad.controller.playerIndex); - newEvent.SetPeripheralIndex(playerIndex); - axisEvent.SetPeripheralIndex(playerIndex); - - CSingleLock lock(m_GCMutex); - - // left trigger - if (gamepad.leftTrigger == element) - { - message = - [self setButtonState:gamepad.leftTrigger - withEvent:&newEvent - withMessage:@"Left Trigger" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTTRIGGER}]; - } - // right trigger - else if (gamepad.rightTrigger == element) - { - message = - [self setButtonState:gamepad.rightTrigger - withEvent:&newEvent - withMessage:@"Right Trigger" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTTRIGGER}]; - } - // left shoulder button - else if (gamepad.leftShoulder == element) - { - message = - [self setButtonState:gamepad.leftShoulder - withEvent:&newEvent - withMessage:@"Left Shoulder Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTSHOULDER}]; - } - // right shoulder button - else if (gamepad.rightShoulder == element) - { - message = - [self setButtonState:gamepad.rightShoulder - withEvent:&newEvent - withMessage:@"Right Shoulder Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTSHOULDER}]; - } - // A button - else if (gamepad.buttonA == element) - { - message = [self setButtonState:gamepad.buttonA - withEvent:&newEvent - withMessage:@"A Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::A}]; - } - // B button - else if (gamepad.buttonB == element) - { - message = [self setButtonState:gamepad.buttonB - withEvent:&newEvent - withMessage:@"B Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::B}]; - } - // X button - else if (gamepad.buttonX == element) - { - message = [self setButtonState:gamepad.buttonX - withEvent:&newEvent - withMessage:@"X Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::X}]; - } - // Y button - else if (gamepad.buttonY == element) - { - message = [self setButtonState:gamepad.buttonY - withEvent:&newEvent - withMessage:@"Y Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::Y}]; - } - // buttonMenu - else if ([gamepad respondsToSelector:@selector(buttonMenu)] && - [gamepad performSelector:@selector(buttonMenu)] == element) - { - message = [self setButtonState:static_cast(element) - withEvent:&newEvent - withMessage:@"Menu Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::MENU}]; - } - // buttonOptions - else if ([gamepad respondsToSelector:@selector(buttonOptions)] && - [gamepad performSelector:@selector(buttonOptions)] == element) - { - message = [self setButtonState:static_cast(element) - withEvent:&newEvent - withMessage:@"Option Button" - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::OPTION}]; - } - // d-pad - else if (gamepad.dpad == element) - { - message = [self checkdpad:gamepad.dpad - withEvent:&newEvent - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED} - withplayerIndex:playerIndex]; - } - // left stick - else if (gamepad.leftThumbstick == element) - { - message = @"Left Stick"; - message = [self checkthumbstick:gamepad.leftThumbstick - withEvent:&axisEvent - withMessage:message - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT - withplayerIndex:playerIndex]; - } - // right stick - else if (gamepad.rightThumbstick == element) - { - message = @"Right Stick"; - message = [self checkthumbstick:gamepad.rightThumbstick - withEvent:&axisEvent - withMessage:message - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - withplayerIndex:playerIndex]; - } - if (@available(iOS 12.1, tvOS 12.1, *)) - { - // Left Thumbstick Button - if (gamepad.leftThumbstickButton == element) - { - message = - [self setButtonState:gamepad.leftThumbstickButton - withEvent:&newEvent - withMessage:@"Left Thumbstick Button" - withInputInfo:InputValueInfo{ - GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTTHUMBSTICKBUTTON}]; - } - // Right Thumbstick Button - if (gamepad.rightThumbstickButton == element) - { - message = - [self setButtonState:gamepad.rightThumbstickButton - withEvent:&newEvent - withMessage:@"Right Thumbstick Button" - withInputInfo:InputValueInfo{ - GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTTHUMBSTICKBUTTON}]; - } - } - [cbmanager SetDigitalEvent:newEvent]; - - //! @todo Debug Purposes only - excessive log spam - // utilise spdlog for input compononent logging - // [cbmanager displayMessage:message controllerID:static_cast(controller.playerIndex)]; - }; -} - -- (void)pauseValueChangeHandler:(GCController*)controller -{ - controller.controllerPausedHandler = ^(GCController* controller) { - // check if we're currently paused or not - // then bring up or remove the paused view controller - - kodi::addon::PeripheralEvent newEvent; - newEvent.SetPeripheralIndex(static_cast(controller.playerIndex)); - newEvent.SetType(PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON); - - if (controller.extendedGamepad) - newEvent.SetDriverIndex( - static_cast(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::MENU)); - else if (controller.microGamepad) - newEvent.SetDriverIndex(static_cast(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::MENU)); - - // Button Down event - newEvent.SetButtonState(JOYSTICK_STATE_BUTTON_PRESSED); - [cbmanager SetDigitalEvent:newEvent]; - - // Button Up Event - newEvent.SetButtonState(JOYSTICK_STATE_BUTTON_UNPRESSED); - [cbmanager SetDigitalEvent:newEvent]; - }; -} - -#pragma mark - valuechangehandler event state change - -- (NSString*)setButtonState:(GCControllerButtonInput*)button - withEvent:(kodi::addon::PeripheralEvent*)event - withMessage:(NSString*)message - withInputInfo:(InputValueInfo)inputInfo -{ - event->SetType(PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON); - - switch (inputInfo.controllerType) - { - case GCCONTROLLER_TYPE::EXTENDED: - event->SetDriverIndex(static_cast(inputInfo.extendedButton)); - break; - case GCCONTROLLER_TYPE::MICRO: - event->SetDriverIndex(static_cast(inputInfo.microButton)); - break; - default: - return [message - stringByAppendingFormat:@" ERROR:: CONTROLLER_TYPE %d", inputInfo.controllerType]; - } - - if (button.isPressed) - { - event->SetButtonState(JOYSTICK_STATE_BUTTON_PRESSED); - return [message stringByAppendingString:@" Pressed"]; - } - else - { - event->SetButtonState(JOYSTICK_STATE_BUTTON_UNPRESSED); - return [message stringByAppendingString:@" Released"]; - } -} - -- (void)setAxisValue:(GCControllerAxisInput*)axisValue - withEvent:(kodi::addon::PeripheralEvent*)event - withAxis:(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS)axis -{ - event->SetType(PERIPHERAL_EVENT_TYPE_DRIVER_AXIS); - event->SetDriverIndex(static_cast(axis)); - event->SetAxisState(axisValue.value); -} - -- (PERIPHERALS::PeripheralScanResults)GetGCDevices -{ - PERIPHERALS::PeripheralScanResults scanresults; - - if (controllerArray.count == 0) - return scanresults; - - for (GCController* controller in controllerArray) - { - PERIPHERALS::PeripheralScanResult peripheralScanResult; - peripheralScanResult.m_type = PERIPHERALS::PERIPHERAL_JOYSTICK; - peripheralScanResult.m_strLocation = - [cbmanager GetDeviceLocation:static_cast(controller.playerIndex)]; - peripheralScanResult.m_iVendorId = 0; - peripheralScanResult.m_iProductId = 0; - peripheralScanResult.m_mappedType = PERIPHERALS::PERIPHERAL_JOYSTICK; - - if (controller.extendedGamepad) - peripheralScanResult.m_strDeviceName = "Extended Gamepad"; - else if (controller.microGamepad) - peripheralScanResult.m_strDeviceName = "Micro Gamepad"; - else - peripheralScanResult.m_strDeviceName = "Unknown Gamepad"; - - peripheralScanResult.m_busType = PERIPHERALS::PERIPHERAL_BUS_DARWINEMBEDDED; - peripheralScanResult.m_mappedBusType = PERIPHERALS::PERIPHERAL_BUS_DARWINEMBEDDED; - peripheralScanResult.m_iSequence = 0; - scanresults.m_results.push_back(peripheralScanResult); - } - - return scanresults; -} - -- (GCCONTROLLER_TYPE)GetGCControllerType:(int)deviceID -{ - GCController* controller; - for (GCController* aController in controllerArray) - { - if (controller.playerIndex != deviceID) - continue; - controller = aController; - break; - } - if (controller.extendedGamepad) - return GCCONTROLLER_TYPE::EXTENDED; - else if (controller.microGamepad) - return GCCONTROLLER_TYPE::MICRO; - else - return GCCONTROLLER_TYPE::NOTFOUND; -} - -- (int)checkOptionalButtons:(int)deviceID -{ - int optionalButtonCount = 0; - - for (GCController* controller in controllerArray) - { - if (controller.playerIndex != deviceID) - continue; - - if (!controller.extendedGamepad) - continue; - - // Check if optional buttons exist on mapped controller - // button object is nil if button doesn't exist - if ([controller.extendedGamepad respondsToSelector:@selector(buttonOptions)] && - [controller.extendedGamepad performSelector:@selector(buttonOptions)] != nil) - ++optionalButtonCount; - - if (@available(iOS 12.1, tvOS 12.1, *)) - { - if (controller.extendedGamepad.leftThumbstickButton) - ++optionalButtonCount; - if (controller.extendedGamepad.rightThumbstickButton) - ++optionalButtonCount; - } - } - return optionalButtonCount; -} - -- (instancetype)initWithName:(CBPeripheralBusDarwinEmbeddedManager*)callbackManager -{ - self = [super init]; - if (!self) - return nil; - - cbmanager = callbackManager; - - [self addModeSwitchObserver]; - - controllerArray = [[NSMutableArray alloc] initWithCapacity:4]; - - auto controllers = [GCController controllers]; - // Iterate through any pre-existing controller connections at startup to enable value handlers - for (GCController* controller in controllers) - [self controllerConnection:controller]; - - return self; -} - -- (NSString*)checkthumbstick:(GCControllerDirectionPad*)thumbstick - withEvent:(kodi::addon::PeripheralEvent*)event - withMessage:(NSString*)message - withAxis:(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS)thumbstickside - withplayerIndex:(int)playerIndex -{ - // thumbstick released completely - zero both axis - if (!thumbstick.up.isPressed && !thumbstick.down.isPressed && !thumbstick.left.isPressed && - !thumbstick.right.isPressed) - { - if (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT) - { - controllerState[playerIndex].RightThumbLeftPressed = NO; - controllerState[playerIndex].RightThumbRightPressed = NO; - controllerState[playerIndex].RightThumbUpPressed = NO; - controllerState[playerIndex].RightThumbDownPressed = NO; - - // Thumbstick release event - kodi::addon::PeripheralEvent releaseEvent; - releaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&releaseEvent - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X]; - - [cbmanager SetAxisEvent:releaseEvent]; - - [self setAxisValue:0 - withEvent:&releaseEvent - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y]; - - message = [message stringByAppendingString:@" Released"]; - [cbmanager SetAxisEvent:releaseEvent]; - } - else - { - controllerState[playerIndex].LeftThumbLeftPressed = NO; - controllerState[playerIndex].LeftThumbRightPressed = NO; - controllerState[playerIndex].LeftThumbUpPressed = NO; - controllerState[playerIndex].LeftThumbDownPressed = NO; - - // Thumbstick release event - kodi::addon::PeripheralEvent releaseEvent; - releaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&releaseEvent - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X]; - - [cbmanager SetAxisEvent:releaseEvent]; - - [self setAxisValue:0 - withEvent:&releaseEvent - withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y]; - - message = [message stringByAppendingString:@" Released"]; - [cbmanager SetAxisEvent:releaseEvent]; - } - } - else - { - - if (thumbstick.up.isPressed || controllerState[playerIndex].RightThumbUpPressed || - controllerState[playerIndex].LeftThumbUpPressed) - { - // Thumbstick centered - if (!thumbstick.up.isPressed) - { - if (controllerState[playerIndex].RightThumbUpPressed) - controllerState[playerIndex].RightThumbUpPressed = - !controllerState[playerIndex].RightThumbUpPressed; - else if (controllerState[playerIndex].LeftThumbUpPressed) - controllerState[playerIndex].LeftThumbUpPressed = - !controllerState[playerIndex].LeftThumbUpPressed; - - // Thumbstick release event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&newReleaseEvent - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; - - message = [message stringByAppendingFormat:@" Up %f", 0.0]; - [cbmanager SetAxisEvent:newReleaseEvent]; - } - else - { - controllerState[playerIndex].RightThumbUpPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? YES - : controllerState[playerIndex].RightThumbUpPressed); - controllerState[playerIndex].LeftThumbUpPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT - ? YES - : controllerState[playerIndex].LeftThumbUpPressed); - - [self setAxisValue:thumbstick.yAxis - withEvent:event - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; - - message = [message - stringByAppendingFormat:@" Up %f", static_cast(thumbstick.yAxis.value)]; - [cbmanager SetAxisEvent:*event]; - } - } - if (thumbstick.down.isPressed || controllerState[playerIndex].RightThumbDownPressed || - controllerState[playerIndex].LeftThumbDownPressed) - { - // Thumbstick centered - if (!thumbstick.down.isPressed) - { - if (controllerState[playerIndex].RightThumbDownPressed) - controllerState[playerIndex].RightThumbDownPressed = - !controllerState[playerIndex].RightThumbDownPressed; - else if (controllerState[playerIndex].LeftThumbDownPressed) - controllerState[playerIndex].LeftThumbDownPressed = - !controllerState[playerIndex].LeftThumbDownPressed; - - // Thumbstick release event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&newReleaseEvent - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; - - message = [message stringByAppendingFormat:@" Down %f", 0.0]; - [cbmanager SetAxisEvent:newReleaseEvent]; - } - else - { - controllerState[playerIndex].RightThumbDownPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? YES - : controllerState[playerIndex].RightThumbDownPressed); - controllerState[playerIndex].LeftThumbDownPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT - ? YES - : controllerState[playerIndex].LeftThumbDownPressed); - - [self setAxisValue:thumbstick.yAxis - withEvent:event - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; - - message = [message - stringByAppendingFormat:@" Down %f", static_cast(thumbstick.yAxis.value)]; - [cbmanager SetAxisEvent:*event]; - } - } - if (thumbstick.left.isPressed || controllerState[playerIndex].RightThumbLeftPressed || - controllerState[playerIndex].LeftThumbLeftPressed) - { - // Thumbstick centered - if (!thumbstick.left.isPressed) - { - if (controllerState[playerIndex].RightThumbLeftPressed) - controllerState[playerIndex].RightThumbLeftPressed = - !controllerState[playerIndex].RightThumbLeftPressed; - else if (controllerState[playerIndex].LeftThumbLeftPressed) - controllerState[playerIndex].LeftThumbLeftPressed = - !controllerState[playerIndex].LeftThumbLeftPressed; - - // Thumbstick release event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&newReleaseEvent - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; - - message = [message stringByAppendingFormat:@" Left %f", 0.0]; - [cbmanager SetAxisEvent:newReleaseEvent]; - } - else - { - controllerState[playerIndex].RightThumbLeftPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? YES - : controllerState[playerIndex].RightThumbLeftPressed); - controllerState[playerIndex].LeftThumbLeftPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT - ? YES - : controllerState[playerIndex].LeftThumbLeftPressed); - - [self setAxisValue:thumbstick.xAxis - withEvent:event - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; - - message = [message - stringByAppendingFormat:@" Left %f", static_cast(thumbstick.xAxis.value)]; - [cbmanager SetAxisEvent:*event]; - } - } - if (thumbstick.right.isPressed || controllerState[playerIndex].RightThumbRightPressed || - controllerState[playerIndex].LeftThumbRightPressed) - { - // Thumbstick centered - if (!thumbstick.right.isPressed) - { - if (controllerState[playerIndex].RightThumbRightPressed) - controllerState[playerIndex].RightThumbRightPressed = - !controllerState[playerIndex].RightThumbRightPressed; - else if (controllerState[playerIndex].LeftThumbRightPressed) - controllerState[playerIndex].LeftThumbRightPressed = - !controllerState[playerIndex].LeftThumbRightPressed; - - // Thumbstick release event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - [self setAxisValue:0 - withEvent:&newReleaseEvent - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; - - message = [message stringByAppendingFormat:@" Right %f", 0.0]; - [cbmanager SetAxisEvent:newReleaseEvent]; - } - else - { - controllerState[playerIndex].RightThumbRightPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? YES - : controllerState[playerIndex].RightThumbRightPressed); - controllerState[playerIndex].LeftThumbRightPressed = - (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT - ? YES - : controllerState[playerIndex].LeftThumbRightPressed); - - [self setAxisValue:thumbstick.xAxis - withEvent:event - withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT - ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X - : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; - - message = [message - stringByAppendingFormat:@" Right %f", static_cast(thumbstick.xAxis.value)]; - [cbmanager SetAxisEvent:*event]; - } - } - } - return message; -} - -- (NSString*)checkdpad:(GCControllerDirectionPad*)dpad - withEvent:(kodi::addon::PeripheralEvent*)event - withInputInfo:(InputValueInfo)inputInfo - withplayerIndex:(int)playerIndex -{ - NSString* message = nil; - if ((dpad.up.isPressed && !controllerState[playerIndex].dpadUpPressed) || - (!dpad.up.isPressed && controllerState[playerIndex].dpadUpPressed)) - { - message = @"D-Pad Up"; - - if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) - inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UP; - else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) - inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UP; - - if (!controllerState[playerIndex].dpadUpPressed) - { - // Button Down event - message = [self setButtonState:dpad.up - withEvent:event - withMessage:message - withInputInfo:inputInfo]; - } - else - { - // Button Up event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - message = [self setButtonState:dpad.up - withEvent:&newReleaseEvent - withMessage:message - withInputInfo:inputInfo]; - [cbmanager SetDigitalEvent:newReleaseEvent]; - } - controllerState[playerIndex].dpadUpPressed = !controllerState[playerIndex].dpadUpPressed; - } - if ((dpad.down.isPressed && !controllerState[playerIndex].dpadDownPressed) || - (!dpad.down.isPressed && controllerState[playerIndex].dpadDownPressed)) - { - message = @"D-Pad Down"; - - if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) - inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::DOWN; - else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) - inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::DOWN; - - if (!controllerState[playerIndex].dpadDownPressed) - { - // Button Down event - message = [self setButtonState:dpad.down - withEvent:event - withMessage:message - withInputInfo:inputInfo]; - } - else - { - // Button Up event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - message = [self setButtonState:dpad.down - withEvent:&newReleaseEvent - withMessage:message - withInputInfo:inputInfo]; - [cbmanager SetDigitalEvent:newReleaseEvent]; - } - controllerState[playerIndex].dpadDownPressed = !controllerState[playerIndex].dpadDownPressed; - } - if ((dpad.left.isPressed && !controllerState[playerIndex].dpadLeftPressed) || - (!dpad.left.isPressed && controllerState[playerIndex].dpadLeftPressed)) - { - message = @"D-Pad Left"; - - if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) - inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFT; - else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) - inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::LEFT; - - if (!controllerState[playerIndex].dpadLeftPressed) - { - // Button Down event - message = [self setButtonState:dpad.left - withEvent:event - withMessage:message - withInputInfo:inputInfo]; - } - else - { - // Button Up event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - message = [self setButtonState:dpad.left - withEvent:&newReleaseEvent - withMessage:message - withInputInfo:inputInfo]; - [cbmanager SetDigitalEvent:newReleaseEvent]; - } - controllerState[playerIndex].dpadLeftPressed = !controllerState[playerIndex].dpadLeftPressed; - } - if ((dpad.right.isPressed && !controllerState[playerIndex].dpadRightPressed) || - (!dpad.right.isPressed && controllerState[playerIndex].dpadRightPressed)) - { - message = @"D-Pad Right"; - - if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) - inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHT; - else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) - inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::RIGHT; - - if (!controllerState[playerIndex].dpadRightPressed) - { - // Button Down event - message = [self setButtonState:dpad.right - withEvent:event - withMessage:message - withInputInfo:inputInfo]; - } - else - { - // Button Up event - kodi::addon::PeripheralEvent newReleaseEvent; - newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); - message = [self setButtonState:dpad.right - withEvent:&newReleaseEvent - withMessage:message - withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, - GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHT}]; - [cbmanager SetDigitalEvent:newReleaseEvent]; - } - controllerState[playerIndex].dpadRightPressed = !controllerState[playerIndex].dpadRightPressed; - } - return message; -} - -@end diff --git a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h b/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h deleted file mode 100644 index c0227f0bd7..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#pragma once - -#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" -#include "peripherals/PeripheralTypes.h" -#include "peripherals/bus/PeripheralBus.h" -#include "threads/CriticalSection.h" - -#include -#include -#include -#include - -struct PeripheralBusDarwinEmbeddedWrapper; - -namespace PERIPHERALS -{ -class CPeripheralBusDarwinEmbedded : public CPeripheralBus -{ -public: - explicit CPeripheralBusDarwinEmbedded(CPeripherals& manager); - ~CPeripheralBusDarwinEmbedded() override; - - // specialisation of CPeripheralBus - bool InitializeProperties(CPeripheral& peripheral) override; - void Initialise(void) override; - void ProcessEvents() override; - - bool PerformDeviceScan(PeripheralScanResults& results) override; - PeripheralScanResults GetInputDevices(); - - void callOnDeviceAdded(const std::string& strLocation); - void callOnDeviceRemoved(const std::string& strLocation); - - void SetScanResults(const PeripheralScanResults& resScanResults); - - const std::string& getDeviceLocationPrefix() - { - // Initialize the static variable - static std::string DeviceLocationPrefix("darwinembedded/inputdevice/"); - return DeviceLocationPrefix; - } - - -private: - void GetEvents(std::vector& events); - std::unique_ptr m_peripheralDarwinEmbedded; - std::string GetDeviceLocation(int deviceId); - bool GetDeviceId(const std::string& deviceLocation, int& deviceId); - - PeripheralScanResults m_scanResults; - CCriticalSection m_critSectionStates; - CCriticalSection m_critSectionResults; -}; -} // namespace PERIPHERALS diff --git a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.mm b/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.mm deleted file mode 100644 index abdd4dac93..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.mm +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#include "PeripheralBusDarwinEmbedded.h" - -#include "ServiceBroker.h" -#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" -#include "peripherals/bus/PeripheralBus.h" -#include "peripherals/devices/PeripheralJoystick.h" -#include "threads/CriticalSection.h" -#include "threads/SingleLock.h" -#include "utils/log.h" - -#include "platform/darwin/ios-common/peripherals/InputKey.h" -#import "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h" - -#define JOYSTICK_PROVIDER_DARWINEMBEDDED "darwinembedded" - -struct PeripheralBusDarwinEmbeddedWrapper -{ - CBPeripheralBusDarwinEmbeddedManager* callbackClass; -}; - -PERIPHERALS::CPeripheralBusDarwinEmbedded::CPeripheralBusDarwinEmbedded(CPeripherals& manager) - : CPeripheralBus("PeripBusDarwinEmbedded", manager, PERIPHERAL_BUS_DARWINEMBEDDED) -{ - m_peripheralDarwinEmbedded = std::make_unique(); - m_peripheralDarwinEmbedded->callbackClass = - [[CBPeripheralBusDarwinEmbeddedManager alloc] initWithName:this]; - m_bNeedsPolling = false; - - // get all currently connected input devices - m_scanResults = GetInputDevices(); -} - -PERIPHERALS::CPeripheralBusDarwinEmbedded::~CPeripheralBusDarwinEmbedded() -{ -} - -bool PERIPHERALS::CPeripheralBusDarwinEmbedded::InitializeProperties(CPeripheral& peripheral) -{ - // Returns true regardless, why is it necessary? - if (!CPeripheralBus::InitializeProperties(peripheral)) - return false; - - if (peripheral.Type() != PERIPHERALS::PERIPHERAL_JOYSTICK) - { - CLog::Log(LOGWARNING, "CPeripheralBusDarwinEmbedded: invalid peripheral type: %s", - PERIPHERALS::PeripheralTypeTranslator::TypeToString(peripheral.Type())); - return false; - } - - // deviceId will be our playerIndex - int deviceId; - if (!GetDeviceId(peripheral.Location(), deviceId)) - { - CLog::Log(LOGWARNING, - "CPeripheralBusDarwinEmbedded: failed to initialize properties for peripheral \"%s\"", - peripheral.Location().c_str()); - return false; - } - - CLog::Log(LOGDEBUG, "CPeripheralBusDarwinEmbedded: Initializing device \"{}\"", - peripheral.DeviceName()); - - auto& joystick = static_cast(peripheral); - - joystick.SetRequestedPort(deviceId); - joystick.SetProvider(JOYSTICK_PROVIDER_DARWINEMBEDDED); - - auto controllerType = [m_peripheralDarwinEmbedded->callbackClass GetControllerType:deviceId]; - - switch (controllerType) - { - case GCCONTROLLER_TYPE::EXTENDED: - // Extended Gamepad - joystick.SetButtonCount([m_peripheralDarwinEmbedded->callbackClass - GetControllerButtonCount:deviceId - withControllerType:controllerType]); - joystick.SetAxisCount([m_peripheralDarwinEmbedded->callbackClass - GetControllerAxisCount:deviceId - withControllerType:controllerType]); - break; - case GCCONTROLLER_TYPE::MICRO: - // Micro Gamepad - joystick.SetButtonCount([m_peripheralDarwinEmbedded->callbackClass - GetControllerButtonCount:deviceId - withControllerType:controllerType]); - joystick.SetAxisCount([m_peripheralDarwinEmbedded->callbackClass - GetControllerAxisCount:deviceId - withControllerType:controllerType]); - break; - default: - CLog::Log(LOGDEBUG, "CPeripheralBusDarwinEmbedded: Unknown Controller Type"); - return false; - } - - CLog::Log(LOGDEBUG, "CPeripheralBusDarwinEmbedded: Device has %u buttons and %u axes", - joystick.ButtonCount(), joystick.AxisCount()); - - return true; -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::Initialise(void) -{ - CPeripheralBus::Initialise(); - TriggerDeviceScan(); -} - -bool PERIPHERALS::CPeripheralBusDarwinEmbedded::PerformDeviceScan(PeripheralScanResults& results) -{ - CSingleLock lock(m_critSectionResults); - results = m_scanResults; - - return true; -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::SetScanResults( - const PERIPHERALS::PeripheralScanResults& resScanResults) -{ - CSingleLock lock(m_critSectionResults); - m_scanResults = resScanResults; -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::GetEvents( - std::vector& events) -{ - CSingleLock lock(m_critSectionStates); - std::vector digitalEvents; - digitalEvents = [m_peripheralDarwinEmbedded->callbackClass GetButtonEvents]; - - std::vector axisEvents; - axisEvents = [m_peripheralDarwinEmbedded->callbackClass GetAxisEvents]; - - events.reserve(digitalEvents.size() + axisEvents.size()); // preallocate memory - events.insert(events.end(), digitalEvents.begin(), digitalEvents.end()); - events.insert(events.end(), axisEvents.begin(), axisEvents.end()); -} - -bool PERIPHERALS::CPeripheralBusDarwinEmbedded::GetDeviceId(const std::string& deviceLocation, - int& deviceId) -{ - if (deviceLocation.empty() || - !StringUtils::StartsWith(deviceLocation, getDeviceLocationPrefix()) || - deviceLocation.size() <= getDeviceLocationPrefix().size()) - return false; - - std::string strDeviceId = deviceLocation.substr(getDeviceLocationPrefix().size()); - if (!StringUtils::IsNaturalNumber(strDeviceId)) - return false; - - deviceId = static_cast(strtol(strDeviceId.c_str(), nullptr, 10)); - return true; -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::ProcessEvents() -{ - std::vector events; - { - CSingleLock lock(m_critSectionStates); - - //! @todo Multiple controller event processing - GetEvents(events); - } - - for (const auto& event : events) - { - PeripheralPtr device = GetPeripheral(GetDeviceLocation(event.PeripheralIndex())); - if (!device || device->Type() != PERIPHERAL_JOYSTICK) - continue; - - auto joystick = static_cast(device.get()); - switch (event.Type()) - { - case PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON: - { - const bool bPressed = (event.ButtonState() == JOYSTICK_STATE_BUTTON_PRESSED); - joystick->OnButtonMotion(event.DriverIndex(), bPressed); - break; - } - case PERIPHERAL_EVENT_TYPE_DRIVER_AXIS: - { - joystick->OnAxisMotion(event.DriverIndex(), event.AxisState()); - break; - } - default: - break; - } - } - { - CSingleLock lock(m_critSectionStates); - //! @todo Multiple controller handling - PeripheralPtr device = GetPeripheral(GetDeviceLocation(0)); - - if (device && device->Type() == PERIPHERAL_JOYSTICK) - static_cast(device.get())->ProcessAxisMotions(); - } -} - -std::string PERIPHERALS::CPeripheralBusDarwinEmbedded::GetDeviceLocation(int deviceId) -{ - return [m_peripheralDarwinEmbedded->callbackClass GetDeviceLocation:deviceId]; -} - -PERIPHERALS::PeripheralScanResults PERIPHERALS::CPeripheralBusDarwinEmbedded::GetInputDevices() -{ - CLog::Log(LOGINFO, "CPeripheralBusDarwinEmbedded: scanning for input devices..."); - - return [m_peripheralDarwinEmbedded->callbackClass GetInputDevices]; -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::callOnDeviceAdded(const std::string& strLocation) -{ - OnDeviceAdded(strLocation); -} - -void PERIPHERALS::CPeripheralBusDarwinEmbedded::callOnDeviceRemoved(const std::string& strLocation) -{ - OnDeviceRemoved(strLocation); -} diff --git a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h b/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h deleted file mode 100644 index 2da2ef3765..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" -#include "peripherals/PeripheralTypes.h" -#include "threads/CriticalSection.h" - -#import "platform/darwin/ios-common/peripherals/Input_Gamecontroller.h" -#include "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h" - -#include -#include - -#import - -@interface CBPeripheralBusDarwinEmbeddedManager : NSObject - -@property(nonatomic, strong) Input_IOSGamecontroller* input_GC; - -- (instancetype)initWithName:(PERIPHERALS::CPeripheralBusDarwinEmbedded*)parentClass; -- (PERIPHERALS::PeripheralScanResults)GetInputDevices; -- (void)DeviceAdded:(int)deviceID; -- (void)DeviceRemoved:(int)deviceID; -- (void)SetDigitalEvent:(kodi::addon::PeripheralEvent)event; -- (void)SetAxisEvent:(kodi::addon::PeripheralEvent)event; -- (std::vector)GetButtonEvents; -- (std::vector)GetAxisEvents; -- (int)GetControllerAxisCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType; -- (int)GetControllerButtonCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType; -- (GCCONTROLLER_TYPE)GetControllerType:(int)deviceID; -- (std::string)GetDeviceLocation:(int)deviceId; -- (void)displayMessage:(NSString*)message controllerID:(int)controllerID; -@end diff --git a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.mm b/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.mm deleted file mode 100644 index 82973ba29f..0000000000 --- a/xbmc/platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbeddedManager.mm +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2020 Team Kodi - * This file is part of Kodi - https://kodi.tv - * - * SPDX-License-Identifier: GPL-2.0-or-later - * See LICENSES/README.md for more information. - */ - -#import "PeripheralBusDarwinEmbeddedManager.h" - -#include "peripherals/PeripheralTypes.h" -#include "threads/SingleLock.h" -#include "utils/StringUtils.h" -#include "utils/log.h" - -#include "platform/darwin/ios-common/peripherals/InputKey.h" -#import "platform/darwin/ios-common/peripherals/Input_Gamecontroller.h" -#include "platform/darwin/ios-common/peripherals/PeripheralBusDarwinEmbedded.h" - -#pragma mark - objc implementation - -@implementation CBPeripheralBusDarwinEmbeddedManager -{ - PERIPHERALS::CPeripheralBusDarwinEmbedded* parentClass; - std::vector m_digitalEvents; - std::vector m_axisEvents; - CCriticalSection m_eventMutex; -} - -#pragma mark - callbackClass inputdevices - -- (PERIPHERALS::PeripheralScanResults)GetInputDevices -{ - PERIPHERALS::PeripheralScanResults scanresults = {}; - - scanresults = [self.input_GC GetGCDevices]; - - return scanresults; -} - -- (void)DeviceAdded:(int)deviceID -{ - parentClass->SetScanResults([self GetInputDevices]); - parentClass->callOnDeviceAdded([self GetDeviceLocation:deviceID]); -} - -- (void)DeviceRemoved:(int)deviceID -{ - parentClass->callOnDeviceRemoved([self GetDeviceLocation:deviceID]); - parentClass->SetScanResults([self GetInputDevices]); -} - -#pragma mark - init - -- (instancetype)initWithName:(PERIPHERALS::CPeripheralBusDarwinEmbedded*)initClass -{ - self = [super init]; - - parentClass = initClass; - - _input_GC = [[Input_IOSGamecontroller alloc] initWithName:self]; - - return self; -} - -- (void)SetDigitalEvent:(kodi::addon::PeripheralEvent)event -{ - CSingleLock lock(m_eventMutex); - - m_digitalEvents.emplace_back(event); -} - -- (void)SetAxisEvent:(kodi::addon::PeripheralEvent)event -{ - CSingleLock lock(m_eventMutex); - - m_axisEvents.emplace_back(event); -} - -#pragma mark - GetEvents - -- (std::vector)GetAxisEvents -{ - std::vector events; - CSingleLock lock(m_eventMutex); - - for (unsigned int i = 0; i < m_axisEvents.size(); i++) - events.emplace_back(m_axisEvents[i]); - - m_axisEvents.clear(); - - return events; -} - -- (std::vector)GetButtonEvents -{ - std::vector events; - - CSingleLock lock(m_eventMutex); - // Only report a single event per button (avoids dropping rapid presses) - std::vector repeatButtons; - - for (const auto& digitalEvent : m_digitalEvents) - { - auto HasButton = [&digitalEvent](const auto& event) { - if (event.Type() == PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON) - return event.DriverIndex() == digitalEvent.DriverIndex(); - return false; - }; - - if (std::find_if(events.begin(), events.end(), HasButton) == events.end()) - events.emplace_back(digitalEvent); - else - repeatButtons.emplace_back(digitalEvent); - } - - m_digitalEvents.swap(repeatButtons); - - return events; -} - -- (int)GetControllerAxisCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType -{ - int axisCount = 0; - if (controllerType == GCCONTROLLER_TYPE::EXTENDED) - { - // Base GCController axis = X/Y Left, X/Y Right thumb - // Potentially L/R trigger are axis buttons - not implemented - axisCount = 4; - } - else if (controllerType == GCCONTROLLER_TYPE::MICRO) - axisCount = 0; - - return axisCount; -} - -- (int)GetControllerButtonCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType -{ - int buttonCount = 0; - if (controllerType == GCCONTROLLER_TYPE::EXTENDED) - { - // Base GCController buttons = Menu, 4 dpad, 2 trigger, 2 shoulder, 4 face - // As of *OS 13, there are possibly 3 optional buttons - Options, Left/Right Thumbstick button - buttonCount = 13; - buttonCount += [self.input_GC checkOptionalButtons:deviceId]; - } - else if (controllerType == GCCONTROLLER_TYPE::MICRO) - buttonCount = 6; - - return buttonCount; -} - -#pragma mark - callbackClass Controller ID matching - -- (GCCONTROLLER_TYPE)GetControllerType:(int)deviceID -{ - - auto gcinputtype = [self.input_GC GetGCControllerType:deviceID]; - - if (gcinputtype != GCCONTROLLER_TYPE::NOTFOUND) - return gcinputtype; - - return GCCONTROLLER_TYPE::UNKNOWN; -} - -- (std::string)GetDeviceLocation:(int)deviceId -{ - return StringUtils::Format("{}{}", parentClass->getDeviceLocationPrefix(), deviceId); -} - -#pragma mark - Logging Utils - -- (void)displayMessage:(NSString*)message controllerID:(int)controllerID -{ - // Only log if message has any data - if (message) - { - CLog::Log(LOGDEBUG, "CBPeripheralBusDarwinEmbeddedManager: inputhandler - ID {} - Action {}", - controllerID, message.UTF8String); - } -} - -@end diff --git a/xbmc/platform/darwin/peripherals/CMakeLists.txt b/xbmc/platform/darwin/peripherals/CMakeLists.txt new file mode 100644 index 0000000000..a91f19fc51 --- /dev/null +++ b/xbmc/platform/darwin/peripherals/CMakeLists.txt @@ -0,0 +1,10 @@ +set(SOURCES Input_Gamecontroller.mm + PeripheralBusGCController.mm + PeripheralBusGCControllerManager.mm) + +set(HEADERS Input_Gamecontroller.h + InputKey.h + PeripheralBusGCController.h + PeripheralBusGCControllerManager.h) + +core_add_library(platform_darwin_peripherals) diff --git a/xbmc/platform/darwin/peripherals/InputKey.h b/xbmc/platform/darwin/peripherals/InputKey.h new file mode 100644 index 0000000000..9afaa661a7 --- /dev/null +++ b/xbmc/platform/darwin/peripherals/InputKey.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +enum class GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON +{ + UP = 0, + DOWN = 1, + LEFT = 2, + RIGHT = 3, + A = 4, + B = 5, + X = 6, + Y = 7, + LEFTSHOULDER = 8, + LEFTTRIGGER = 9, + RIGHTSHOULDER = 10, + RIGHTTRIGGER = 11, + MENU = 12, + OPTION = 13, + LEFTTHUMBSTICKBUTTON = 14, + RIGHTTHUMBSTICKBUTTON = 15, + UNUSED = 99 +}; + +enum class GCCONTROLLER_EXTENDED_GAMEPAD_AXIS +{ + // Thumbstick Axis + LEFTTHUMB_X = 0, + LEFTTHUMB_Y = 1, + RIGHTTHUMB_X = 2, + RIGHTTHUMB_Y = 3, + // Thumbstick + LEFT = 90, + RIGHT = 91, + UNUSED = 99 +}; + +enum class GCCONTROLLER_MICRO_GAMEPAD_BUTTON +{ + UP = 0, + DOWN = 1, + LEFT = 2, + RIGHT = 3, + MENU = 4, + A = 5, + X = 6, + UNUSED = 99 +}; + +enum class GCCONTROLLER_TYPE +{ + UNKNOWN = 0, + EXTENDED = 1, + MICRO = 2, + NOTFOUND = 98, + UNUSED = 99 +}; + +struct InputValueInfo +{ + GCCONTROLLER_TYPE controllerType; + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON extendedButton; + GCCONTROLLER_EXTENDED_GAMEPAD_AXIS extendedAxis; + GCCONTROLLER_MICRO_GAMEPAD_BUTTON microButton; + + InputValueInfo(GCCONTROLLER_TYPE controllerType) + : controllerType(controllerType), + extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), + extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), + microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) + { + } + + InputValueInfo(GCCONTROLLER_TYPE controllerType, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON extendedButton) + : controllerType(controllerType), + extendedButton(extendedButton), + extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), + microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) + { + } + + InputValueInfo(GCCONTROLLER_TYPE controllerType, GCCONTROLLER_MICRO_GAMEPAD_BUTTON microButton) + : controllerType(controllerType), + extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), + extendedAxis(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::UNUSED), + microButton(microButton) + { + } + + InputValueInfo(GCCONTROLLER_TYPE controllerType, GCCONTROLLER_EXTENDED_GAMEPAD_AXIS extendedAxis) + : controllerType(controllerType), + extendedButton(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UNUSED), + extendedAxis(extendedAxis), + microButton(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UNUSED) + { + } +}; diff --git a/xbmc/platform/darwin/peripherals/Input_Gamecontroller.h b/xbmc/platform/darwin/peripherals/Input_Gamecontroller.h new file mode 100644 index 0000000000..138c7f7837 --- /dev/null +++ b/xbmc/platform/darwin/peripherals/Input_Gamecontroller.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "peripherals/PeripheralTypes.h" +#include "threads/CriticalSection.h" + +#import + +enum class GCCONTROLLER_TYPE; +@class CBPeripheralBusGCControllerManager; + +@interface Input_GCController : NSObject + +- (instancetype)initWithName:(CBPeripheralBusGCControllerManager*)callbackManager; +- (PERIPHERALS::PeripheralScanResults)GetGCDevices; +- (GCCONTROLLER_TYPE)GetGCControllerType:(int)deviceID; +- (int)checkOptionalButtons:(int)deviceID; + +@end diff --git a/xbmc/platform/darwin/peripherals/Input_Gamecontroller.mm b/xbmc/platform/darwin/peripherals/Input_Gamecontroller.mm new file mode 100644 index 0000000000..001b4396ec --- /dev/null +++ b/xbmc/platform/darwin/peripherals/Input_Gamecontroller.mm @@ -0,0 +1,986 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#import "Input_Gamecontroller.h" + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" +#include "threads/CriticalSection.h" +#include "threads/SingleLock.h" +#include "utils/log.h" + +#import "platform/darwin/peripherals/InputKey.h" +#import "platform/darwin/peripherals/PeripheralBusGCControllerManager.h" + +#import +#import + +// TODO: replace all `respondsToSelector:` checks with @available after switching to 13 SDK + +struct PlayerControllerState +{ + BOOL dpadLeftPressed; + BOOL dpadRightPressed; + BOOL dpadUpPressed; + BOOL dpadDownPressed; + BOOL LeftThumbLeftPressed; + BOOL LeftThumbRightPressed; + BOOL LeftThumbUpPressed; + BOOL LeftThumbDownPressed; + BOOL RightThumbLeftPressed; + BOOL RightThumbRightPressed; + BOOL RightThumbUpPressed; + BOOL RightThumbDownPressed; +}; + +@implementation Input_GCController +{ + NSMutableArray* controllerArray; + // State for each controller + struct PlayerControllerState controllerState[4]; + CBPeripheralBusGCControllerManager* cbmanager; + CCriticalSection m_GCMutex; + CCriticalSection m_controllerMutex; +} + +#pragma mark - Notificaton Observer + +- (void)addModeSwitchObserver +{ + // notifications for controller (dis)connect + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(controllerWasConnected:) + name:GCControllerDidConnectNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(controllerWasDisconnected:) + name:GCControllerDidDisconnectNotification + object:nil]; +} + +#pragma mark - Controller connection + +- (void)controllerWasConnected:(NSNotification*)notification +{ + GCController* controller = (GCController*)notification.object; + + [self controllerConnection:controller]; +} + +- (GCControllerPlayerIndex)getAvailablePlayerIndex +{ + // No known controllers + if (!controllerArray) + return GCControllerPlayerIndex1; + + bool player1 = false; + bool player2 = false; + bool player3 = false; + bool player4 = false; + + for (GCController* controller in controllerArray) + { + switch (controller.playerIndex) + { + case GCControllerPlayerIndex1: + player1 = true; + break; + case GCControllerPlayerIndex2: + player2 = true; + break; + case GCControllerPlayerIndex3: + player3 = true; + break; + case GCControllerPlayerIndex4: + player4 = true; + break; + default: + break; + } + } + + if (!player1) + return GCControllerPlayerIndex1; + else if (!player2) + return GCControllerPlayerIndex2; + else if (!player3) + return GCControllerPlayerIndex3; + else if (!player4) + return GCControllerPlayerIndex4; + else + return GCControllerPlayerIndexUnset; +} + +- (void)controllerConnection:(GCController*)controller +{ + // Lock so add/remove events are serialised + CSingleLock lock(m_controllerMutex); + + if ([controllerArray containsObject:controller]) + { + CLog::Log(LOGINFO, "INPUT - GAMECONTROLLER: ignoring input device with ID {} already known", + [controller.vendorName UTF8String]); + return; + } + + controller.playerIndex = [self getAvailablePlayerIndex]; + + // set microgamepad to absolute values for dpad (ie center touchpad is 0,0) + if (controller.microGamepad) + controller.microGamepad.reportsAbsoluteDpadValues = YES; + + CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: input device with ID {} playerIndex {} added ", + [controller.vendorName UTF8String], static_cast(controller.playerIndex)); + [controllerArray addObject:controller]; + + [cbmanager DeviceAdded:static_cast(controller.playerIndex)]; + + [self registerChangeHandler:controller]; +} + +- (void)registerChangeHandler:(GCController*)controller +{ + if (controller.extendedGamepad) + { + CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: extendedGamepad changehandler added"); + // register block for input change detection + [self extendedValueChangeHandler:controller]; + } + else if (controller.microGamepad) + { + CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: microGamepad changehandler added"); + [self microValueChangeHandler:controller]; + } + if (@available(iOS 13.0, tvOS 13.0, macOS 10.15, *)) + { + // Do Nothing - Cant negate @available + } + else + { + // pausevaluechangehandler only required for <= *os12/macos10.14 + CLog::Log(LOGDEBUG, "INPUT - GAMECONTROLLER: pauseValueChangeHandler added"); + [self pauseValueChangeHandler:controller]; + } +} + +#pragma mark - Controller disconnection + +- (void)controllerWasDisconnected:(NSNotification*)notification +{ + // Lock so add/remove events are serialised + CSingleLock lock(m_controllerMutex); + // a controller was disconnected + GCController* controller = (GCController*)notification.object; + if (!controllerArray) + return; + + // Reset controller state to ensure default state for next time playerIndex + controllerState[static_cast(controller.playerIndex)] = {}; + + auto i = [controllerArray indexOfObject:controller]; + + if (i == NSNotFound) + { + CLog::Log(LOGWARNING, "INPUT - GAMECONTROLLER: failed to remove input device {} Not Found ", + [controller.vendorName UTF8String]); + return; + } + + CLog::Log(LOGINFO, "INPUT - GAMECONTROLLER: input device \"{}\" removed", + [controller.vendorName UTF8String]); + + [controllerArray removeObjectAtIndex:i]; + [cbmanager DeviceRemoved:static_cast(controller.playerIndex)]; +} + +#pragma mark - GCMicroGamepad valueChangeHandler + +- (void)microValueChangeHandler:(GCController*)controller +{ + controller.microGamepad.valueChangedHandler = + ^(GCMicroGamepad* gamepad, GCControllerElement* element) { + NSString* message; + + kodi::addon::PeripheralEvent newEvent; + newEvent.SetPeripheralIndex(static_cast(gamepad.controller.playerIndex)); + + CSingleLock lock(m_GCMutex); + + // A button + if (gamepad.buttonA == element) + { + message = [self setButtonState:gamepad.buttonA + withEvent:&newEvent + withMessage:@"A Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, + GCCONTROLLER_MICRO_GAMEPAD_BUTTON::A}]; + } + // X button + else if (gamepad.buttonX == element) + { + message = [self setButtonState:gamepad.buttonX + withEvent:&newEvent + withMessage:@"X Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, + GCCONTROLLER_MICRO_GAMEPAD_BUTTON::X}]; + } + + // buttonMenu + else if ([gamepad respondsToSelector:@selector(buttonMenu)] && + [gamepad performSelector:@selector(buttonMenu)] == element) + { + message = [self setButtonState:static_cast(element) + withEvent:&newEvent + withMessage:@"Menu Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO, + GCCONTROLLER_MICRO_GAMEPAD_BUTTON::MENU}]; + } + // d-pad + else if (gamepad.dpad == element) + { + message = [self checkdpad:gamepad.dpad + withEvent:&newEvent + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::MICRO} + withplayerIndex:static_cast(controller.playerIndex)]; + } + + [cbmanager SetDigitalEvent:newEvent]; + //! @todo Debug Purposes only - excessive log spam + // utilise spdlog for input compononent logging + // [cbmanager displayMessage:message controllerID:static_cast(controller.playerIndex)]; + }; +} + +#pragma mark - GCExtendedGamepad valueChangeHandler + +- (void)extendedValueChangeHandler:(GCController*)controller +{ + controller.extendedGamepad.valueChangedHandler = ^(GCExtendedGamepad* gamepad, + GCControllerElement* element) { + NSString* message; + + kodi::addon::PeripheralEvent newEvent; + kodi::addon::PeripheralEvent axisEvent; + auto playerIndex = static_cast(gamepad.controller.playerIndex); + newEvent.SetPeripheralIndex(playerIndex); + axisEvent.SetPeripheralIndex(playerIndex); + + CSingleLock lock(m_GCMutex); + + // left trigger + if (gamepad.leftTrigger == element) + { + message = + [self setButtonState:gamepad.leftTrigger + withEvent:&newEvent + withMessage:@"Left Trigger" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTTRIGGER}]; + } + // right trigger + else if (gamepad.rightTrigger == element) + { + message = + [self setButtonState:gamepad.rightTrigger + withEvent:&newEvent + withMessage:@"Right Trigger" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTTRIGGER}]; + } + // left shoulder button + else if (gamepad.leftShoulder == element) + { + message = + [self setButtonState:gamepad.leftShoulder + withEvent:&newEvent + withMessage:@"Left Shoulder Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTSHOULDER}]; + } + // right shoulder button + else if (gamepad.rightShoulder == element) + { + message = + [self setButtonState:gamepad.rightShoulder + withEvent:&newEvent + withMessage:@"Right Shoulder Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTSHOULDER}]; + } + // A button + else if (gamepad.buttonA == element) + { + message = [self setButtonState:gamepad.buttonA + withEvent:&newEvent + withMessage:@"A Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::A}]; + } + // B button + else if (gamepad.buttonB == element) + { + message = [self setButtonState:gamepad.buttonB + withEvent:&newEvent + withMessage:@"B Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::B}]; + } + // X button + else if (gamepad.buttonX == element) + { + message = [self setButtonState:gamepad.buttonX + withEvent:&newEvent + withMessage:@"X Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::X}]; + } + // Y button + else if (gamepad.buttonY == element) + { + message = [self setButtonState:gamepad.buttonY + withEvent:&newEvent + withMessage:@"Y Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::Y}]; + } + // buttonMenu + else if ([gamepad respondsToSelector:@selector(buttonMenu)] && + [gamepad performSelector:@selector(buttonMenu)] == element) + { + message = [self setButtonState:static_cast(element) + withEvent:&newEvent + withMessage:@"Menu Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::MENU}]; + } + // buttonOptions + else if ([gamepad respondsToSelector:@selector(buttonOptions)] && + [gamepad performSelector:@selector(buttonOptions)] == element) + { + message = [self setButtonState:static_cast(element) + withEvent:&newEvent + withMessage:@"Option Button" + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::OPTION}]; + } + // d-pad + else if (gamepad.dpad == element) + { + message = [self checkdpad:gamepad.dpad + withEvent:&newEvent + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED} + withplayerIndex:playerIndex]; + } + // left stick + else if (gamepad.leftThumbstick == element) + { + message = @"Left Stick"; + message = [self checkthumbstick:gamepad.leftThumbstick + withEvent:&axisEvent + withMessage:message + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT + withplayerIndex:playerIndex]; + } + // right stick + else if (gamepad.rightThumbstick == element) + { + message = @"Right Stick"; + message = [self checkthumbstick:gamepad.rightThumbstick + withEvent:&axisEvent + withMessage:message + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + withplayerIndex:playerIndex]; + } + if (@available(iOS 12.1, tvOS 12.1, macOS 10.14.1, *)) + { + // Left Thumbstick Button + if (gamepad.leftThumbstickButton == element) + { + message = + [self setButtonState:gamepad.leftThumbstickButton + withEvent:&newEvent + withMessage:@"Left Thumbstick Button" + withInputInfo:InputValueInfo{ + GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFTTHUMBSTICKBUTTON}]; + } + // Right Thumbstick Button + if (gamepad.rightThumbstickButton == element) + { + message = + [self setButtonState:gamepad.rightThumbstickButton + withEvent:&newEvent + withMessage:@"Right Thumbstick Button" + withInputInfo:InputValueInfo{ + GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHTTHUMBSTICKBUTTON}]; + } + } + [cbmanager SetDigitalEvent:newEvent]; + + //! @todo Debug Purposes only - excessive log spam + // utilise spdlog for input compononent logging + // [cbmanager displayMessage:message controllerID:static_cast(controller.playerIndex)]; + }; +} + +- (void)pauseValueChangeHandler:(GCController*)controller +{ + controller.controllerPausedHandler = ^(GCController* controller) { + // check if we're currently paused or not + // then bring up or remove the paused view controller + + kodi::addon::PeripheralEvent newEvent; + newEvent.SetPeripheralIndex(static_cast(controller.playerIndex)); + newEvent.SetType(PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON); + + if (controller.extendedGamepad) + newEvent.SetDriverIndex( + static_cast(GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::MENU)); + else if (controller.microGamepad) + newEvent.SetDriverIndex(static_cast(GCCONTROLLER_MICRO_GAMEPAD_BUTTON::MENU)); + + // Button Down event + newEvent.SetButtonState(JOYSTICK_STATE_BUTTON_PRESSED); + [cbmanager SetDigitalEvent:newEvent]; + + // Button Up Event + newEvent.SetButtonState(JOYSTICK_STATE_BUTTON_UNPRESSED); + [cbmanager SetDigitalEvent:newEvent]; + }; +} + +#pragma mark - valuechangehandler event state change + +- (NSString*)setButtonState:(GCControllerButtonInput*)button + withEvent:(kodi::addon::PeripheralEvent*)event + withMessage:(NSString*)message + withInputInfo:(InputValueInfo)inputInfo +{ + event->SetType(PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON); + + switch (inputInfo.controllerType) + { + case GCCONTROLLER_TYPE::EXTENDED: + event->SetDriverIndex(static_cast(inputInfo.extendedButton)); + break; + case GCCONTROLLER_TYPE::MICRO: + event->SetDriverIndex(static_cast(inputInfo.microButton)); + break; + default: + return [message + stringByAppendingFormat:@" ERROR:: CONTROLLER_TYPE %d", inputInfo.controllerType]; + } + + if (button.isPressed) + { + event->SetButtonState(JOYSTICK_STATE_BUTTON_PRESSED); + return [message stringByAppendingString:@" Pressed"]; + } + else + { + event->SetButtonState(JOYSTICK_STATE_BUTTON_UNPRESSED); + return [message stringByAppendingString:@" Released"]; + } +} + +- (void)setAxisValue:(GCControllerAxisInput*)axisValue + withEvent:(kodi::addon::PeripheralEvent*)event + withAxis:(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS)axis +{ + event->SetType(PERIPHERAL_EVENT_TYPE_DRIVER_AXIS); + event->SetDriverIndex(static_cast(axis)); + event->SetAxisState(axisValue.value); +} + +- (PERIPHERALS::PeripheralScanResults)GetGCDevices +{ + PERIPHERALS::PeripheralScanResults scanresults; + + if (controllerArray.count == 0) + return scanresults; + + for (GCController* controller in controllerArray) + { + PERIPHERALS::PeripheralScanResult peripheralScanResult; + peripheralScanResult.m_type = PERIPHERALS::PERIPHERAL_JOYSTICK; + peripheralScanResult.m_strLocation = + [cbmanager GetDeviceLocation:static_cast(controller.playerIndex)]; + peripheralScanResult.m_iVendorId = 0; + peripheralScanResult.m_iProductId = 0; + peripheralScanResult.m_mappedType = PERIPHERALS::PERIPHERAL_JOYSTICK; + + if (controller.extendedGamepad) + peripheralScanResult.m_strDeviceName = "Extended Gamepad"; + else if (controller.microGamepad) + peripheralScanResult.m_strDeviceName = "Micro Gamepad"; + else + peripheralScanResult.m_strDeviceName = "Unknown Gamepad"; + + peripheralScanResult.m_busType = PERIPHERALS::PERIPHERAL_BUS_GCCONTROLLER; + peripheralScanResult.m_mappedBusType = PERIPHERALS::PERIPHERAL_BUS_GCCONTROLLER; + peripheralScanResult.m_iSequence = 0; + scanresults.m_results.push_back(peripheralScanResult); + } + + return scanresults; +} + +- (GCCONTROLLER_TYPE)GetGCControllerType:(int)deviceID +{ + GCController* controller; + for (GCController* aController in controllerArray) + { + if (controller.playerIndex != deviceID) + continue; + controller = aController; + break; + } + if (controller.extendedGamepad) + return GCCONTROLLER_TYPE::EXTENDED; + else if (controller.microGamepad) + return GCCONTROLLER_TYPE::MICRO; + else + return GCCONTROLLER_TYPE::NOTFOUND; +} + +- (int)checkOptionalButtons:(int)deviceID +{ + int optionalButtonCount = 0; + + for (GCController* controller in controllerArray) + { + if (controller.playerIndex != deviceID) + continue; + + if (!controller.extendedGamepad) + continue; + + // Check if optional buttons exist on mapped controller + // button object is nil if button doesn't exist + if ([controller.extendedGamepad respondsToSelector:@selector(buttonOptions)] && + [controller.extendedGamepad performSelector:@selector(buttonOptions)] != nil) + ++optionalButtonCount; + + if (@available(iOS 12.1, tvOS 12.1, macOS 10.14.1, *)) + { + if (controller.extendedGamepad.leftThumbstickButton) + ++optionalButtonCount; + if (controller.extendedGamepad.rightThumbstickButton) + ++optionalButtonCount; + } + } + return optionalButtonCount; +} + +- (instancetype)initWithName:(CBPeripheralBusGCControllerManager*)callbackManager +{ + self = [super init]; + if (!self) + return nil; + + cbmanager = callbackManager; + + [self addModeSwitchObserver]; + + controllerArray = [[NSMutableArray alloc] initWithCapacity:4]; + + auto controllers = [GCController controllers]; + // Iterate through any pre-existing controller connections at startup to enable value handlers + for (GCController* controller in controllers) + [self controllerConnection:controller]; + + return self; +} + +- (NSString*)checkthumbstick:(GCControllerDirectionPad*)thumbstick + withEvent:(kodi::addon::PeripheralEvent*)event + withMessage:(NSString*)message + withAxis:(GCCONTROLLER_EXTENDED_GAMEPAD_AXIS)thumbstickside + withplayerIndex:(int)playerIndex +{ + // thumbstick released completely - zero both axis + if (!thumbstick.up.isPressed && !thumbstick.down.isPressed && !thumbstick.left.isPressed && + !thumbstick.right.isPressed) + { + if (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT) + { + controllerState[playerIndex].RightThumbLeftPressed = NO; + controllerState[playerIndex].RightThumbRightPressed = NO; + controllerState[playerIndex].RightThumbUpPressed = NO; + controllerState[playerIndex].RightThumbDownPressed = NO; + + // Thumbstick release event + kodi::addon::PeripheralEvent releaseEvent; + releaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&releaseEvent + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X]; + + [cbmanager SetAxisEvent:releaseEvent]; + + [self setAxisValue:0 + withEvent:&releaseEvent + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y]; + + message = [message stringByAppendingString:@" Released"]; + [cbmanager SetAxisEvent:releaseEvent]; + } + else + { + controllerState[playerIndex].LeftThumbLeftPressed = NO; + controllerState[playerIndex].LeftThumbRightPressed = NO; + controllerState[playerIndex].LeftThumbUpPressed = NO; + controllerState[playerIndex].LeftThumbDownPressed = NO; + + // Thumbstick release event + kodi::addon::PeripheralEvent releaseEvent; + releaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&releaseEvent + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X]; + + [cbmanager SetAxisEvent:releaseEvent]; + + [self setAxisValue:0 + withEvent:&releaseEvent + withAxis:GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y]; + + message = [message stringByAppendingString:@" Released"]; + [cbmanager SetAxisEvent:releaseEvent]; + } + } + else + { + + if (thumbstick.up.isPressed || controllerState[playerIndex].RightThumbUpPressed || + controllerState[playerIndex].LeftThumbUpPressed) + { + // Thumbstick centered + if (!thumbstick.up.isPressed) + { + if (controllerState[playerIndex].RightThumbUpPressed) + controllerState[playerIndex].RightThumbUpPressed = + !controllerState[playerIndex].RightThumbUpPressed; + else if (controllerState[playerIndex].LeftThumbUpPressed) + controllerState[playerIndex].LeftThumbUpPressed = + !controllerState[playerIndex].LeftThumbUpPressed; + + // Thumbstick release event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&newReleaseEvent + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; + + message = [message stringByAppendingFormat:@" Up %f", 0.0]; + [cbmanager SetAxisEvent:newReleaseEvent]; + } + else + { + controllerState[playerIndex].RightThumbUpPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? YES + : controllerState[playerIndex].RightThumbUpPressed); + controllerState[playerIndex].LeftThumbUpPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT + ? YES + : controllerState[playerIndex].LeftThumbUpPressed); + + [self setAxisValue:thumbstick.yAxis + withEvent:event + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; + + message = [message + stringByAppendingFormat:@" Up %f", static_cast(thumbstick.yAxis.value)]; + [cbmanager SetAxisEvent:*event]; + } + } + if (thumbstick.down.isPressed || controllerState[playerIndex].RightThumbDownPressed || + controllerState[playerIndex].LeftThumbDownPressed) + { + // Thumbstick centered + if (!thumbstick.down.isPressed) + { + if (controllerState[playerIndex].RightThumbDownPressed) + controllerState[playerIndex].RightThumbDownPressed = + !controllerState[playerIndex].RightThumbDownPressed; + else if (controllerState[playerIndex].LeftThumbDownPressed) + controllerState[playerIndex].LeftThumbDownPressed = + !controllerState[playerIndex].LeftThumbDownPressed; + + // Thumbstick release event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&newReleaseEvent + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; + + message = [message stringByAppendingFormat:@" Down %f", 0.0]; + [cbmanager SetAxisEvent:newReleaseEvent]; + } + else + { + controllerState[playerIndex].RightThumbDownPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? YES + : controllerState[playerIndex].RightThumbDownPressed); + controllerState[playerIndex].LeftThumbDownPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT + ? YES + : controllerState[playerIndex].LeftThumbDownPressed); + + [self setAxisValue:thumbstick.yAxis + withEvent:event + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_Y + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_Y)]; + + message = [message + stringByAppendingFormat:@" Down %f", static_cast(thumbstick.yAxis.value)]; + [cbmanager SetAxisEvent:*event]; + } + } + if (thumbstick.left.isPressed || controllerState[playerIndex].RightThumbLeftPressed || + controllerState[playerIndex].LeftThumbLeftPressed) + { + // Thumbstick centered + if (!thumbstick.left.isPressed) + { + if (controllerState[playerIndex].RightThumbLeftPressed) + controllerState[playerIndex].RightThumbLeftPressed = + !controllerState[playerIndex].RightThumbLeftPressed; + else if (controllerState[playerIndex].LeftThumbLeftPressed) + controllerState[playerIndex].LeftThumbLeftPressed = + !controllerState[playerIndex].LeftThumbLeftPressed; + + // Thumbstick release event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&newReleaseEvent + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; + + message = [message stringByAppendingFormat:@" Left %f", 0.0]; + [cbmanager SetAxisEvent:newReleaseEvent]; + } + else + { + controllerState[playerIndex].RightThumbLeftPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? YES + : controllerState[playerIndex].RightThumbLeftPressed); + controllerState[playerIndex].LeftThumbLeftPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT + ? YES + : controllerState[playerIndex].LeftThumbLeftPressed); + + [self setAxisValue:thumbstick.xAxis + withEvent:event + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; + + message = [message + stringByAppendingFormat:@" Left %f", static_cast(thumbstick.xAxis.value)]; + [cbmanager SetAxisEvent:*event]; + } + } + if (thumbstick.right.isPressed || controllerState[playerIndex].RightThumbRightPressed || + controllerState[playerIndex].LeftThumbRightPressed) + { + // Thumbstick centered + if (!thumbstick.right.isPressed) + { + if (controllerState[playerIndex].RightThumbRightPressed) + controllerState[playerIndex].RightThumbRightPressed = + !controllerState[playerIndex].RightThumbRightPressed; + else if (controllerState[playerIndex].LeftThumbRightPressed) + controllerState[playerIndex].LeftThumbRightPressed = + !controllerState[playerIndex].LeftThumbRightPressed; + + // Thumbstick release event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + [self setAxisValue:0 + withEvent:&newReleaseEvent + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; + + message = [message stringByAppendingFormat:@" Right %f", 0.0]; + [cbmanager SetAxisEvent:newReleaseEvent]; + } + else + { + controllerState[playerIndex].RightThumbRightPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? YES + : controllerState[playerIndex].RightThumbRightPressed); + controllerState[playerIndex].LeftThumbRightPressed = + (thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFT + ? YES + : controllerState[playerIndex].LeftThumbRightPressed); + + [self setAxisValue:thumbstick.xAxis + withEvent:event + withAxis:(thumbstickside == GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHT + ? GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::RIGHTTHUMB_X + : GCCONTROLLER_EXTENDED_GAMEPAD_AXIS::LEFTTHUMB_X)]; + + message = [message + stringByAppendingFormat:@" Right %f", static_cast(thumbstick.xAxis.value)]; + [cbmanager SetAxisEvent:*event]; + } + } + } + return message; +} + +- (NSString*)checkdpad:(GCControllerDirectionPad*)dpad + withEvent:(kodi::addon::PeripheralEvent*)event + withInputInfo:(InputValueInfo)inputInfo + withplayerIndex:(int)playerIndex +{ + NSString* message = nil; + if ((dpad.up.isPressed && !controllerState[playerIndex].dpadUpPressed) || + (!dpad.up.isPressed && controllerState[playerIndex].dpadUpPressed)) + { + message = @"D-Pad Up"; + + if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) + inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::UP; + else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) + inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::UP; + + if (!controllerState[playerIndex].dpadUpPressed) + { + // Button Down event + message = [self setButtonState:dpad.up + withEvent:event + withMessage:message + withInputInfo:inputInfo]; + } + else + { + // Button Up event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + message = [self setButtonState:dpad.up + withEvent:&newReleaseEvent + withMessage:message + withInputInfo:inputInfo]; + [cbmanager SetDigitalEvent:newReleaseEvent]; + } + controllerState[playerIndex].dpadUpPressed = !controllerState[playerIndex].dpadUpPressed; + } + if ((dpad.down.isPressed && !controllerState[playerIndex].dpadDownPressed) || + (!dpad.down.isPressed && controllerState[playerIndex].dpadDownPressed)) + { + message = @"D-Pad Down"; + + if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) + inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::DOWN; + else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) + inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::DOWN; + + if (!controllerState[playerIndex].dpadDownPressed) + { + // Button Down event + message = [self setButtonState:dpad.down + withEvent:event + withMessage:message + withInputInfo:inputInfo]; + } + else + { + // Button Up event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + message = [self setButtonState:dpad.down + withEvent:&newReleaseEvent + withMessage:message + withInputInfo:inputInfo]; + [cbmanager SetDigitalEvent:newReleaseEvent]; + } + controllerState[playerIndex].dpadDownPressed = !controllerState[playerIndex].dpadDownPressed; + } + if ((dpad.left.isPressed && !controllerState[playerIndex].dpadLeftPressed) || + (!dpad.left.isPressed && controllerState[playerIndex].dpadLeftPressed)) + { + message = @"D-Pad Left"; + + if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) + inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::LEFT; + else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) + inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::LEFT; + + if (!controllerState[playerIndex].dpadLeftPressed) + { + // Button Down event + message = [self setButtonState:dpad.left + withEvent:event + withMessage:message + withInputInfo:inputInfo]; + } + else + { + // Button Up event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + message = [self setButtonState:dpad.left + withEvent:&newReleaseEvent + withMessage:message + withInputInfo:inputInfo]; + [cbmanager SetDigitalEvent:newReleaseEvent]; + } + controllerState[playerIndex].dpadLeftPressed = !controllerState[playerIndex].dpadLeftPressed; + } + if ((dpad.right.isPressed && !controllerState[playerIndex].dpadRightPressed) || + (!dpad.right.isPressed && controllerState[playerIndex].dpadRightPressed)) + { + message = @"D-Pad Right"; + + if (inputInfo.controllerType == GCCONTROLLER_TYPE::EXTENDED) + inputInfo.extendedButton = GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHT; + else if (inputInfo.controllerType == GCCONTROLLER_TYPE::MICRO) + inputInfo.microButton = GCCONTROLLER_MICRO_GAMEPAD_BUTTON::RIGHT; + + if (!controllerState[playerIndex].dpadRightPressed) + { + // Button Down event + message = [self setButtonState:dpad.right + withEvent:event + withMessage:message + withInputInfo:inputInfo]; + } + else + { + // Button Up event + kodi::addon::PeripheralEvent newReleaseEvent; + newReleaseEvent.SetPeripheralIndex(static_cast(playerIndex)); + message = [self setButtonState:dpad.right + withEvent:&newReleaseEvent + withMessage:message + withInputInfo:InputValueInfo{GCCONTROLLER_TYPE::EXTENDED, + GCCONTROLLER_EXTENDED_GAMEPAD_BUTTON::RIGHT}]; + [cbmanager SetDigitalEvent:newReleaseEvent]; + } + controllerState[playerIndex].dpadRightPressed = !controllerState[playerIndex].dpadRightPressed; + } + return message; +} + +@end diff --git a/xbmc/platform/darwin/peripherals/PeripheralBusGCController.h b/xbmc/platform/darwin/peripherals/PeripheralBusGCController.h new file mode 100644 index 0000000000..128417df1a --- /dev/null +++ b/xbmc/platform/darwin/peripherals/PeripheralBusGCController.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#pragma once + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" +#include "peripherals/PeripheralTypes.h" +#include "peripherals/bus/PeripheralBus.h" +#include "threads/CriticalSection.h" + +#include +#include +#include +#include + +struct PeripheralBusGCControllerWrapper; + +namespace PERIPHERALS +{ +class CPeripheralBusGCController : public CPeripheralBus +{ +public: + explicit CPeripheralBusGCController(CPeripherals& manager); + ~CPeripheralBusGCController() override; + + // specialisation of CPeripheralBus + bool InitializeProperties(CPeripheral& peripheral) override; + void Initialise(void) override; + void ProcessEvents() override; + + bool PerformDeviceScan(PeripheralScanResults& results) override; + PeripheralScanResults GetInputDevices(); + + void callOnDeviceAdded(const std::string& strLocation); + void callOnDeviceRemoved(const std::string& strLocation); + + void SetScanResults(const PeripheralScanResults& resScanResults); + + const std::string& getDeviceLocationPrefix() + { + // Initialize the static variable + static std::string DeviceLocationPrefix("darwin/inputdevice/"); + return DeviceLocationPrefix; + } + + +private: + void GetEvents(std::vector& events); + std::unique_ptr m_peripheralGCController; + std::string GetDeviceLocation(int deviceId); + bool GetDeviceId(const std::string& deviceLocation, int& deviceId); + + PeripheralScanResults m_scanResults; + CCriticalSection m_critSectionStates; + CCriticalSection m_critSectionResults; +}; +} // namespace PERIPHERALS diff --git a/xbmc/platform/darwin/peripherals/PeripheralBusGCController.mm b/xbmc/platform/darwin/peripherals/PeripheralBusGCController.mm new file mode 100644 index 0000000000..7034ff89e4 --- /dev/null +++ b/xbmc/platform/darwin/peripherals/PeripheralBusGCController.mm @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "PeripheralBusGCController.h" + +#include "ServiceBroker.h" +#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" +#include "peripherals/bus/PeripheralBus.h" +#include "peripherals/devices/PeripheralJoystick.h" +#include "threads/CriticalSection.h" +#include "threads/SingleLock.h" +#include "utils/log.h" + +#include "platform/darwin/peripherals/InputKey.h" +#import "platform/darwin/peripherals/PeripheralBusGCControllerManager.h" + +#define JOYSTICK_PROVIDER_DARWIN_GCController "GCController" + +struct PeripheralBusGCControllerWrapper +{ + CBPeripheralBusGCControllerManager* callbackClass; +}; + +PERIPHERALS::CPeripheralBusGCController::CPeripheralBusGCController(CPeripherals& manager) + : CPeripheralBus("PeripBusGCController", manager, PERIPHERAL_BUS_GCCONTROLLER) +{ + m_peripheralGCController = std::make_unique(); + m_peripheralGCController->callbackClass = + [[CBPeripheralBusGCControllerManager alloc] initWithName:this]; + m_bNeedsPolling = false; + + // get all currently connected input devices + m_scanResults = GetInputDevices(); +} + +PERIPHERALS::CPeripheralBusGCController::~CPeripheralBusGCController() +{ +} + +bool PERIPHERALS::CPeripheralBusGCController::InitializeProperties(CPeripheral& peripheral) +{ + // Returns true regardless, why is it necessary? + if (!CPeripheralBus::InitializeProperties(peripheral)) + return false; + + if (peripheral.Type() != PERIPHERALS::PERIPHERAL_JOYSTICK) + { + CLog::Log(LOGWARNING, "CPeripheralBusGCController: invalid peripheral type: %s", + PERIPHERALS::PeripheralTypeTranslator::TypeToString(peripheral.Type())); + return false; + } + + // deviceId will be our playerIndex + int deviceId; + if (!GetDeviceId(peripheral.Location(), deviceId)) + { + CLog::Log(LOGWARNING, + "CPeripheralBusGCController: failed to initialize properties for peripheral \"%s\"", + peripheral.Location().c_str()); + return false; + } + + CLog::Log(LOGDEBUG, "CPeripheralBusGCController: Initializing device \"{}\"", + peripheral.DeviceName()); + + auto& joystick = static_cast(peripheral); + + joystick.SetRequestedPort(deviceId); + joystick.SetProvider(JOYSTICK_PROVIDER_DARWIN_GCController); + + auto controllerType = [m_peripheralGCController->callbackClass GetControllerType:deviceId]; + + switch (controllerType) + { + case GCCONTROLLER_TYPE::EXTENDED: + // Extended Gamepad + joystick.SetButtonCount([m_peripheralGCController->callbackClass + GetControllerButtonCount:deviceId + withControllerType:controllerType]); + joystick.SetAxisCount([m_peripheralGCController->callbackClass + GetControllerAxisCount:deviceId + withControllerType:controllerType]); + break; + case GCCONTROLLER_TYPE::MICRO: + // Micro Gamepad + joystick.SetButtonCount([m_peripheralGCController->callbackClass + GetControllerButtonCount:deviceId + withControllerType:controllerType]); + joystick.SetAxisCount([m_peripheralGCController->callbackClass + GetControllerAxisCount:deviceId + withControllerType:controllerType]); + break; + default: + CLog::Log(LOGDEBUG, "CPeripheralBusGCController: Unknown Controller Type"); + return false; + } + + CLog::Log(LOGDEBUG, "CPeripheralBusGCController: Device has %u buttons and %u axes", + joystick.ButtonCount(), joystick.AxisCount()); + + return true; +} + +void PERIPHERALS::CPeripheralBusGCController::Initialise(void) +{ + CPeripheralBus::Initialise(); + TriggerDeviceScan(); +} + +bool PERIPHERALS::CPeripheralBusGCController::PerformDeviceScan(PeripheralScanResults& results) +{ + CSingleLock lock(m_critSectionResults); + results = m_scanResults; + + return true; +} + +void PERIPHERALS::CPeripheralBusGCController::SetScanResults( + const PERIPHERALS::PeripheralScanResults& resScanResults) +{ + CSingleLock lock(m_critSectionResults); + m_scanResults = resScanResults; +} + +void PERIPHERALS::CPeripheralBusGCController::GetEvents( + std::vector& events) +{ + CSingleLock lock(m_critSectionStates); + std::vector digitalEvents; + digitalEvents = [m_peripheralGCController->callbackClass GetButtonEvents]; + + std::vector axisEvents; + axisEvents = [m_peripheralGCController->callbackClass GetAxisEvents]; + + events.reserve(digitalEvents.size() + axisEvents.size()); // preallocate memory + events.insert(events.end(), digitalEvents.begin(), digitalEvents.end()); + events.insert(events.end(), axisEvents.begin(), axisEvents.end()); +} + +bool PERIPHERALS::CPeripheralBusGCController::GetDeviceId(const std::string& deviceLocation, + int& deviceId) +{ + if (deviceLocation.empty() || + !StringUtils::StartsWith(deviceLocation, getDeviceLocationPrefix()) || + deviceLocation.size() <= getDeviceLocationPrefix().size()) + return false; + + std::string strDeviceId = deviceLocation.substr(getDeviceLocationPrefix().size()); + if (!StringUtils::IsNaturalNumber(strDeviceId)) + return false; + + deviceId = static_cast(strtol(strDeviceId.c_str(), nullptr, 10)); + return true; +} + +void PERIPHERALS::CPeripheralBusGCController::ProcessEvents() +{ + std::vector events; + { + CSingleLock lock(m_critSectionStates); + + //! @todo Multiple controller event processing + GetEvents(events); + } + + for (const auto& event : events) + { + PeripheralPtr device = GetPeripheral(GetDeviceLocation(event.PeripheralIndex())); + if (!device || device->Type() != PERIPHERAL_JOYSTICK) + continue; + + auto joystick = static_cast(device.get()); + switch (event.Type()) + { + case PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON: + { + const bool bPressed = (event.ButtonState() == JOYSTICK_STATE_BUTTON_PRESSED); + joystick->OnButtonMotion(event.DriverIndex(), bPressed); + break; + } + case PERIPHERAL_EVENT_TYPE_DRIVER_AXIS: + { + joystick->OnAxisMotion(event.DriverIndex(), event.AxisState()); + break; + } + default: + break; + } + } + { + CSingleLock lock(m_critSectionStates); + //! @todo Multiple controller handling + PeripheralPtr device = GetPeripheral(GetDeviceLocation(0)); + + if (device && device->Type() == PERIPHERAL_JOYSTICK) + static_cast(device.get())->ProcessAxisMotions(); + } +} + +std::string PERIPHERALS::CPeripheralBusGCController::GetDeviceLocation(int deviceId) +{ + return [m_peripheralGCController->callbackClass GetDeviceLocation:deviceId]; +} + +PERIPHERALS::PeripheralScanResults PERIPHERALS::CPeripheralBusGCController::GetInputDevices() +{ + CLog::Log(LOGINFO, "CPeripheralBusGCController: scanning for input devices..."); + + return [m_peripheralGCController->callbackClass GetInputDevices]; +} + +void PERIPHERALS::CPeripheralBusGCController::callOnDeviceAdded(const std::string& strLocation) +{ + OnDeviceAdded(strLocation); +} + +void PERIPHERALS::CPeripheralBusGCController::callOnDeviceRemoved(const std::string& strLocation) +{ + OnDeviceRemoved(strLocation); +} diff --git a/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.h b/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.h new file mode 100644 index 0000000000..2232ad1b81 --- /dev/null +++ b/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "addons/kodi-dev-kit/include/kodi/addon-instance/peripheral/PeripheralUtils.h" +#include "peripherals/PeripheralTypes.h" +#include "threads/CriticalSection.h" + +#import "platform/darwin/peripherals/Input_Gamecontroller.h" +#include "platform/darwin/peripherals/PeripheralBusGCController.h" + +#include +#include + +#import + +@interface CBPeripheralBusGCControllerManager : NSObject + +@property(nonatomic, strong) Input_GCController* input_GC; + +- (instancetype)initWithName:(PERIPHERALS::CPeripheralBusGCController*)parentClass; +- (PERIPHERALS::PeripheralScanResults)GetInputDevices; +- (void)DeviceAdded:(int)deviceID; +- (void)DeviceRemoved:(int)deviceID; +- (void)SetDigitalEvent:(kodi::addon::PeripheralEvent)event; +- (void)SetAxisEvent:(kodi::addon::PeripheralEvent)event; +- (std::vector)GetButtonEvents; +- (std::vector)GetAxisEvents; +- (int)GetControllerAxisCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType; +- (int)GetControllerButtonCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType; +- (GCCONTROLLER_TYPE)GetControllerType:(int)deviceID; +- (std::string)GetDeviceLocation:(int)deviceId; +- (void)displayMessage:(NSString*)message controllerID:(int)controllerID; +@end diff --git a/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.mm b/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.mm new file mode 100644 index 0000000000..8281e8daba --- /dev/null +++ b/xbmc/platform/darwin/peripherals/PeripheralBusGCControllerManager.mm @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2020 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#import "PeripheralBusGCControllerManager.h" + +#include "peripherals/PeripheralTypes.h" +#include "threads/SingleLock.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include "platform/darwin/peripherals/InputKey.h" +#import "platform/darwin/peripherals/Input_Gamecontroller.h" +#include "platform/darwin/peripherals/PeripheralBusGCController.h" + +#pragma mark - objc implementation + +@implementation CBPeripheralBusGCControllerManager +{ + PERIPHERALS::CPeripheralBusGCController* parentClass; + std::vector m_digitalEvents; + std::vector m_axisEvents; + CCriticalSection m_eventMutex; +} + +#pragma mark - callbackClass inputdevices + +- (PERIPHERALS::PeripheralScanResults)GetInputDevices +{ + PERIPHERALS::PeripheralScanResults scanresults = {}; + + scanresults = [self.input_GC GetGCDevices]; + + return scanresults; +} + +- (void)DeviceAdded:(int)deviceID +{ + parentClass->SetScanResults([self GetInputDevices]); + parentClass->callOnDeviceAdded([self GetDeviceLocation:deviceID]); +} + +- (void)DeviceRemoved:(int)deviceID +{ + parentClass->callOnDeviceRemoved([self GetDeviceLocation:deviceID]); + parentClass->SetScanResults([self GetInputDevices]); +} + +#pragma mark - init + +- (instancetype)initWithName:(PERIPHERALS::CPeripheralBusGCController*)initClass +{ + self = [super init]; + + parentClass = initClass; + + _input_GC = [[Input_GCController alloc] initWithName:self]; + + return self; +} + +- (void)SetDigitalEvent:(kodi::addon::PeripheralEvent)event +{ + CSingleLock lock(m_eventMutex); + + m_digitalEvents.emplace_back(event); +} + +- (void)SetAxisEvent:(kodi::addon::PeripheralEvent)event +{ + CSingleLock lock(m_eventMutex); + + m_axisEvents.emplace_back(event); +} + +#pragma mark - GetEvents + +- (std::vector)GetAxisEvents +{ + std::vector events; + CSingleLock lock(m_eventMutex); + + for (unsigned int i = 0; i < m_axisEvents.size(); i++) + events.emplace_back(m_axisEvents[i]); + + m_axisEvents.clear(); + + return events; +} + +- (std::vector)GetButtonEvents +{ + std::vector events; + + CSingleLock lock(m_eventMutex); + // Only report a single event per button (avoids dropping rapid presses) + std::vector repeatButtons; + + for (const auto& digitalEvent : m_digitalEvents) + { + auto HasButton = [&digitalEvent](const auto& event) { + if (event.Type() == PERIPHERAL_EVENT_TYPE_DRIVER_BUTTON) + return event.DriverIndex() == digitalEvent.DriverIndex(); + return false; + }; + + if (std::find_if(events.begin(), events.end(), HasButton) == events.end()) + events.emplace_back(digitalEvent); + else + repeatButtons.emplace_back(digitalEvent); + } + + m_digitalEvents.swap(repeatButtons); + + return events; +} + +- (int)GetControllerAxisCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType +{ + int axisCount = 0; + if (controllerType == GCCONTROLLER_TYPE::EXTENDED) + { + // Base GCController axis = X/Y Left, X/Y Right thumb + // Potentially L/R trigger are axis buttons - not implemented + axisCount = 4; + } + else if (controllerType == GCCONTROLLER_TYPE::MICRO) + axisCount = 0; + + return axisCount; +} + +- (int)GetControllerButtonCount:(int)deviceId withControllerType:(GCCONTROLLER_TYPE)controllerType +{ + int buttonCount = 0; + if (controllerType == GCCONTROLLER_TYPE::EXTENDED) + { + // Base GCController buttons = Menu, 4 dpad, 2 trigger, 2 shoulder, 4 face + // As of *OS 13, there are possibly 3 optional buttons - Options, Left/Right Thumbstick button + buttonCount = 13; + buttonCount += [self.input_GC checkOptionalButtons:deviceId]; + } + else if (controllerType == GCCONTROLLER_TYPE::MICRO) + buttonCount = 6; + + return buttonCount; +} + +#pragma mark - callbackClass Controller ID matching + +- (GCCONTROLLER_TYPE)GetControllerType:(int)deviceID +{ + + auto gcinputtype = [self.input_GC GetGCControllerType:deviceID]; + + if (gcinputtype != GCCONTROLLER_TYPE::NOTFOUND) + return gcinputtype; + + return GCCONTROLLER_TYPE::UNKNOWN; +} + +- (std::string)GetDeviceLocation:(int)deviceId +{ + return StringUtils::Format("{}{}", parentClass->getDeviceLocationPrefix(), deviceId); +} + +#pragma mark - Logging Utils + +- (void)displayMessage:(NSString*)message controllerID:(int)controllerID +{ + // Only log if message has any data + if (message) + { + CLog::Log(LOGDEBUG, "CBPeripheralBusGCControllerManager: inputhandler - ID {} - Action {}", + controllerID, message.UTF8String); + } +} + +@end -- cgit v1.2.3