From aee6bce679d4a443f53cf3cf078b3c6a28b635c9 Mon Sep 17 00:00:00 2001 From: meancoot Date: Fri, 27 Dec 2013 20:12:19 -0500 Subject: [PATCH] (iOS) Add initial MFi game controller support. --- apple/RetroArch_iOS.xcodeproj/project.pbxproj | 10 ++ apple/common/apple_gamecontroller.h | 22 +++ apple/common/apple_gamecontroller.m | 125 ++++++++++++++++++ apple/common/apple_input.c | 7 + apple/common/apple_input.h | 1 + apple/common/apple_joypad.c | 18 ++- apple/iOS/platform.m | 5 +- 7 files changed, 186 insertions(+), 2 deletions(-) create mode 100644 apple/common/apple_gamecontroller.h create mode 100644 apple/common/apple_gamecontroller.m diff --git a/apple/RetroArch_iOS.xcodeproj/project.pbxproj b/apple/RetroArch_iOS.xcodeproj/project.pbxproj index ffbf978263..55c3cac0d4 100644 --- a/apple/RetroArch_iOS.xcodeproj/project.pbxproj +++ b/apple/RetroArch_iOS.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ 96337E82176AC6E5004685F3 /* utility.m in Sources */ = {isa = PBXBuildFile; fileRef = 96337E81176AC6E5004685F3 /* utility.m */; }; 96366C5516C9AC3300D64A22 /* CoreAudio.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96366C5416C9AC3300D64A22 /* CoreAudio.framework */; }; 96366C5916C9ACF500D64A22 /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96366C5816C9ACF500D64A22 /* AudioToolbox.framework */; }; + 963C3C32186E3D2600A6EB1E /* apple_gamecontroller.m in Sources */ = {isa = PBXBuildFile; fileRef = 963C3C31186E3D2600A6EB1E /* apple_gamecontroller.m */; }; + 963C3C34186E3DED00A6EB1E /* GameController.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 963C3C33186E3DED00A6EB1E /* GameController.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 963F5AC816CC523B009BBD19 /* RAGameView.m in Sources */ = {isa = PBXBuildFile; fileRef = 963F5AC516CC523B009BBD19 /* RAGameView.m */; }; 9646869517BBBEAE00C5EA69 /* platform.m in Sources */ = {isa = PBXBuildFile; fileRef = 9646869417BBBEAE00C5EA69 /* platform.m */; }; 966B9CBD16E41E7A005B61E1 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 966B9CB816E41E7A005B61E1 /* Default-568h@2x.png */; }; @@ -54,6 +56,9 @@ 96355CD11788CF190010DBFA /* RetroArch_Apple.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RetroArch_Apple.h; sourceTree = ""; }; 96366C5416C9AC3300D64A22 /* CoreAudio.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreAudio.framework; path = System/Library/Frameworks/CoreAudio.framework; sourceTree = SDKROOT; }; 96366C5816C9ACF500D64A22 /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = System/Library/Frameworks/AudioToolbox.framework; sourceTree = SDKROOT; }; + 963C3C30186E3D2600A6EB1E /* apple_gamecontroller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apple_gamecontroller.h; sourceTree = ""; }; + 963C3C31186E3D2600A6EB1E /* apple_gamecontroller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = apple_gamecontroller.m; sourceTree = ""; }; + 963C3C33186E3DED00A6EB1E /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = System/Library/Frameworks/GameController.framework; sourceTree = SDKROOT; }; 963F5AC516CC523B009BBD19 /* RAGameView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RAGameView.m; sourceTree = ""; }; 9646869417BBBEAE00C5EA69 /* platform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = platform.m; path = iOS/platform.m; sourceTree = SOURCE_ROOT; }; 966B9CB816E41E7A005B61E1 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = ""; }; @@ -86,6 +91,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 963C3C34186E3DED00A6EB1E /* GameController.framework in Frameworks */, 50CCC828185E0E7D001F5BC8 /* CoreLocation.framework in Frameworks */, 501881EE184BB54C006F665D /* CoreMedia.framework in Frameworks */, 501881EC184BAD6D006F665D /* AVFoundation.framework in Frameworks */, @@ -144,6 +150,7 @@ 96AFAE2816C1D4EA009DE44C /* Frameworks */ = { isa = PBXGroup; children = ( + 963C3C33186E3DED00A6EB1E /* GameController.framework */, 50CCC827185E0E7D001F5BC8 /* CoreLocation.framework */, 501881ED184BB54C006F665D /* CoreMedia.framework */, 501881EB184BAD6D006F665D /* AVFoundation.framework */, @@ -162,6 +169,8 @@ 96AFAE3316C1D4EA009DE44C /* common */ = { isa = PBXGroup; children = ( + 963C3C30186E3D2600A6EB1E /* apple_gamecontroller.h */, + 963C3C31186E3D2600A6EB1E /* apple_gamecontroller.m */, 509FC978183F9F18007A5A30 /* menu.m */, 9646869417BBBEAE00C5EA69 /* platform.m */, 967894571788EAAE00D6CA69 /* browser.m */, @@ -304,6 +313,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 963C3C32186E3D2600A6EB1E /* apple_gamecontroller.m in Sources */, 96297A0F16C5AEA100E6DCE0 /* main.m in Sources */, 963F5AC816CC523B009BBD19 /* RAGameView.m in Sources */, D48581DE16F823F9004BEB17 /* griffin.c in Sources */, diff --git a/apple/common/apple_gamecontroller.h b/apple/common/apple_gamecontroller.h new file mode 100644 index 0000000000..d16626f413 --- /dev/null +++ b/apple/common/apple_gamecontroller.h @@ -0,0 +1,22 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#ifndef __APPLE_RARCH_GAMECONTROLLER_H__ +#define __APPLE_RARCH_GAMECONTROLLER_H__ + +void apple_gamecontroller_init(); +void apple_gamecontroller_poll_all(); + +#endif diff --git a/apple/common/apple_gamecontroller.m b/apple/common/apple_gamecontroller.m new file mode 100644 index 0000000000..11904f0124 --- /dev/null +++ b/apple/common/apple_gamecontroller.m @@ -0,0 +1,125 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2013 - Jason Fetters + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +#include + +#if __IPHONE_7_0 + +#define IS_PRESSED(x) (x.value > .01f) + +#import +#include "apple_input.h" + +static void apple_gamecontroller_poll(GCController* controller) +{ + if (!controller || controller.playerIndex == MAX_PLAYERS) + return; + + uint32_t slot = controller.playerIndex; + g_current_input_data.pad_buttons[slot] = 0; + memset(g_current_input_data.pad_axis[slot], 0, sizeof(g_current_input_data.pad_axis[0])); + + if (controller.extendedGamepad) + { + GCExtendedGamepad* gp = controller.extendedGamepad; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.up) ? 1 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.down) ? 2 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.left) ? 4 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.right) ? 8 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonA) ? 16 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonB) ? 32 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonX) ? 64 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonY) ? 128 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.leftShoulder) ? 256 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.rightShoulder) ? 512 : 0; + + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.leftTrigger) ? 1024 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.rightTrigger) ? 2048 : 0; + g_current_input_data.pad_axis[slot][0] = gp.leftThumbstick.xAxis.value * 32767.0f; + g_current_input_data.pad_axis[slot][1] = gp.leftThumbstick.yAxis.value * 32767.0f; + g_current_input_data.pad_axis[slot][2] = gp.rightThumbstick.xAxis.value * 32767.0f; + g_current_input_data.pad_axis[slot][3] = gp.rightThumbstick.yAxis.value * 32767.0f; + } + else if (controller.gamepad) + { + GCGamepad* gp = controller.gamepad; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.up) ? 1 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.down) ? 2 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.left) ? 4 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.dpad.right) ? 8 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonA) ? 16 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonB) ? 32 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonX) ? 64 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.buttonY) ? 128 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.leftShoulder) ? 256 : 0; + g_current_input_data.pad_buttons[slot] |= IS_PRESSED(gp.rightShoulder) ? 512 : 0; + } +} + +void apple_gamecontroller_poll_all() +{ + NSArray* controllers = [GCController controllers]; + + for (int i = 0; i != [controllers count]; i ++) + apple_gamecontroller_poll([controllers objectAtIndex:i]); +} + +void apple_gamecontroller_connect(GCController* controller) +{ + int32_t slot = apple_joypad_connect_gcapi(); + controller.playerIndex = (slot >= 0 && slot < MAX_PLAYERS) ? slot : GCControllerPlayerIndexUnset; + +/* + if (controller.playerIndex == GCControllerPlayerIndexUnset) + return; + else if (controller.extendedGamepad) + controller.extendedGamepad.valueChangedHandler = + ^(GCExtendedGamepad *gamepad, GCControllerElement *element) { apple_gamecontroller_poll(gamepad.controller); }; + else if (controller.gamepad) + controller.gamepad.valueChangedHandler = + ^(GCGamepad *gamepad, GCControllerElement *element) { apple_gamecontroller_poll(gamepad.controller); }; +*/ +} + +void apple_gamecontroller_disconnect(GCController* controller) +{ + if (controller.playerIndex == GCControllerPlayerIndexUnset) + return; + + apple_joypad_disconnect(controller.playerIndex); +} + +void apple_gamecontroller_init() +{ + [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidConnectNotification object:nil queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { apple_gamecontroller_connect([note object]); } ]; + + [[NSNotificationCenter defaultCenter] addObserverForName:GCControllerDidDisconnectNotification object:nil queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note) { apple_gamecontroller_disconnect([note object]); } ]; +} + +#else + +void apple_gamecontroller_init() +{ + +} + +void apple_gamecontroller_poll_all() +{ + +} + +#endif diff --git a/apple/common/apple_input.c b/apple/common/apple_input.c index 9e8eda791b..65f11a2ceb 100644 --- a/apple/common/apple_input.c +++ b/apple/common/apple_input.c @@ -16,6 +16,7 @@ #include +#include "apple_gamecontroller.h" #include "input/input_common.h" #include "apple_input.h" #include "general.h" @@ -108,6 +109,7 @@ void apple_input_handle_key_event(unsigned keycode, bool down) int32_t apple_input_find_any_key(void) { + apple_gamecontroller_poll_all(); input_init_keyboard_lut(apple_key_map_hidusage); for (int i = 0; apple_key_name_map[i].hid_id; i++) @@ -119,6 +121,8 @@ int32_t apple_input_find_any_key(void) int32_t apple_input_find_any_button(uint32_t port) { + apple_gamecontroller_poll_all(); + uint32_t buttons = g_current_input_data.pad_buttons[port] | ((port == 0) ? apple_input_get_icade_buttons() : 0); @@ -132,6 +136,8 @@ int32_t apple_input_find_any_button(uint32_t port) int32_t apple_input_find_any_axis(uint32_t port) { + apple_gamecontroller_poll_all(); + for (int i = 0; i < 4; i++) { int16_t value = g_current_input_data.pad_axis[port][i]; @@ -167,6 +173,7 @@ static void *apple_input_init(void) static void apple_input_poll(void *data) { + apple_gamecontroller_poll_all(); memcpy(&g_polled_input_data, &g_current_input_data, sizeof(apple_input_data_t)); for (int i = 0; i != g_polled_input_data.touch_count; i ++) diff --git a/apple/common/apple_input.h b/apple/common/apple_input.h index 9bfb39afec..8eace7a050 100644 --- a/apple/common/apple_input.h +++ b/apple/common/apple_input.h @@ -55,6 +55,7 @@ struct apple_pad_interface // Joypad data int32_t apple_joypad_connect(const char* name, struct apple_pad_connection* connection); +int32_t apple_joypad_connect_gcapi(); void apple_joypad_disconnect(uint32_t slot); void apple_joypad_packet(uint32_t slot, uint8_t* data, uint32_t length); diff --git a/apple/common/apple_joypad.c b/apple/common/apple_joypad.c index 1bc71bb17e..dbef29e592 100644 --- a/apple/common/apple_joypad.c +++ b/apple/common/apple_joypad.c @@ -33,6 +33,8 @@ typedef struct bool used; struct apple_pad_interface* iface; void* data; + + bool is_gcapi; } joypad_slot_t; static joypad_slot_t slots[MAX_PLAYERS]; @@ -73,6 +75,20 @@ int32_t apple_joypad_connect(const char* name, struct apple_pad_connection* conn return slot; } +int32_t apple_joypad_connect_gcapi() +{ + int32_t slot = find_empty_slot(); + + if (slot >= 0 && slot < MAX_PLAYERS) + { + joypad_slot_t* s = &slots[slot]; + s->used = true; + s->is_gcapi = true; + } + + return slot; +} + void apple_joypad_disconnect(uint32_t slot) { if (slot < MAX_PLAYERS && slots[slot].used) @@ -82,7 +98,7 @@ void apple_joypad_disconnect(uint32_t slot) if (s->iface && s->data) s->iface->disconnect(s->data); - s->used = false; + memset(s, 0, sizeof(joypad_slot_t)); } } diff --git a/apple/iOS/platform.m b/apple/iOS/platform.m index 7dff1cb361..c988f340d7 100644 --- a/apple/iOS/platform.m +++ b/apple/iOS/platform.m @@ -20,6 +20,7 @@ #include "apple/common/apple_input.h" #include "apple/common/setting_data.h" +#include "apple/common/apple_gamecontroller.h" #include "menu.h" #import "views.h" @@ -221,7 +222,9 @@ static void handle_touch_event(NSArray* touches) if (!core_list || core_list->count == 0) apple_display_alert(@"No libretro cores were found. You will not be able to run any content.", 0); - + + apple_gamecontroller_init(); + // Load system config const rarch_setting_t* frontend_settings = apple_get_frontend_settings(); setting_data_reset(frontend_settings);