mirror of
https://github.com/vanilla-wiiu/vanilla.git
synced 2026-10-28 09:43:51 +00:00
- Port vanilla-pipe on Android for users with rooted devices and compatible Wi-Fi hardware - Drop OpenSSL as a build dependency (only used for hostap's crypto backend, but didn't work on Android so now we're using hostap's internal crypto) - Improvements to event loop (targets 180Hz input polling + 60Hz video display) - Implement MediaCodec hardware decoding on Android - Use smaller and simpler DHCP client
350 lines
12 KiB
C
350 lines
12 KiB
C
#include "menu_connection.h"
|
|
|
|
#ifdef _WIN32
|
|
#include <winsock2.h>
|
|
typedef uint32_t in_addr_t;
|
|
#else
|
|
#include <arpa/inet.h>
|
|
#ifdef ANDROID
|
|
#include <net/if.h>
|
|
#endif
|
|
#endif
|
|
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "config.h"
|
|
#include "lang.h"
|
|
#include "menu_common.h"
|
|
#include "menu_settings.h"
|
|
#include "pipemgmt.h"
|
|
#include "platform.h"
|
|
#include "ui/ui_anim.h"
|
|
#include "ui/ui_util.h"
|
|
|
|
static int bglayer;
|
|
static int fglayer;
|
|
static int error_lbl;
|
|
static int ip_textedit;
|
|
|
|
#define MAX_WIRELESS_ENTRIES 4
|
|
static size_t detected_wireless_interface_count = 0;
|
|
static char detected_wireless_interfaces[256][256];
|
|
#define MAX_DETECTED_WIRELESS_INTERFACES (sizeof(detected_wireless_interfaces) / sizeof(detected_wireless_interfaces[0]))
|
|
static int intf_start = 0; // NOTE: Not currently implemented, but may be used for pagination in the future
|
|
static int intf_buttons[MAX_WIRELESS_ENTRIES];
|
|
|
|
static const char *checkmark = 0;//"checkmark.svg";
|
|
|
|
static vui_callback_t g_continue_callback = 0;
|
|
static void *g_continue_callback_data = 0;
|
|
static vui_callback_t g_fail_callback = 0;
|
|
static void *g_fail_callback_data = 0;
|
|
|
|
static void return_to_settings(vui_context_t *vui, int btn, void *v)
|
|
{
|
|
int layer = (intptr_t) v;
|
|
|
|
vui_callback_t cb = g_fail_callback ? g_fail_callback : vpi_menu_settings;
|
|
void *cb_data = g_fail_callback ? g_fail_callback_data : 0;
|
|
|
|
vui_transition_fade_layer_out(vui, layer, cb, cb_data);
|
|
}
|
|
|
|
static void return_to_connection(vui_context_t *vui, int btn, void *v)
|
|
{
|
|
int layer = (intptr_t) v;
|
|
|
|
vui_callback_t cb;
|
|
void *cb_data;
|
|
|
|
if (g_continue_callback) {
|
|
cb = g_continue_callback;
|
|
cb_data = g_continue_callback_data;
|
|
} else {
|
|
cb = vpi_menu_settings;
|
|
cb_data = NULL;
|
|
layer = bglayer; // Returning to settings, so we should fade out the entire background
|
|
}
|
|
|
|
vui_transition_fade_layer_out(vui, layer, cb, cb_data);
|
|
}
|
|
|
|
static void add_wireless_interface(const char *intf)
|
|
{
|
|
// Sanity checks
|
|
if (!intf || !intf[0] || detected_wireless_interface_count >= MAX_DETECTED_WIRELESS_INTERFACES) {
|
|
return;
|
|
}
|
|
|
|
// Ensure interface hasn't already been added
|
|
for (size_t i = 0; i < detected_wireless_interface_count; i++) {
|
|
if (!strcmp(detected_wireless_interfaces[i], intf)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Add interface
|
|
vui_strncpy(
|
|
detected_wireless_interfaces[detected_wireless_interface_count],
|
|
intf,
|
|
sizeof(detected_wireless_interfaces[detected_wireless_interface_count]));
|
|
detected_wireless_interface_count++;
|
|
}
|
|
|
|
static void populate_wireless_intf(void)
|
|
{
|
|
#ifdef ANDROID
|
|
static const char *android_prefixes[] = {"wlan", "wifi", "swlan", "mlan", "ath"};
|
|
static const size_t android_prefix_count = sizeof(android_prefixes) / sizeof(android_prefixes[0]);
|
|
#endif
|
|
|
|
struct dirent *dir;
|
|
DIR *d = opendir("/sys/class/net");
|
|
char buf[1024];
|
|
detected_wireless_interface_count = 0;
|
|
if (d) {
|
|
while ((dir = readdir(d)) != 0) {
|
|
const char *intf = dir->d_name;
|
|
if (intf[0] != '.') {
|
|
snprintf(buf, sizeof(buf), "/sys/class/net/%s/wireless", intf);
|
|
DIR *wifi_check = opendir(buf);
|
|
if (wifi_check) {
|
|
add_wireless_interface(intf);
|
|
closedir(wifi_check);
|
|
} else {
|
|
#ifdef ANDROID
|
|
// Fallback heuristic approach for Android
|
|
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
|
|
const char *p = android_prefixes[prefix];
|
|
if (!strncmp(intf, p, strlen(p))) {
|
|
add_wireless_interface(intf);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
closedir(d);
|
|
}
|
|
|
|
#ifdef ANDROID
|
|
// Some Android releases may restrict /sys/class/net altogether. Probe
|
|
// heuristically.
|
|
for (size_t prefix = 0; prefix < android_prefix_count; prefix++) {
|
|
for (unsigned int index = 0; index < 8; index++) {
|
|
char intf[IF_NAMESIZE];
|
|
snprintf(intf, sizeof(intf), "%s%u", android_prefixes[prefix], index);
|
|
if (if_nametoindex(intf)) {
|
|
add_wireless_interface(intf);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void intf_pressed(vui_context_t *vui, int button, void *v)
|
|
{
|
|
for (int i = 0; i < MAX_WIRELESS_ENTRIES; i++) {
|
|
if (button == intf_buttons[i]) {
|
|
vpi_config.server_address = VANILLA_ADDRESS_LOCAL;
|
|
vui_strncpy(vpi_config.wireless_interface, detected_wireless_interfaces[intf_start + i], sizeof(vpi_config.wireless_interface));
|
|
vpi_config.connection_setup = 1;
|
|
vpi_stop_pipe();
|
|
vpi_config_save();
|
|
break;
|
|
}
|
|
}
|
|
return_to_connection(vui, button, v);
|
|
}
|
|
|
|
static void update_wireless_buttons(vui_context_t *vui)
|
|
{
|
|
for (int i = 0; i < MAX_WIRELESS_ENTRIES; i++) {
|
|
const char *intf = detected_wireless_interfaces[intf_start + i];
|
|
int b = intf_buttons[i];
|
|
if (intf[0] == 0) {
|
|
vui_button_update_visible(vui, b, 0);
|
|
} else {
|
|
vui_button_update_visible(vui, b, 1);
|
|
vui_button_update_text(vui, b, intf);
|
|
|
|
int sel = !strcmp(intf, vpi_config.wireless_interface);
|
|
vui_button_update_checked(vui, b, sel);
|
|
vui_button_update_icon(vui, b, sel ? checkmark : 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void local_connection_menu(vui_context_t *vui, void *v)
|
|
{
|
|
vui_reset(vui);
|
|
|
|
int bglayer = vui_layer_create(vui);
|
|
fglayer = vui_layer_create(vui);
|
|
|
|
vui_rect_t bkg_rect;
|
|
int margin;
|
|
vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin);
|
|
|
|
const int lbl_margin = margin * 6;
|
|
vui_label_create(vui, bkg_rect.x + lbl_margin, bkg_rect.y + lbl_margin, bkg_rect.w - lbl_margin - lbl_margin, bkg_rect.h - lbl_margin - lbl_margin, lang(VPI_LANG_LOCAL_CONNECTION_HELP), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer);
|
|
|
|
// Detect wireless interfaces
|
|
memset(detected_wireless_interfaces, 0, sizeof(detected_wireless_interfaces));
|
|
populate_wireless_intf();
|
|
|
|
const int button_w = bkg_rect.w/2;
|
|
const int button_h = BTN_SZ*2/3;
|
|
const int button_x = bkg_rect.x+bkg_rect.w/2-button_w/2;
|
|
const int button_y = bkg_rect.y+bkg_rect.h*2/5;
|
|
for (int i = 0; i < MAX_WIRELESS_ENTRIES; i++) {
|
|
intf_buttons[i] = vui_button_create(vui, button_x, button_y+button_h*i, button_w, button_h, "intf", 0, VUI_BUTTON_STYLE_BUTTON, fglayer, intf_pressed, (void *) (intptr_t) fglayer);
|
|
}
|
|
|
|
update_wireless_buttons(vui);
|
|
|
|
vpi_menu_create_back_button(vui, fglayer, return_to_connection, (void *) (intptr_t) fglayer);
|
|
|
|
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
|
|
}
|
|
|
|
static void check_ip_address(vui_context_t *vui, int btn, void *v)
|
|
{
|
|
char buf[256];
|
|
vui_textedit_get_text(vui, ip_textedit, buf, sizeof(buf));
|
|
in_addr_t ip = inet_addr(buf);
|
|
|
|
int valid = (ip != (in_addr_t) -1);
|
|
|
|
vui_label_update_visible(vui, error_lbl, !valid);
|
|
|
|
if (valid) {
|
|
vpi_config.server_address = ip;
|
|
vpi_config.connection_setup = 1;
|
|
vpi_stop_pipe();
|
|
vpi_config_save();
|
|
|
|
return_to_connection(vui, btn, v);
|
|
}
|
|
}
|
|
|
|
static void via_server_connection_menu(vui_context_t *vui, void *v)
|
|
{
|
|
vui_reset(vui);
|
|
|
|
int bglayer = vui_layer_create(vui);
|
|
fglayer = vui_layer_create(vui);
|
|
|
|
vui_rect_t bkg_rect;
|
|
int margin;
|
|
vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin);
|
|
|
|
const int lbl_margin = margin * 6;
|
|
vui_label_create(vui, bkg_rect.x + lbl_margin, bkg_rect.y + lbl_margin, bkg_rect.w - lbl_margin - lbl_margin, bkg_rect.h - lbl_margin - lbl_margin, lang(VPI_LANG_SERVER_CONNECTION_HELP), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer);
|
|
|
|
const int textedit_w = bkg_rect.w/2;
|
|
const int textedit_h = vui_get_font_height(vui, VUI_FONT_SIZE_NORMAL);
|
|
const int textedit_x = bkg_rect.x + bkg_rect.w/2 - textedit_w/2;
|
|
|
|
const char *ip_text;
|
|
if (vpi_config.server_address == VANILLA_ADDRESS_LOCAL) {
|
|
ip_text = 0;
|
|
} else {
|
|
struct in_addr in;
|
|
in.s_addr = vpi_config.server_address;
|
|
ip_text = inet_ntoa(in);
|
|
}
|
|
|
|
ip_textedit = vui_textedit_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 3 / 7, textedit_w, textedit_h, ip_text, VUI_FONT_SIZE_NORMAL, 0, fglayer);
|
|
|
|
error_lbl = vui_label_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 4 / 7, textedit_w, BTN_SZ, "Invalid IP address", vui_color_create(1,0,0,1), VUI_FONT_SIZE_SMALL, fglayer);
|
|
vui_label_update_visible(vui, error_lbl, 0);
|
|
|
|
vui_button_create(vui, textedit_x, bkg_rect.y + bkg_rect.h * 5 / 7, textedit_w, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, check_ip_address, (void *) (intptr_t) fglayer);
|
|
|
|
vpi_menu_create_back_button(vui, fglayer, return_to_connection, (void *) (intptr_t) fglayer);
|
|
|
|
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
|
|
}
|
|
|
|
void connection_btn_pressed(vui_context_t *vui, int btn, void *v)
|
|
{
|
|
vui_callback_t cb = (vui_callback_t) v;
|
|
vui_transition_fade_layer_out(vui, fglayer, cb, 0);
|
|
}
|
|
|
|
void vpi_menu_connection_and_return_to(vui_context_t *vui, int fade_fglayer, vui_callback_t success_callback, void *success_data, vui_callback_t fail_callback, void *fail_data)
|
|
{
|
|
vui_reset(vui);
|
|
|
|
bglayer = vui_layer_create(vui);
|
|
fglayer = vui_layer_create(vui);
|
|
|
|
vui_rect_t bkg_rect;
|
|
int margin;
|
|
vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin);
|
|
|
|
const int lbl_margin = margin * 6;
|
|
vui_label_create(vui, bkg_rect.x + lbl_margin, bkg_rect.y + lbl_margin, bkg_rect.w - lbl_margin - lbl_margin, bkg_rect.h - lbl_margin - lbl_margin, lang(VPI_LANG_CONNECTION_HELP_1), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer);
|
|
|
|
const int btn_w = bkg_rect.w/2;
|
|
const int btn_y = bkg_rect.y + bkg_rect.h * 2 / 4;
|
|
const int btn_x = bkg_rect.x + bkg_rect.w/2 - btn_w/2;
|
|
|
|
int is_local = vpi_config.server_address == VANILLA_ADDRESS_LOCAL;
|
|
int is_via_server = vpi_config.server_address != VANILLA_ADDRESS_LOCAL;
|
|
int local_available = 1;
|
|
|
|
const int lbl2_width = bkg_rect.w*2/3;
|
|
const char *lbl2_txt;
|
|
|
|
#ifdef VANILLA_PIPE_AVAILABLE
|
|
lbl2_txt = lang(VPI_LANG_CONNECTION_HELP_2);
|
|
#else
|
|
lbl2_txt = lang(VPI_LANG_NO_LOCAL_AVAILABLE);
|
|
is_local = 0;
|
|
local_available = 0;
|
|
#endif
|
|
|
|
#ifdef ANDROID
|
|
if (local_available && !vpi_android_has_root_access()) {
|
|
lbl2_txt = lang(VPI_LANG_ANDROID_ROOT_REQUIRED);
|
|
is_local = 0;
|
|
local_available = 0;
|
|
}
|
|
#endif
|
|
|
|
vui_label_create(vui, bkg_rect.x + bkg_rect.w/2 - lbl2_width/2, bkg_rect.y + lbl_margin + bkg_rect.h/8, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, lbl2_txt, vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, fglayer);
|
|
|
|
int local_btn = vui_button_create(vui, btn_x, btn_y, btn_w, BTN_SZ, lang(VPI_LANG_LOCAL), is_local ? checkmark : 0, VUI_BUTTON_STYLE_BUTTON, fglayer, connection_btn_pressed, local_connection_menu);
|
|
int via_server_btn = vui_button_create(vui, btn_x, btn_y + BTN_SZ, btn_w, BTN_SZ, lang(VPI_LANG_VIA_SERVER), is_via_server ? checkmark : 0, VUI_BUTTON_STYLE_BUTTON, fglayer, connection_btn_pressed, via_server_connection_menu);
|
|
|
|
vui_button_update_checkable(vui, local_btn, 1);
|
|
vui_button_update_checkable(vui, via_server_btn, 1);
|
|
|
|
vui_button_update_checked(vui, local_btn, is_local);
|
|
vui_button_update_checked(vui, via_server_btn, is_via_server);
|
|
|
|
vui_button_update_enabled(vui, local_btn, local_available);
|
|
|
|
// Back button
|
|
vpi_menu_create_back_button(vui, fglayer, return_to_settings, (void *) (intptr_t) bglayer);
|
|
|
|
vui_transition_fade_layer_in(vui, fade_fglayer ? fglayer : bglayer, 0, 0);
|
|
|
|
g_continue_callback = success_callback;
|
|
g_continue_callback_data = success_data;
|
|
g_fail_callback = fail_callback;
|
|
g_fail_callback_data = fail_data;
|
|
}
|
|
|
|
void vpi_menu_connection(vui_context_t *vui, void *v)
|
|
{
|
|
vpi_menu_connection_and_return_to(vui, (intptr_t) v, 0, 0, 0, 0);
|
|
}
|