diff --git a/lib/gamepad/gamepad.c b/lib/gamepad/gamepad.c index de06ee3..4bd4dd3 100644 --- a/lib/gamepad/gamepad.c +++ b/lib/gamepad/gamepad.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include "audio.h" @@ -25,83 +26,142 @@ static const uint32_t STOP_CODE = 0xCAFEBABE; static uint32_t SERVER_ADDRESS = 0; - -uint16_t PORT_MSG; -uint16_t PORT_VID; -uint16_t PORT_AUD; -uint16_t PORT_HID; -uint16_t PORT_CMD; +static const int MAX_PIPE_RETRY = 5; #define EVENT_BUFFER_SIZE 65536 #define EVENT_BUFFER_ARENA_SIZE VANILLA_MAX_EVENT_COUNT * 2 uint8_t *EVENT_BUFFER_ARENA[EVENT_BUFFER_ARENA_SIZE] = {0}; pthread_mutex_t event_buffer_mutex = PTHREAD_MUTEX_INITIALIZER; -void send_to_console(int fd, const void *data, size_t data_size, int port) +in_addr_t get_real_server_address() { - struct sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = SERVER_ADDRESS; - address.sin_port = htons((uint16_t) (port - 100)); + if (SERVER_ADDRESS == VANILLA_ADDRESS_DIRECT) { + // The Wii U always places itself at this address + return inet_addr("192.168.1.10"); + } else if (SERVER_ADDRESS != VANILLA_ADDRESS_LOCAL) { + // If there's a remote pipe, we send to that + return htonl(SERVER_ADDRESS); + } - ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) &address, sizeof(address)); - if (sent == -1) { - char ip[INET_ADDRSTRLEN]; - inet_ntop(AF_INET, &address.sin_addr, ip, INET_ADDRSTRLEN); - print_info("Failed to send to Wii U socket: address: %s, fd: %d, port: %d, errno: %i", ip, fd, port - 100, errno); + // Must be local, in which case we don't care + return INADDR_ANY; +} + +void create_sockaddr(struct sockaddr_in *in, struct sockaddr_un *un, const struct sockaddr **addr, size_t *size, in_addr_t inaddr, uint16_t port) +{ + if (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) { + memset(un, 0, sizeof(struct sockaddr_un)); + un->sun_family = AF_UNIX; + snprintf(un->sun_path, sizeof(un->sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port); + + if (size) *size = sizeof(struct sockaddr_un); + if (addr) *addr = (const struct sockaddr *) un; + } else { + memset(in, 0, sizeof(struct sockaddr_in)); + in->sin_family = AF_INET; + in->sin_port = htons(port); + in->sin_addr.s_addr = inaddr; + + if (size) *size = sizeof(struct sockaddr_in); + if (addr) *addr = (const struct sockaddr *) in; } } -int create_socket(int *socket_out, uint16_t port) +void send_to_console(int fd, const void *data, size_t data_size, uint16_t port) { - struct sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = INADDR_ANY; - address.sin_port = htons(port); + struct sockaddr_in in; + struct sockaddr_un un; + const struct sockaddr *addr; + size_t addr_size; - int skt = socket(AF_INET, SOCK_DGRAM, 0); - if (skt == -1) { - print_info("FAILED TO CREATE SOCKET FOR PORT %i", port); - return 0; + in_port_t console_port = port - 100; + + create_sockaddr(&in, &un, &addr, &addr_size, get_real_server_address(), console_port); + + ssize_t sent = sendto(fd, data, data_size, 0, addr, addr_size); + if (sent == -1) { + print_info("Failed to send to Wii U socket: fd: %d, port: %d, errno: %i", fd, console_port, errno); } +} +int create_socket(int *socket_out, in_port_t port) +{ + int skt = -1; + struct sockaddr_un un; + struct sockaddr_in in; + + create_sockaddr(&in, &un, 0, 0, INADDR_ANY, port); + + if (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) { + skt = socket(AF_UNIX, SOCK_STREAM, 0); + if (skt == -1) { + print_info("FAILED TO CREATE SOCKET: %i", errno); + return VANILLA_ERR_BAD_SOCKET; + } + + if (connect(skt, (const struct sockaddr *) &un, sizeof(un)) == -1) { + print_info("FAILED TO CONNECT TO LOCAL SOCKET %u: %i", port, errno); + close(skt); + return VANILLA_ERR_PIPE_UNRESPONSIVE; + } + + print_info("SUCCESSFULLY CONNECTED SOCKET TO PATH %s", un.sun_path); + } else { + in.sin_addr.s_addr = INADDR_ANY; + skt = socket(AF_INET, SOCK_DGRAM, 0); + if (skt == -1) { + print_info("FAILED TO CREATE SOCKET: %i", errno); + return VANILLA_ERR_BAD_SOCKET; + } + + if (bind(skt, (const struct sockaddr *) &in, sizeof(in)) == -1) { + print_info("FAILED TO BIND PORT %u: %i", port, errno); + close(skt); + return VANILLA_ERR_BAD_SOCKET; + } + + print_info("SUCCESSFULLY BOUND SOCKET ON PORT %i", port); + } + (*socket_out) = skt; - if (bind((*socket_out), (const struct sockaddr *) &address, sizeof(address)) == -1) { - print_info("FAILED TO BIND PORT %u: %i", port, errno); - return 0; - } - - print_info("SUCCESSFULLY BOUND SOCKET %i on PORT %i", skt, port); - return 1; + return VANILLA_SUCCESS; } void send_stop_code(int from_socket, in_port_t port) { - struct sockaddr_in address; - address.sin_family = AF_INET; - address.sin_addr.s_addr = inet_addr("127.0.0.1"); + struct sockaddr_in in; + struct sockaddr_un un; + const struct sockaddr *addr; + size_t addr_size; - address.sin_port = htons(port); - sendto(from_socket, &STOP_CODE, sizeof(STOP_CODE), 0, (struct sockaddr *)&address, sizeof(address)); + // FIXME: There are probably better ways of stopping the sockets than this + + create_sockaddr(&in, &un, &addr, &addr_size, inet_addr("127.0.0.1"), htons(port)); + sendto(from_socket, &STOP_CODE, sizeof(STOP_CODE), 0, addr, addr_size); } int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) { - struct sockaddr_in addr = {0}; + struct sockaddr_in in; + struct sockaddr_un un; + const struct sockaddr *addr; + size_t addr_size; - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = SERVER_ADDRESS; - addr.sin_port = htons(VANILLA_PIPE_CMD_SERVER_PORT); + create_sockaddr(&in, &un, &addr, &addr_size, get_real_server_address(), VANILLA_PIPE_CMD_PORT); ssize_t read_size; uint32_t send_cc = htonl(cc); uint32_t recv_cc; - do { - sendto(skt, &send_cc, sizeof(send_cc), 0, (struct sockaddr *) &addr, sizeof(addr)); + for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) { + // sendto(skt, &send_cc, sizeof(send_cc), 0, addr, addr_size); + if (write(skt, &send_cc, sizeof(send_cc)) == -1) { + print_info("Failed to write control code to socket"); + return 0; + } - if (!wait_for_reply) { + if (!wait_for_reply || is_interrupted()) { return 1; } @@ -110,8 +170,10 @@ int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) return 1; } + print_info("STILL WAITING FOR REPLY"); + sleep(1); - } while (!is_interrupted()); + } return 0; } @@ -119,9 +181,10 @@ int send_pipe_cc(int skt, uint32_t cc, int wait_for_reply) int connect_to_backend(int *socket, uint32_t cc) { // Try to bind with backend - int pipe_cc_skt; - if (!create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT)) { - return VANILLA_ERROR; + int pipe_cc_skt = -1; + int ret = create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_PORT); + if (ret != VANILLA_SUCCESS) { + return ret; } struct timeval tv = {0}; @@ -131,7 +194,7 @@ int connect_to_backend(int *socket, uint32_t cc) if (!send_pipe_cc(pipe_cc_skt, cc, 1)) { print_info("FAILED TO BIND TO PIPE"); close(pipe_cc_skt); - return VANILLA_ERROR; + return VANILLA_ERR_PIPE_UNRESPONSIVE; } *socket = pipe_cc_skt; @@ -139,79 +202,67 @@ int connect_to_backend(int *socket, uint32_t cc) return VANILLA_SUCCESS; } -int sync_internal(uint16_t code, uint32_t server_address) +void sync_internal(thread_data_t *data) { clear_interrupt(); - - if (server_address == 0) { - SERVER_ADDRESS = inet_addr("192.168.1.10"); - } else { - SERVER_ADDRESS = htonl(server_address); - } - int skt = 0; - int ret = VANILLA_ERROR; - if (server_address != 0) { - ret = connect_to_backend(&skt, VANILLA_PIPE_SYNC_CODE(code)); - if (ret != VANILLA_SUCCESS) { - goto exit; - } - } + SERVER_ADDRESS = data->server_address; - // Wait for sync result from pipe - uint32_t recv_cc; - while (1) { - ssize_t read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0); - if (read_size == sizeof(recv_cc)) { - recv_cc = ntohl(recv_cc); - if (recv_cc >> 8 == VANILLA_PIPE_CC_SYNC_STATUS >> 8) { - ret = recv_cc & 0xFF; + uint16_t code = (uintptr_t) data->thread_data; + + int skt = -1; + int ret = connect_to_backend(&skt, VANILLA_PIPE_SYNC_CODE(code)); + + if (ret == VANILLA_SUCCESS) { + // Wait for sync result from pipe + uint32_t recv_cc; + ret = VANILLA_ERR_PIPE_UNRESPONSIVE; + for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) { + ssize_t read_size = recv(skt, &recv_cc, sizeof(recv_cc), 0); + if (read_size == sizeof(recv_cc)) { + recv_cc = ntohl(recv_cc); + if (recv_cc >> 8 == VANILLA_PIPE_CC_SYNC_STATUS >> 8) { + ret = recv_cc & 0xFF; + break; + } else if (recv_cc == VANILLA_PIPE_CC_SYNC_PING) { + // Pipe is still responsive but hasn't found anything yet + printf("received ping from pipe\n"); + retries = -1; + } + } + + if (is_interrupted()) { + send_pipe_cc(skt, VANILLA_PIPE_CC_UNBIND, 0); break; } } + } - if (is_interrupted()) { - send_pipe_cc(skt, VANILLA_PIPE_CC_UNBIND, 0); - break; - } + push_event(data->event_loop, VANILLA_EVENT_SYNC, &ret, sizeof(ret)); + + // Wait for interrupt so frontend has a chance to receive event + while (!is_interrupted()) { + usleep(100000); } exit_pipe: - if (skt) + if (skt != -1) close(skt); - -exit: - return ret; } -int connect_as_gamepad_internal(event_loop_t *event_loop, uint32_t server_address) +void connect_as_gamepad_internal(thread_data_t *data) { clear_interrupt(); - PORT_MSG = 50110; - PORT_VID = 50120; - PORT_AUD = 50121; - PORT_HID = 50122; - PORT_CMD = 50123; - - if (server_address == 0) { - SERVER_ADDRESS = inet_addr("192.168.1.10"); - } else { - SERVER_ADDRESS = htonl(server_address); - PORT_MSG += 200; - PORT_VID += 200; - PORT_AUD += 200; - PORT_HID += 200; - PORT_CMD += 200; - } + SERVER_ADDRESS = data->server_address; gamepad_context_t info; - info.event_loop = event_loop; + info.event_loop = data->event_loop; - int ret = VANILLA_ERROR; + int ret = VANILLA_ERR_GENERIC; int pipe_cc_skt = 0; - if (server_address != 0) { + if (SERVER_ADDRESS != 0) { ret = connect_to_backend(&pipe_cc_skt, VANILLA_PIPE_CC_CONNECT); if (ret != VANILLA_SUCCESS) { goto exit; @@ -219,11 +270,11 @@ int connect_as_gamepad_internal(event_loop_t *event_loop, uint32_t server_addres } // Open all required sockets - if (!create_socket(&info.socket_vid, PORT_VID)) goto exit_pipe; - if (!create_socket(&info.socket_msg, PORT_MSG)) goto exit_vid; - if (!create_socket(&info.socket_hid, PORT_HID)) goto exit_msg; - if (!create_socket(&info.socket_aud, PORT_AUD)) goto exit_hid; - if (!create_socket(&info.socket_cmd, PORT_CMD)) goto exit_aud; + if (create_socket(&info.socket_vid, PORT_VID) != VANILLA_SUCCESS) goto exit_pipe; + if (create_socket(&info.socket_msg, PORT_MSG) != VANILLA_SUCCESS) goto exit_vid; + if (create_socket(&info.socket_hid, PORT_HID) != VANILLA_SUCCESS) goto exit_msg; + if (create_socket(&info.socket_aud, PORT_AUD) != VANILLA_SUCCESS) goto exit_hid; + if (create_socket(&info.socket_cmd, PORT_CMD) != VANILLA_SUCCESS) goto exit_aud; pthread_t video_thread, audio_thread, input_thread, msg_thread, cmd_thread; @@ -255,7 +306,7 @@ int connect_as_gamepad_internal(event_loop_t *event_loop, uint32_t server_addres pthread_join(input_thread, NULL); pthread_join(cmd_thread, NULL); - if (server_address != 0) { + if (SERVER_ADDRESS != 0) { send_pipe_cc(pipe_cc_skt, VANILLA_PIPE_CC_UNBIND, 0); } @@ -281,7 +332,7 @@ exit_pipe: close(pipe_cc_skt); exit: - return ret; + // TODO: Return error code to frontend? } int is_stop_code(const char *data, size_t data_length) @@ -301,7 +352,7 @@ int push_event(event_loop_t *loop, int type, const void *data, size_t size) ev->data = get_event_buffer(); if (!ev->data) { print_info("OUT OF MEMORY FOR NEW EVENTS"); - return VANILLA_ERROR; + return VANILLA_ERR_OUT_OF_MEMORY; } ev->type = type; diff --git a/lib/gamepad/gamepad.h b/lib/gamepad/gamepad.h index 8cc914e..566be7f 100644 --- a/lib/gamepad/gamepad.h +++ b/lib/gamepad/gamepad.h @@ -6,11 +6,7 @@ #include #include -extern uint16_t PORT_MSG; -extern uint16_t PORT_VID; -extern uint16_t PORT_AUD; -extern uint16_t PORT_HID; -extern uint16_t PORT_CMD; +#include "../pipe/ports.h" struct wpa_ctrl; @@ -35,9 +31,19 @@ typedef struct int socket_cmd; } gamepad_context_t; -int sync_internal(uint16_t code, uint32_t server_address); -int connect_as_gamepad_internal(event_loop_t *ctx, uint32_t server_address); -void send_to_console(int fd, const void *data, size_t data_size, int port); +typedef struct thread_data_t thread_data_t; +typedef void (*thread_start_t)(thread_data_t *); +typedef struct thread_data_t +{ + uint32_t server_address; + event_loop_t *event_loop; + thread_start_t thread_start; + void *thread_data; +} thread_data_t; + +void sync_internal(thread_data_t *data); +void connect_as_gamepad_internal(thread_data_t *data); +void send_to_console(int fd, const void *data, size_t data_size, uint16_t port); int is_stop_code(const char *data, size_t data_length); int push_event(event_loop_t *loop, int type, const void *data, size_t size); int get_event(event_loop_t *loop, vanilla_event_t *event, int wait); diff --git a/lib/status.c b/lib/status.c index f63bc07..975cbe7 100644 --- a/lib/status.c +++ b/lib/status.c @@ -24,8 +24,8 @@ void print_info(const char *errstr, ...) va_list args; va_start(args, errstr); - vanilla_log_no_newline(VANILLA_STATUS_STRINGS[-VANILLA_INFO]); - vanilla_log_no_newline(" "); + // vanilla_log_no_newline(VANILLA_STATUS_STRINGS[-VANILLA_INFO]); + // vanilla_log_no_newline(" "); vanilla_log_no_newline_va(errstr, args); vanilla_log_no_newline("\n"); diff --git a/lib/vanilla.c b/lib/vanilla.c index 3bd59a5..171bb61 100644 --- a/lib/vanilla.c +++ b/lib/vanilla.c @@ -20,14 +20,9 @@ pthread_mutex_t main_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t gamepad_mutex = PTHREAD_MUTEX_INITIALIZER; event_loop_t event_loop = {{0}, 0, 0, 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_COND_INITIALIZER}; -struct gamepad_data_t +void *start_event_loop(void *arg) { - uint32_t server_address; -}; - -void *start_gamepad(void *arg) -{ - struct gamepad_data_t *data = (struct gamepad_data_t *) arg; + thread_data_t *data = (thread_data_t *) arg; pthread_mutex_lock(&event_loop.mutex); init_event_buffer_arena(); @@ -40,12 +35,22 @@ void *start_gamepad(void *arg) pthread_cond_broadcast(&event_loop.waitcond); pthread_mutex_unlock(&event_loop.mutex); - connect_as_gamepad_internal(&event_loop, data->server_address); + data->event_loop = &event_loop; + data->thread_start(data); free(data); pthread_mutex_lock(&event_loop.mutex); event_loop.active = 0; + + // Free any unconsumed events + while (event_loop.used_index < event_loop.new_index) { + // Output data to pointer + vanilla_event_t *pull_event = &event_loop.events[event_loop.used_index % VANILLA_MAX_EVENT_COUNT]; + vanilla_free_event(pull_event); + event_loop.used_index++; + } + free_event_buffer_arena(); pthread_cond_broadcast(&event_loop.waitcond); pthread_mutex_unlock(&event_loop.mutex); @@ -54,19 +59,21 @@ void *start_gamepad(void *arg) return 0; } -int vanilla_start_internal(uint32_t server_address) +int vanilla_start_internal(uint32_t server_address, thread_start_t thread_start, void *thread_data) { if (pthread_mutex_trylock(&main_mutex) == 0) { pthread_t other; - struct gamepad_data_t *data = malloc(sizeof(struct gamepad_data_t)); + thread_data_t *data = malloc(sizeof(thread_data_t)); data->server_address = server_address; + data->thread_start = thread_start; + data->thread_data = thread_data; // Lock event loop mutex so it can't be set to active until we're ready pthread_mutex_lock(&event_loop.mutex); // Start other thread (which will set event loop to active) - pthread_create(&other, NULL, start_gamepad, data); + pthread_create(&other, NULL, start_event_loop, data); pthread_setname_np(other, "vanilla-gamepad"); // Wait for event loop to be set active before returning @@ -77,13 +84,14 @@ int vanilla_start_internal(uint32_t server_address) return VANILLA_SUCCESS; } else { - return VANILLA_ERROR; + // We're already doing something + return VANILLA_ERR_BUSY; } } int vanilla_start(uint32_t server_address) { - return vanilla_start_internal(server_address); + return vanilla_start_internal(server_address, connect_as_gamepad_internal, 0); } void vanilla_stop() @@ -164,7 +172,7 @@ void vanilla_set_battery_status(int battery_status) int vanilla_sync(uint16_t code, uint32_t server_address) { - return sync_internal(code, server_address); + return vanilla_start_internal(server_address, sync_internal, (void *) (uintptr_t) code); } int vanilla_poll_event(vanilla_event_t *event) diff --git a/lib/vanilla.h b/lib/vanilla.h index e5d1280..be42109 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -9,12 +9,17 @@ extern "C" { #include #include -static const int VANILLA_SUCCESS = 0; -static const int VANILLA_ERROR = -1; -static const int VANILLA_READY = -2; -static const int VANILLA_INFO = -3; -static const int VANILLA_UNKNOWN_COMMAND = -4; -static const int VANILLA_INVALID_ARGUMENT = -5; +static const int VANILLA_SUCCESS = 0; +static const int VANILLA_ERR_GENERIC = -1; +static const int VANILLA_ERR_UNKNOWN_COMMAND = -2; +static const int VANILLA_ERR_INVALID_ARGUMENT = -3; +static const int VANILLA_ERR_PIPE_UNRESPONSIVE = -4; +static const int VANILLA_ERR_OUT_OF_MEMORY = -5; +static const int VANILLA_ERR_BUSY = -6; +static const int VANILLA_ERR_BAD_SOCKET = -7; + +static const uint32_t VANILLA_ADDRESS_LOCAL = 0xFFFFFFFF; +static const uint32_t VANILLA_ADDRESS_DIRECT = 0x0; enum VanillaGamepadButtons { @@ -63,7 +68,8 @@ enum VanillaEvent VANILLA_EVENT_NONE, VANILLA_EVENT_VIDEO, VANILLA_EVENT_AUDIO, - VANILLA_EVENT_VIBRATE + VANILLA_EVENT_VIBRATE, + VANILLA_EVENT_SYNC }; enum VanillaRegion @@ -136,7 +142,7 @@ void vanilla_set_button(int button, int32_t value); * * This can be called from another thread to change the button state while vanilla_connect_to_console() is running. * - * `x` and `y` are expected to be in gamepad screen coordinates (0x0 to 853x479). + * `x` and `y` are expected to be in gamepad screen coordinates (0x0 to 853x479 inclusive). * If either `x` or `y` are -1, this point will be disabled. */ void vanilla_set_touch(int x, int y); diff --git a/pipe/def.h b/pipe/def.h index 032a782..9da94d6 100644 --- a/pipe/def.h +++ b/pipe/def.h @@ -1,15 +1,17 @@ #ifndef VANILLA_PIPE_DEF_H #define VANILLA_PIPE_DEF_H -#define VANILLA_PIPE_CMD_SERVER_PORT 51000 -#define VANILLA_PIPE_CMD_CLIENT_PORT 51001 +#define VANILLA_PIPE_CMD_PORT 51000 #define VANILLA_PIPE_CC_SYNC 0x56530000 #define VANILLA_PIPE_CC_CONNECT 0x56414249 #define VANILLA_PIPE_CC_BIND_ACK 0x56414245 #define VANILLA_PIPE_CC_SYNC_STATUS 0x53595300 +#define VANILLA_PIPE_CC_SYNC_PING 0x5359504E #define VANILLA_PIPE_CC_BUSY 0x56414255 #define VANILLA_PIPE_CC_UNBIND 0x5641554E #define VANILLA_PIPE_SYNC_CODE(x) (VANILLA_PIPE_CC_SYNC | (x & 0xFFFF)) +#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe.%i" + #endif // VANILLA_PIPE_DEF_H \ No newline at end of file diff --git a/pipe/linux/main.c b/pipe/linux/main.c index 053b89f..299fc21 100644 --- a/pipe/linux/main.c +++ b/pipe/linux/main.c @@ -13,11 +13,11 @@ int main(int argc, const char **argv) return 1; } - if (argc < 2) { + if (argc != 3) { pprint("vanilla-pipe - brokers a connection between Vanilla and the Wii U\n"); pprint("--------------------------------------------------------------------------------\n"); pprint("\n"); - pprint("Usage: %s \n", argv[0]); + pprint("Usage: %s <-local | -udp> \n", argv[0]); pprint("\n"); pprint("Connecting to the Wii U as a gamepad requires some modifications to the 802.11n\n"); pprint("protocol, and not all platforms allow such modifications to be made.\n"); @@ -33,16 +33,39 @@ int main(int argc, const char **argv) pprint("embedded devices such as MCUs or SBCs, providing more versatility in hardware\n"); pprint("configurations.\n"); pprint("\n"); - pprint("`vanilla-pipe` cannot be controlled directly, it can only be controlled via UDP\n"); - pprint("sockets by a compatible frontend.\n"); + pprint("`vanilla-pipe` cannot be controlled directly, it can only be controlled via\n"); + pprint("sockets by a compatible frontend. By choosing '-local' or '-udp', you can\n"); + pprint("choose what type of socket to use to best suit the environment.\n"); pprint("\n"); return 1; } - const char *wireless_interface = argv[1]; + int udp_mode = 0; + int local_mode = 0; + const char *wireless_interface = 0; - vanilla_listen(wireless_interface); + for (int i = 1; i < argc; i++) { + if (!strcmp(argv[i], "-udp")) { + udp_mode = 1; + } else if (!strcmp(argv[i], "-local")) { + local_mode = 1; + } else { + wireless_interface = argv[i]; + } + } + + if (udp_mode == local_mode) { + pprint("Must choose either '-local' OR '-udp'\n"); + return 1; + } + + if (!wireless_interface) { + pprint("Must identify a wireless interface to use\n"); + return 1; + } + + vanilla_listen(local_mode, wireless_interface); return 0; } \ No newline at end of file diff --git a/pipe/linux/wpa.c b/pipe/linux/wpa.c index 24ae4b5..bde94b6 100644 --- a/pipe/linux/wpa.c +++ b/pipe/linux/wpa.c @@ -13,12 +13,13 @@ #include #include #include +#include #include #include #include #include "../def.h" -#include "ports.h" +#include "../ports.h" #include "status.h" #include "util.h" #include "vanilla.h" @@ -51,10 +52,13 @@ struct sync_args { uint16_t code; void *(*start_routine)(void *); struct wpa_ctrl *ctrl; + int local; + int skt; }; struct relay_info { const char *wireless_interface; + int local; in_port_t port; }; @@ -226,7 +230,7 @@ int start_process(const char **argv, pid_t *pid_out, int *stdout_pipe, int *stde _exit(1); } else if (pid < 0) { // Fork error - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } else { // Continuation of parent close(out_pipes[1]); @@ -293,7 +297,7 @@ int start_wpa_supplicant(const char *wireless_interface, const char *config_file // Give up give_up: kill((*pid), SIGTERM); - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } } @@ -307,6 +311,13 @@ void quit_loop() pthread_mutex_unlock(&running_mutex); } +void interrupt() +{ + pthread_mutex_lock(&running_mutex); + running = 0; + pthread_mutex_unlock(&running_mutex); +} + void sigint_handler(int signum) { if (signum == SIGINT) { @@ -341,7 +352,7 @@ void *read_stdin(void *) void *wpa_setup_environment(void *data) { - void *ret = THREADRESULT(VANILLA_ERROR); + void *ret = THREADRESULT(VANILLA_ERR_GENERIC); struct sync_args *args = (struct sync_args *) data; @@ -446,7 +457,7 @@ int call_dhcp(const char *network_interface, pid_t *dhclient_pid) print_info("FAILED TO ESTABLISH DHCP"); kill(*dhclient_pid, SIGTERM); - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } } @@ -472,11 +483,11 @@ int is_networkmanager_managing_device(const char *wireless_interface, int *is_ma if (!WIFEXITED(status)) { // Something went wrong print_info("NMCLI DID NOT EXIT NORMALLY"); - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } char buf[100]; - int ret = VANILLA_ERROR; + int ret = VANILLA_ERR_GENERIC; while (read_line_from_fd(pipe, buf, sizeof(buf))) { if (memcmp(buf, "GENERAL.STATE", 13) == 0) { *is_managed = !strstr(buf, "unmanaged"); @@ -505,7 +516,7 @@ int set_networkmanager_on_device(const char *wireless_interface, int on) if (WIFEXITED(status)) { return VANILLA_SUCCESS; } else { - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } } @@ -554,14 +565,29 @@ relay_ports create_ports(int from_socket, in_port_t from_port, int to_socket, in return ports; } -int open_socket(in_port_t port) +int open_socket(int local, in_port_t port) { struct sockaddr_in in = {0}; - in.sin_family = AF_INET; - in.sin_addr.s_addr = INADDR_ANY; - in.sin_port = htons(port); - - int skt = socket(AF_INET, SOCK_DGRAM, 0); + struct sockaddr_un un = {0}; + + const struct sockaddr *sa; + size_t sa_size; + + int skt; + if (local) { + skt = socket(AF_UNIX, SOCK_STREAM, 0); + un.sun_family = AF_UNIX; + snprintf(un.sun_path, sizeof(un.sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port); + sa = (const struct sockaddr *) &un; + sa_size = sizeof(un); + } else { + skt = socket(AF_INET, SOCK_DGRAM, 0); + in.sin_family = AF_INET; + in.sin_addr.s_addr = INADDR_ANY; + in.sin_port = htons(port); + sa = (const struct sockaddr *) ∈ + sa_size = sizeof(in); + } if (skt == -1) { return -1; } @@ -570,12 +596,18 @@ int open_socket(in_port_t port) tv.tv_usec = 250000; setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - if (bind(skt, (const struct sockaddr *) &in, sizeof(in)) == -1) { - print_info("FAILED TO BIND PORT %u: %i\n", port, errno); + if (bind(skt, sa, sa_size) == -1) { + print_info("FAILED TO BIND PORT %u: %i", port, errno); close(skt); return -1; } + if (local) { + if (listen(skt, 20) == -1) { + print_info("FAILED TO LISTEN ON LOCAL SOCKET: %i", errno); + } + } + return skt; } @@ -586,7 +618,7 @@ void *open_relay(void *data) int ret = -1; // Open an incoming port from the console - int from_console = open_socket(port); + int from_console = open_socket(0, port); if (from_console == -1) { goto close; } @@ -594,7 +626,7 @@ void *open_relay(void *data) setsockopt(from_console, SOL_SOCKET, SO_BINDTODEVICE, info->wireless_interface, strlen(info->wireless_interface)); // Open an incoming port from the frontend - int from_frontend = open_socket(port + 100); + int from_frontend = open_socket(info->local, port + 100); if (from_frontend == -1) { goto close_console_connection; } @@ -631,13 +663,14 @@ close: return THREADRESULT(ret); } -void create_all_relays(const char *wireless_interface) +void create_all_relays(int local, const char *wireless_interface) { pthread_t vid_thread, aud_thread, msg_thread, cmd_thread, hid_thread; struct relay_info vid_info, aud_info, msg_info, cmd_info, hid_info; - // Set wireless interface on all + // Set common info for all vid_info.wireless_interface = aud_info.wireless_interface = msg_info.wireless_interface = cmd_info.wireless_interface = hid_info.wireless_interface = wireless_interface; + vid_info.local = aud_info.local = msg_info.local = cmd_info.local = hid_info.local = local; vid_info.port = PORT_VID; aud_info.port = PORT_AUD; @@ -670,9 +703,7 @@ void *thread_handler(void *data) free(args); - pthread_mutex_lock(&running_mutex); - running = 0; - pthread_mutex_unlock(&running_mutex); + interrupt(); // Locked by calling thread pthread_mutex_unlock(&action_mutex); @@ -751,14 +782,14 @@ int create_connect_config(const char *input_config, const char *bssid) FILE *in_file = fopen(input_config, "r"); if (!in_file) { print_info("FAILED TO OPEN INPUT CONFIG FILE"); - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } FILE *out_file = fopen(get_wireless_connect_config_filename(), "w"); if (!out_file) { print_info("FAILED TO OPEN OUTPUT CONFIG FILE"); fclose(in_file); - return VANILLA_ERROR; + return VANILLA_ERR_GENERIC; } int len; @@ -800,6 +831,13 @@ void *sync_with_console_internal(void *data) while (1) { if (is_interrupted()) goto exit_loop; + uint32_t cc = htonl(VANILLA_PIPE_CC_SYNC_PING); + if (send(args->skt, &cc, sizeof(cc), MSG_NOSIGNAL) == -1) { + // Client has probably disconnected + interrupt(); + goto exit_loop; + } + // print_info("SCANNING"); actual_buf_len = buf_len; wpa_ctrl_command(args->ctrl, "SCAN", buf, &actual_buf_len); @@ -884,7 +922,7 @@ void *sync_with_console_internal(void *data) } while (!found_console); exit_loop: - return THREADRESULT(found_console ? VANILLA_SUCCESS : VANILLA_ERROR); + return THREADRESULT(found_console ? VANILLA_SUCCESS : VANILLA_ERR_GENERIC); } void *do_connect(void *data) @@ -896,7 +934,7 @@ void *do_connect(void *data) sleep(2); print_info("WAITING FOR CONNECTION"); - if (is_interrupted()) return THREADRESULT(VANILLA_ERROR); + if (is_interrupted()) return THREADRESULT(VANILLA_ERR_GENERIC); } char buf[1024]; @@ -911,7 +949,7 @@ void *do_connect(void *data) break; } - if (is_interrupted()) return THREADRESULT(VANILLA_ERROR); + if (is_interrupted()) return THREADRESULT(VANILLA_ERR_GENERIC); } print_info("CONNECTED TO CONSOLE"); @@ -926,7 +964,7 @@ void *do_connect(void *data) print_info("DHCP ESTABLISHED"); } - create_all_relays(args->wireless_interface); + create_all_relays(args->local, args->wireless_interface); int kill_ret = kill(dhclient_pid, SIGTERM); print_info("KILLING DHCLIENT %i", dhclient_pid); @@ -945,7 +983,7 @@ void *vanilla_sync_with_console(void *data) config = fopen(wireless_conf_file, "w"); if (!config) { print_info("FAILED TO WRITE TEMP CONFIG: %s", wireless_conf_file); - return THREADRESULT(VANILLA_ERROR); + return THREADRESULT(VANILLA_ERR_GENERIC); } fprintf(config, "ctrl_interface=%s\nupdate_config=1\n", wpa_ctrl_interface); @@ -972,9 +1010,12 @@ void *vanilla_connect_to_console(void *data) return wpa_setup_environment(args); } -void vanilla_listen(const char *wireless_interface) +void vanilla_listen(int local, const char *wireless_interface) { - int skt = open_socket(VANILLA_PIPE_CMD_SERVER_PORT); + // Ensure local domain sockets can be written to by everyone + umask(0000); + + int skt = open_socket(local, VANILLA_PIPE_CMD_PORT); if (skt == -1) { pprint("Failed to open server socket\n"); return; @@ -1013,46 +1054,70 @@ void vanilla_listen(const char *wireless_interface) } pthread_mutex_unlock(&sync_mutex); - ssize_t r = recvfrom(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, &addr_size); - if (r <= 0) { + int data_socket = accept(skt, 0, 0); + if (data_socket == -1) { + if (errno != EAGAIN) + print_info("Failed to accept: %i", errno); goto repeat_loop; } - control_code = ntohl(control_code); + pthread_mutex_lock(&main_loop_mutex); - if ((control_code >> 16) == (VANILLA_PIPE_CC_SYNC >> 16) || control_code == VANILLA_PIPE_CC_CONNECT) { - print_info("RECEIVED SYNC/CONNECT SIGNAL"); - - if (pthread_mutex_trylock(&action_mutex) == 0) { - struct sync_args *args = malloc(sizeof(struct sync_args)); - args->wireless_interface = wireless_interface; - args->code = control_code & 0xFFFF; - args->start_routine = (control_code == VANILLA_PIPE_CC_CONNECT) ? vanilla_connect_to_console : vanilla_sync_with_console; - - client_address = addr.sin_addr; + while (main_loop) { + pthread_mutex_unlock(&main_loop_mutex); - // Acknowledge - control_code = htonl(VANILLA_PIPE_CC_BIND_ACK); - sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); - - pthread_t thread; - int thread_ret; - if (pthread_create(&thread, NULL, thread_handler, args) != 0) { - print_info("FAILED TO CREATE THREAD"); - free(args); - } - } else { - // Busy - control_code = htonl(VANILLA_PIPE_CC_BUSY); - sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + // ssize_t r = recvfrom(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, &addr_size); + ssize_t r = read(data_socket, &control_code, sizeof(control_code)); + if (r <= 0) { + break; } - } else if (control_code == VANILLA_PIPE_CC_UNBIND) { - print_info("RECEIVED UNBIND SIGNAL"); - pthread_mutex_lock(&running_mutex); - running = 0; - pthread_mutex_unlock(&running_mutex); + + control_code = ntohl(control_code); + + if ((control_code >> 16) == (VANILLA_PIPE_CC_SYNC >> 16) || control_code == VANILLA_PIPE_CC_CONNECT) { + if (pthread_mutex_trylock(&action_mutex) == 0) { + struct sync_args *args = malloc(sizeof(struct sync_args)); + args->wireless_interface = wireless_interface; + args->code = control_code & 0xFFFF; + args->local = local; + args->start_routine = (control_code == VANILLA_PIPE_CC_CONNECT) ? vanilla_connect_to_console : vanilla_sync_with_console; + args->skt = data_socket; + + client_address = addr.sin_addr; + + // Acknowledge + control_code = htonl(VANILLA_PIPE_CC_BIND_ACK); + // sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + write(data_socket, &control_code, sizeof(control_code)); + + pthread_t thread; + int thread_ret; + if (pthread_create(&thread, NULL, thread_handler, args) != 0) { + print_info("FAILED TO CREATE THREAD"); + free(args); + } + } else { + // Busy + control_code = htonl(VANILLA_PIPE_CC_BUSY); + // sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr)); + write(data_socket, &control_code, sizeof(control_code)); + } + } else if (control_code == VANILLA_PIPE_CC_UNBIND) { + print_info("RECEIVED UNBIND SIGNAL"); + interrupt(); + } + +repeat_read_loop: + pthread_mutex_lock(&main_loop_mutex); } + if (data_socket != -1) { + close(data_socket); + data_socket = -1; + } + + pthread_mutex_unlock(&main_loop_mutex); + repeat_loop: pthread_mutex_lock(&main_loop_mutex); } diff --git a/pipe/linux/wpa.h b/pipe/linux/wpa.h index d63c840..db328ca 100644 --- a/pipe/linux/wpa.h +++ b/pipe/linux/wpa.h @@ -19,6 +19,6 @@ int enable_networkmanager_on_device(const char *wireless_interface); void pprint(const char *fmt, ...); -void vanilla_listen(const char *wireless_interface); +void vanilla_listen(int local, const char *wireless_interface); #endif // VANILLA_WPA_H diff --git a/pipe/linux/ports.h b/pipe/ports.h similarity index 100% rename from pipe/linux/ports.h rename to pipe/ports.h diff --git a/rpi/CMakeLists.txt b/rpi/CMakeLists.txt index e9f008b..b787360 100644 --- a/rpi/CMakeLists.txt +++ b/rpi/CMakeLists.txt @@ -5,9 +5,12 @@ find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil avfilter swscale add_executable(vanilla-pi drm.c + lang.c main2.c menu/menu.c + menu/menu_common.c menu/menu_main.c + menu/menu_sudo.c menu/menu_sync.c ui/ui.c ui/ui_anim.c @@ -21,6 +24,7 @@ install(TARGETS vanilla-pi) target_include_directories(vanilla-pi PRIVATE "${CMAKE_SOURCE_DIR}/lib" "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_SOURCE_DIR}" # xf86drm.h needs this /usr/include/libdrm diff --git a/rpi/lang.c b/rpi/lang.c new file mode 100644 index 0000000..5f2ab5d --- /dev/null +++ b/rpi/lang.c @@ -0,0 +1,21 @@ +#include "lang.h" + +static const char *lang_str[__VPI_LANG_T_COUNT] = { + "Sync", + "Connect", + "Edit", + "Settings", + "OK", + "Cancel", + "Touch the symbols in the order they are displayed on the TV screen from left to right.", + "If the symbols are not displayed on the TV screen, press the SYNC Button on the Wii U console.", + "Connecting to the Wii U console...", + "Sync Failed", + "Vanilla needs root permission to configure the wireless interface for connection.", + "Please enter your \"sudo\" password here.", +}; + +const char *lang(vpi_lang_t id) +{ + return lang_str[id]; +} \ No newline at end of file diff --git a/rpi/lang.h b/rpi/lang.h new file mode 100644 index 0000000..3022dc9 --- /dev/null +++ b/rpi/lang.h @@ -0,0 +1,22 @@ +#ifndef VANILLA_PI_LANG_H +#define VANILLA_PI_LANG_H + +typedef enum { + VPI_LANG_SYNC_BTN, + VPI_LANG_CONNECT_BTN, + VPI_LANG_EDIT_BTN, + VPI_LANG_SETTINGS_BTN, + VPI_LANG_OK_BTN, + VPI_LANG_CANCEL_BTN, + VPI_LANG_SYNC_HELP_1, + VPI_LANG_SYNC_HELP_2, + VPI_LANG_SYNC_CONNECTING, + VPI_LANG_SYNC_FAILED, + VPI_LANG_SUDO_HELP_1, + VPI_LANG_SUDO_HELP_2, + __VPI_LANG_T_COUNT +} vpi_lang_t; + +const char *lang(vpi_lang_t id); + +#endif // VANILLA_PI_LANG_H \ No newline at end of file diff --git a/rpi/main2.c b/rpi/main2.c index 97f12b0..e6f3def 100644 --- a/rpi/main2.c +++ b/rpi/main2.c @@ -27,7 +27,7 @@ int main(int argc, const char **argv) goto exit; } - vanilla_menu_init(vui); + vpi_menu_init(vui); while (vui_update_sdl(vui)) { } diff --git a/rpi/menu/menu.c b/rpi/menu/menu.c index 9fc8c09..072eb58 100644 --- a/rpi/menu/menu.c +++ b/rpi/menu/menu.c @@ -6,8 +6,8 @@ #include "menu_main.h" -void vanilla_menu_init(vui_context_t *vui) +void vpi_menu_init(vui_context_t *vui) { // Start with main menu - vanilla_menu_main(vui, 0); + vpi_menu_main(vui, 0); } \ No newline at end of file diff --git a/rpi/menu/menu.h b/rpi/menu/menu.h index 349ba11..91acc94 100644 --- a/rpi/menu/menu.h +++ b/rpi/menu/menu.h @@ -3,6 +3,6 @@ #include "ui/ui.h" -void vanilla_menu_init(vui_context_t *vui); +void vpi_menu_init(vui_context_t *vui); #endif // VANILLA_PI_MENU_H \ No newline at end of file diff --git a/rpi/menu/menu_common.c b/rpi/menu/menu_common.c new file mode 100644 index 0000000..20af3ee --- /dev/null +++ b/rpi/menu/menu_common.c @@ -0,0 +1,16 @@ +#include "menu_common.h" + +void vpi_menu_create_background(vui_context_t *vui, int layer, vui_rect_t *bkg_rect, int *margin) +{ + int scrw, scrh; + vui_get_screen_size(vui, &scrw, &scrh); + + int m = scrw/75; + *margin = m; + bkg_rect->x = m; + bkg_rect->y = m; + bkg_rect->w = scrw-m-m; + bkg_rect->h = scrh-m-m; + + vui_rect_create(vui, bkg_rect->x, bkg_rect->y, bkg_rect->w, bkg_rect->h, scrh*2/10, vui_color_create(0, 0, 0, 0.66f), layer); +} \ No newline at end of file diff --git a/rpi/menu/menu_common.h b/rpi/menu/menu_common.h index cddba2b..75fed6b 100644 --- a/rpi/menu/menu_common.h +++ b/rpi/menu/menu_common.h @@ -1,6 +1,10 @@ #ifndef VANILLA_MENU_COMMON_H #define VANILLA_MENU_COMMON_H +#include "ui/ui.h" + #define BTN_SZ 80 +void vpi_menu_create_background(vui_context_t *vui, int layer, vui_rect_t *bkg_rect, int *margin); + #endif // VANILLA_MENU_COMMON_H \ No newline at end of file diff --git a/rpi/menu/menu_main.c b/rpi/menu/menu_main.c index ff95e08..66905a7 100644 --- a/rpi/menu/menu_main.c +++ b/rpi/menu/menu_main.c @@ -2,6 +2,7 @@ #include +#include "lang.h" #include "menu_common.h" #include "menu_sync.h" #include "ui/ui_anim.h" @@ -15,12 +16,12 @@ static int main_menu_current = 0; static int console_menu_layer = 0; -void vanilla_menu_main_sync_action(vui_context_t *vui, int btn, void *v) +void vpi_menu_main_sync_action(vui_context_t *vui, int btn, void *v) { - vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vanilla_menu_sync, 0); + vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vpi_menu_sync, 0); } -void vanilla_menu_main_populate(vui_context_t *vui) +void vpi_menu_main_populate(vui_context_t *vui) { for (int i = 0; i < MAIN_MENU_ENTRIES; i++) { int b = main_menu_btns[i]; @@ -30,7 +31,7 @@ void vanilla_menu_main_populate(vui_context_t *vui) } } -void vanilla_menu_main_finish_transition(vui_context_t *vui, void *v) +void vpi_menu_main_finish_transition(vui_context_t *vui, void *v) { intptr_t data = (intptr_t) v; @@ -39,32 +40,32 @@ void vanilla_menu_main_finish_transition(vui_context_t *vui, void *v) main_menu_current = target; - vanilla_menu_main_populate(vui); + vpi_menu_main_populate(vui); vui_transition_fade_layer_in(vui, layer, 0, 0); } -void vanilla_menu_main_start_transition(vui_context_t *vui, int layer, int to) +void vpi_menu_main_start_transition(vui_context_t *vui, int layer, int to) { if (to < 0) { // Can't go into negatives return; } - vui_transition_fade_layer_out(vui, layer, vanilla_menu_main_finish_transition, (void *) (intptr_t) (layer | to << 8)); + vui_transition_fade_layer_out(vui, layer, vpi_menu_main_finish_transition, (void *) (intptr_t) (layer | to << 8)); } -void vanilla_menu_main_left_arrow(vui_context_t *vui, int btn, void *v) +void vpi_menu_main_left_arrow(vui_context_t *vui, int btn, void *v) { - vanilla_menu_main_start_transition(vui, (intptr_t) v, main_menu_current - MAIN_MENU_ENTRIES); + vpi_menu_main_start_transition(vui, (intptr_t) v, main_menu_current - MAIN_MENU_ENTRIES); } -void vanilla_menu_main_right_arrow(vui_context_t *vui, int btn, void *v) +void vpi_menu_main_right_arrow(vui_context_t *vui, int btn, void *v) { - vanilla_menu_main_start_transition(vui, (intptr_t) v, main_menu_current + MAIN_MENU_ENTRIES); + vpi_menu_main_start_transition(vui, (intptr_t) v, main_menu_current + MAIN_MENU_ENTRIES); } -void vanilla_menu_main(vui_context_t *vui, void *v) +void vpi_menu_main(vui_context_t *vui, void *v) { vui_reset(vui); @@ -78,20 +79,20 @@ void vanilla_menu_main(vui_context_t *vui, void *v) console_menu_layer = vui_layer_create(vui); // Left arrow - vui_button_create(vui, 0, arrow_y, BTN_SZ, BTN_SZ, "<", NULL, VUI_BUTTON_STYLE_BUTTON, layer, vanilla_menu_main_left_arrow, (void *) (intptr_t) console_menu_layer); + vui_button_create(vui, 0, arrow_y, BTN_SZ, BTN_SZ, "<", NULL, VUI_BUTTON_STYLE_BUTTON, layer, vpi_menu_main_left_arrow, (void *) (intptr_t) console_menu_layer); // Right arrow - vui_button_create(vui, SCREEN_WIDTH-BTN_SZ, arrow_y, BTN_SZ, BTN_SZ, ">", NULL, VUI_BUTTON_STYLE_BUTTON, layer, vanilla_menu_main_right_arrow, (void *) (intptr_t) console_menu_layer); + vui_button_create(vui, SCREEN_WIDTH-BTN_SZ, arrow_y, BTN_SZ, BTN_SZ, ">", NULL, VUI_BUTTON_STYLE_BUTTON, layer, vpi_menu_main_right_arrow, (void *) (intptr_t) console_menu_layer); // Sync button - vui_button_create(vui, BTN_SZ, SCREEN_HEIGHT-BTN_SZ, (SCREEN_WIDTH-BTN_SZ*2)/2, BTN_SZ, "Sync", NULL, VUI_BUTTON_STYLE_BUTTON, layer, vanilla_menu_main_sync_action, (void *) (intptr_t) layer); + vui_button_create(vui, BTN_SZ, SCREEN_HEIGHT-BTN_SZ, (SCREEN_WIDTH-BTN_SZ*2)/2, BTN_SZ, lang(VPI_LANG_SYNC_BTN), NULL, VUI_BUTTON_STYLE_BUTTON, layer, vpi_menu_main_sync_action, (void *) (intptr_t) layer); // Connect button - vui_button_create(vui, BTN_SZ + (SCREEN_WIDTH-BTN_SZ*2)/2, SCREEN_HEIGHT-BTN_SZ, (SCREEN_WIDTH-BTN_SZ*2)/2, BTN_SZ, "Connect", "/home/matt/src/vanilla/rpi/tex/red.png", VUI_BUTTON_STYLE_BUTTON, layer, NULL, NULL); + vui_button_create(vui, BTN_SZ + (SCREEN_WIDTH-BTN_SZ*2)/2, SCREEN_HEIGHT-BTN_SZ, (SCREEN_WIDTH-BTN_SZ*2)/2, BTN_SZ, lang(VPI_LANG_CONNECT_BTN), NULL, VUI_BUTTON_STYLE_BUTTON, layer, NULL, NULL); // Delete button - vui_button_create(vui, 0, 0, BTN_SZ, BTN_SZ, "Edit", NULL, VUI_BUTTON_STYLE_CORNER, layer, NULL, NULL); - vui_button_create(vui, SCREEN_WIDTH-BTN_SZ, 0, BTN_SZ, BTN_SZ, "Settings", NULL, VUI_BUTTON_STYLE_CORNER, layer, NULL, NULL); + vui_button_create(vui, 0, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_EDIT_BTN), NULL, VUI_BUTTON_STYLE_CORNER, layer, NULL, NULL); + vui_button_create(vui, SCREEN_WIDTH-BTN_SZ, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_SETTINGS_BTN), NULL, VUI_BUTTON_STYLE_CORNER, layer, NULL, NULL); // Console list int list_item_width = SCREEN_WIDTH - BTN_SZ - BTN_SZ; @@ -99,7 +100,7 @@ void vanilla_menu_main(vui_context_t *vui, void *v) for (int i = 0; i < MAIN_MENU_ENTRIES; i++) { main_menu_btns[i] = vui_button_create(vui, BTN_SZ, BTN_SZ + list_item_height * i, list_item_width, list_item_height, NULL, NULL, VUI_BUTTON_STYLE_LIST, console_menu_layer, NULL, NULL); } - vanilla_menu_main_populate(vui); + vpi_menu_main_populate(vui); if (main_menu_first_run) { vui_transition_fade_black_out(vui, NULL, NULL); diff --git a/rpi/menu/menu_main.h b/rpi/menu/menu_main.h index 0b76e6a..8a97b14 100644 --- a/rpi/menu/menu_main.h +++ b/rpi/menu/menu_main.h @@ -3,6 +3,6 @@ #include "ui/ui.h" -void vanilla_menu_main(vui_context_t *vui, void *v); +void vpi_menu_main(vui_context_t *vui, void *v); #endif // VANILLA_PI_MENU_MAIN_H \ No newline at end of file diff --git a/rpi/menu/menu_sudo.c b/rpi/menu/menu_sudo.c new file mode 100644 index 0000000..50250c8 --- /dev/null +++ b/rpi/menu/menu_sudo.c @@ -0,0 +1,55 @@ +#include "menu_sudo.h" + +#include "menu_common.h" +#include "lang.h" +#include "ui/ui_anim.h" + +static int sudo_fglayer; +static vui_callback_t sudo_success; +static void *sudo_success_data; +static vui_callback_t sudo_cancel; +static void *sudo_cancel_data; + +void cancel_sudo(vui_context_t *vui, int button, void *data) +{ + vui_transition_fade_layer_out(vui, sudo_fglayer, sudo_cancel, sudo_cancel_data); +} + +void ok_sudo(vui_context_t *vui, int button, void *data) +{ + vui_transition_fade_layer_out(vui, sudo_fglayer, sudo_success, sudo_success_data); +} + +void vpi_menu_sudo(vui_context_t *vui, vui_callback_t success, void *success_data, vui_callback_t cancel, void *cancel_data) +{ + vui_reset(vui); + + sudo_success = success; + sudo_success_data = success_data; + sudo_cancel = cancel; + sudo_cancel_data = cancel_data; + + int bglayer = vui_layer_create(vui); + sudo_fglayer = vui_layer_create(vui); + + vui_rect_t bkg_rect; + int margin; + vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin); + + int scrw, scrh; + vui_get_screen_size(vui, &scrw, &scrh); + + const int lbl_margin = margin * 3; + 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_SUDO_HELP_1), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sudo_fglayer); + + const int lbl2_width = scrw/2; + vui_label_create(vui, scrw/2 - lbl2_width/2, bkg_rect.y + lbl_margin + scrh/5, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, lang(VPI_LANG_SUDO_HELP_2), vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, sudo_fglayer); + + static const int ok_btn_w = BTN_SZ * 3; + static const int cancel_btn_w = BTN_SZ * 3; + + vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_btn_w + cancel_btn_w)/2, bkg_rect.y + bkg_rect.h * 3 / 4, ok_btn_w, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, sudo_fglayer, ok_sudo, 0); + vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_btn_w + cancel_btn_w)/2 + ok_btn_w, bkg_rect.y + bkg_rect.h * 3 / 4, cancel_btn_w, BTN_SZ, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_BUTTON, sudo_fglayer, cancel_sudo, 0); + + vui_transition_fade_layer_in(vui, sudo_fglayer, 0, 0); +} \ No newline at end of file diff --git a/rpi/menu/menu_sudo.h b/rpi/menu/menu_sudo.h new file mode 100644 index 0000000..1247236 --- /dev/null +++ b/rpi/menu/menu_sudo.h @@ -0,0 +1,8 @@ +#ifndef VANILLA_PI_MENU_SUDO_H +#define VANILLA_PI_MENU_SUDO_H + +#include "ui/ui.h" + +void vpi_menu_sudo(vui_context_t *vui, vui_callback_t success, void *success_data, vui_callback_t cancel, void *cancel_data); + +#endif // VANILLA_PI_MENU_SUDO_H \ No newline at end of file diff --git a/rpi/menu/menu_sync.c b/rpi/menu/menu_sync.c index f5e8cff..7a624ba 100644 --- a/rpi/menu/menu_sync.c +++ b/rpi/menu/menu_sync.c @@ -1,10 +1,15 @@ #include "menu_sync.h" +#include #include #include +#include +#include +#include "lang.h" #include "menu_common.h" #include "menu_main.h" +#include "menu_sudo.h" #include "ui/ui_anim.h" #include "ui/ui_util.h" @@ -31,38 +36,157 @@ static const char *suit_icons_white[4] = { "club_white.svg", }; +void sync_return_to_main(vui_context_t *vui, int button, void *data) +{ + vui_transition_fade_layer_out(vui, sync_fglayer, vpi_menu_sync, (void *) (intptr_t) 1); +} + +void sync_show_success(vui_context_t *vui, void *data) +{ + vui_reset(vui); + + int bglayer = vui_layer_create(vui); + + vui_rect_t bkg_rect; + int margin; + vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin); + + sync_fglayer = vui_layer_create(vui); + + vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h/4, bkg_rect.w, bkg_rect.h, "TEMP: Sync successful!", vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); +} + +void sync_show_error(vui_context_t *vui, void *data) +{ + vui_reset(vui); + + int bglayer = vui_layer_create(vui); + + vui_rect_t bkg_rect; + int margin; + vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin); + + sync_fglayer = vui_layer_create(vui); + + vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h/4, bkg_rect.w, bkg_rect.h, lang(VPI_LANG_SYNC_FAILED), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); + + int ret = (int) (intptr_t) data; + char buf[20]; + sprintf(buf, "%i", ret); + vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h/2, bkg_rect.w, bkg_rect.h, buf, vui_color_create(1,0,0,1), VUI_FONT_SIZE_SMALL, sync_fglayer); + + vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - BTN_SZ, bkg_rect.y + bkg_rect.h * 3 / 4, BTN_SZ*2, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, sync_fglayer, sync_return_to_main, 0); + + vui_transition_fade_layer_in(vui, sync_fglayer, 0, 0); +} + +void sync_success(void *data) +{ + vui_context_t *vui = data; + vui_transition_fade_layer_out(vui, sync_fglayer, sync_show_success, 0); +} + +int intpow(int x, unsigned int p) +{ + if (p == 0) return 1; + if (p == 1) return x; + + int tmp = intpow(x, p/2); + if (p%2 == 0) return tmp * tmp; + else return x * tmp * tmp; +} + +void sync_animation_step(vui_context_t *ctx, int64_t time, void *userdata) +{ + int progress_lbl = (int) (intptr_t) userdata; + + static const int utf8_char_len = 3; // Somewhat hacky, but the important thing is that it works + static const int progress_txt_len = 9; + static const size_t progress_txt_sz = progress_txt_len * utf8_char_len + 1; + + char progress_txt[progress_txt_sz]; + progress_txt[progress_txt_sz-1] = 0; + + int64_t m = int64min((sin(time * 0.000004) * 0.5 + 0.5) * progress_txt_len, progress_txt_len - 1); + for (int i = 0; i < progress_txt_len; i++) { + memcpy(progress_txt + utf8_char_len * i, (i == m) ? "\xE2\xAC\xA4" : "\xE2\x80\xA2", utf8_char_len); + } + + vui_label_update_text(ctx, progress_lbl, progress_txt); + + // Poll Vanilla for sync status + // if (time > 0) { + vanilla_event_t event; + while (vanilla_poll_event(&event)) { + if (event.type == VANILLA_EVENT_SYNC) { + int sync_ret; + memcpy(&sync_ret, event.data, sizeof(int)); + vanilla_free_event(&event); + vanilla_stop(); + vui_transition_fade_layer_out(ctx, sync_fglayer, sync_show_error, (void *) (intptr_t) sync_ret); + } else { + vanilla_free_event(&event); + } + } + // } +} + +void cancel_sync(vui_context_t *vui, int button, void *v) +{ + vanilla_stop(); + sync_return_to_main(vui, button, 0); +} + void start_syncing(vui_context_t *vui, void *v) { vui_reset(vui); int bglayer = vui_layer_create(vui); - int fglayer = vui_layer_create(vui); + sync_fglayer = vui_layer_create(vui); - int scrw, scrh; - vui_get_screen_size(vui, &scrw, &scrh); - - const int margin = scrw/75; vui_rect_t bkg_rect; - bkg_rect.x = margin; - bkg_rect.y = margin; - bkg_rect.w = scrw-margin-margin; - bkg_rect.h = scrh-margin-margin; - - vui_rect_create(vui, bkg_rect.x, bkg_rect.y, bkg_rect.w, bkg_rect.h, scrh*2/10, vui_color_create(0, 0, 0, 0.66f), bglayer); + int margin; + vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin); - vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h/4, bkg_rect.w, bkg_rect.h, "Connecting to the Wii U console...", vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer); + vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*1/5, bkg_rect.w, bkg_rect.h, lang(VPI_LANG_SYNC_CONNECTING), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); - vui_transition_fade_layer_in(vui, fglayer, 0, 0); + uint16_t code = 0; + for (int i = 0; i < SYNC_BTN_COUNT; i++) { + code += sync_str[i] * intpow(10, SYNC_BTN_COUNT - 1 - i); + } + + int progress_lbl = vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*2/5, bkg_rect.w, bkg_rect.h, "", vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); + sync_animation_step(vui, 0, (void *) (intptr_t) progress_lbl); + + vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - BTN_SZ*3/2, bkg_rect.y + bkg_rect.h * 3 / 4, BTN_SZ*3, BTN_SZ, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_BUTTON, sync_fglayer, cancel_sync, 0); + + int ret = vanilla_sync(code, VANILLA_ADDRESS_LOCAL); + if (ret == VANILLA_SUCCESS) { + vui_transition_fade_layer_in(vui, sync_fglayer, 0, 0); + vui_start_passive_animation(vui, sync_animation_step, (void *) (intptr_t) progress_lbl); + } else { + sync_show_error(vui, (void *) (intptr_t) ret); + } +} + +void start_syncing_or_sudo(vui_context_t *vui, void *v) +{ + const int sudo_required = 1; + if (sudo_required) { + vpi_menu_sudo(vui, start_syncing, v, vpi_menu_sync, (void *) (intptr_t) 1); + } else { + start_syncing(vui, v); + } } void transition_to_sync_window(vui_context_t *vui, void *v) { - vui_transition_fade_layer_out(vui, sync_fglayer, start_syncing, 0); + vui_transition_fade_layer_out(vui, sync_fglayer, start_syncing_or_sudo, 0); } -void vanilla_sync_menu_back_action(vui_context_t *vui, int btn, void *v) +void vpi_sync_menu_back_action(vui_context_t *vui, int btn, void *v) { - vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vanilla_menu_main, 0); + vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vpi_menu_main, 0); } void sync_btn_clicked(vui_context_t *vui, int button, void *data) @@ -115,7 +239,7 @@ void create_sync_btn(vui_context_t *vui, int x, int data, int layer, int origin_ sync_btns[data] = btn; } -void vanilla_menu_sync(vui_context_t *vui, void *d) +void vpi_menu_sync(vui_context_t *vui, void *d) { // Clears all extra layers vui_reset(vui); @@ -129,23 +253,18 @@ void vanilla_menu_sync(vui_context_t *vui, void *d) sync_bglayer = vui_layer_create(vui); sync_fglayer = vui_layer_create(vui); + vui_rect_t bkg_rect; + int margin; + vpi_menu_create_background(vui, sync_bglayer, &bkg_rect, &margin); + int scrw, scrh; vui_get_screen_size(vui, &scrw, &scrh); - const int margin = scrw/75; - vui_rect_t bkg_rect; - bkg_rect.x = margin; - bkg_rect.y = margin; - bkg_rect.w = scrw-margin-margin; - bkg_rect.h = scrh-margin-margin; - - vui_rect_create(vui, bkg_rect.x, bkg_rect.y, bkg_rect.w, bkg_rect.h, scrh*2/10, vui_color_create(0, 0, 0, 0.66f), sync_bglayer); - const int lbl_margin = margin * 3; - 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, "Touch the symbols in the order they are displayed on the TV screen from left to right.", vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); + 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_SYNC_HELP_1), vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, sync_fglayer); const int lbl2_width = scrw/2; - vui_label_create(vui, scrw/2 - lbl2_width/2, bkg_rect.y + lbl_margin + scrh/5, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, "If the symbols are not displayed on the TV screen, press the SYNC Button on the Wii U console.", vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, sync_fglayer); + vui_label_create(vui, scrw/2 - lbl2_width/2, bkg_rect.y + lbl_margin + scrh/5, lbl2_width, bkg_rect.h - lbl_margin - lbl_margin, lang(VPI_LANG_SYNC_HELP_2), vui_color_create(0.66f,0.66f,0.66f,1), VUI_FONT_SIZE_SMALL, sync_fglayer); // Create 4 symbol buttons const int sync_btn_x = scrw/2 - (BTN_SZ*SYNC_BTN_COUNT)/2; @@ -170,7 +289,7 @@ void vanilla_menu_sync(vui_context_t *vui, void *d) vui_button_create(vui, sync_btn_x + BTN_SZ * 3, sync_entry_rect.y, BTN_SZ, sync_entry_rect.h, 0, "backspace_black.svg", VUI_BUTTON_STYLE_BUTTON, sync_fglayer, bksp_btn_clicked, 0); int back_btn_h = scrh/6; - vui_button_create(vui, 0, scrh-back_btn_h, scrw/4, back_btn_h, "Cancel", 0, VUI_BUTTON_STYLE_CORNER, sync_fglayer, vanilla_sync_menu_back_action, (void *) (intptr_t) sync_bglayer); + vui_button_create(vui, 0, scrh-back_btn_h, scrw/4, back_btn_h, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_CORNER, sync_fglayer, vpi_sync_menu_back_action, (void *) (intptr_t) sync_bglayer); - vui_transition_fade_layer_in(vui, sync_bglayer, 0, 0); + vui_transition_fade_layer_in(vui, d ? sync_fglayer : sync_bglayer, 0, 0); } \ No newline at end of file diff --git a/rpi/menu/menu_sync.h b/rpi/menu/menu_sync.h index ccdea10..a92b204 100644 --- a/rpi/menu/menu_sync.h +++ b/rpi/menu/menu_sync.h @@ -3,6 +3,6 @@ #include "ui/ui.h" -void vanilla_menu_sync(vui_context_t *vui, void *d); +void vpi_menu_sync(vui_context_t *vui, void *d); #endif // VANILLA_PI_MENU_SYNC_H \ No newline at end of file diff --git a/rpi/ui/ui.c b/rpi/ui/ui.c index 3f238f7..97fdf15 100644 --- a/rpi/ui/ui.c +++ b/rpi/ui/ui.c @@ -30,6 +30,7 @@ int vui_reset(vui_context_t *ctx) ctx->button_count = 0; ctx->label_count = 0; ctx->rect_count = 0; + ctx->passive_animation_count = 0; memset(ctx->images, 0, sizeof(ctx->images)); ctx->animation_enabled = 0; ctx->button_active = -1; @@ -203,12 +204,29 @@ void vui_cancel_animation(vui_context_t *ctx) } } +int vui_start_passive_animation(vui_context_t *ctx, vui_anim_step_callback_t step, void *step_data) +{ + int cur_anim = ctx->passive_animation_count; + if (cur_anim == MAX_BUTTON_COUNT) { + return -1; + } + + ctx->passive_animation_count++; + + // Set layer defaults + vui_animation_t *a = &ctx->passive_animations[cur_anim]; + a->step = step; + a->step_data = step_data; + gettimeofday(&a->start_time, NULL); + + return cur_anim; +} + void vui_start_animation(vui_context_t *ctx, int64_t length, vui_anim_step_callback_t step, void *step_data, vui_callback_t complete, void *complete_data) { vui_cancel_animation(ctx); ctx->animation_enabled = 1; - ctx->animation.progress = 0; ctx->animation.length = length; ctx->animation.step = step; ctx->animation.step_data = step_data; @@ -222,10 +240,10 @@ void vui_start_animation(vui_context_t *ctx, int64_t length, vui_anim_step_callb void vui_update(vui_context_t *ctx) { - if (ctx->animation_enabled) { - struct timeval now; - gettimeofday(&now, NULL); + struct timeval now; + gettimeofday(&now, NULL); + if (ctx->animation_enabled) { int64_t diff = (now.tv_sec - ctx->animation.start_time.tv_sec) * 1000000 + (now.tv_usec - ctx->animation.start_time.tv_usec); if (diff > ctx->animation.length) { @@ -244,6 +262,14 @@ void vui_update(vui_context_t *ctx) } } } + + for (int i = 0; i < ctx->passive_animation_count; i++) { + vui_animation_t *a = &ctx->passive_animations[i]; + int64_t diff = (now.tv_sec - a->start_time.tv_sec) * 1000000 + (now.tv_usec - a->start_time.tv_usec); + if (a->step) { + a->step(ctx, diff, a->step_data); + } + } } int vui_layer_create(vui_context_t *ctx) @@ -318,13 +344,19 @@ int vui_label_create(vui_context_t *ctx, int x, int y, int w, int h, const char lbl->size = size; lbl->layer = layer; - vui_strcpy(lbl->text, text); + vui_label_update_text(ctx, index, text); ctx->label_count++; return index; } +int vui_label_update_text(vui_context_t *ctx, int index, const char *text) +{ + vui_label_t *lbl = &ctx->labels[index]; + vui_strcpy(lbl->text, text); +} + int vui_rect_create(vui_context_t *ctx, int x, int y, int w, int h, int border_radius, vui_color_t color, int layer) { if (ctx->rect_count == MAX_BUTTON_COUNT) { diff --git a/rpi/ui/ui.h b/rpi/ui/ui.h index 2fc4822..6da2fa1 100644 --- a/rpi/ui/ui.h +++ b/rpi/ui/ui.h @@ -72,6 +72,7 @@ void vui_button_update_style(vui_context_t *ctx, int button, vui_button_style_t * Label-related functions */ int vui_label_create(vui_context_t *ctx, int x, int y, int w, int h, const char *text, vui_color_t color, vui_font_size_t size, int layer); +int vui_label_update_text(vui_context_t *ctx, int label, const char *text); /** * Rect-related functions @@ -94,6 +95,7 @@ vui_color_t vui_color_create(float r, float g, float b, float a); */ void vui_start_animation(vui_context_t *ctx, int64_t length, vui_anim_step_callback_t step, void *step_data, vui_callback_t complete, void *complete_data); void vui_cancel_animation(vui_context_t *ctx); +int vui_start_passive_animation(vui_context_t *ctx, vui_anim_step_callback_t step, void *step_data); /** * Input-related functions diff --git a/rpi/ui/ui_priv.h b/rpi/ui/ui_priv.h index da974f3..fdc148b 100644 --- a/rpi/ui/ui_priv.h +++ b/rpi/ui/ui_priv.h @@ -60,9 +60,6 @@ typedef struct { /// @brief System/game time that the animation starts struct timeval start_time; - /// @brief Progress stored in microseconds - int64_t progress; - /// @brief Length stored in microseconds int64_t length; @@ -90,6 +87,8 @@ typedef struct vui_context_t { vui_image_t images[MAX_BUTTON_COUNT]; vui_animation_t animation; int animation_enabled; + vui_animation_t passive_animations[MAX_BUTTON_COUNT]; + int passive_animation_count; int button_active; int screen_width; int screen_height; diff --git a/rpi/ui/ui_sdl.c b/rpi/ui/ui_sdl.c index caea857..4e5751c 100644 --- a/rpi/ui/ui_sdl.c +++ b/rpi/ui/ui_sdl.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "ui_priv.h" #include "ui_util.h" @@ -25,6 +26,7 @@ typedef struct { SDL_Rect dst_rect; SDL_Texture *background; SDL_Texture *layer_data[MAX_BUTTON_COUNT]; + SDL_Texture *label_data[MAX_BUTTON_COUNT]; vui_sdl_cached_texture_t button_cache[MAX_BUTTON_COUNT]; vui_sdl_cached_texture_t image_cache[MAX_BUTTON_COUNT]; } vui_sdl_context_t; @@ -83,6 +85,7 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen) sdl_ctx->background = 0; memset(sdl_ctx->layer_data, 0, sizeof(sdl_ctx->layer_data)); + memset(sdl_ctx->label_data, 0, sizeof(sdl_ctx->label_data)); memset(sdl_ctx->button_cache, 0, sizeof(sdl_ctx->button_cache)); memset(sdl_ctx->image_cache, 0, sizeof(sdl_ctx->image_cache)); @@ -343,33 +346,34 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer) // Draw labels for (int i = 0; i < ctx->label_count; i++) { vui_label_t *lbl = &ctx->labels[i]; + if (lbl->text[0]) { + SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[lbl->layer]); - SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[lbl->layer]); + SDL_Color c; + c.r = lbl->color.r * 0xFF; + c.g = lbl->color.g * 0xFF; + c.b = lbl->color.b * 0xFF; + c.a = lbl->color.a * 0xFF; - SDL_Color c; - c.r = lbl->color.r * 0xFF; - c.g = lbl->color.g * 0xFF; - c.b = lbl->color.b * 0xFF; - c.a = lbl->color.a * 0xFF; + SDL_Surface *surface = TTF_RenderUTF8_Blended_Wrapped(lbl->size == VUI_FONT_SIZE_SMALL ? sysfont_mini : sysfont, lbl->text, c, lbl->w); - SDL_Surface *surface = TTF_RenderUTF8_Blended_Wrapped(lbl->size == VUI_FONT_SIZE_SMALL ? sysfont_mini : sysfont, lbl->text, c, lbl->w); + SDL_Rect sr; + sr.x = 0; + sr.y = 0; + sr.w = intmin(lbl->w, surface->w); + sr.h = intmin(lbl->h, surface->h); - SDL_Rect sr; - sr.x = 0; - sr.y = 0; - sr.w = intmin(lbl->w, surface->w); - sr.h = intmin(lbl->h, surface->h); - - SDL_Rect dr; - dr.x = lbl->x; - dr.y = lbl->y; - dr.w = sr.w; - dr.h = sr.h; - - SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); - SDL_RenderCopy(renderer, texture, &sr, &dr); - SDL_DestroyTexture(texture); - SDL_FreeSurface(surface); + SDL_Rect dr; + dr.x = lbl->x; + dr.y = lbl->y; + dr.w = sr.w; + dr.h = sr.h; + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer, surface); + SDL_RenderCopy(renderer, texture, &sr, &dr); + SDL_DestroyTexture(texture); + SDL_FreeSurface(surface); + } } // Draw buttons @@ -452,6 +456,7 @@ int vui_update_sdl(vui_context_t *vui) SDL_Event ev; while (SDL_PollEvent(&ev)) { if (ev.type == SDL_QUIT) { + vanilla_stop(); return 0; } else if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) { if (dst_rect->w > 0 && dst_rect->h > 0) { diff --git a/rpi/ui/ui_util.h b/rpi/ui/ui_util.h index e573d70..2afd08d 100644 --- a/rpi/ui/ui_util.h +++ b/rpi/ui/ui_util.h @@ -1,9 +1,13 @@ #ifndef VANILLA_PI_UI_UTIL_H #define VANILLA_PI_UI_UTIL_H +#include + void vui_strcpy(char *dst, const char *src); static inline int intmin(int a, int b) { return a < b ? a : b; } static inline int intmax(int a, int b) { return a > b ? a : b; } +static inline int64_t int64min(int64_t a, int64_t b) { return a < b ? a : b; } +static inline int64_t int64max(int64_t a, int64_t b) { return a > b ? a : b; } #endif // VANILLA_PI_UI_UTIL_H \ No newline at end of file