diff --git a/frontend/frontend_ios.c b/frontend/frontend_ios.c index d351c1d428..2492711955 100644 --- a/frontend/frontend_ios.c +++ b/frontend/frontend_ios.c @@ -92,10 +92,9 @@ void* rarch_main_ios(void* args) #ifdef HAVE_RGUI char* system_directory = ios_get_rarch_system_directory(); - strlcpy(g_extern.savestate_dir, system_directory, - sizeof(g_extern.savestate_dir)); - strlcpy(g_extern.savefile_dir, system_directory, - sizeof(g_extern.savefile_dir)); + strlcpy(g_extern.savestate_dir, system_directory, sizeof(g_extern.savestate_dir)); + strlcpy(g_extern.savefile_dir, system_directory, sizeof(g_extern.savefile_dir)); + free(system_directory); menu_init(); g_extern.lifecycle_mode_state |= 1ULL << MODE_GAME; @@ -168,8 +167,6 @@ void* rarch_main_ios(void* args) if (g_extern.main_is_init) rarch_main_deinit(); - - free(system_directory); #else while ((g_extern.is_paused && !g_extern.is_oneshot) ? rarch_main_idle_iterate() : rarch_main_iterate()); rarch_main_deinit(); diff --git a/griffin/griffin.c b/griffin/griffin.c index d420276399..e647c924d1 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -260,6 +260,7 @@ INPUT #include "../ios/RetroArch/input/BTStack/btpad.c" #include "../ios/RetroArch/input/BTStack/btpad_ps3.c" #include "../ios/RetroArch/input/BTStack/btpad_wii.c" +#include "../ios/RetroArch/input/BTStack/btpad_queue.c" #elif defined(__BLACKBERRY_QNX__) #include "../blackberry-qnx/qnx_input.c" #endif diff --git a/ios/Assets/PauseView.xib b/ios/Assets/PauseView.xib index 6a3a7d88ba..4136ceee9c 100644 --- a/ios/Assets/PauseView.xib +++ b/ios/Assets/PauseView.xib @@ -41,8 +41,11 @@ 292 {{160, 237}, {120, 44}} + + _NS:9 NO + 4 IBIPadFramework 0 0 @@ -75,6 +78,7 @@ 292 {{20, 20}, {120, 44}} + _NS:9 NO @@ -97,6 +101,7 @@ 292 {{160, 20}, {120, 44}} + _NS:9 NO @@ -119,9 +124,11 @@ 292 {{20, 237}, {120, 44}} + _NS:9 NO + 1 IBIPadFramework 0 0 @@ -141,9 +148,11 @@ 292 {{20, 186}, {120, 44}} + _NS:9 NO + 3 IBIPadFramework 0 0 @@ -163,9 +172,11 @@ 292 {{160, 186}, {120, 44}} + _NS:9 NO + 2 IBIPadFramework 0 0 @@ -185,14 +196,16 @@ 292 {{20, 135}, {260, 44}} + _NS:9 NO - 1 + 10 IBIPadFramework 10 0 + 0 1 2 3 @@ -202,7 +215,6 @@ 7 8 9 - 10 @@ -258,6 +270,7 @@ 292 {{20, 71}, {120, 44}} + _NS:9 NO @@ -280,6 +293,7 @@ 292 {{160, 71}, {120, 44}} + _NS:9 NO @@ -300,6 +314,7 @@ {{0, 20}, {300, 300}} + 3 @@ -324,12 +339,12 @@ - closeGamePressed: + basicEvent: 7 - 23 + 215 @@ -342,30 +357,30 @@ - resetGame: + basicEvent: 7 - 138 + 214 - saveState: + basicEvent: 7 - 137 + 212 - loadState: + basicEvent: 7 - 136 + 213 @@ -418,13 +433,13 @@ - + @@ -515,9 +530,20 @@ - 210 + 215 + + + + + RetroArch_iOS + UINavigationController + + IBProjectSource + ./Classes/RetroArch_iOS.h + + + - 0 IBIPadFramework YES diff --git a/ios/RetroArch/RAGameView.m b/ios/RetroArch/RAGameView.m index 13329491ed..c8d451338a 100644 --- a/ios/RetroArch/RAGameView.m +++ b/ios/RetroArch/RAGameView.m @@ -81,21 +81,21 @@ static UIView* g_pause_indicator_view; - (void)openPauseMenu { // Setup save state selector - UISegmentedControl* stateSelect = (UISegmentedControl*)[g_pause_view viewWithTag:1]; + UISegmentedControl* stateSelect = (UISegmentedControl*)[g_pause_view viewWithTag:10]; stateSelect.selectedSegmentIndex = (g_extern.state_slot < 10) ? g_extern.state_slot : -1; g_extern.is_paused = true; // [UIView animateWithDuration:0.2 - animations:^ { g_pause_view.alpha = 1.0f; } - completion:^(BOOL finished){}]; + animations:^{ g_pause_view.alpha = 1.0f; } + completion:^(BOOL finished) { }]; } - (void)closePauseMenu { [UIView animateWithDuration:0.2 - animations:^ { g_pause_view.alpha = 0.0f; } + animations:^{ g_pause_view.alpha = 0.0f; } completion:^(BOOL finished) { } ]; @@ -105,9 +105,9 @@ static UIView* g_pause_indicator_view; - (void)hidePauseButton { [UIView animateWithDuration:0.2 - animations:^ { g_pause_indicator_view.alpha = ALMOST_INVISIBLE; } - completion:^(BOOL finished) { } - ]; + animations:^{ g_pause_indicator_view.alpha = ALMOST_INVISIBLE; } + completion:^(BOOL finished) { } + ]; } - (void)suspend @@ -170,9 +170,6 @@ void ios_flip_game_view() { dispatch_sync(dispatch_get_main_queue(), ^{ [g_view display]; - - // HACK: While here, copy input structures - ios_copy_input(&g_ios_input_data); }); g_fast_forward_skips = g_is_syncing ? 0 : 3; } diff --git a/ios/RetroArch/RALogView.m b/ios/RetroArch/RALogView.m index 61e4e41fbc..d384515334 100644 --- a/ios/RetroArch/RALogView.m +++ b/ios/RetroArch/RALogView.m @@ -22,7 +22,6 @@ static NSMutableArray* g_messages; static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER; -#ifndef HAVE_DEBUG_DIAGLOG void ios_add_log_message(const char* format, ...) { pthread_mutex_lock(&g_lock); @@ -39,54 +38,6 @@ void ios_add_log_message(const char* format, ...) pthread_mutex_unlock(&g_lock); } -#else -static FILE* old_stdout; -static FILE* old_stderr; -static FILE* log_stream; - -static int stdout_write(void* mem_buffer, const char* data, int len) -{ - pthread_mutex_lock(&g_lock); - - g_messages = g_messages ? g_messages : [NSMutableArray array]; - - char buffer[len + 10]; - strncpy(buffer, data, len); - buffer[len] = 0; - - [g_messages addObject:[NSString stringWithFormat:@"%s", buffer]]; - - pthread_mutex_unlock(&g_lock); - - return len; -} - -void ios_log_init() -{ - if (!log_stream) - { - old_stdout = stdout; - old_stderr = stderr; - - log_stream = fwopen(0, stdout_write); - setvbuf(log_stream, 0, _IOLBF, 0); - - stdout = log_stream; - stderr = log_stream; - } -} - -void ios_log_quit() -{ - if (log_stream) - { - stdout = old_stdout; - stderr = old_stderr; - fclose(log_stream); - log_stream = 0; - } -} -#endif @implementation RALogView diff --git a/ios/RetroArch/RAModuleInfo.m b/ios/RetroArch/RAModuleInfo.m index d0bb6ee2e7..e9aa9d5478 100644 --- a/ios/RetroArch/RAModuleInfo.m +++ b/ios/RetroArch/RAModuleInfo.m @@ -165,7 +165,7 @@ static NSString* build_string_pair(NSString* stringA, NSString* stringB) if (indexPath.section == _firmwareSectionIndex) { NSString* item = (NSString*)[self itemForIndexPath:indexPath]; - [RetroArch_iOS displayErrorMessage:objc_getAssociatedObject(item, "OTHER") withTitle:item]; + ios_display_alert(objc_getAssociatedObject(item, "OTHER"), item); } } diff --git a/ios/RetroArch/RetroArch_iOS.h b/ios/RetroArch/RetroArch_iOS.h index 6295c5fe64..1fccfe18e6 100644 --- a/ios/RetroArch/RetroArch_iOS.h +++ b/ios/RetroArch/RetroArch_iOS.h @@ -17,18 +17,12 @@ @interface RetroArch_iOS : UINavigationController -+ (void)displayErrorMessage:(NSString*)message; -+ (void)displayErrorMessage:(NSString*)message withTitle:(NSString*)title; - + (RetroArch_iOS*)get; - (void)runGame:(NSString*)path withModule:(RAModuleInfo*)module; - (void)refreshConfig; - (void)refreshSystemConfig; -- (IBAction)startBluetooth; -- (IBAction)stopBluetooth; - @property (strong, nonatomic) NSString* documentsDirectory; // e.g. /var/mobile/Documents @property (strong, nonatomic) NSString* systemDirectory; // e.g. /var/mobile/Documents/.RetroArch @property (strong, nonatomic) NSString* systemConfigPath; // e.g. /var/mobile/Documents/.RetroArch/frontend.cfg @@ -36,4 +30,7 @@ @end // utility.m +extern void ios_display_alert(NSString* message, NSString* title); +extern void ios_clear_config_hack(); +extern bool path_make_and_check_directory(const char* path, mode_t mode, int amode); extern NSString* ios_get_value_from_config(config_file_t* config, NSString* name, NSString* defaultValue); diff --git a/ios/RetroArch/browser.m b/ios/RetroArch/browser.m index fb5b6f8649..eaa772d2b4 100644 --- a/ios/RetroArch/browser.m +++ b/ios/RetroArch/browser.m @@ -115,7 +115,7 @@ } } else - [RetroArch_iOS displayErrorMessage:[NSString stringWithFormat:@"Browsed path is not a directory: %@", _path]]; + ios_display_alert([NSString stringWithFormat:@"Browsed path is not a directory: %@", _path], 0); [self.tableView reloadData]; } @@ -129,8 +129,8 @@ else { if (access(_path.UTF8String, R_OK | W_OK | X_OK)) - [RetroArch_iOS displayErrorMessage:@"The directory containing the selected file has limited permissions. This may " - "prevent zipped games from loading, and will cause some cores to not function."]; + ios_display_alert(@"The directory containing the selected file has limited permissions. This may " + "prevent zipped games from loading, and will cause some cores to not function.", 0); [[RetroArch_iOS get] pushViewController:[[RAModuleList alloc] initWithGame:path.path] animated:YES]; } @@ -199,7 +199,7 @@ if (info && info.data) [RetroArch_iOS.get pushViewController:[[RAModuleInfoList alloc] initWithModuleInfo:info] animated:YES]; else - [RetroArch_iOS displayErrorMessage:@"No information available."]; + ios_display_alert(@"No information available.", 0); } - (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath diff --git a/ios/RetroArch/input/BTStack/btdynamic.c b/ios/RetroArch/input/BTStack/btdynamic.c index c95f13ec25..bc48a27659 100644 --- a/ios/RetroArch/input/BTStack/btdynamic.c +++ b/ios/RetroArch/input/BTStack/btdynamic.c @@ -37,9 +37,8 @@ static struct GRAB(bt_send_l2cap), GRAB(run_loop_init), GRAB(run_loop_execute), - GRAB(btstack_get_system_bluetooth_enabled), + GRAB(btstack_set_power_mode), - GRAB(btstack_set_system_bluetooth_enabled), GRAB(hci_delete_stored_link_key), GRAB(hci_disconnect), GRAB(hci_read_bd_addr), @@ -65,7 +64,7 @@ static bool btstack_loaded; static bool btstack_open; static bool btstack_poweron; -bool btstack_load() +bool btstack_try_load() { assert(sizeof(void**) == sizeof(void(*)())); @@ -109,47 +108,28 @@ bool btstack_load() return true; } -void btstack_start() +void btstack_set_poweron(bool on) { - if (!btstack_load()) + if (!btstack_try_load()) return; - - if (!btstack_open) + + if (!btstack_open && bt_open_ptr()) { - if (bt_open_ptr()) - { - ios_add_log_message("BTstack: bt_open failed"); - btstack_loaded = false; - return; - } + ios_add_log_message("BTstack: bt_open failed"); + btstack_loaded = false; + return; } btstack_open = true; - if (!btstack_poweron) + if (on != btstack_poweron) { - ios_add_log_message("BTstack: Turning on"); - bt_send_cmd_ptr(btstack_set_power_mode_ptr, HCI_POWER_ON); - btstack_poweron = true; - } -} - -void btstack_stop() -{ - if (btstack_load() && btstack_open && btstack_poweron) - { - ios_add_log_message("BTstack: Turning off"); - bt_send_cmd_ptr(btstack_set_power_mode_ptr, HCI_POWER_OFF); - btstack_poweron = false; - } -} - -bool btstack_is_loaded() -{ - return btstack_load(); + btstack_poweron = on; + ios_add_log_message("BTstack: Turning %s", on ? "on" : "off"); + bt_send_cmd_ptr(btstack_set_power_mode_ptr, on ? HCI_POWER_ON : HCI_POWER_OFF); + } } bool btstack_is_running() { return btstack_poweron; } - diff --git a/ios/RetroArch/input/BTStack/btdynamic.h b/ios/RetroArch/input/BTStack/btdynamic.h index 70c20da6da..ce8d3d11a7 100644 --- a/ios/RetroArch/input/BTStack/btdynamic.h +++ b/ios/RetroArch/input/BTStack/btdynamic.h @@ -20,10 +20,8 @@ #include "btstack/utils.h" #include "btstack/btstack.h" -bool btstack_load(); -void btstack_start(); -void btstack_stop(); -bool btstack_is_loaded(); +bool btstack_try_load(); +void btstack_set_poweron(bool on); bool btstack_is_running(); #ifndef BUILDING_BTDYNAMIC @@ -41,9 +39,7 @@ BTDIMPORT void (*bt_send_l2cap_ptr)(uint16_t local_cid, uint8_t *data, uint16_t BTDIMPORT void (*run_loop_init_ptr)(RUN_LOOP_TYPE type); BTDIMPORT void (*run_loop_execute_ptr)(); -BTDIMPORT const hci_cmd_t* btstack_get_system_bluetooth_enabled_ptr; BTDIMPORT const hci_cmd_t* btstack_set_power_mode_ptr; -BTDIMPORT const hci_cmd_t* btstack_set_system_bluetooth_enabled_ptr; BTDIMPORT const hci_cmd_t* hci_delete_stored_link_key_ptr; BTDIMPORT const hci_cmd_t* hci_disconnect_ptr; BTDIMPORT const hci_cmd_t* hci_read_bd_addr_ptr; diff --git a/ios/RetroArch/input/BTStack/btpad.c b/ios/RetroArch/input/BTStack/btpad.c index 998d8d4b73..9e8aae02ce 100644 --- a/ios/RetroArch/input/BTStack/btpad.c +++ b/ios/RetroArch/input/BTStack/btpad.c @@ -23,67 +23,94 @@ #include "../../rarch_wrapper.h" #include "btdynamic.h" #include "btpad.h" +#include "btpad_queue.h" #include "wiimote.h" -static btpad_connection_t btpad_connection; +static btpad_connection_t btpad_connection[MAX_PADS]; +static struct btpad_interface* btpad_iface[MAX_PADS]; +static void* btpad_device[MAX_PADS]; +static bool inquiry_off; +static bool inquiry_running; -static bool btpad_connection_test(uint16_t handle, bd_addr_t address) +// External interface (MAIN THREAD ONLY) +void btpad_set_inquiry_state(bool on) { - if (handle && btpad_connection.handle && handle != btpad_connection.handle) - return false; - btpad_connection.handle = handle ? handle : btpad_connection.handle; + inquiry_off = !on; - if (address && btpad_connection.has_address && (BD_ADDR_CMP(address, btpad_connection.address))) - return false; - - if (address) - { - btpad_connection.has_address = true; - memcpy(btpad_connection.address, address, sizeof(bd_addr_t)); - } - - return true; + if (!inquiry_off && !inquiry_running) + btpad_queue_hci_inquiry(HCI_INQUIRY_LAP, 3, 1); } -static struct btpad_interface* btpad_iface; -static void* btpad_device; - // MAIN THREAD ONLY -uint32_t btpad_get_buttons() +uint32_t btpad_get_buttons(uint32_t slot) { - return (btpad_device && btpad_iface) ? btpad_iface->get_buttons(btpad_device) : 0; + if (slot < MAX_PADS && btpad_device[slot] && btpad_iface[slot]) + return btpad_iface[slot]->get_buttons(btpad_device[slot]); + + return 0; } -int16_t btpad_get_axis(unsigned axis) +int16_t btpad_get_axis(uint32_t slot, unsigned axis) { - return (btpad_device && btpad_iface) ? btpad_iface->get_axis(btpad_device, axis) : 0; + if (slot < MAX_PADS && btpad_device[slot] && btpad_iface[slot]) + return btpad_iface[slot]->get_axis(btpad_device[slot], axis); + + return 0; } -static void btpad_disconnect_pad() +// Internal interface: +static int32_t btpad_find_slot_for(uint16_t handle, bd_addr_t address) { - if (btpad_iface && btpad_device) + for (int i = 0; i < MAX_PADS; i ++) { - ios_add_log_message("BTpad: Disconnecting"); - - btpad_iface->disconnect(btpad_device); - btpad_device = 0; - btpad_iface = 0; + if (!btpad_connection[i].handle && !btpad_connection[i].has_address) + continue; + + if (handle && btpad_connection[i].handle && handle != btpad_connection[i].handle) + continue; + + if (address && btpad_connection[i].has_address && (BD_ADDR_CMP(address, btpad_connection[i].address))) + continue; + + return i; } - if (btpad_connection.handle) - bt_send_cmd_ptr(hci_disconnect_ptr, btpad_connection.handle, 0x15); - - memset(&btpad_connection, 0, sizeof(btpad_connection_t)); + return -1; } -static void btpad_connect_pad() +static int32_t btpad_find_slot_with_state(enum btpad_state state) { - if (btpad_connection.state == BTPAD_CONNECTED) - btpad_disconnect_pad(); - memset(&btpad_connection, 0, sizeof(btpad_connection_t)); + for (int i = 0; i < MAX_PADS; i ++) + if (btpad_connection[i].state == state) + return i; - ios_add_log_message("BTpad: Requesting local address"); - bt_send_cmd_ptr(hci_read_bd_addr_ptr); + return -1; +} + +static void btpad_disconnect_pad(uint32_t slot) +{ + if (slot > MAX_PADS) + return; + + if (btpad_iface[slot] && btpad_device[slot]) + { + ios_add_log_message("BTpad: Disconnecting slot %d", slot); + + btpad_iface[slot]->disconnect(btpad_device[slot]); + btpad_device[slot] = 0; + btpad_iface[slot] = 0; + } + + if (btpad_connection[slot].handle) + btpad_queue_hci_disconnect(btpad_connection[slot].handle, 0x15); + + memset(&btpad_connection[slot], 0, sizeof(btpad_connection_t)); +} + +static void btpad_disconnect_all_pads() +{ + for (int i = 0; i < MAX_PADS; i ++) + btpad_disconnect_pad(i); } void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) @@ -94,45 +121,41 @@ void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet { switch (packet[0]) { - case BTSTACK_EVENT_STATE: + case BTSTACK_EVENT_STATE: + { if (packet[2] == HCI_STATE_WORKING) - btpad_connect_pad(); + { + btpad_queue_reset(); + + btpad_queue_hci_read_bd_addr(); + bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_CONTROL, 672); // TODO: Where did I get 672 for mtu? + bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_INTERRUPT, 672); + btpad_queue_hci_inquiry(HCI_INQUIRY_LAP, 3, 1); + + btpad_queue_run(1); + } else if(packet[2] > HCI_STATE_WORKING && btpad_iface && btpad_device) - btpad_disconnect_pad(); - break; + { + btpad_disconnect_all_pads(); + } + } + break; + + case HCI_EVENT_COMMAND_STATUS: + { + btpad_queue_run(packet[3]); + } + break; case HCI_EVENT_COMMAND_COMPLETE: + { + btpad_queue_run(packet[2]); + if (COMMAND_COMPLETE_EVENT(packet, (*hci_read_bd_addr_ptr))) { - if (packet[5]) - ios_add_log_message("BTpad: Failed to get local address (E: %02X)", packet[5]); - else - { - bt_flip_addr_ptr(event_addr, &packet[6]); - ios_add_log_message("BTpad: Local address is %s", bd_addr_to_str_ptr(event_addr)); - } - - ios_add_log_message("BTpad: Registering HID INTERRUPT service"); - bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_INTERRUPT, 672); - } - break; - - case L2CAP_EVENT_SERVICE_REGISTERED: - { - uint32_t psm = READ_BT_16(packet, 3); - if (packet[2]) - ios_add_log_message("BTpad: Failed to register HID service (PSM: %02X, E: %02X)", psm, packet[2]); - else if (psm == PSM_HID_INTERRUPT) - { - ios_add_log_message("BTpad: HID INTERRUPT service registered"); - ios_add_log_message("BTpad: Registering HID CONTROL service"); - bt_send_cmd_ptr(l2cap_register_service_ptr, PSM_HID_CONTROL, 672); - } - else if(psm == PSM_HID_CONTROL) - { - ios_add_log_message("BTpad: HID CONTROL service registered"); - ios_add_log_message("BTpad: Starting inquiry"); - bt_send_cmd_ptr(hci_inquiry_ptr, HCI_INQUIRY_LAP, 3, 1); + bt_flip_addr_ptr(event_addr, &packet[6]); + if (!packet[5]) ios_add_log_message("BTpad: Local address is %s", bd_addr_to_str_ptr(event_addr)); + else ios_add_log_message("BTpad: Failed to get local address (Status: %02X)", packet[5]); } } break; @@ -142,15 +165,20 @@ void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet if (packet[2]) { bt_flip_addr_ptr(event_addr, &packet[3]); - if (btpad_connection_test(0, event_addr)) + + const int32_t slot = btpad_find_slot_with_state(BTPAD_EMPTY); + if (slot >= 0) { - btpad_connection.state = BTPAD_WANT_INQ_COMPLETE; + ios_add_log_message("BTpad: Inquiry found device (Slot %d)", slot); - btpad_connection.page_scan_repetition_mode = packet [3 + packet[2] * (6)]; - btpad_connection.class = READ_BT_24(packet, 3 + packet[2] * (6+1+1+1)); - btpad_connection.clock_offset = READ_BT_16(packet, 3 + packet[2] * (6+1+1+1+3)) & 0x7fff; + memcpy(btpad_connection[slot].address, event_addr, sizeof(bd_addr_t)); - ios_add_log_message("BTpad: Inquiry found device"); + btpad_connection[slot].has_address = true; + btpad_connection[slot].state = BTPAD_CONNECTING; + btpad_connection[slot].slot = slot; + + bt_send_cmd_ptr(l2cap_create_channel_ptr, btpad_connection[slot].address, PSM_HID_CONTROL); + bt_send_cmd_ptr(l2cap_create_channel_ptr, btpad_connection[slot].address, PSM_HID_INTERRUPT); } } } @@ -158,62 +186,50 @@ void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet case HCI_EVENT_INQUIRY_COMPLETE: { - if (btpad_connection.state == BTPAD_WANT_INQ_COMPLETE) - { - ios_add_log_message("BTpad: Got inquiry complete; connecting\n"); - bt_send_cmd_ptr(l2cap_create_channel_ptr, btpad_connection.address, PSM_HID_CONTROL); - } - else if(btpad_connection.state == BTPAD_EMPTY) - { - if (btpad_connection.laps < 40) - { - btpad_connection.laps ++; - bt_send_cmd_ptr(hci_inquiry_ptr, HCI_INQUIRY_LAP, 3, 1); - } - else - ios_add_log_message("BTpad: Did not find wiimote, will stop searching"); - } + // TODO: Check performance and battery effect of this + + inquiry_running = !inquiry_off; + + if (inquiry_running) + btpad_queue_hci_inquiry(HCI_INQUIRY_LAP, 3, 1); } break; case L2CAP_EVENT_CHANNEL_OPENED: { - const uint8_t status = packet[2]; bt_flip_addr_ptr(event_addr, &packet[3]); const uint16_t handle = READ_BT_16(packet, 9); const uint16_t psm = READ_BT_16(packet, 11); const uint16_t channel_id = READ_BT_16(packet, 13); - if (!btpad_connection_test(handle, event_addr)) - { - ios_add_log_message("BTpad: Incoming L2CAP connection not recognized; ignoring"); - break; - } + const int32_t slot = btpad_find_slot_for(handle, event_addr); - if (status == 0) + if (!packet[2]) { - ios_add_log_message("BTpad: L2CAP channel opened for psm: %02X", psm); + if (slot < 0) + { + ios_add_log_message("BTpad: Got L2CAP 'Channel Opened' event for unrecognized device"); + break; + } + + ios_add_log_message("BTpad: L2CAP channel opened: (Slot: %d, PSM: %02X)", slot, psm); + btpad_connection[slot].handle = handle; if (psm == PSM_HID_CONTROL) - { - btpad_connection.channels[0] = channel_id; - - ios_add_log_message("BTpad: Got HID CONTROL channel; Opening INTERRUPT"); - bt_send_cmd_ptr(l2cap_create_channel_ptr, btpad_connection.address, PSM_HID_INTERRUPT); - } + btpad_connection[slot].channels[0] = channel_id; else if (psm == PSM_HID_INTERRUPT) - { - btpad_connection.channels[1] = channel_id; - - ios_add_log_message("BTpad: Got HID INTERRUPT channel; Requesting name"); - bt_send_cmd_ptr(hci_remote_name_request_ptr, btpad_connection.address, btpad_connection.page_scan_repetition_mode, - 0, btpad_connection.clock_offset | 0x8000); - } + btpad_connection[slot].channels[1] = channel_id; else - ios_add_log_message("BTpad: Got unknown L2CAP channel, ignoring"); + ios_add_log_message("BTpad: Got unknown L2CAP PSM, ignoring (Slot: %d, PSM: %02X)", slot, psm); + + if (btpad_connection[slot].channels[0] && btpad_connection[slot].channels[1]) + { + ios_add_log_message("BTpad: Got both L2CAP channels, requesting name (Slot: %d)", slot); + btpad_queue_hci_remote_name_request(btpad_connection[slot].address, 0, 0, 0); + } } else - ios_add_log_message("BTpad: Failed to open WiiMote L2CAP channel (PSM: %02X, E: %02X)", psm, status); + ios_add_log_message("BTpad: Got failed L2CAP 'Channel Opened' event (Slot %d, PSM: %02X, Status: %02X)", -1, psm, packet[2]); } break; @@ -223,31 +239,28 @@ void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet const uint16_t handle = READ_BT_16(packet, 8); const uint32_t psm = READ_BT_16(packet, 10); const uint32_t channel_id = READ_BT_16(packet, 12); - - const unsigned interrupt = (psm == PSM_HID_INTERRUPT) ? 1 : 0; - ios_add_log_message("BTpad: Incoming L2CAP connection for PSM: %02X", psm); - - if (!btpad_connection_test(handle, event_addr)) + int32_t slot = btpad_find_slot_for(handle, event_addr); + if (slot < 0) { - ios_add_log_message("BTpad: Connection is for unregnized handle or address, denying"); + slot = btpad_find_slot_with_state(BTPAD_EMPTY); - // TODO: Check error code - bt_send_cmd_ptr(l2cap_decline_connection_ptr, 0x15); + if (slot >= 0) + { + ios_add_log_message("BTpad: Got new incoming connection (Slot: %d)", slot); - break; + memcpy(btpad_connection[slot].address, event_addr, sizeof(bd_addr_t)); + + btpad_connection[slot].has_address = true; + btpad_connection[slot].handle = handle; + btpad_connection[slot].state = BTPAD_CONNECTING; + btpad_connection[slot].slot = slot; + } + else break; } - btpad_connection.channels[interrupt] = channel_id; - - bt_send_cmd_ptr(l2cap_accept_connection_ptr, btpad_connection.channels[interrupt]); - ios_add_log_message("BTpad: L2CAP Connection accepted"); - - if (btpad_connection.channels[0] && btpad_connection.channels[1]) - { - ios_add_log_message("BTpad: Got both L2CAP channels, requesting name"); - bt_send_cmd_ptr(hci_remote_name_request_ptr, btpad_connection.address, 0, 0, 0); - } + ios_add_log_message("BTpad: Incoming L2CAP connection (Slot: %d, PSM: %02X)", slot, psm); + bt_send_cmd_ptr(l2cap_accept_connection_ptr, channel_id); } break; @@ -255,38 +268,66 @@ void btpad_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet { bt_flip_addr_ptr(event_addr, &packet[3]); - if (!btpad_connection_test(0, event_addr)) + const int32_t slot = btpad_find_slot_for(0, event_addr); + if (slot < 0) { ios_add_log_message("BTpad: Got unexpected remote name, ignoring"); break; } - ios_add_log_message("BTpad: Got %200s", (char*)&packet[9]); + ios_add_log_message("BTpad: Got %.200s (Slot: %d)", (char*)&packet[9], slot); if (strncmp((char*)&packet[9], "PLAYSTATION(R)3 Controller", 26) == 0) - btpad_iface = &btpad_ps3; + btpad_iface[slot] = &btpad_ps3; else if (strncmp((char*)&packet[9], "Nintendo RVL-CNT-01", 19) == 0) - btpad_iface = &btpad_wii; + btpad_iface[slot] = &btpad_wii; - if (btpad_iface) + if (btpad_iface[slot]) { - btpad_device = btpad_iface->connect(&btpad_connection); - btpad_connection.state = BTPAD_CONNECTED; + btpad_device[slot] = btpad_iface[slot]->connect(&btpad_connection[slot]); + btpad_connection[slot].state = BTPAD_CONNECTED; } - } break; case HCI_EVENT_PIN_CODE_REQUEST: { - ios_add_log_message("BTpad: Sending PIN"); + ios_add_log_message("BTpad: Sending WiiMote PIN"); bt_flip_addr_ptr(event_addr, &packet[2]); - bt_send_cmd_ptr(hci_pin_code_request_reply_ptr, event_addr, 6, &packet[2]); + btpad_queue_hci_pin_code_request_reply(event_addr, &packet[2]); + } + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + { + const uint32_t handle = READ_BT_16(packet, 3); + + if (!packet[2]) + { + const int32_t slot = btpad_find_slot_for(handle, 0); + if (slot >= 0) + { + btpad_connection[slot].handle = 0; + btpad_disconnect_pad(slot); + + ios_add_log_message("BTpad: Device disconnected (Slot: %d)", slot); + } + } + else + ios_add_log_message("BTpad: Got failed 'Disconnection Complete' event (Status: %02X)", packet[2]); + } + break; + + case L2CAP_EVENT_SERVICE_REGISTERED: + { + if (!packet[2]) + ios_add_log_message("BTpad: Got failed 'Service Registered' event (PSM: %02X, Status: %02X)", READ_BT_16(packet, 3), packet[2]); } break; } } - if (btpad_device && btpad_iface) - btpad_iface->packet_handler(btpad_device, packet_type, channel, packet, size); + for (int i = 0; i < MAX_PADS; i ++) + if (btpad_device[i] && btpad_iface[i] && (btpad_connection[i].channels[0] == channel || btpad_connection[i].channels[1] == channel)) + btpad_iface[i]->packet_handler(btpad_device[i], packet_type, channel, packet, size); } diff --git a/ios/RetroArch/input/BTStack/btpad.h b/ios/RetroArch/input/BTStack/btpad.h index ba64e56fd0..0bc70d060e 100644 --- a/ios/RetroArch/input/BTStack/btpad.h +++ b/ios/RetroArch/input/BTStack/btpad.h @@ -18,26 +18,24 @@ #include "btstack/btstack.h" -uint32_t btpad_get_buttons(); -int16_t btpad_get_axis(unsigned axis); +void btpad_set_inquiry_state(bool on); + +uint32_t btpad_get_buttons(uint32_t slot); +int16_t btpad_get_axis(uint32_t slot, unsigned axis); // Private interface -enum btpad_state { BTPAD_EMPTY, BTPAD_WANT_INQ_COMPLETE, BTPAD_CONNECTED }; +enum btpad_state { BTPAD_EMPTY, BTPAD_CONNECTING, BTPAD_CONNECTED }; typedef struct { enum btpad_state state; + uint32_t slot; uint16_t handle; bool has_address; bd_addr_t address; - uint32_t laps; - uint32_t page_scan_repetition_mode; - uint32_t class; - uint32_t clock_offset; - uint16_t channels[2]; //0: Control, 1: Interrupt bool connected; @@ -47,7 +45,6 @@ struct btpad_interface { void* (*connect)(const btpad_connection_t* connection); void (*disconnect)(void* device); - void (*set_leds)(void* device, unsigned leds); uint32_t (*get_buttons)(void* device); int16_t (*get_axis)(void* device, unsigned axis); diff --git a/ios/RetroArch/input/BTStack/btpad_ps3.c b/ios/RetroArch/input/BTStack/btpad_ps3.c index 263f2f7ed9..0fe285a25d 100644 --- a/ios/RetroArch/input/BTStack/btpad_ps3.c +++ b/ios/RetroArch/input/BTStack/btpad_ps3.c @@ -31,39 +31,16 @@ struct btpad_ps3_data uint32_t handle; uint32_t channels[2]; + uint32_t slot; bool have_led; }; -static void btpad_ps3_setleds(struct btpad_ps3_data* device, unsigned leds); -static void* btpad_ps3_connect(const btpad_connection_t* connection) -{ - struct btpad_ps3_data* device = malloc(sizeof(struct btpad_ps3_data)); - memset(device, 0, sizeof(*device)); - - memcpy(device->address, connection->address, BD_ADDR_LEN); - device->handle = connection->handle; - device->channels[0] = connection->channels[0]; - device->channels[1] = connection->channels[1]; - - // Magic packet to start reports - static uint8_t data[] = {0x53, 0xF4, 0x42, 0x03, 0x00, 0x00}; - bt_send_l2cap_ptr(device->channels[0], data, 6); - - // Without this the digital buttons won't be reported - btpad_ps3_setleds(device, 1); - - return device; -} - -static void btpad_ps3_disconnect(struct btpad_ps3_data* device) -{ -} - -static void btpad_ps3_setleds(struct btpad_ps3_data* device, unsigned leds) +static void btpad_ps3_send_control(struct btpad_ps3_data* device) { // TODO: Can this be modified to turn of motion tracking? static uint8_t report_buffer[] = { - 0x52, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x27, 0x10, 0x00, 0x32, 0xff, 0x27, 0x10, 0x00, 0x32, @@ -74,13 +51,55 @@ static void btpad_ps3_setleds(struct btpad_ps3_data* device, unsigned leds) 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - report_buffer[11] = (leds & 0xF) << 1; + report_buffer[11] = 1 << ((device->slot % 4) + 1); bt_send_l2cap_ptr(device->channels[0], report_buffer, sizeof(report_buffer)); } +static void* btpad_ps3_connect(const btpad_connection_t* connection) +{ + struct btpad_ps3_data* device = malloc(sizeof(struct btpad_ps3_data)); + memset(device, 0, sizeof(*device)); + + memcpy(device->address, connection->address, BD_ADDR_LEN); + device->handle = connection->handle; + device->channels[0] = connection->channels[0]; + device->channels[1] = connection->channels[1]; + device->slot = connection->slot; + + // Magic packet to start reports + static uint8_t data[] = {0x53, 0xF4, 0x42, 0x03, 0x00, 0x00}; + bt_send_l2cap_ptr(device->channels[0], data, 6); + + // Without this the digital buttons won't be reported + btpad_ps3_send_control(device); + + return device; +} + +static void btpad_ps3_disconnect(struct btpad_ps3_data* device) +{ +} + static uint32_t btpad_ps3_get_buttons(struct btpad_ps3_data* device) { - return device->data[3] | (device->data[4] << 8) | ((device->data[5] & 1) << 16); + #define KEY(X) RETRO_DEVICE_ID_JOYPAD_##X + static const uint32_t button_mapping[17] = + { + KEY(SELECT), KEY(L3), KEY(R3), KEY(START), + KEY(UP), KEY(RIGHT), KEY(DOWN), KEY(LEFT), + KEY(L2), KEY(R2), KEY(L), KEY(R), + KEY(X), KEY(A), KEY(B), KEY(Y), + 16 //< PS Button + }; + #undef KEY + + const uint32_t pressed_keys = device->data[3] | (device->data[4] << 8) | ((device->data[5] & 1) << 16); + uint32_t result = 0; + + for (int i = 0; i < 17; i ++) + result |= (pressed_keys & (1 << i)) ? (1 << button_mapping[i]) : 0; + + return result; } static int16_t btpad_ps3_get_axis(struct btpad_ps3_data* device, unsigned axis) @@ -101,11 +120,14 @@ static void btpad_ps3_packet_handler(struct btpad_ps3_data* device, uint8_t pack { if (!device->have_led) { - btpad_ps3_setleds(device, 1); + btpad_ps3_send_control(device); device->have_led = true; } memcpy(device->data, packet, size); + g_current_input_data.pad_buttons[device->slot] = btpad_ps3_get_buttons(device); + for (int i = 0; i < 4; i ++) + g_current_input_data.pad_axis[device->slot][i] = btpad_ps3_get_axis(device, i); } } @@ -113,7 +135,6 @@ struct btpad_interface btpad_ps3 = { (void*)&btpad_ps3_connect, (void*)&btpad_ps3_disconnect, - (void*)&btpad_ps3_setleds, (void*)&btpad_ps3_get_buttons, (void*)&btpad_ps3_get_axis, (void*)&btpad_ps3_packet_handler diff --git a/ios/RetroArch/input/BTStack/btpad_queue.c b/ios/RetroArch/input/BTStack/btpad_queue.c new file mode 100644 index 0000000000..eb16b1b59a --- /dev/null +++ b/ios/RetroArch/input/BTStack/btpad_queue.c @@ -0,0 +1,176 @@ +/* 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 "btdynamic.h" +#include "btpad.h" +#include "btpad_queue.h" + +struct btpad_queue_command +{ + const hci_cmd_t* command; + + union + { + struct + { + uint8_t on; + } btstack_set_power_mode; + + struct + { + uint16_t handle; + uint8_t reason; + } hci_disconnect; + + struct + { + uint32_t lap; + uint8_t length; + uint8_t num_responses; + } hci_inquiry; + + struct + { + bd_addr_t bd_addr; + uint8_t page_scan_repetition_mode; + uint8_t reserved; + uint16_t clock_offset; + } hci_remote_name_request; + + struct // For wiimote only + { + bd_addr_t bd_addr; + bd_addr_t pin; + } hci_pin_code_request_reply; + }; +}; + +struct btpad_queue_command commands[64]; +static uint32_t insert_position; +static uint32_t read_position; +static uint32_t can_run; + +#define INCPOS(POS) { POS##_position = (POS##_position + 1) % 64; } + +void btpad_queue_reset() +{ + insert_position = 0; + read_position = 0; + can_run = 1; +} + +void btpad_queue_run(uint32_t count) +{ + can_run = count; + + btpad_queue_process(); +} + +void btpad_queue_process() +{ + for (; can_run && (insert_position != read_position); can_run --) + { + struct btpad_queue_command* cmd = &commands[read_position]; + + if (cmd->command == btstack_set_power_mode_ptr) + bt_send_cmd_ptr(cmd->command, cmd->btstack_set_power_mode.on); + else if (cmd->command == hci_read_bd_addr_ptr) + bt_send_cmd_ptr(cmd->command); + else if (cmd->command == hci_disconnect_ptr) + bt_send_cmd_ptr(cmd->command, cmd->hci_disconnect.handle, cmd->hci_disconnect.reason); + else if (cmd->command == hci_inquiry_ptr) + bt_send_cmd_ptr(cmd->command, cmd->hci_inquiry.lap, cmd->hci_inquiry.length, cmd->hci_inquiry.num_responses); + else if (cmd->command == hci_remote_name_request_ptr) + bt_send_cmd_ptr(cmd->command, cmd->hci_remote_name_request.bd_addr, cmd->hci_remote_name_request.page_scan_repetition_mode, + cmd->hci_remote_name_request.reserved, cmd->hci_remote_name_request.clock_offset); + else if (cmd->command == hci_pin_code_request_reply_ptr) + bt_send_cmd_ptr(cmd->command, cmd->hci_pin_code_request_reply.bd_addr, 6, cmd->hci_pin_code_request_reply.pin); + + INCPOS(read); + } +} + +void btpad_queue_btstack_set_power_mode(uint8_t on) +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = btstack_set_power_mode_ptr; + cmd->btstack_set_power_mode.on = on; + + INCPOS(insert); + btpad_queue_process(); +} + +void btpad_queue_hci_read_bd_addr() +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = hci_read_bd_addr_ptr; + + INCPOS(insert); + btpad_queue_process(); +} + +void btpad_queue_hci_disconnect(uint16_t handle, uint8_t reason) +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = hci_disconnect_ptr; + cmd->hci_disconnect.handle = handle; + cmd->hci_disconnect.reason = reason; + + INCPOS(insert); + btpad_queue_process(); +} + +void btpad_queue_hci_inquiry(uint32_t lap, uint8_t length, uint8_t num_responses) +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = hci_inquiry_ptr; + cmd->hci_inquiry.lap = lap; + cmd->hci_inquiry.length = length; + cmd->hci_inquiry.num_responses = num_responses; + + INCPOS(insert); + btpad_queue_process(); +} + +void btpad_queue_hci_remote_name_request(bd_addr_t bd_addr, uint8_t page_scan_repetition_mode, uint8_t reserved, uint16_t clock_offset) +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = hci_remote_name_request_ptr; + memcpy(cmd->hci_remote_name_request.bd_addr, bd_addr, sizeof(bd_addr_t)); + cmd->hci_remote_name_request.page_scan_repetition_mode = page_scan_repetition_mode; + cmd->hci_remote_name_request.reserved = reserved; + cmd->hci_remote_name_request.clock_offset = clock_offset; + + INCPOS(insert); + btpad_queue_process(); +} + +void btpad_queue_hci_pin_code_request_reply(bd_addr_t bd_addr, bd_addr_t pin) +{ + struct btpad_queue_command* cmd = &commands[insert_position]; + + cmd->command = hci_pin_code_request_reply_ptr; + memcpy(cmd->hci_pin_code_request_reply.bd_addr, bd_addr, sizeof(bd_addr_t)); + memcpy(cmd->hci_pin_code_request_reply.pin, pin, sizeof(bd_addr_t)); + + INCPOS(insert); + btpad_queue_process(); +} + diff --git a/ios/RetroArch/input/BTStack/btpad_queue.h b/ios/RetroArch/input/BTStack/btpad_queue.h new file mode 100644 index 0000000000..f152aab02b --- /dev/null +++ b/ios/RetroArch/input/BTStack/btpad_queue.h @@ -0,0 +1,30 @@ +/* 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 __IOS_RARCH_BTPAD_QUEUE_H__ +#define __IOS_RARCH_BTPAD_QUEUE_H__ + +void btpad_queue_reset(); +void btpad_queue_run(uint32_t count); +void btpad_queue_process(); + +void btpad_queue_btstack_set_power_mode(uint8_t on); +void btpad_queue_hci_read_bd_addr(); +void btpad_queue_hci_disconnect(uint16_t handle, uint8_t reason); +void btpad_queue_hci_inquiry(uint32_t lap, uint8_t length, uint8_t num_responses); +void btpad_queue_hci_remote_name_request(bd_addr_t bd_addr, uint8_t page_scan_repetition_mode, uint8_t reserved, uint16_t clock_offset); +void btpad_queue_hci_pin_code_request_reply(bd_addr_t bd_addr, bd_addr_t pin); + +#endif diff --git a/ios/RetroArch/input/BTStack/btpad_wii.c b/ios/RetroArch/input/BTStack/btpad_wii.c index c24f334864..3b635d5af4 100644 --- a/ios/RetroArch/input/BTStack/btpad_wii.c +++ b/ios/RetroArch/input/BTStack/btpad_wii.c @@ -31,6 +31,7 @@ static void* btpad_wii_connect(const btpad_connection_t* connection) memcpy(device->addr, connection->address, BD_ADDR_LEN); + device->unid = connection->slot; device->wiiMoteConHandle = connection->handle; device->c_source_cid = connection->channels[0]; device->i_source_cid = connection->channels[1]; @@ -46,11 +47,6 @@ static void btpad_wii_disconnect(struct wiimote_t* device) { } -static void btpad_wii_setleds(struct wiimote_t* device, unsigned leds) -{ - // TODO -} - static uint32_t btpad_wii_get_buttons(struct wiimote_t* device) { return device->btns | (device->exp.classic.btns << 16); @@ -86,30 +82,34 @@ static void btpad_wii_packet_handler(struct wiimote_t* device, uint8_t packet_ty case WM_RPT_BTN: { wiimote_pressed_buttons(device, msg); - return; + break; } case WM_RPT_READ: { wiimote_pressed_buttons(device, msg); wiimote_handshake(device, WM_RPT_READ, msg + 5, ((msg[2] & 0xF0) >> 4) + 1); - return; + break; } case WM_RPT_CTRL_STATUS: { wiimote_pressed_buttons(device, msg); wiimote_handshake(device,WM_RPT_CTRL_STATUS,msg,-1); - return; + break; } case WM_RPT_BTN_EXP: { wiimote_pressed_buttons(device, msg); wiimote_handle_expansion(device, msg+2); - return; + break; } } + + g_current_input_data.pad_buttons[device->unid] = btpad_wii_get_buttons(device); + for (int i = 0; i < 4; i ++) + g_current_input_data.pad_axis[device->unid][i] = btpad_wii_get_axis(device, i); } } @@ -117,7 +117,6 @@ struct btpad_interface btpad_wii = { (void*)&btpad_wii_connect, (void*)&btpad_wii_disconnect, - (void*)&btpad_wii_setleds, (void*)&btpad_wii_get_buttons, (void*)&btpad_wii_get_axis, (void*)&btpad_wii_packet_handler diff --git a/ios/RetroArch/input/ios_input.c b/ios/RetroArch/input/ios_input.c index f2959b77a6..10c1c7d76d 100644 --- a/ios/RetroArch/input/ios_input.c +++ b/ios/RetroArch/input/ios_input.c @@ -13,7 +13,10 @@ * If not, see . */ + #include +#include + #include "input/input_common.h" #include "ios_input.h" #include "general.h" @@ -22,6 +25,215 @@ extern const rarch_joypad_driver_t ios_joypad; static const rarch_joypad_driver_t* const g_joydriver = &ios_joypad; +ios_input_data_t g_current_input_data; +ios_input_data_t g_polled_input_data; + +static const struct rarch_key_map rarch_key_map_hidusage[]; + +// Main thread interface +static bool icade_enabled; +static uint32_t icade_buttons; + +static void handle_icade_event(unsigned keycode) +{ + static const struct + { + bool up; + int button; + } icade_map[0x20] = + { + { false, -1 }, { false, -1 }, { false, -1 }, { false, -1 }, // 0 + { false, 2 }, { false, -1 }, { true , 3 }, { false, 3 }, // 4 + { true , 0 }, { true, 5 }, { true , 7 }, { false, 8 }, // 8 + { false, 6 }, { false, 9 }, { false, 10 }, { false, 11 }, // C + { true , 6 }, { true , 9 }, { false, 7 }, { true, 10 }, // 0 + { true , 2 }, { true , 8 }, { false, -1 }, { true , 4 }, // 4 + { false, 5 }, { true , 11 }, { false, 0 }, { false, 1 }, // 8 + { false, 4 }, { true , 1 }, { false, -1 }, { false, -1 } // C + }; + + if (icade_enabled && (keycode < 0x20) && (icade_map[keycode].button >= 0)) + { + const int button = icade_map[keycode].button; + + if (icade_map[keycode].up) + icade_buttons &= ~(1 << button); + else + icade_buttons |= (1 << button); + } +} + +void ios_input_enable_icade(bool on) +{ + icade_enabled = on; + icade_buttons = 0; +} + +void ios_input_handle_key_event(unsigned keycode, bool down) +{ + if (icade_enabled) + handle_icade_event(keycode); + else if (keycode < MAX_KEYS) + g_current_input_data.keys[keycode] = down; +} + +// Game thread interface +static bool ios_key_pressed(enum retro_key key) +{ + if ((int)key >= 0 && key < RETROK_LAST) + return g_polled_input_data.keys[input_translate_rk_to_keysym(key)]; + + return false; +} + +static bool ios_is_pressed(unsigned port_num, const struct retro_keybind *binds, unsigned key) +{ + return ios_key_pressed(binds[key].key) || input_joypad_pressed(g_joydriver, port_num, binds, key); +} + +// Exported input driver +static void *ios_input_init(void) +{ + input_init_keyboard_lut(rarch_key_map_hidusage); + memset(&g_polled_input_data, 0, sizeof(g_polled_input_data)); + return (void*)-1; +} + +static void ios_input_poll(void *data) +{ + dispatch_sync(dispatch_get_main_queue(), ^{ + memcpy(&g_polled_input_data, &g_current_input_data, sizeof(ios_input_data_t)); + + for (int i = 0; i != g_polled_input_data.touch_count; i ++) + { + input_translate_coord_viewport(g_polled_input_data.touches[i].screen_x, g_polled_input_data.touches[i].screen_y, + &g_polled_input_data.touches[i].fixed_x, &g_polled_input_data.touches[i].fixed_y, + &g_polled_input_data.touches[i].full_x, &g_polled_input_data.touches[i].full_y); + } + + input_joypad_poll(g_joydriver); + }); +} + +static int16_t ios_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) +{ + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return (id < RARCH_BIND_LIST_END) ? ios_is_pressed(port, binds[port], id) : false; + + case RETRO_DEVICE_ANALOG: + return input_joypad_analog(g_joydriver, port, index, id, binds[port]); + + case RETRO_DEVICE_KEYBOARD: + return ios_key_pressed(id); + + case RETRO_DEVICE_POINTER: + case RARCH_DEVICE_POINTER_SCREEN: + { + const bool want_full = device == RARCH_DEVICE_POINTER_SCREEN; + + if (index < g_polled_input_data.touch_count && index < MAX_TOUCHES) + { + const ios_touch_data_t* touch = &g_polled_input_data.touches[index]; + + switch (id) + { + case RETRO_DEVICE_ID_POINTER_PRESSED: return 1; + case RETRO_DEVICE_ID_POINTER_X: return want_full ? touch->full_x : touch->fixed_x; + case RETRO_DEVICE_ID_POINTER_Y: return want_full ? touch->full_y : touch->fixed_y; + } + } + + return 0; + } + + default: + return 0; + } +} + +static bool ios_bind_button_pressed(void *data, int key) +{ + const struct retro_keybind *binds = g_settings.input.binds[0]; + return (key >= 0 && key < RARCH_BIND_LIST_END) ? ios_is_pressed(0, binds, key) : false; +} + +static void ios_input_free_input(void *data) +{ + (void)data; +} + +static void ios_input_set_keybinds(void *data, unsigned device, unsigned port, + unsigned id, unsigned keybind_action) +{ + (void)device; + + if (keybind_action & (1ULL << KEYBINDS_ACTION_SET_DEFAULT_BINDS)) + { + switch (device) + { + case DEVICE_NONE: + break; + case DEVICE_WIIMOTE: + strlcpy(g_settings.input.device_names[port], "Wiimote + Classic", + sizeof(g_settings.input.device_names[port])); + g_settings.input.device[port] = device; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_B].joykey = 22; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_Y].joykey = 21; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_SELECT].joykey = 28; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_START].joykey = 26; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_UP].joykey = 16; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_DOWN].joykey = 30; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_LEFT].joykey = 17; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_RIGHT].joykey = 31; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_A].joykey = 20; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_X].joykey = 19; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L].joykey = 29; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R].joykey = 25; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L2].joykey = 23; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R2].joykey = 18; + g_settings.input.binds[port][RARCH_MENU_TOGGLE].joykey = 27; + g_settings.input.dpad_emulation[port] = ANALOG_DPAD_NONE; + break; + case DEVICE_SIXAXIS: + strlcpy(g_settings.input.device_names[port], "SixAxis/DualShock3", + sizeof(g_settings.input.device_names[port])); + g_settings.input.device[port] = device; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_B].joykey = 14; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_Y].joykey = 15; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_SELECT].joykey = 0; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_START].joykey = 3; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_UP].joykey = 4; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_DOWN].joykey = 6; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_LEFT].joykey = 7; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_RIGHT].joykey = 5; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_A].joykey = 13; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_X].joykey = 12; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L].joykey = 10; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R].joykey = 11; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L2].joykey = 8; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R2].joykey = 9; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L3].joykey = 1; + g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R3].joykey = 2; + g_settings.input.binds[port][RARCH_MENU_TOGGLE].joykey = 16; + g_settings.input.dpad_emulation[port] = ANALOG_DPAD_NONE; + break; + } + } +} + +const input_driver_t input_ios = { + ios_input_init, + ios_input_poll, + ios_input_state, + ios_bind_button_pressed, + ios_input_free_input, + ios_input_set_keybinds, + "ios_input", +}; + + // Key table #include "keycode.h" static const struct rarch_key_map rarch_key_map_hidusage[] = { @@ -166,158 +378,4 @@ static const struct rarch_key_map rarch_key_map_hidusage[] = { // RETROK_EURO }, // RETROK_UNDO }, { 0, RETROK_UNKNOWN } -}; - -ios_input_data_t g_ios_input_data; - -// Non-exported helpers -static bool ios_key_pressed(enum retro_key key) -{ - if ((int)key >= 0 && key < RETROK_LAST) - return g_ios_input_data.keys[input_translate_rk_to_keysym(key)]; - - return false; -} - -static bool ios_is_pressed(unsigned port_num, const struct retro_keybind *binds, unsigned key) -{ - return ios_key_pressed(binds[key].key) || input_joypad_pressed(g_joydriver, port_num, binds, key); -} - -// Exported input driver -static void *ios_input_init(void) -{ - input_init_keyboard_lut(rarch_key_map_hidusage); - memset(&g_ios_input_data, 0, sizeof(g_ios_input_data)); - return (void*)-1; -} - -static void ios_input_poll(void *data) -{ - for (int i = 0; i != g_ios_input_data.touch_count; i ++) - { - input_translate_coord_viewport(g_ios_input_data.touches[i].screen_x, g_ios_input_data.touches[i].screen_y, - &g_ios_input_data.touches[i].fixed_x, &g_ios_input_data.touches[i].fixed_y, - &g_ios_input_data.touches[i].full_x, &g_ios_input_data.touches[i].full_y); - } - - input_joypad_poll(g_joydriver); -} - -static int16_t ios_input_state(void *data, const struct retro_keybind **binds, unsigned port, unsigned device, unsigned index, unsigned id) -{ - switch (device) - { - case RETRO_DEVICE_JOYPAD: - return (id < RARCH_BIND_LIST_END) ? ios_is_pressed(port, binds[port], id) : false; - - case RETRO_DEVICE_ANALOG: - return input_joypad_analog(g_joydriver, port, index, id, binds[port]); - - case RETRO_DEVICE_KEYBOARD: - return ios_key_pressed(id); - - case RETRO_DEVICE_POINTER: - case RARCH_DEVICE_POINTER_SCREEN: - { - const bool want_full = device == RARCH_DEVICE_POINTER_SCREEN; - - if (index < g_ios_input_data.touch_count && index < MAX_TOUCHES) - { - const ios_touch_data_t* touch = &g_ios_input_data.touches[index]; - - switch (id) - { - case RETRO_DEVICE_ID_POINTER_PRESSED: return 1; - case RETRO_DEVICE_ID_POINTER_X: return want_full ? touch->full_x : touch->fixed_x; - case RETRO_DEVICE_ID_POINTER_Y: return want_full ? touch->full_y : touch->fixed_y; - } - } - - return 0; - } - - default: - return 0; - } -} - -static bool ios_bind_button_pressed(void *data, int key) -{ - const struct retro_keybind *binds = g_settings.input.binds[0]; - return (key >= 0 && key < RARCH_BIND_LIST_END) ? ios_is_pressed(0, binds, key) : false; -} - -static void ios_input_free_input(void *data) -{ - (void)data; -} - -static void ios_input_set_keybinds(void *data, unsigned device, unsigned port, - unsigned id, unsigned keybind_action) -{ - (void)device; - - if (keybind_action & (1ULL << KEYBINDS_ACTION_SET_DEFAULT_BINDS)) - { - switch (device) - { - case DEVICE_NONE: - break; - case DEVICE_WIIMOTE: - strlcpy(g_settings.input.device_names[port], "Wiimote + Classic", - sizeof(g_settings.input.device_names[port])); - g_settings.input.device[port] = device; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_B].joykey = 22; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_Y].joykey = 21; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_SELECT].joykey = 28; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_START].joykey = 26; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_UP].joykey = 16; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_DOWN].joykey = 30; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_LEFT].joykey = 17; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_RIGHT].joykey = 31; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_A].joykey = 20; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_X].joykey = 19; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L].joykey = 29; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R].joykey = 25; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L2].joykey = 23; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R2].joykey = 18; - g_settings.input.binds[port][RARCH_MENU_TOGGLE].joykey = 27; - g_settings.input.dpad_emulation[port] = ANALOG_DPAD_NONE; - break; - case DEVICE_SIXAXIS: - strlcpy(g_settings.input.device_names[port], "SixAxis/DualShock3", - sizeof(g_settings.input.device_names[port])); - g_settings.input.device[port] = device; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_B].joykey = 14; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_Y].joykey = 15; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_SELECT].joykey = 0; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_START].joykey = 3; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_UP].joykey = 4; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_DOWN].joykey = 6; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_LEFT].joykey = 7; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_RIGHT].joykey = 5; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_A].joykey = 13; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_X].joykey = 12; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L].joykey = 10; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R].joykey = 11; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L2].joykey = 8; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R2].joykey = 9; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_L3].joykey = 1; - g_settings.input.binds[port][RETRO_DEVICE_ID_JOYPAD_R3].joykey = 2; - g_settings.input.binds[port][RARCH_MENU_TOGGLE].joykey = 16; - g_settings.input.dpad_emulation[port] = ANALOG_DPAD_NONE; - break; - } - } -} - -const input_driver_t input_ios = { - ios_input_init, - ios_input_poll, - ios_input_state, - ios_bind_button_pressed, - ios_input_free_input, - ios_input_set_keybinds, - "ios_input", -}; +}; \ No newline at end of file diff --git a/ios/RetroArch/input/ios_input.h b/ios/RetroArch/input/ios_input.h index 336a407f24..e21a9c2d57 100644 --- a/ios/RetroArch/input/ios_input.h +++ b/ios/RetroArch/input/ios_input.h @@ -19,6 +19,7 @@ // Input responder #define MAX_TOUCHES 16 #define MAX_KEYS 256 +#define MAX_PADS 4 typedef struct { @@ -34,16 +35,15 @@ typedef struct uint32_t keys[MAX_KEYS]; - uint32_t pad_buttons; - int16_t pad_axis[4]; + uint32_t pad_buttons[MAX_PADS]; + int16_t pad_axis[MAX_PADS][4]; } ios_input_data_t; -extern ios_input_data_t g_ios_input_data; +extern ios_input_data_t g_current_input_data; //< Main thread data +extern ios_input_data_t g_polled_input_data; //< Game thread data -// Defined in main.m, must be called on the emu thread in a dispatch_sync block -void ios_copy_input(ios_input_data_t* data); - -// Called from main.m, defined in ios_input.c -void ios_add_key_event(bool down, unsigned keycode, uint32_t character, uint16_t keyModifiers); +// Main thread only +void ios_input_enable_icade(bool on); +void ios_input_handle_key_event(unsigned keycode, bool down); #endif diff --git a/ios/RetroArch/input/ios_joypad.c b/ios/RetroArch/input/ios_joypad.c index 1f7110495d..16211912bf 100644 --- a/ios/RetroArch/input/ios_joypad.c +++ b/ios/RetroArch/input/ios_joypad.c @@ -41,7 +41,7 @@ static bool ios_joypad_button(unsigned port, uint16_t joykey) if (GET_HAT_DIR(joykey)) return false; else // Check the button - return (port == 0 && joykey < 32) ? (g_ios_input_data.pad_buttons & (1 << joykey)) != 0 : false; + return (port < MAX_PADS && joykey < 32) ? (g_polled_input_data.pad_buttons[port] & (1 << joykey)) != 0 : false; } static int16_t ios_joypad_axis(unsigned port, uint32_t joyaxis) @@ -52,12 +52,12 @@ static int16_t ios_joypad_axis(unsigned port, uint32_t joyaxis) int16_t val = 0; if (AXIS_NEG_GET(joyaxis) < 4) { - val = g_ios_input_data.pad_axis[AXIS_NEG_GET(joyaxis)]; + val = g_polled_input_data.pad_axis[AXIS_NEG_GET(joyaxis)]; val = (val < 0) ? val : 0; } else if(AXIS_POS_GET(joyaxis) < 4) { - val = g_ios_input_data.pad_axis[AXIS_POS_GET(joyaxis)]; + val = g_polled_input_data.pad_axis[AXIS_POS_GET(joyaxis)]; val = (val > 0) ? val : 0; } diff --git a/ios/RetroArch/main.m b/ios/RetroArch/main.m index 40e2ed5e41..0d77321973 100644 --- a/ios/RetroArch/main.m +++ b/ios/RetroArch/main.m @@ -13,7 +13,6 @@ * If not, see . */ -#include #include #include @@ -30,82 +29,29 @@ #include "file.h" //#define HAVE_DEBUG_FILELOG +static bool use_tv_mode; -static ios_input_data_t g_input_data; - -static bool enable_btstack; -static bool use_icade; -static uint32_t icade_buttons; - -bool path_make_and_check_directory(const char* path, mode_t mode, int amode) -{ - if (!path_is_directory(path) && mkdir(path, mode) != 0) - return false; - - return access(path, amode) == 0; -} - -// Input helpers -void ios_copy_input(ios_input_data_t* data) -{ - // Call only from main thread - - memcpy(data, &g_input_data, sizeof(g_input_data)); - data->pad_buttons = btpad_get_buttons() | (use_icade ? icade_buttons : 0); - - for (int i = 0; i < 4; i ++) - data->pad_axis[i] = btpad_get_axis(i); -} - +// Input helpers: This is kept here because it needs objective-c static void handle_touch_event(NSArray* touches) { const int numTouches = [touches count]; const float scale = [[UIScreen mainScreen] scale]; - g_input_data.touch_count = 0; + g_current_input_data.touch_count = 0; - for(int i = 0; i != numTouches && g_input_data.touch_count < MAX_TOUCHES; i ++) + for(int i = 0; i != numTouches && g_current_input_data.touch_count < MAX_TOUCHES; i ++) { UITouch* touch = [touches objectAtIndex:i]; const CGPoint coord = [touch locationInView:touch.view]; if (touch.phase != UITouchPhaseEnded && touch.phase != UITouchPhaseCancelled) { - g_input_data.touches[g_input_data.touch_count ].screen_x = coord.x * scale; - g_input_data.touches[g_input_data.touch_count ++].screen_y = coord.y * scale; + g_current_input_data.touches[g_current_input_data.touch_count ].screen_x = coord.x * scale; + g_current_input_data.touches[g_current_input_data.touch_count ++].screen_y = coord.y * scale; } } } -static void handle_icade_event(unsigned keycode) -{ - static const struct - { - bool up; - int button; - } icade_map[0x20] = - { - { false, -1 }, { false, -1 }, { false, -1 }, { false, -1 }, // 0 - { false, 2 }, { false, -1 }, { true , 3 }, { false, 3 }, // 4 - { true , 0 }, { true, 5 }, { true , 7 }, { false, 8 }, // 8 - { false, 6 }, { false, 9 }, { false, 10 }, { false, 11 }, // C - { true , 6 }, { true , 9 }, { false, 7 }, { true, 10 }, // 0 - { true , 2 }, { true , 8 }, { false, -1 }, { true , 4 }, // 4 - { false, 5 }, { true , 11 }, { false, 0 }, { false, 1 }, // 8 - { false, 4 }, { true , 1 }, { false, -1 }, { false, -1 } // C - }; - - if ((keycode < 0x20) && (icade_map[keycode].button >= 0)) - { - const int button = icade_map[keycode].button; - - if (icade_map[keycode].up) - icade_buttons &= ~(1 << button); - else - icade_buttons |= (1 << button); - } -} - @interface RApplication : UIApplication @end @@ -127,14 +73,7 @@ static void handle_icade_event(unsigned keycode) int eventType = eventMem ? *(int*)&eventMem[8] : 0; if (eventType == GSEVENT_TYPE_KEYDOWN || eventType == GSEVENT_TYPE_KEYUP) - { - uint16_t key = *(uint16_t*)&eventMem[0x3C]; - - if (!use_icade && key < MAX_KEYS) - g_input_data.keys[key] = (eventType == GSEVENT_TYPE_KEYDOWN); - else if (eventType == GSEVENT_TYPE_KEYDOWN) - handle_icade_event(key); - } + ios_input_handle_key_event(*(uint16_t*)&eventMem[0x3C], eventType == GSEVENT_TYPE_KEYDOWN); CFBridgingRelease(eventMem); } @@ -160,19 +99,18 @@ int main(int argc, char *argv[]) extern void* rarch_main_ios(void* args); extern void ios_frontend_post_event(void (*fn)(void*), void* userdata); -static void event_game_reset(void* userdata) -{ - rarch_game_reset(); -} -static void event_load_state(void* userdata) +// These are based on the tag property of the button used to trigger the event +enum basic_event_t { RESET = 1, LOAD_STATE = 2, SAVE_STATE = 3, QUIT = 4 }; +static void event_basic_command(void* userdata) { - rarch_load_state(); -} - -static void event_save_state(void* userdata) -{ - rarch_save_state(); + switch ((enum basic_event_t)userdata) + { + case RESET: rarch_game_reset(); return; + case LOAD_STATE: rarch_load_state(); return; + case SAVE_STATE: rarch_save_state(); return; + case QUIT: g_extern.system.shutdown = true; return; + } } static void event_set_state_slot(void* userdata) @@ -182,31 +120,16 @@ static void event_set_state_slot(void* userdata) static void event_show_rgui(void* userdata) { - if (g_extern.lifecycle_mode_state & (1ULL << MODE_MENU)) - { - g_extern.lifecycle_mode_state &= ~(1ULL << MODE_MENU); - g_extern.lifecycle_mode_state |= (1ULL << MODE_GAME); - } - else - { - g_extern.lifecycle_mode_state &= ~(1ULL << MODE_GAME); - g_extern.lifecycle_mode_state |= (1ULL << MODE_MENU); - } -} - -static void event_quit(void* userdata) -{ - g_extern.system.shutdown = true; + const bool in_menu = g_extern.lifecycle_mode_state & (1 << MODE_MENU); + g_extern.lifecycle_mode_state &= ~(1ULL << (in_menu ? MODE_MENU : MODE_GAME)); + g_extern.lifecycle_mode_state |= (1ULL << (in_menu ? MODE_GAME : MODE_MENU)); } static void event_reload_config(void* userdata) { - // Need to clear these otherwise stale versions may be used! - memset(g_settings.input.overlay, 0, sizeof(g_settings.input.overlay)); - memset(g_settings.video.shader_path, 0, sizeof(g_settings.video.shader_path)); + ios_clear_config_hack(); uninit_drivers(); - g_extern.block_config_read = false; config_load(); init_drivers(); } @@ -226,21 +149,6 @@ static void event_reload_config(void* userdata) RAModuleInfo* _module; } -+ (void)displayErrorMessage:(NSString*)message -{ - [RetroArch_iOS displayErrorMessage:message withTitle:@"RetroArch"]; -} - -+ (void)displayErrorMessage:(NSString*)message withTitle:(NSString*)title -{ - UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title - message:message - delegate:nil - cancelButtonTitle:@"OK" - otherButtonTitles:nil]; - [alert show]; -} - + (RetroArch_iOS*)get { return (RetroArch_iOS*)[[UIApplication sharedApplication] delegate]; @@ -249,10 +157,6 @@ static void event_reload_config(void* userdata) // UIApplicationDelegate - (void)applicationDidFinishLaunching:(UIApplication *)application { -#ifdef HAVE_DEBUG_DIAGLOG - ios_log_init(); -#endif - self.delegate = self; // Setup window @@ -266,18 +170,21 @@ static void event_reload_config(void* userdata) self.systemConfigPath = [self.systemDirectory stringByAppendingPathComponent:@"frontend.cfg"]; if (!path_make_and_check_directory(self.documentsDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) - [RetroArch_iOS displayErrorMessage:[NSString stringWithFormat:@"Failed to create or access base directory: %@", self.documentsDirectory]]; + ios_display_alert([NSString stringWithFormat:@"Failed to create or access base directory: %@", self.documentsDirectory], 0); else if (!path_make_and_check_directory(self.systemDirectory.UTF8String, 0755, R_OK | W_OK | X_OK)) - [RetroArch_iOS displayErrorMessage:[NSString stringWithFormat:@"Failed to create or access system directory: %@", self.systemDirectory]]; + ios_display_alert([NSString stringWithFormat:@"Failed to create or access system directory: %@", self.systemDirectory], 0); else { [self pushViewController:[RADirectoryList directoryListAtBrowseRoot] animated:YES]; [self refreshSystemConfig]; + + if (use_tv_mode) + [self runGame:nil withModule:nil]; } // Warn if there are no cores present if ([RAModuleInfo getModules].count == 0) - [RetroArch_iOS displayErrorMessage:@"No libretro cores were found. You will not be able to play any games."]; + ios_display_alert(@"No libretro cores were found. You will not be able to play any games.", 0); } - (void)applicationWillEnterForeground:(UIApplication *)application @@ -351,29 +258,36 @@ static void event_reload_config(void* userdata) { if (!_isRunning) { - _module = module; - - [RASettingsList refreshModuleConfig:_module]; - [self pushViewController:RAGameView.get animated:NO]; _isRunning = true; + _module = module; + [RASettingsList refreshModuleConfig:_module]; + + btpad_set_inquiry_state(false); + struct rarch_main_wrap* load_data = malloc(sizeof(struct rarch_main_wrap)); memset(load_data, 0, sizeof(struct rarch_main_wrap)); - load_data->libretro_path = strdup(_module.path.UTF8String); - load_data->rom_path = strdup(path.UTF8String); + load_data->sram_path = strdup(self.systemDirectory.UTF8String); load_data->state_path = strdup(self.systemDirectory.UTF8String); - load_data->config_path = strdup(_module.configPath.UTF8String); - load_data->verbose = false; + if (path && module) + { + load_data->libretro_path = strdup(_module.path.UTF8String); + load_data->rom_path = strdup(path.UTF8String); + load_data->config_path = strdup(_module.configPath.UTF8String); + } + else + load_data->config_path = strdup(RAModuleInfo.globalConfigPath.UTF8String); + if (pthread_create(&_retroThread, 0, rarch_main_ios, load_data)) { [self rarchExited:NO]; return; } + pthread_detach(_retroThread); - [self refreshSystemConfig]; } } @@ -381,7 +295,7 @@ static void event_reload_config(void* userdata) - (void)rarchExited:(BOOL)successful { if (!successful) - [RetroArch_iOS displayErrorMessage:@"Failed to load game."]; + ios_display_alert(@"Failed to load game.", 0); if (_isRunning) { @@ -390,7 +304,12 @@ static void event_reload_config(void* userdata) // [self popToViewController:[RAGameView get] animated:NO]; [self popViewControllerAnimated:NO]; + + btpad_set_inquiry_state(true); } + + if (use_tv_mode) + [self runGame:nil withModule:nil]; _module = nil; } @@ -400,11 +319,7 @@ static void event_reload_config(void* userdata) if (_isRunning) ios_frontend_post_event(&event_reload_config, 0); else - { - // Need to clear these otherwise stale versions may be used! - memset(g_settings.input.overlay, 0, sizeof(g_settings.input.overlay)); - memset(g_settings.video.shader_path, 0, sizeof(g_settings.video.shader_path)); - } + ios_clear_config_hack(); } - (void)refreshSystemConfig @@ -435,13 +350,10 @@ static void event_reload_config(void* userdata) } // - config_get_bool(conf, "ios_use_icade", &use_icade); - config_get_bool(conf, "ios_use_btstack", &enable_btstack); - - if (enable_btstack) - [self startBluetooth]; - else - [self stopBluetooth]; + bool val; + ios_input_enable_icade(config_get_bool(conf, "ios_use_icade", &val) && val); + btstack_set_poweron(config_get_bool(conf, "ios_use_btstack", &val) && val); + use_tv_mode = config_get_bool(conf, "ios_tv_mode", & val) && val; config_file_free(conf); } @@ -467,33 +379,19 @@ static void event_reload_config(void* userdata) { _isPaused = true; [[RAGameView get] openPauseMenu]; + + btpad_set_inquiry_state(true); } } -- (IBAction)resetGame:(id)sender +- (IBAction)basicEvent:(id)sender { if (_isRunning) - ios_frontend_post_event(&event_game_reset, 0); + ios_frontend_post_event(&event_basic_command, ((UIView*)sender).tag); [self closePauseMenu:sender]; } -- (IBAction)loadState:(id)sender -{ - if (_isRunning) - ios_frontend_post_event(&event_load_state, 0); - - [self closePauseMenu:sender]; -} - -- (IBAction)saveState:(id)sender -{ - if (_isRunning) - ios_frontend_post_event(&event_save_state, 0); - - [self closePauseMenu:sender]; -} - - (IBAction)chooseState:(id)sender { if (_isRunning) @@ -512,18 +410,13 @@ static void event_reload_config(void* userdata) { [[RAGameView get] closePauseMenu]; _isPaused = false; -} - -- (IBAction)closeGamePressed:(id)sender -{ - [self closePauseMenu:sender]; - ios_frontend_post_event(event_quit, 0); + + btpad_set_inquiry_state(false); } - (IBAction)showSettings { - if (_module) - [self pushViewController:[[RASettingsList alloc] initWithModule:_module] animated:YES]; + [self pushViewController:[[RASettingsList alloc] initWithModule:_module] animated:YES]; } - (IBAction)showSystemSettings @@ -531,19 +424,6 @@ static void event_reload_config(void* userdata) [self pushViewController:[RASystemSettingsList new] animated:YES]; } -#pragma mark Bluetooth Helpers -- (IBAction)startBluetooth -{ - if (btstack_is_loaded() && !btstack_is_running()) - btstack_start(); -} - -- (IBAction)stopBluetooth -{ - if (btstack_is_loaded()) - btstack_stop(); -} - @end void ios_rarch_exited(void* result) diff --git a/ios/RetroArch/rarch_wrapper.h b/ios/RetroArch/rarch_wrapper.h index 67f7f894f5..be061a16ef 100644 --- a/ios/RetroArch/rarch_wrapper.h +++ b/ios/RetroArch/rarch_wrapper.h @@ -30,13 +30,6 @@ void ios_set_game_view_sync(unsigned interval); void ios_get_game_view_size(unsigned *width, unsigned *height); void ios_bind_game_view_fbo(); -#ifndef HAVE_DEBUG_DIAGLOG void ios_add_log_message(const char* format, ...); -#else -void ios_log_init(); -void ios_log_quit(); -// Thread safe -#define ios_add_log_message(...) do { printf(__VA_ARGS__); printf("\n"); } while(0) -#endif #endif diff --git a/ios/RetroArch/settings.m b/ios/RetroArch/settings.m index 8e654f21be..eca0244a18 100644 --- a/ios/RetroArch/settings.m +++ b/ios/RetroArch/settings.m @@ -41,6 +41,8 @@ enum SettingTypes @property double rangeMin; @property double rangeMax; +@property uint32_t player; + @property bool haveNoneOption; - (id)initWithType:(enum SettingTypes)aType label:(NSString*)aLabel name:(NSString*)aName; @@ -73,14 +75,17 @@ static RASettingData* boolean_setting(config_file_t* config, NSString* name, NSS return result; } -static RASettingData* button_setting(config_file_t* config, NSString* name, NSString* label, NSString* defaultValue) +static RASettingData* button_setting(config_file_t* config, uint32_t player, NSString* name, NSString* label, NSString* defaultValue) { - RASettingData* result = [[RASettingData alloc] initWithType:ButtonSetting label:label name:name]; + NSString* realname = player ? [NSString stringWithFormat:@"input_player%d_%@", player, name] : name; + + RASettingData* result = [[RASettingData alloc] initWithType:ButtonSetting label:label name:realname]; result.msubValues = [NSMutableArray arrayWithObjects: - ios_get_value_from_config(config, name, defaultValue), - ios_get_value_from_config(config, [name stringByAppendingString:@"_btn"], @"nul"), - ios_get_value_from_config(config, [name stringByAppendingString:@"_axis"], @"nul"), + ios_get_value_from_config(config, realname, defaultValue), + ios_get_value_from_config(config, [realname stringByAppendingString:@"_btn"], @"nul"), + ios_get_value_from_config(config, [realname stringByAppendingString:@"_axis"], @"nul"), nil]; + result.player = player ? player - 1 : 0; return result; } @@ -163,6 +168,42 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) return result; } +static NSArray* build_input_port_group(config_file_t* config, uint32_t player) +{ + return [NSArray arrayWithObjects: + [NSArray arrayWithObjects:[NSString stringWithFormat:@"Player %d", player], + button_setting(config, player, @"up", @"Up", @"up"), + button_setting(config, player, @"down", @"Down", @"down"), + button_setting(config, player, @"left", @"Left", @"left"), + button_setting(config, player, @"right", @"Right", @"right"), + + button_setting(config, player, @"start", @"Start", @"enter"), + button_setting(config, player, @"select", @"Select", @"rshift"), + + button_setting(config, player, @"b", @"B", @"z"), + button_setting(config, player, @"a", @"A", @"x"), + button_setting(config, player, @"x", @"X", @"s"), + button_setting(config, player, @"y", @"Y", @"a"), + + button_setting(config, player, @"l", @"L", @"q"), + button_setting(config, player, @"r", @"R", @"w"), + button_setting(config, player, @"l2", @"L2", @"nul"), + button_setting(config, player, @"r2", @"R2", @"nul"), + button_setting(config, player, @"l3", @"L3", @"nul"), + button_setting(config, player, @"r3", @"R3", @"nul"), + + button_setting(config, player, @"l_y_minus", @"Left Stick Up", @"nul"), + button_setting(config, player, @"l_y_plus", @"Left Stick Down", @"nul"), + button_setting(config, player, @"l_x_minus", @"Left Stick Left", @"nul"), + button_setting(config, player, @"l_x_plus", @"Left Stick Right", @"nul"), + button_setting(config, player, @"r_y_minus", @"Right Stick Up", @"nul"), + button_setting(config, player, @"r_y_plus", @"Right Stick Down", @"nul"), + button_setting(config, player, @"r_x_minus", @"Right Stick Left", @"nul"), + button_setting(config, player, @"r_x_plus", @"Right Stick Right", @"nul"), + nil], + nil]; +} + @implementation RASettingsList { RAModuleInfo* _module; @@ -212,57 +253,29 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) [NSArray arrayWithObjects:@"Input", subpath_setting(config, @"input_overlay", @"Input Overlay", @"", overlay_path, @"cfg"), range_setting(config, @"input_overlay_opacity", @"Overlay Opacity", @"1.0", 0.0, 1.0), - group_setting(@"Player 1 Keys", [NSArray arrayWithObjects: - [NSArray arrayWithObjects:@"Player 1", - button_setting(config, @"input_player1_up", @"Up", @"up"), - button_setting(config, @"input_player1_down", @"Down", @"down"), - button_setting(config, @"input_player1_left", @"Left", @"left"), - button_setting(config, @"input_player1_right", @"Right", @"right"), - - button_setting(config, @"input_player1_start", @"Start", @"enter"), - button_setting(config, @"input_player1_select", @"Select", @"rshift"), - - button_setting(config, @"input_player1_b", @"B", @"z"), - button_setting(config, @"input_player1_a", @"A", @"x"), - button_setting(config, @"input_player1_x", @"X", @"s"), - button_setting(config, @"input_player1_y", @"Y", @"a"), - - button_setting(config, @"input_player1_l", @"L", @"q"), - button_setting(config, @"input_player1_r", @"R", @"w"), - button_setting(config, @"input_player1_l2", @"L2", @"nul"), - button_setting(config, @"input_player1_r2", @"R2", @"nul"), - button_setting(config, @"input_player1_l3", @"L3", @"nul"), - button_setting(config, @"input_player1_r3", @"R3", @"nul"), - - button_setting(config, @"input_player1_l_y_minus", @"Left Stick Up", @"nul"), - button_setting(config, @"input_player1_l_y_plus", @"Left Stick Down", @"nul"), - button_setting(config, @"input_player1_l_x_minus", @"Left Stick Left", @"nul"), - button_setting(config, @"input_player1_l_x_plus", @"Left Stick Right", @"nul"), - button_setting(config, @"input_player1_r_y_minus", @"Right Stick Up", @"nul"), - button_setting(config, @"input_player1_r_y_plus", @"Right Stick Down", @"nul"), - button_setting(config, @"input_player1_r_x_minus", @"Right Stick Left", @"nul"), - button_setting(config, @"input_player1_r_x_plus", @"Right Stick Right", @"nul"), - nil], - nil]), group_setting(@"System Keys", [NSArray arrayWithObjects: // TODO: Many of these strings will be cut off on an iPhone [NSArray arrayWithObjects:@"System Keys", - button_setting(config, @"input_menu_toggle", @"Show RGUI", @"f1"), - button_setting(config, @"input_disk_eject_toggle", @"Insert/Eject Disk", @"nul"), - button_setting(config, @"input_disk_next", @"Cycle Disks", @"nul"), - button_setting(config, @"input_save_state", @"Save State", @"f2"), - button_setting(config, @"input_load_state", @"Load State", @"f4"), - button_setting(config, @"input_state_slot_increase", @"Next State Slot", @"f7"), - button_setting(config, @"input_state_slot_decrease", @"Previous State Slot", @"f6"), - button_setting(config, @"input_toggle_fast_forward", @"Toggle Fast Forward", @"space"), - button_setting(config, @"input_hold_fast_forward", @"Hold Fast Forward", @"l"), - button_setting(config, @"input_rewind", @"Rewind", @"r"), - button_setting(config, @"input_slowmotion", @"Slow Motion", @"e"), - button_setting(config, @"input_reset", @"Reset", @"h"), - button_setting(config, @"input_exit_emulator", @"Close Game", @"escape"), - button_setting(config, @"input_enable_hotkey", @"Hotkey Enable (Always on if not set)", @"nul"), + button_setting(config, 0, @"input_menu_toggle", @"Show RGUI", @"f1"), + button_setting(config, 0, @"input_disk_eject_toggle", @"Insert/Eject Disk", @"nul"), + button_setting(config, 0, @"input_disk_next", @"Cycle Disks", @"nul"), + button_setting(config, 0, @"input_save_state", @"Save State", @"f2"), + button_setting(config, 0, @"input_load_state", @"Load State", @"f4"), + button_setting(config, 0, @"input_state_slot_increase", @"Next State Slot", @"f7"), + button_setting(config, 0, @"input_state_slot_decrease", @"Previous State Slot", @"f6"), + button_setting(config, 0, @"input_toggle_fast_forward", @"Toggle Fast Forward", @"space"), + button_setting(config, 0, @"input_hold_fast_forward", @"Hold Fast Forward", @"l"), + button_setting(config, 0, @"input_rewind", @"Rewind", @"r"), + button_setting(config, 0, @"input_slowmotion", @"Slow Motion", @"e"), + button_setting(config, 0, @"input_reset", @"Reset", @"h"), + button_setting(config, 0, @"input_exit_emulator", @"Close Game", @"escape"), + button_setting(config, 0, @"input_enable_hotkey", @"Hotkey Enable (Always on if not set)", @"nul"), nil], nil]), + group_setting(@"Player 1 Keys", build_input_port_group(config, 1)), + group_setting(@"Player 2 Keys", build_input_port_group(config, 2)), + group_setting(@"Player 3 Keys", build_input_port_group(config, 3)), + group_setting(@"Player 4 Keys", build_input_port_group(config, 4)), nil], [NSArray arrayWithObjects:@"Save States", @@ -334,11 +347,12 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) NSArray* settings = [NSArray arrayWithObjects: [NSArray arrayWithObjects:@"Frontend", custom_action(@"Diagnostic Log", nil, nil), + boolean_setting(config, @"ios_tv_mode", @"TV Mode", @"false"), nil], [NSArray arrayWithObjects:@"Bluetooth", // TODO: Note that with this turned off the native bluetooth is expected to be a real keyboard boolean_setting(config, @"ios_use_icade", @"Native BT is iCade", @"false"), - btstack_is_loaded() ? boolean_setting(config, @"ios_use_btstack", @"Enable BTstack", @"false") : nil, + btstack_try_load() ? boolean_setting(config, @"ios_use_btstack", @"Enable BTstack", @"false") : nil, nil], [NSArray arrayWithObjects:@"Orientations", boolean_setting(config, @"ios_allow_portrait", @"Portrait", @"true"), @@ -379,16 +393,9 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) if ([@"Diagnostic Log" isEqualToString:setting.label]) [[RetroArch_iOS get] pushViewController:[RALogView new] animated:YES]; else if ([@"Enable BTstack" isEqualToString:setting.label]) - { - if ([@"true" isEqualToString:setting.value]) - [RetroArch_iOS.get startBluetooth]; - else - [RetroArch_iOS.get stopBluetooth]; - } + btstack_set_poweron([setting.value isEqualToString:@"true"]); else if([@"Global Core Config" isEqualToString:setting.label]) - { [RetroArch_iOS.get pushViewController:[[RASettingsList alloc] initWithModule:nil] animated:YES]; - } else { RAModuleInfo* data = (RAModuleInfo*)objc_getAssociatedObject(setting, "USERDATA"); @@ -741,9 +748,6 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) - (void)checkInput { - ios_input_data_t data; - ios_copy_input(&data); - // Keyboard static const struct { @@ -802,7 +806,7 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) for (int i = 0; ios_key_name_map[i].hid_id; i++) { - if (data.keys[ios_key_name_map[i].hid_id]) + if (g_current_input_data.keys[ios_key_name_map[i].hid_id]) { _value.msubValues[0] = [NSString stringWithUTF8String:ios_key_name_map[i].keyname]; [self finish]; @@ -811,9 +815,9 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) } // Pad Buttons - for (int i = 0; data.pad_buttons && i < sizeof(data.pad_buttons) * 8; i++) + for (int i = 0; g_current_input_data.pad_buttons[_value.player] && i < sizeof(g_current_input_data.pad_buttons[_value.player]) * 8; i++) { - if (data.pad_buttons & (1 << i)) + if (g_current_input_data.pad_buttons[_value.player] & (1 << i)) { _value.msubValues[1] = [NSString stringWithFormat:@"%d", i]; [self finish]; @@ -824,7 +828,7 @@ static RASettingData* custom_action(NSString* action, NSString* value, id data) // Pad Axis for (int i = 0; i < 4; i++) { - int16_t value = data.pad_axis[i]; + int16_t value = g_current_input_data.pad_axis[_value.player][i]; if (abs(value) > 0x1000) { diff --git a/ios/RetroArch/utility.m b/ios/RetroArch/utility.m index 8d51a5d33b..0967605606 100644 --- a/ios/RetroArch/utility.m +++ b/ios/RetroArch/utility.m @@ -13,8 +13,30 @@ * If not, see . */ +#include + +#include "general.h" +#include "file.h" #import "views.h" +void ios_display_alert(NSString* message, NSString* title) +{ + UIAlertView* alert = [[UIAlertView alloc] initWithTitle:title ? title : @"RetroArch" + message:message + delegate:nil + cancelButtonTitle:@"OK" + otherButtonTitles:nil]; + [alert show]; +} + +// Little nudge to prevent stale values when reloading the confg file +void ios_clear_config_hack() +{ + g_extern.block_config_read = false; + memset(g_settings.input.overlay, 0, sizeof(g_settings.input.overlay)); + memset(g_settings.video.shader_path, 0, sizeof(g_settings.video.shader_path)); +} + // Fetch a value from a config file, returning defaultValue if the value is not present NSString* ios_get_value_from_config(config_file_t* config, NSString* name, NSString* defaultValue) { @@ -27,6 +49,15 @@ NSString* ios_get_value_from_config(config_file_t* config, NSString* name, NSStr return result; } +// Ensures a directory exists and has correct permissions +bool path_make_and_check_directory(const char* path, mode_t mode, int amode) +{ + if (!path_is_directory(path) && mkdir(path, mode) != 0) + return false; + + return access(path, amode) == 0; +} + // Simple class to reduce code duplication for fixed table views @implementation RATableViewController