diff --git a/Makefile.common b/Makefile.common index f3cfaf753a..2fdc341072 100644 --- a/Makefile.common +++ b/Makefile.common @@ -1086,8 +1086,10 @@ ifeq ($(HAVE_NETWORKING), 1) OBJ += $(LIBRETRO_COMM_DIR)/net/net_compat.o \ $(LIBRETRO_COMM_DIR)/net/net_http.o \ $(LIBRETRO_COMM_DIR)/net/net_socket.o \ + $(LIBRETRO_COMM_DIR)/net/net_natt.o \ network/net_http_special.o \ - tasks/task_http.o + tasks/task_http.o \ + tasks/task_wifi.o ifneq ($(HAVE_SOCKET_LEGACY),1) OBJ += $(LIBRETRO_COMM_DIR)/net/net_ifinfo.o @@ -1123,6 +1125,10 @@ ifeq ($(HAVE_NETWORKING), 1) OBJ += input/input_remote.o \ cores/libretro-net-retropad/net_retropad_core.o endif + + ifeq ($(HAVE_MINIUPNPC), 1) + LIBS += -lminiupnpc + endif endif ifneq ($(findstring Win32,$(OS)),) diff --git a/config.def.h b/config.def.h index 9afa317c8c..78cb4e3ee7 100644 --- a/config.def.h +++ b/config.def.h @@ -437,7 +437,7 @@ static const bool load_dummy_on_core_shutdown = false; #else static const bool load_dummy_on_core_shutdown = true; #endif -static const bool check_firmware_before_loading = true; +static const bool check_firmware_before_loading = false; /* Forcibly disable composition. * Only valid on Windows Vista/7/8 for now. */ static const bool disable_composition = false; diff --git a/configuration.c b/configuration.c index c727ed3dd5..9284821974 100644 --- a/configuration.c +++ b/configuration.c @@ -800,6 +800,7 @@ static int populate_settings_bool(settings_t *settings, struct config_bool_setti #endif #ifdef HAVE_NETWORKING SETTING_BOOL("netplay_spectator_mode_enable",&settings->netplay.is_spectate, false, false /* TODO */, false); + SETTING_BOOL("netplay_nat_traversal", &settings->netplay.nat_traversal, true, true, false); #endif SETTING_BOOL("block_sram_overwrite", &settings->block_sram_overwrite, true, block_sram_overwrite, false); SETTING_BOOL("savestate_auto_index", &settings->savestate_auto_index, true, savestate_auto_index, false); @@ -922,7 +923,7 @@ static int populate_settings_int(settings_t *settings, struct config_int_setting #ifdef HAVE_NETWORKING SETTING_INT("netplay_ip_port", &settings->netplay.port, false, 0 /* TODO */, false); SETTING_INT("netplay_delay_frames", &settings->netplay.sync_frames, true, 16, false); - SETTING_INT("netplay_check_frames", &settings->netplay.check_frames, false, 30, false); + SETTING_INT("netplay_check_frames", &settings->netplay.check_frames, true, 30, false); #endif #ifdef HAVE_LANGEXTRA SETTING_INT("user_language", &settings->user_language, true, RETRO_LANGUAGE_ENGLISH, false); diff --git a/configuration.h b/configuration.h index fc52188ea4..861143a3dd 100644 --- a/configuration.h +++ b/configuration.h @@ -403,6 +403,7 @@ typedef struct settings unsigned check_frames; bool is_spectate; bool swap_input; + bool nat_traversal; } netplay; #endif diff --git a/griffin/griffin.c b/griffin/griffin.c index b57420f4a5..ab734ccd0d 100644 --- a/griffin/griffin.c +++ b/griffin/griffin.c @@ -879,10 +879,12 @@ NETPLAY #include "../libretro-common/net/net_compat.c" #include "../libretro-common/net/net_socket.c" #include "../libretro-common/net/net_http.c" +#include "../libretro-common/net/net_natt.c" #ifndef HAVE_SOCKET_LEGACY #include "../libretro-common/net/net_ifinfo.c" #endif #include "../tasks/task_http.c" +#include "../tasks/task_wifi.c" #endif /*============================================================ diff --git a/input/drivers_joypad/udev_joypad.c b/input/drivers_joypad/udev_joypad.c index bac6829b8d..c576f47a9b 100644 --- a/input/drivers_joypad/udev_joypad.c +++ b/input/drivers_joypad/udev_joypad.c @@ -160,15 +160,13 @@ static void udev_poll_pad(struct udev_joypad *pad, unsigned p) } } -static bool udev_hotplug_available(void) +static INLINE bool udev_hotplug_available(void) { - struct pollfd fds = {0}; + struct pollfd fds; - if (!g_udev_mon) - return false; - - fds.fd = udev_monitor_get_fd(g_udev_mon); - fds.events = POLLIN; + fds.fd = udev_monitor_get_fd(g_udev_mon); + fds.events = POLLIN; + fds.revents = 0; return (poll(&fds, 1, 0) == 1) && (fds.revents & POLLIN); } @@ -420,18 +418,11 @@ static void udev_joypad_destroy(void) g_udev = NULL; } -static void udev_joypad_handle_hotplug(void) +static void udev_joypad_handle_hotplug(struct udev_device *dev) { - struct udev_device *dev = udev_monitor_receive_device(g_udev_mon); - const char *val; - const char *action; - const char *devnode; - if (!dev) - return; - - val = udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); - action = udev_device_get_action(dev); - devnode = udev_device_get_devnode(dev); + const char *val = udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); + const char *action = udev_device_get_action(dev); + const char *devnode = udev_device_get_devnode(dev); if (!val || !string_is_equal(val, "1") || !devnode) goto end; @@ -523,8 +514,13 @@ static bool udev_set_rumble(unsigned i, enum retro_rumble_effect effect, uint16_ static void udev_joypad_poll(void) { unsigned i; - while (udev_hotplug_available()) - udev_joypad_handle_hotplug(); + + while (g_udev_mon && udev_hotplug_available()) + { + struct udev_device *dev = udev_monitor_receive_device(g_udev_mon); + if (dev) + udev_joypad_handle_hotplug(dev); + } for (i = 0; i < MAX_USERS; i++) udev_poll_pad(&udev_pads[i], i); diff --git a/input/input_autodetect.c b/input/input_autodetect.c index 85a3bfa6b1..43cede3528 100644 --- a/input/input_autodetect.c +++ b/input/input_autodetect.c @@ -322,19 +322,6 @@ error: return false; } -const struct retro_keybind *input_get_auto_bind(unsigned port, unsigned id) -{ - settings_t *settings = config_get_ptr(); - unsigned joy_idx = 0; - - if (settings) - joy_idx = settings->input.joypad_map[port]; - - if (joy_idx < MAX_USERS) - return &settings->input.autoconf_binds[joy_idx][id]; - return NULL; -} - void input_config_autoconfigure_disconnect(unsigned i, const char *ident) { char msg[255]; diff --git a/input/input_autodetect.h b/input/input_autodetect.h index 542e525219..6e6bb9b912 100644 --- a/input/input_autodetect.h +++ b/input/input_autodetect.h @@ -32,9 +32,6 @@ typedef struct autoconfig_params int32_t pid; } autoconfig_params_t; -const struct retro_keybind *input_get_auto_bind(unsigned port, - unsigned id); - bool input_config_autoconfigure_joypad(autoconfig_params_t *params); void input_config_autoconfigure_disconnect(unsigned i, const char *ident); diff --git a/input/input_config.c b/input/input_config.c index b8f713422e..1f7f6531fd 100644 --- a/input/input_config.c +++ b/input/input_config.c @@ -470,3 +470,16 @@ void input_config_get_bind_string(char *buf, const struct retro_keybind *bind, strlcat(buf, keybuf, size); #endif } + +const struct retro_keybind *input_config_get_bind_auto(unsigned port, unsigned id) +{ + settings_t *settings = config_get_ptr(); + unsigned joy_idx = 0; + + if (settings) + joy_idx = settings->input.joypad_map[port]; + + if (joy_idx < MAX_USERS) + return &settings->input.autoconf_binds[joy_idx][id]; + return NULL; +} diff --git a/input/input_config.h b/input/input_config.h index 8822f30d5a..79dd7f8acb 100644 --- a/input/input_config.h +++ b/input/input_config.h @@ -69,4 +69,6 @@ void input_config_parse_joy_button(config_file_t *conf, const char *prefix, void input_config_parse_joy_axis(config_file_t *conf, const char *prefix, const char *axis, struct retro_keybind *bind); +const struct retro_keybind *input_config_get_bind_auto(unsigned port, unsigned id); + #endif diff --git a/input/input_driver.c b/input/input_driver.c index 17db604604..733c35348e 100644 --- a/input/input_driver.c +++ b/input/input_driver.c @@ -524,31 +524,29 @@ int16_t input_state(unsigned port, unsigned device, /** * check_input_driver_block_hotkey: - * @enable_hotkey : Is hotkey enable key enabled? * * Checks if 'hotkey enable' key is pressed. **/ -static bool check_input_driver_block_hotkey(bool enable_hotkey) +static bool check_input_driver_block_hotkey(void) { - bool use_hotkey_enable = false; settings_t *settings = config_get_ptr(); const struct retro_keybind *bind = &settings->input.binds[0][RARCH_ENABLE_HOTKEY]; const struct retro_keybind *autoconf_bind = &settings->input.autoconf_binds[0][RARCH_ENABLE_HOTKEY]; - bool kb_mapping_is_blocked = current_input->keyboard_mapping_is_blocked && - current_input->keyboard_mapping_is_blocked(current_input_data); /* Don't block the check to RARCH_ENABLE_HOTKEY * unless we're really supposed to. */ - if (kb_mapping_is_blocked) + if (current_input->keyboard_mapping_is_blocked && + current_input->keyboard_mapping_is_blocked(current_input_data)) input_driver_block_hotkey = true; - else - input_driver_block_hotkey = false; /* If we haven't bound anything to this, * always allow hotkeys. */ - use_hotkey_enable = + + /* If we hold ENABLE_HOTKEY button, block all libretro input to allow + * hotkeys to be bound to same keys as RetroPad. */ + return (bind->key != RETROK_UNKNOWN) || (bind->joykey != NO_BTN) || (bind->joyaxis != AXIS_NONE) @@ -556,14 +554,6 @@ static bool check_input_driver_block_hotkey(bool enable_hotkey) || (autoconf_bind->joykey != NO_BTN) || (autoconf_bind->joyaxis != AXIS_NONE); - if (kb_mapping_is_blocked || (use_hotkey_enable && !enable_hotkey)) - input_driver_block_hotkey = true; - else - input_driver_block_hotkey = false; - - /* If we hold ENABLE_HOTKEY button, block all libretro input to allow - * hotkeys to be bound to same keys as RetroPad. */ - return (use_hotkey_enable && enable_hotkey); } static const unsigned buttons[] = { @@ -680,17 +670,21 @@ static INLINE bool input_keys_pressed_internal(unsigned i, uint64_t input_keys_pressed(void) { unsigned i; - uint64_t ret = 0; - settings_t *settings = config_get_ptr(); + uint64_t ret = 0; + settings_t *settings = config_get_ptr(); const struct retro_keybind *binds = settings->input.binds[0]; - if ( - check_input_driver_block_hotkey( - current_input->input_state(current_input_data, &binds, 0, - RETRO_DEVICE_JOYPAD, 0, RARCH_ENABLE_HOTKEY))) - input_driver_block_libretro_input = true; - else - input_driver_block_libretro_input = false; + input_driver_block_libretro_input = false; + input_driver_block_hotkey = false; + + if (check_input_driver_block_hotkey()) + { + if (current_input->input_state(current_input_data, &binds, 0, + RETRO_DEVICE_JOYPAD, 0, RARCH_ENABLE_HOTKEY)) + input_driver_block_libretro_input = true; + else + input_driver_block_hotkey = true; + } for (i = 0; i < RARCH_BIND_LIST_END; i++) { @@ -797,14 +791,17 @@ uint64_t input_menu_keys_pressed(void) input_push_analog_dpad(settings->input.autoconf_binds[i], ANALOG_DPAD_LSTICK); - if ( - check_input_driver_block_hotkey( - current_input->input_state(current_input_data, &binds[0], 0, - RETRO_DEVICE_JOYPAD, 0, RARCH_ENABLE_HOTKEY))) - input_driver_block_libretro_input = true; - else - input_driver_block_libretro_input = false; + input_driver_block_libretro_input = false; + input_driver_block_hotkey = false; + if (check_input_driver_block_hotkey()) + { + if (current_input->input_state(current_input_data, &binds[0], 0, + RETRO_DEVICE_JOYPAD, 0, RARCH_ENABLE_HOTKEY)) + input_driver_block_libretro_input = true; + else + input_driver_block_hotkey = true; + } for (i = 0; i < RARCH_BIND_LIST_END; i++) { diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index a553a2e312..ba73b123d8 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -624,6 +624,8 @@ MSG_HASH(MENU_ENUM_LABEL_NO_HISTORY_AVAILABLE, "no_history") MSG_HASH(MENU_ENUM_LABEL_NO_ITEMS, "no_items") +MSG_HASH(MENU_ENUM_LABEL_NO_NETWORKS_FOUND, + "no_networks_found") MSG_HASH(MENU_ENUM_LABEL_NO_PERFORMANCE_COUNTERS, "no_performance_counters") MSG_HASH(MENU_ENUM_LABEL_NO_PLAYLISTS, diff --git a/intl/msg_hash_us.c b/intl/msg_hash_us.c index 1379a72a7d..eb815ad7e9 100644 --- a/intl/msg_hash_us.c +++ b/intl/msg_hash_us.c @@ -1558,6 +1558,12 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) "no checks. This value is only used on the \n" "netplay host. \n"); break; + case MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL: + snprintf(s, len, + "When hosting, attempt to listen for\n" + "connections from the public internet, using\n" + "UPnP or similar technologies to escape LANs. \n"); + break; case MENU_ENUM_LABEL_VIDEO_MAX_SWAPCHAIN_IMAGES: snprintf(s, len, "Maximum amount of swapchain images. This \n" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 41c5878965..1315f26510 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -22,6 +22,10 @@ MSG_HASH( MSG_GOT_CONNECTION_FROM, "Got connection from" ) +MSG_HASH( + MSG_PUBLIC_ADDRESS, + "Public address" + ) MSG_HASH( MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN, "No arguments supplied and no menu builtin, displaying help..." @@ -922,6 +926,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATOR_MODE_ENABLE, "Netplay Spectator Enable") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TCP_UDP_PORT, "Netplay TCP/UDP Port") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL, + "Netplay NAT Traversal") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_ENABLE, "Network Commands") MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT, @@ -958,6 +964,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE, "No information is available.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_ITEMS, "No items.") +MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND, + "No networks found.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PERFORMANCE_COUNTERS, "No performance counters.") MSG_HASH(MENU_ENUM_LABEL_VALUE_NO_PLAYLISTS, @@ -2233,3 +2241,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_EDGE_MAGAZINE_RATING, "Database - Filter : Edge Magazine Rating") MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST_ENTRY_DATABASE_INFO, "Database Info") +MSG_HASH(MSG_WIFI_SCAN_COMPLETE, + "Wi-Fi scan complete.") +MSG_HASH(MSG_SCANNING_WIRELESS_NETWORKS, + "Scanning wireless networks...") \ No newline at end of file diff --git a/libretro-common/include/net/net_natt.h b/libretro-common/include/net/net_natt.h new file mode 100644 index 0000000000..bb9c791f61 --- /dev/null +++ b/libretro-common/include/net/net_natt.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (net_natt.h). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _LIBRETRO_SDK_NET_NATT_H +#define _LIBRETRO_SDK_NET_NATT_H + +#include +#include + +struct natt_status { + /** nfds for select when checking for input */ + int nfds; + + /** The fdset to be selected upon to check for responses */ + fd_set fds; + + /** True if there might be a request outstanding */ + bool request_outstanding; + + /** True if we've resolved an external IPv4 address */ + bool have_inet4; + + /** External IPv4 address */ + struct sockaddr_in ext_inet4_addr; + + /** True if we've resolved an external IPv6 address */ + bool have_inet6; + +#ifdef AF_INET6 + /** External IPv6 address */ + struct sockaddr_in6 ext_inet6_addr; +#endif + + /** Internal status (currently unused) */ + void *internal; +}; + +/** + * Initialize global NAT traversal structures (must be called once to use other + * functions) */ +void natt_init(void); + +/** Initialize a NAT traversal status object */ +bool natt_new(struct natt_status *status); + +/** Free a NAT traversal status object */ +void natt_free(struct natt_status *status); + +/** + * Make a port forwarding request. This may finish immediately or just send a + * request to the network. */ +bool natt_open_port(struct natt_status *status, struct sockaddr *addr, + socklen_t addrlen, enum socket_protocol proto); + +/** + * Make a port forwarding request when only the port is known. Forwards any + * address it can find. */ +bool natt_open_port_any(struct natt_status *status, uint16_t port, + enum socket_protocol proto); + +/** Check for port forwarding responses */ +bool natt_read(struct natt_status *status); + +#endif diff --git a/libretro-common/net/net_ifinfo.c b/libretro-common/net/net_ifinfo.c index b119d207ce..5432f5a73d 100644 --- a/libretro-common/net/net_ifinfo.c +++ b/libretro-common/net/net_ifinfo.c @@ -65,7 +65,6 @@ void net_ifinfo_free(net_ifinfo_t *list) ptr->host = NULL; } free(list->entries); - free(list); } bool net_ifinfo_new(net_ifinfo_t *list) @@ -82,6 +81,8 @@ bool net_ifinfo_new(net_ifinfo_t *list) rv = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, adapter_addresses, &size); + memset(list, 0, sizeof(net_ifinfo_t)); + if (rv != ERROR_SUCCESS) goto error; @@ -122,6 +123,8 @@ bool net_ifinfo_new(net_ifinfo_t *list) struct ifaddrs *ifa = NULL; struct ifaddrs *ifaddr = NULL; + memset(list, 0, sizeof(net_ifinfo_t)); + if (getifaddrs(&ifaddr) == -1) goto error; diff --git a/libretro-common/net/net_natt.c b/libretro-common/net/net_natt.c new file mode 100644 index 0000000000..f66d81205c --- /dev/null +++ b/libretro-common/net/net_natt.c @@ -0,0 +1,223 @@ +/* Copyright (C) 2016 The RetroArch team + * + * --------------------------------------------------------------------------------------- + * The following license statement only applies to this file (net_natt.c). + * --------------------------------------------------------------------------------------- + * + * Permission is hereby granted, free of charge, + * to any person obtaining a copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#if HAVE_MINIUPNPC +#include +#include +#include + +static struct UPNPUrls urls; +static struct IGDdatas data; +#endif + +void natt_init(void) +{ +#if HAVE_MINIUPNPC + struct UPNPDev * devlist; + struct UPNPDev * dev; + char * descXML; + int descXMLsize = 0; + int upnperror = 0; + memset(&urls, 0, sizeof(struct UPNPUrls)); + memset(&data, 0, sizeof(struct IGDdatas)); +#if MINIUPNPC_API_VERSION < 16 + devlist = upnpDiscover(2000, NULL, NULL, 0, 0, &upnperror); +#else + devlist = upnpDiscover(2000, NULL, NULL, 0, 0, 2, &upnperror); +#endif + if (devlist) + { + dev = devlist; + while (dev) + { + if (strstr (dev->st, "InternetGatewayDevice")) + break; + dev = dev->pNext; + } + if (!dev) + dev = devlist; + +#if MINIUPNPC_API_VERSION < 16 + descXML = (char *) miniwget(dev->descURL, &descXMLsize, 0); +#else + descXML = (char *) miniwget(dev->descURL, &descXMLsize, 0, NULL); +#endif + if (descXML) + { + parserootdesc (descXML, descXMLsize, &data); + free (descXML); descXML = 0; + GetUPNPUrls (&urls, &data, dev->descURL, 0); + } + freeUPNPDevlist(devlist); + } +#endif +} + +bool natt_new(struct natt_status *status) +{ + memset(status, 0, sizeof(struct natt_status)); + return true; +} + +void natt_free(struct natt_status *status) +{ + /* Nothing */ +} + +bool natt_open_port(struct natt_status *status, struct sockaddr *addr, socklen_t addrlen, enum socket_protocol proto) +{ +#if HAVE_MINIUPNPC + char host[PATH_MAX_LENGTH], ext_host[PATH_MAX_LENGTH], + port_str[6], ext_port_str[6]; + const char *proto_str; + struct addrinfo hints = {0}, *ext_addrinfo; + int r; + + /* if NAT traversal is uninitialized or unavailable, oh well */ + if (!urls.controlURL[0]) + return false; + + /* figure out the internal info */ + if (getnameinfo(addr, addrlen, host, PATH_MAX_LENGTH, port_str, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) + return false; + proto_str = (proto == SOCKET_PROTOCOL_UDP) ? "UDP" : "TCP"; + + /* add the port mapping */ + r = UPNP_AddAnyPortMapping(urls.controlURL, data.first.servicetype, port_str, + port_str, host, "retroarch", proto_str, NULL, "3600", ext_port_str); + if (r == 501 /* Action Failed */) + { + /* try the older AddPortMapping */ + memcpy(ext_port_str, port_str, 6); + r = UPNP_AddPortMapping(urls.controlURL, data.first.servicetype, port_str, + port_str, host, "retroarch", proto_str, NULL, "3600"); + } + if (r != 0) + return false; + + /* get the external IP */ + r = UPNP_GetExternalIPAddress(urls.controlURL, data.first.servicetype, ext_host); + if (r != 0) + return false; + + /* update the status */ + if (getaddrinfo_retro(ext_host, ext_port_str, &hints, &ext_addrinfo) != 0) + return false; + + if (ext_addrinfo->ai_family == AF_INET && + ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in)) + { + status->have_inet4 = true; + status->ext_inet4_addr = *((struct sockaddr_in *) ext_addrinfo->ai_addr); + } +#ifdef AF_INET6 + else if (ext_addrinfo->ai_family == AF_INET6 && + ext_addrinfo->ai_addrlen >= sizeof(struct sockaddr_in6)) + { + status->have_inet6 = true; + status->ext_inet6_addr = *((struct sockaddr_in6 *) ext_addrinfo->ai_addr); + } + else + { + freeaddrinfo_retro(ext_addrinfo); + return false; + } +#endif + + return true; + +#else + return false; +#endif +} + +bool natt_open_port_any(struct natt_status *status, uint16_t port, enum socket_protocol proto) +{ + struct net_ifinfo list; + bool ret = false; + size_t i; + struct addrinfo hints = {0}, *addr; + char port_str[6]; + + sprintf(port_str, "%hu", port); + + /* get our interfaces */ + if (!net_ifinfo_new(&list)) + return false; + + /* loop through them */ + for (i = 0; i < list.size; i++) + { + struct net_ifinfo_entry *entry = list.entries + i; + + /* ignore localhost */ + if (!strcmp(entry->host, "127.0.0.1") || !strcmp(entry->host, "::1")) + continue; + + /* make a request for this host */ + if (getaddrinfo_retro(entry->host, port_str, &hints, &addr) == 0) + { + ret = natt_open_port(status, addr->ai_addr, addr->ai_addrlen, proto) || ret; + freeaddrinfo_retro(addr); + } + } + + net_ifinfo_free(&list); + + return ret; +} + +bool natt_read(struct natt_status *status) +{ + /* MiniUPNPC is always synchronous, so there's nothing to read here. + * Reserved for future backends. */ + return false; +} + +#if 0 +/* If we want to remove redirects in the future, this is a sample of how to do + * that */ +void upnp_rem_redir (int port) +{ + char port_str[16]; + int t; + printf("TB : upnp_rem_redir (%d)\n", port); + if(urls.controlURL[0] == '\0') + { + printf("TB : the init was not done !\n"); + return; + } + sprintf(port_str, "%d", port); + UPNP_DeletePortMapping(urls.controlURL, data.first.servicetype, port_str, "TCP", NULL); +} +#endif diff --git a/libretro-common/samples/net/net_ifinfo_test.c b/libretro-common/samples/net/net_ifinfo_test.c index 4ac96bcbe0..1b825b3093 100644 --- a/libretro-common/samples/net/net_ifinfo_test.c +++ b/libretro-common/samples/net/net_ifinfo_test.c @@ -29,21 +29,17 @@ int main(int argc, const char *argv[]) { unsigned k = 0; - net_ifinfo_t *list = - (net_ifinfo_t*)calloc(1, sizeof(*list)); + net_ifinfo_t list; - if (!list) + if (!net_ifinfo_new(&list)) return -1; - if (!net_ifinfo_new(list)) - return -1; - - for (k = 0; k < list->size; k++) + for (k = 0; k < list.size; k++) { - printf("%s:%s\n", list->entries[k].name, list->entries[k].host); + printf("%s:%s\n", list.entries[k].name, list.entries[k].host); } - net_ifinfo_free(list); + net_ifinfo_free(&list); return 0; } diff --git a/menu/cbs/menu_cbs_get_value.c b/menu/cbs/menu_cbs_get_value.c index c1a11ce419..271a5977fb 100644 --- a/menu/cbs/menu_cbs_get_value.c +++ b/menu/cbs/menu_cbs_get_value.c @@ -475,7 +475,7 @@ static void menu_action_setting_disp_set_label_input_desc( keybind = (const struct retro_keybind*) &settings->input.binds[inp_desc_user][remap_id]; auto_bind = (const struct retro_keybind*) - input_get_auto_bind(inp_desc_user, remap_id); + input_config_get_bind_auto(inp_desc_user, remap_id); input_config_get_bind_string(descriptor, keybind, auto_bind, sizeof(descriptor)); diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index f3975acdaa..5a8790bb4e 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -70,6 +70,7 @@ #include "../performance_counters.h" #include "../core_info.h" #include "../wifi/wifi_driver.h" +#include "../tasks/tasks_internal.h" #ifdef HAVE_NETWORKING static void print_buf_lines(file_list_t *list, char *buf, @@ -429,16 +430,12 @@ static int menu_displaylist_parse_core_info(menu_displaylist_info_t *info) static int menu_displaylist_parse_network_info(menu_displaylist_info_t *info) { unsigned k = 0; - net_ifinfo_t *list = - (net_ifinfo_t*)calloc(1, sizeof(*list)); + net_ifinfo_t list; - if (!list) + if (!net_ifinfo_new(&list)) return -1; - if (!net_ifinfo_new(list)) - return -1; - - for (k = 0; k < list->size; k++) + for (k = 0; k < list.size; k++) { char tmp[255]; @@ -446,12 +443,12 @@ static int menu_displaylist_parse_network_info(menu_displaylist_info_t *info) snprintf(tmp, sizeof(tmp), "%s (%s) : %s\n", msg_hash_to_str(MSG_INTERFACE), - list->entries[k].name, list->entries[k].host); + list.entries[k].name, list.entries[k].host); menu_entries_append_enum(info->list, tmp, "", MENU_ENUM_LABEL_NETWORK_INFO_ENTRY, MENU_SETTINGS_CORE_INFO_NONE, 0, 0); } - net_ifinfo_free(list); + net_ifinfo_free(&list); return 0; } #endif @@ -4887,27 +4884,47 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) case DISPLAYLIST_WIFI_SETTINGS_LIST: if (string_is_equal(settings->wifi.driver, "null")) menu_entries_append_enum(info->list, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SETTINGS_FOUND), - msg_hash_to_str(MENU_ENUM_LABEL_NO_SETTINGS_FOUND), - MENU_ENUM_LABEL_NO_SETTINGS_FOUND, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_NETWORKS_FOUND), + MENU_ENUM_LABEL_NO_NETWORKS_FOUND, 0, 0, 0); +#ifdef HAVE_NETWORKING else { - unsigned i; struct string_list *ssid_list = string_list_new(); - driver_wifi_scan(); driver_wifi_get_ssids(ssid_list); - for (i = 0; i < ssid_list->size; i++) + if (ssid_list->size == 0) { - const char *ssid = ssid_list->elems[i].data; + task_push_wifi_scan(); + menu_entries_append_enum(info->list, - ssid, - msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_WIFI), - MENU_ENUM_LABEL_CONNECT_WIFI, - MENU_WIFI, 0, 0); + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_NETWORKS_FOUND), + MENU_ENUM_LABEL_NO_NETWORKS_FOUND, + 0, 0, 0); + } + else + { + unsigned i; + for (i = 0; i < ssid_list->size; i++) + { + const char *ssid = ssid_list->elems[i].data; + menu_entries_append_enum(info->list, + ssid, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_WIFI), + MENU_ENUM_LABEL_CONNECT_WIFI, + MENU_WIFI, 0, 0); + } } } +#else + menu_entries_append_enum(info->list, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_NETWORKS_FOUND), + msg_hash_to_str(MENU_ENUM_LABEL_NO_NETWORKS_FOUND), + MENU_ENUM_LABEL_NO_NETWORKS_FOUND, + 0, 0, 0); +#endif info->need_refresh = true; info->need_push = true; @@ -4942,6 +4959,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data) MENU_ENUM_LABEL_NETPLAY_CLIENT_SWAP_INPUT, PARSE_ONLY_BOOL, false) != -1) count++; + if (menu_displaylist_parse_settings_enum(menu, info, + MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL, + PARSE_ONLY_BOOL, false) != -1) + count++; if (menu_displaylist_parse_settings_enum(menu, info, MENU_ENUM_LABEL_NETWORK_CMD_ENABLE, PARSE_ONLY_BOOL, false) != -1) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 6f334610ac..bdfca9c6c6 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -5589,6 +5589,21 @@ static bool setting_append_list( general_read_handler, SD_FLAG_NONE); + CONFIG_BOOL( + list, list_info, + &settings->netplay.is_spectate, + MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL, + MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL, + false, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); + CONFIG_BOOL( list, list_info, &settings->netplay.swap_input, diff --git a/menu/widgets/menu_dialog.c b/menu/widgets/menu_dialog.c index f272685156..19205dcbfb 100644 --- a/menu/widgets/menu_dialog.c +++ b/menu/widgets/menu_dialog.c @@ -111,7 +111,7 @@ int menu_dialog_iterate(char *s, size_t len, const char *label) &settings->input.binds[0][binds[i]]; const struct retro_keybind *auto_bind = (const struct retro_keybind*) - input_get_auto_bind(0, binds[i]); + input_config_get_bind_auto(0, binds[i]); input_config_get_bind_string(desc[i], keybind, auto_bind, sizeof(desc[i])); diff --git a/msg_hash.h b/msg_hash.h index 0ba1f8f489..7d65595c13 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -173,6 +173,7 @@ enum msg_hash_enums MSG_NO_STATE_HAS_BEEN_LOADED_YET, MSG_GOT_CONNECTION_FROM, MSG_CONNECTION_SLOT, + MSG_PUBLIC_ADDRESS, MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET, MSG_CANNOT_INFER_NEW_CONFIG_PATH, MSG_UNDID_LOAD_STATE, @@ -277,6 +278,8 @@ enum msg_hash_enums MSG_MOVIE_RECORD_STOPPED, MSG_MOVIE_PLAYBACK_ENDED, MSG_TAKING_SCREENSHOT, + MSG_WIFI_SCAN_COMPLETE, + MSG_SCANNING_WIRELESS_NETWORKS, MSG_FAILED_TO_TAKE_SCREENSHOT, MSG_CUSTOM_TIMING_GIVEN, MSG_SAVING_STATE, @@ -861,6 +864,7 @@ enum msg_hash_enums MENU_LABEL(CONTENT_SETTINGS), MENU_LABEL(LOAD_CONTENT_LIST), MENU_LABEL(NO_SETTINGS_FOUND), + MENU_LABEL(NO_NETWORKS_FOUND), MENU_LABEL(NO_PERFORMANCE_COUNTERS), MENU_LABEL(FRAME_THROTTLE_SETTINGS), MENU_LABEL(FRAME_THROTTLE_ENABLE), @@ -975,6 +979,7 @@ enum msg_hash_enums MENU_LABEL(NETPLAY_CHECK_FRAMES), MENU_LABEL(NETPLAY_SPECTATOR_MODE_ENABLE), MENU_LABEL(NETPLAY_TCP_UDP_PORT), + MENU_LABEL(NETPLAY_NAT_TRAVERSAL), MENU_LABEL(SORT_SAVEFILES_ENABLE), MENU_LABEL(SORT_SAVESTATES_ENABLE), MENU_LABEL(NETPLAY_IP_ADDRESS), diff --git a/network/netplay/netplay.c b/network/netplay/netplay.c index 3fcea60f06..3ea2095969 100644 --- a/network/netplay/netplay.c +++ b/network/netplay/netplay.c @@ -63,6 +63,8 @@ static netplay_t *netplay_data = NULL; /* Used to avoid recursive netplay calls */ static bool in_netplay = false; +static void announce_nat_traversal(netplay_t *netplay); + static int init_tcp_connection(const struct addrinfo *res, bool server, bool spectate, struct sockaddr *other_addr, socklen_t addr_size) @@ -186,6 +188,22 @@ static bool init_tcp_socket(netplay_t *netplay, const char *server, return ret; } +static void init_nat_traversal(netplay_t *netplay) +{ + natt_init(); + + if (!natt_new(&netplay->nat_traversal_state)) + { + netplay->nat_traversal = false; + return; + } + + natt_open_port_any(&netplay->nat_traversal_state, netplay->tcp_port, SOCKET_PROTOCOL_TCP); + + if (!netplay->nat_traversal_state.request_outstanding) + announce_nat_traversal(netplay); +} + static bool init_ad_socket(netplay_t *netplay, uint16_t port) { int fd = socket_init((void**)&netplay->addr, port, NULL, SOCKET_TYPE_DATAGRAM); @@ -216,6 +234,9 @@ static bool init_socket(netplay_t *netplay, const char *server, uint16_t port) if (!init_tcp_socket(netplay, server, port, netplay->spectate.enabled)) return false; + if (netplay->is_server && netplay->nat_traversal) + init_nat_traversal(netplay); + return true; } @@ -1061,6 +1082,53 @@ void netplay_log_connection(const struct sockaddr_storage *their_addr, #endif +static void announce_nat_traversal(netplay_t *netplay) +{ + char msg[512], host[PATH_MAX_LENGTH], port[6]; + +#ifndef HAVE_SOCKET_LEGACY + if (netplay->nat_traversal_state.have_inet4) + { + if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet4_addr, + sizeof(struct sockaddr_in), + host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) + return; + + } +#ifdef AF_INET6 + else if (netplay->nat_traversal_state.have_inet6) + { + if (getnameinfo((const struct sockaddr *) &netplay->nat_traversal_state.ext_inet6_addr, + sizeof(struct sockaddr_in6), + host, PATH_MAX_LENGTH, port, 6, NI_NUMERICHOST|NI_NUMERICSERV) != 0) + return; + + } +#endif + else return; + +#else + if (netplay->nat_traversal_state.have_inet4) + { + strncpy(host, + inet_ntoa(netplay->nat_traversal_state.ext_inet4_addr.sin_addr), + PATH_MAX_LENGTH); + host[PATH_MAX_LENGTH-1] = '\0'; + snprintf(port, 6, "%hu", + ntohs(netplay->nat_traversal_state.ext_inet4_addr.sin_port)); + port[5] = '\0'; + + } + else return; + +#endif + + snprintf(msg, sizeof(msg), "%s: %s:%s\n", + msg_hash_to_str(MSG_PUBLIC_ADDRESS), + host, port); + runloop_msg_queue_push(msg, 1, 180, false); + RARCH_LOG("%s\n", msg); +} bool netplay_try_init_serialization(netplay_t *netplay) @@ -1180,6 +1248,7 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. * @spectate : If true, enable spectator mode. + * @nat_traversal : If true, attempt NAT traversal. * @nick : Nickname of user. * @quirks : Netplay quirks required for this session. * @@ -1188,10 +1257,9 @@ static bool netplay_init_buffers(netplay_t *netplay, unsigned frames) * * Returns: new netplay handle. **/ -netplay_t *netplay_new(const char *server, uint16_t port, - unsigned frames, unsigned check_frames, - const struct retro_callbacks *cb, - bool spectate, const char *nick, uint64_t quirks) +netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, + unsigned check_frames, const struct retro_callbacks *cb, bool spectate, + bool nat_traversal, const char *nick, uint64_t quirks) { netplay_t *netplay = (netplay_t*)calloc(1, sizeof(*netplay)); if (!netplay) @@ -1203,6 +1271,7 @@ netplay_t *netplay_new(const char *server, uint16_t port, netplay->port = server ? 0 : 1; netplay->spectate.enabled = spectate; netplay->is_server = server == NULL; + netplay->nat_traversal = netplay->is_server ? nat_traversal : false; netplay->stall_frames = frames; netplay->check_frames = check_frames; netplay->quirks = quirks; @@ -1338,6 +1407,9 @@ void netplay_free(netplay_t *netplay) free(netplay->spectate.input); } + if (netplay->nat_traversal) + natt_free(&netplay->nat_traversal_state); + if (netplay->buffer) { for (i = 0; i < netplay->buffer_size; i++) @@ -1383,11 +1455,26 @@ bool netplay_pre_frame(netplay_t *netplay) netplay_try_init_serialization(netplay); } - /* Advertise our server if applicable */ if (netplay->is_server) { + /* Advertise our server if applicable */ if (netplay_ad_fd >= 0 || init_ad_socket(netplay, RARCH_DEFAULT_PORT)) netplay_ad_server(netplay, netplay_ad_fd); + + /* NAT traversal if applicable */ + if (netplay->nat_traversal && + netplay->nat_traversal_state.request_outstanding && + !netplay->nat_traversal_state.have_inet4) + { + struct timeval tmptv = {0}; + fd_set fds = netplay->nat_traversal_state.fds; + if (socket_select(netplay->nat_traversal_state.nfds, &fds, NULL, NULL, &tmptv) > 0) + natt_read(&netplay->nat_traversal_state); + + if (!netplay->nat_traversal_state.request_outstanding || + netplay->nat_traversal_state.have_inet4) + announce_nat_traversal(netplay); + } } if (!netplay->net_cbs->pre_frame(netplay)) @@ -1613,7 +1700,8 @@ bool init_netplay(bool is_spectate, const char *server, unsigned port) netplay_is_client ? server : NULL, port ? port : RARCH_DEFAULT_PORT, settings->netplay.sync_frames, settings->netplay.check_frames, &cbs, - is_spectate, settings->username, quirks); + is_spectate, settings->netplay.nat_traversal, settings->username, + quirks); if (netplay_data) return true; diff --git a/network/netplay/netplay.h b/network/netplay/netplay.h index 83b41b69a1..b1470bb96b 100644 --- a/network/netplay/netplay.h +++ b/network/netplay/netplay.h @@ -137,6 +137,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames); * @check_frames : Frequency with which to check CRCs. * @cb : Libretro callbacks. * @spectate : If true, enable spectator mode. + * @nat_traversal : If true, attempt NAT traversal. * @nick : Nickname of user. * @quirks : Netplay quirks. * @@ -147,7 +148,7 @@ size_t audio_sample_batch_net(const int16_t *data, size_t frames); **/ netplay_t *netplay_new(const char *server, uint16_t port, unsigned frames, unsigned check_frames, - const struct retro_callbacks *cb, bool spectate, + const struct retro_callbacks *cb, bool spectate, bool nat_traversal, const char *nick, uint64_t quirks); /** diff --git a/network/netplay/netplay_net.c b/network/netplay/netplay_net.c index c594587e0f..cc6e409fc6 100644 --- a/network/netplay/netplay_net.c +++ b/network/netplay/netplay_net.c @@ -20,6 +20,7 @@ #include #include +#include #include "netplay_private.h" @@ -337,6 +338,19 @@ static bool netplay_net_info_cb(netplay_t* netplay, unsigned frames) netplay->has_connection = true; } + { + struct natt_status status; + natt_init(); + if (natt_new(&status) && natt_open_port_any(&status, netplay->tcp_port, SOCKET_PROTOCOL_TCP)) + { + fprintf(stderr, "Forwarded to %d!\n", status.ext_inet4_addr.sin_port); + } + else + { + fprintf(stderr, "Forwarding failed :(\n"); + } + } + return true; } diff --git a/network/netplay/netplay_private.h b/network/netplay/netplay_private.h index 40689561c0..9318a70681 100644 --- a/network/netplay/netplay_private.h +++ b/network/netplay/netplay_private.h @@ -20,6 +20,7 @@ #include "netplay.h" #include +#include #include #include #include @@ -124,6 +125,9 @@ struct netplay int fd; /* TCP port (if serving) */ uint16_t tcp_port; + /* NAT traversal info (if NAT traversal is used and serving) */ + bool nat_traversal; + struct natt_status nat_traversal_state; /* Which port is governed by netplay (other user)? */ unsigned port; bool has_connection; diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 3d68a861a8..6d789eac9f 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -182,6 +182,10 @@ if [ "$HAVE_NETWORKING" = 'yes' ]; then fi HAVE_NETWORK_CMD=yes HAVE_NETWORKGAMEPAD=yes + + if [ "$HAVE_MINIUPNPC" != "no" ]; then + check_lib MINIUPNPC "-lminiupnpc" + fi else echo "Warning: All networking features have been disabled." HAVE_NETWORK_CMD='no' diff --git a/qb/config.params.sh b/qb/config.params.sh index 202e158b27..3a338d8872 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -28,6 +28,7 @@ HAVE_DYLIB=auto # Dynamic loading support HAVE_NETWORKING=auto # Networking features (recommended) HAVE_NETWORKGAMEPAD=auto # Networked game pad (plus baked-in core) C89_NETWORKGAMEPAD=no +HAVE_MINIUPNPC=auto # Mini UPnP client library (for NAT traversal) HAVE_D3D9=yes # Direct3D 9 support HAVE_OPENGL=auto # OpenGL support HAVE_MALI_FBDEV=no # Mali fbdev context support diff --git a/retroarch.cfg b/retroarch.cfg index d54ef28f6a..bd5f90b9f2 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -497,14 +497,15 @@ # input_player1_r3_btn = # Menu buttons. -# menu_ok_btn = -# menu_cancel_btn = # menu_search_btn = # menu_info_btn = # menu_default_btn = # menu_scroll_down_btn = # menu_scroll_up_btn = +# Swap buttons for OK/Cancel +# menu_swap_ok_cancel = false + # Axis for RetroArch D-Pad. # Needs to be either '+' or '-' in the first character signaling either positive or negative direction of the axis, then the axis number. # Do note that every other input option has the corresponding _btn and _axis binds as well; they are omitted here for clarity. diff --git a/setting_list.c b/setting_list.c index 9949da39c1..97fcfca679 100644 --- a/setting_list.c +++ b/setting_list.c @@ -610,7 +610,7 @@ static void setting_get_string_representation_st_bind(void *data, index_offset = setting->index_offset; keybind = (const struct retro_keybind*)setting->value.target.keybind; auto_bind = (const struct retro_keybind*) - input_get_auto_bind(index_offset, keybind->id); + input_config_get_bind_auto(index_offset, keybind->id); input_config_get_bind_string(s, keybind, auto_bind, len); } diff --git a/tasks/task_content.c b/tasks/task_content.c index 6092f5611e..9dad03364d 100644 --- a/tasks/task_content.c +++ b/tasks/task_content.c @@ -1221,6 +1221,8 @@ bool task_push_content_load_default( default: break; } + + RARCH_LOG("MODE: %d\n", mode); /* Load content */ switch (mode) { diff --git a/tasks/task_wifi.c b/tasks/task_wifi.c new file mode 100644 index 0000000000..387b5a65fb --- /dev/null +++ b/tasks/task_wifi.c @@ -0,0 +1,112 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2016 - Jean-André Santoni + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tasks_internal.h" +#include "../verbosity.h" +#include "../runloop.h" +#include "../wifi/wifi_driver.h" +#include "../menu/menu_entries.h" +#include "../menu/menu_driver.h" + +typedef struct +{ + struct string_list *ssid_list; +} wifi_handle_t; + +static void wifi_scan_callback(void *task_data, + void *user_data, const char *error) +{ + unsigned menu_type = 0; + const char *path = NULL; + const char *label = NULL; + enum msg_hash_enums enum_idx = MSG_UNKNOWN; + + menu_entries_get_last_stack(&path, &label, &menu_type, &enum_idx, NULL); + + /* Don't push the results if we left the wifi menu */ + if (!string_is_equal(label, + msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_WIFI_SETTINGS_LIST))) + return; + + file_list_t *file_list = menu_entries_get_selection_buf_ptr(0); + + menu_entries_ctl(MENU_ENTRIES_CTL_CLEAR, file_list); + + struct string_list *ssid_list = string_list_new(); + driver_wifi_get_ssids(ssid_list); + + unsigned i; + for (i = 0; i < ssid_list->size; i++) + { + const char *ssid = ssid_list->elems[i].data; + menu_entries_append_enum(file_list, + ssid, + msg_hash_to_str(MENU_ENUM_LABEL_CONNECT_WIFI), + MENU_ENUM_LABEL_CONNECT_WIFI, + MENU_WIFI, 0, 0); + } +} + +static void task_wifi_scan_handler(retro_task_t *task) +{ + driver_wifi_scan(); + + task->progress = 100; + task->title = strdup(msg_hash_to_str(MSG_WIFI_SCAN_COMPLETE)); + task->finished = true; + + return; +} + +bool task_push_wifi_scan(void) +{ + retro_task_t *task = (retro_task_t*)calloc(1, sizeof(*task)); + wifi_handle_t *state = (wifi_handle_t*)calloc(1, sizeof(*state)); + + if (!task || !state) + goto error; + + state->ssid_list = string_list_new(); + + /* blocking means no other task can run while this one is running, which is the default */ + task->type = TASK_TYPE_BLOCKING; + task->state = state; + task->handler = task_wifi_scan_handler; + task->callback = wifi_scan_callback; + task->title = strdup(msg_hash_to_str(MSG_SCANNING_WIRELESS_NETWORKS)); + + task_queue_ctl(TASK_QUEUE_CTL_PUSH, task); + + return true; + +error: + if (state) + free(state); + if (task) + free(task); + + return false; +} diff --git a/tasks/tasks_internal.h b/tasks/tasks_internal.h index f39636f89a..0975ccd120 100644 --- a/tasks/tasks_internal.h +++ b/tasks/tasks_internal.h @@ -83,6 +83,9 @@ void *task_push_http_transfer(const char *url, bool mute, const char *type, retro_task_callback_t cb, void *userdata); task_retriever_info_t *http_task_get_transfer_list(void); + +bool task_push_wifi_scan(void); + #endif bool task_push_image_load(const char *fullpath, diff --git a/wifi/drivers/connmanctl.c b/wifi/drivers/connmanctl.c index 773b3ba26b..b9a7b2d068 100644 --- a/wifi/drivers/connmanctl.c +++ b/wifi/drivers/connmanctl.c @@ -81,6 +81,9 @@ static void connmanctl_get_ssids(struct string_list* ssids) union string_list_elem_attr attr; attr.i = RARCH_FILETYPE_UNSET; + if (!lines) + return; + for (i = 0; i < lines->size; i++) { char ssid[20];