implement actual gameplay

This commit is contained in:
itsmattkc committed 2025-02-13 15:19:04 -08:00
1 parent db9cd2f134
commit 9b370947e1
44 files changed
+5024 -449

No files matched your search

+1 -1
View File
@@ -2,6 +2,6 @@ FROM --platform=linux/arm/v6 arm32v6/alpine
RUN apk update
RUN apk upgrade
RUN apk add build-base cmake libnl3-dev openssl-dev sdl2-dev linux-headers ffmpeg-dev eudev-dev
RUN apk add build-base cmake libnl3-dev openssl-dev sdl2-dev linux-headers ffmpeg-dev eudev-dev libxml2-dev
CMD [ "make", "-j32", "-C", "/data/buildpi" ]
-1
View File
@@ -76,7 +76,6 @@ void *listen_audio(void *x)
do {
size = recv(info->socket_aud, data, sizeof(data), 0);
if (size > 0) {
if (is_stop_code(data, size)) break;
handle_audio_packet(info, data, size);
}
} while (!is_interrupted());
+1 -5
View File
@@ -430,11 +430,7 @@ void *listen_command(void *x)
do
{
size = recv(info->socket_cmd, data, sizeof(data), 0);
if (size > 0)
{
if (is_stop_code(data, size))
break;
if (size > 0) {
CmdHeader *header = (CmdHeader *)data;
handle_command_packet(info->socket_cmd, header);
}
+145 -153
View File
@@ -24,7 +24,6 @@
#include "status.h"
#include "util.h"
static const uint32_t STOP_CODE = 0xCAFEBABE;
static uint32_t SERVER_ADDRESS = 0;
static const int MAX_PIPE_RETRY = 5;
@@ -33,6 +32,11 @@ static const int MAX_PIPE_RETRY = 5;
uint8_t *EVENT_BUFFER_ARENA[EVENT_BUFFER_ARENA_SIZE] = {0};
pthread_mutex_t event_buffer_mutex = PTHREAD_MUTEX_INITIALIZER;
typedef union {
struct sockaddr_in in;
struct sockaddr_un un;
} sockaddr_u;
in_addr_t get_real_server_address()
{
if (SERVER_ADDRESS == VANILLA_ADDRESS_DIRECT) {
@@ -47,38 +51,34 @@ in_addr_t get_real_server_address()
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)
void create_sockaddr(sockaddr_u *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);
memset(&addr->un, 0, sizeof(addr->un));
addr->un.sun_family = AF_UNIX;
snprintf(addr->un.sun_path, sizeof(addr->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;
memset(&addr->in, 0, sizeof(addr->in));
addr->in.sin_family = AF_INET;
addr->in.sin_port = htons(port);
addr->in.sin_addr.s_addr = inaddr;
if (size) *size = sizeof(struct sockaddr_in);
if (addr) *addr = (const struct sockaddr *) in;
}
}
void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
{
struct sockaddr_in in;
struct sockaddr_un un;
const struct sockaddr *addr;
sockaddr_u addr;
size_t addr_size;
in_port_t console_port = port - 100;
create_sockaddr(&in, &un, &addr, &addr_size, get_real_server_address(), console_port);
create_sockaddr(&addr, &addr_size, get_real_server_address(), console_port);
ssize_t sent = sendto(fd, data, data_size, 0, addr, addr_size);
ssize_t sent = sendto(fd, data, data_size, 0, (const struct sockaddr *) &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);
}
@@ -86,76 +86,48 @@ void send_to_console(int fd, const void *data, size_t data_size, uint16_t port)
int create_socket(int *socket_out, in_port_t port)
{
int skt = -1;
struct sockaddr_un un;
struct sockaddr_in in;
sockaddr_u addr;
size_t addr_size;
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;
}
create_sockaddr(&addr, &addr_size, INADDR_ANY, port);
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);
int domain = (SERVER_ADDRESS == VANILLA_ADDRESS_LOCAL) ? AF_UNIX : AF_INET;
int skt = socket(domain, 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 *) &addr, addr_size) == -1) {
print_info("FAILED TO BIND PORT %u: %i", port, errno);
close(skt);
return VANILLA_ERR_BAD_SOCKET;
}
print_info("SUCCESSFULLY BOUND SOCKET %i ON PORT %i", skt, port);
(*socket_out) = skt;
struct timeval tv = {0};
tv.tv_usec = 250000;
setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
return VANILLA_SUCCESS;
}
void send_stop_code(int from_socket, in_port_t port)
{
struct sockaddr_in in;
struct sockaddr_un un;
const struct sockaddr *addr;
size_t addr_size;
// 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, vanilla_pipe_command_t *cmd, size_t cmd_size, int wait_for_reply)
{
struct sockaddr_in in;
struct sockaddr_un un;
const struct sockaddr *addr;
sockaddr_u addr;
size_t addr_size;
create_sockaddr(&in, &un, &addr, &addr_size, get_real_server_address(), VANILLA_PIPE_CMD_PORT);
create_sockaddr(&addr, &addr_size, get_real_server_address(), VANILLA_PIPE_CMD_SERVER_PORT);
ssize_t read_size;
uint32_t recv_cc;
uint8_t recv_cc;
for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) {
// sendto(skt, &send_cc, sizeof(send_cc), 0, addr, addr_size);
if (write(skt, cmd, cmd_size) == -1) {
if (sendto(skt, cmd, cmd_size, 0, (const struct sockaddr *) &addr, addr_size) == -1) {
print_info("Failed to write control code to socket");
return 0;
}
@@ -188,7 +160,7 @@ int connect_to_backend(int *socket, vanilla_pipe_command_t *cmd, size_t cmd_size
{
// Try to bind with backend
int pipe_cc_skt = -1;
int ret = create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_PORT);
int ret = create_socket(&pipe_cc_skt, VANILLA_PIPE_CMD_CLIENT_PORT);
if (ret != VANILLA_SUCCESS) {
return ret;
}
@@ -208,6 +180,13 @@ int connect_to_backend(int *socket, vanilla_pipe_command_t *cmd, size_t cmd_size
return VANILLA_SUCCESS;
}
void wait_for_interrupt()
{
while (!is_interrupted()) {
usleep(100000);
}
}
void sync_internal(thread_data_t *data)
{
clear_interrupt();
@@ -221,29 +200,28 @@ void sync_internal(thread_data_t *data)
cmd.sync.code = htons(code);
int skt = -1;
int ret = connect_to_backend(&skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.sync));
if (ret == VANILLA_SUCCESS) {
vanilla_sync_event_t syncdata;
syncdata.status = connect_to_backend(&skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.sync));
if (syncdata.status == VANILLA_SUCCESS) {
// Wait for sync result from pipe
vanilla_pipe_command_t recv_cmd;
ret = VANILLA_ERR_PIPE_UNRESPONSIVE;
syncdata.status = VANILLA_ERR_PIPE_UNRESPONSIVE;
for (int retries = 0; retries < MAX_PIPE_RETRY; retries++) {
ssize_t read_size = recv(skt, &recv_cmd, sizeof(recv_cmd), 0);
if (recv_cmd.control_code == VANILLA_PIPE_CC_STATUS) {
ret = (int32_t) ntohl(recv_cmd.status.status);
syncdata.status = (int32_t) ntohl(recv_cmd.status.status);
break;
} else if (recv_cmd.control_code == VANILLA_PIPE_CC_SYNC_SUCCESS) {
ret = VANILLA_SUCCESS;
printf("Got BSSID: ");
for (int i = 0; i < sizeof(recv_cmd.connection.bssid); i++) printf("%02X", recv_cmd.connection.bssid[i]);
printf("\nGot PSK: ");
for (int i = 0; i < sizeof(recv_cmd.connection.psk); i++) printf("%02X", recv_cmd.connection.psk[i]);
printf("\n");
syncdata.status = VANILLA_SUCCESS;
syncdata.data.bssid = recv_cmd.connection.bssid;
syncdata.data.psk = recv_cmd.connection.psk;
break;
} else if (recv_cmd.control_code == VANILLA_PIPE_CC_PING) {
// Pipe is still responsive but hasn't found anything yet
printf("received ping from pipe\n");
retries = -1;
}
@@ -254,12 +232,14 @@ void sync_internal(thread_data_t *data)
}
}
push_event(data->event_loop, VANILLA_EVENT_SYNC, &ret, sizeof(ret));
if (syncdata.status == VANILLA_SUCCESS) {
push_event(data->event_loop, VANILLA_EVENT_SYNC, &syncdata, sizeof(syncdata));
} else {
push_event(data->event_loop, VANILLA_EVENT_ERROR, &syncdata.status, sizeof(syncdata.status));
}
// Wait for interrupt so frontend has a chance to receive event
while (!is_interrupted()) {
usleep(100000);
}
wait_for_interrupt();
exit_pipe:
if (skt != -1)
@@ -275,66 +255,75 @@ void connect_as_gamepad_internal(thread_data_t *data)
gamepad_context_t info;
info.event_loop = data->event_loop;
int ret = VANILLA_ERR_GENERIC;
int ret = VANILLA_SUCCESS;
int pipe_cc_skt = 0;
if (SERVER_ADDRESS != 0) {
int pipe_cc_skt = -1;
if (SERVER_ADDRESS != VANILLA_ADDRESS_DIRECT) {
vanilla_pipe_command_t cmd;
cmd.control_code = VANILLA_PIPE_CC_CONNECT;
// TODO: Implement
// cmd.connection.bssid;
// cmd.connection.psk;
cmd.connection.bssid = data->bssid;
cmd.connection.psk = data->psk;
// Connect to backend pipe
ret = connect_to_backend(&pipe_cc_skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
if (ret != VANILLA_SUCCESS) {
goto exit;
// if (ret == VANILLA_SUCCESS) {
// // Wait for backend to be available
// vanilla_pipe_command_t connected_state;
// ret = VANILLA_ERR_NO_CONNECTION;
// while (!is_interrupted()) {
// ssize_t read_size = recv(pipe_cc_skt, &connected_state, sizeof(connected_state), 0);
// if (read_size < 0) {
// if (errno != EAGAIN) {
// ret = VANILLA_ERR_PIPE_UNRESPONSIVE;
// break;
// }
// } else if (connected_state.control_code == VANILLA_PIPE_CC_CONNECTED) {
// ret = VANILLA_SUCCESS;
// sleep(1);
// break;
// }
// print_info("STILL WAITING FOR CONNECTED STATE");
// sleep(1);
// }
// }
}
if (ret == VANILLA_SUCCESS) {
// Open all required sockets
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;
pthread_create(&video_thread, NULL, listen_video, &info);
pthread_setname_np(video_thread, "vanilla-video");
pthread_create(&audio_thread, NULL, listen_audio, &info);
pthread_setname_np(audio_thread, "vanilla-audio");
pthread_create(&input_thread, NULL, listen_input, &info);
pthread_setname_np(input_thread, "vanilla-input");
pthread_create(&cmd_thread, NULL, listen_command, &info);
pthread_setname_np(cmd_thread, "vanilla-cmd");
while (!is_interrupted()) {
// TODO: Should use a wake condition instead
usleep(250 * 1000);
}
pthread_join(video_thread, NULL);
pthread_join(audio_thread, NULL);
pthread_join(input_thread, NULL);
pthread_join(cmd_thread, NULL);
}
// Open all required sockets
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;
pthread_create(&video_thread, NULL, listen_video, &info);
pthread_setname_np(video_thread, "vanilla-video");
pthread_create(&audio_thread, NULL, listen_audio, &info);
pthread_setname_np(audio_thread, "vanilla-audio");
pthread_create(&input_thread, NULL, listen_input, &info);
pthread_setname_np(input_thread, "vanilla-input");
pthread_create(&cmd_thread, NULL, listen_command, &info);
pthread_setname_np(cmd_thread, "vanilla-cmd");
while (1) {
usleep(250 * 1000);
if (is_interrupted()) {
// Wake up any threads that might be blocked on `recv`
send_stop_code(info.socket_msg, PORT_VID);
send_stop_code(info.socket_msg, PORT_AUD);
send_stop_code(info.socket_msg, PORT_CMD);
break;
}
}
pthread_join(video_thread, NULL);
pthread_join(audio_thread, NULL);
pthread_join(input_thread, NULL);
pthread_join(cmd_thread, NULL);
if (SERVER_ADDRESS != 0) {
send_unbind_cc(pipe_cc_skt);
}
ret = VANILLA_SUCCESS;
exit_cmd:
close(info.socket_cmd);
@@ -351,25 +340,35 @@ exit_vid:
close(info.socket_vid);
exit_pipe:
if (pipe_cc_skt)
if (pipe_cc_skt != -1) {
// Disconnect from pipe if necessary
send_unbind_cc(pipe_cc_skt);
close(pipe_cc_skt);
}
exit:
// TODO: Return error code to frontend?
}
if (ret != VANILLA_SUCCESS) {
push_event(info.event_loop, VANILLA_EVENT_ERROR, &ret, sizeof(ret));
}
int is_stop_code(const char *data, size_t data_length)
{
return (data_length == sizeof(STOP_CODE) && !memcmp(data, &STOP_CODE, sizeof(STOP_CODE)));
// Wait for interrupt so frontend has a chance to receive event
wait_for_interrupt();
}
int push_event(event_loop_t *loop, int type, const void *data, size_t size)
{
pthread_mutex_lock(&loop->mutex);
vanilla_event_t *ev = &loop->events[loop->new_index % VANILLA_MAX_EVENT_COUNT];
if (size <= EVENT_BUFFER_SIZE) {
// Prevent rollover by skipping oldest event if necessary
if (loop->new_index == loop->used_index + VANILLA_MAX_EVENT_COUNT) {
vanilla_free_event(&loop->events[loop->used_index % VANILLA_MAX_EVENT_COUNT]);
print_info("SKIPPED EVENT TO PREVENT ROLLOVER (%lu > %lu + %lu)", loop->new_index, loop->used_index, VANILLA_MAX_EVENT_COUNT);
loop->used_index++;
}
vanilla_event_t *ev = &loop->events[loop->new_index % VANILLA_MAX_EVENT_COUNT];
assert(!ev->data);
ev->data = get_event_buffer();
@@ -384,13 +383,6 @@ int push_event(event_loop_t *loop, int type, const void *data, size_t size)
loop->new_index++;
// Prevent rollover by skipping oldest event if necessary
if (loop->new_index > loop->used_index + VANILLA_MAX_EVENT_COUNT) {
vanilla_free_event(&loop->events[loop->used_index % VANILLA_MAX_EVENT_COUNT]);
print_info("SKIPPED EVENT TO PREVENT ROLLOVER (%lu > %lu + %lu)", loop->new_index, loop->used_index, VANILLA_MAX_EVENT_COUNT);
loop->used_index++;
}
pthread_cond_broadcast(&loop->waitcond);
} else {
print_info("FAILED TO PUSH EVENT: wanted %lu, only had %lu. This is a bug, please report to developers.\n", size, EVENT_BUFFER_SIZE);
@@ -442,8 +434,8 @@ void *get_event_buffer()
buf = EVENT_BUFFER_ARENA[i];
EVENT_BUFFER_ARENA[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&event_buffer_mutex);
return buf;
+2 -1
View File
@@ -39,12 +39,13 @@ typedef struct thread_data_t
event_loop_t *event_loop;
thread_start_t thread_start;
void *thread_data;
vanilla_bssid_t bssid;
vanilla_psk_t psk;
} 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);
+1 -1
View File
@@ -53,6 +53,7 @@ void send_idr_request_to_console(int socket_msg)
{
// Make an IDR request to the Wii U?
unsigned char idr_request[] = {1, 0, 0, 0}; // Undocumented
print_info("SENDING IDR");
send_to_console(socket_msg, idr_request, sizeof(idr_request), PORT_MSG);
}
@@ -219,7 +220,6 @@ void *listen_video(void *x)
size = recv(info->socket_vid, data, sizeof(VideoPacket), 0);
if (size > 0) {
if (is_stop_code(data, size)) break;
handle_video_packet(info, vp, size, info->socket_msg);
}
} while (!is_interrupted());
+6 -4
View File
@@ -59,7 +59,7 @@ void *start_event_loop(void *arg)
return 0;
}
int vanilla_start_internal(uint32_t server_address, thread_start_t thread_start, void *thread_data)
int vanilla_start_internal(uint32_t server_address, vanilla_bssid_t bssid, vanilla_psk_t psk, thread_start_t thread_start, void *thread_data)
{
if (pthread_mutex_trylock(&main_mutex) == 0) {
pthread_t other;
@@ -68,6 +68,8 @@ int vanilla_start_internal(uint32_t server_address, thread_start_t thread_start,
data->server_address = server_address;
data->thread_start = thread_start;
data->thread_data = thread_data;
data->bssid = bssid;
data->psk = psk;
// Lock event loop mutex so it can't be set to active until we're ready
pthread_mutex_lock(&event_loop.mutex);
@@ -89,9 +91,9 @@ int vanilla_start_internal(uint32_t server_address, thread_start_t thread_start,
}
}
int vanilla_start(uint32_t server_address)
int vanilla_start(uint32_t server_address, vanilla_bssid_t bssid, vanilla_psk_t psk)
{
return vanilla_start_internal(server_address, connect_as_gamepad_internal, 0);
return vanilla_start_internal(server_address, bssid, psk, connect_as_gamepad_internal, 0);
}
void vanilla_stop()
@@ -172,7 +174,7 @@ void vanilla_set_battery_status(int battery_status)
int vanilla_sync(uint16_t code, uint32_t server_address)
{
return vanilla_start_internal(server_address, sync_internal, (void *) (uintptr_t) code);
return vanilla_start_internal(server_address, (vanilla_bssid_t){.bssid = {0}}, (vanilla_psk_t){.psk = {0}}, sync_internal, (void *) (uintptr_t) code);
}
int vanilla_poll_event(vanilla_event_t *event)
+17 -2
View File
@@ -17,6 +17,7 @@ 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 int VANILLA_ERR_NO_CONNECTION = -8;
static const uint32_t VANILLA_ADDRESS_LOCAL = 0xFFFFFFFF;
static const uint32_t VANILLA_ADDRESS_DIRECT = 0x0;
@@ -69,7 +70,8 @@ enum VanillaEvent
VANILLA_EVENT_VIDEO,
VANILLA_EVENT_AUDIO,
VANILLA_EVENT_VIBRATE,
VANILLA_EVENT_SYNC
VANILLA_EVENT_SYNC,
VANILLA_EVENT_ERROR,
};
enum VanillaRegion
@@ -108,10 +110,23 @@ typedef struct
size_t size;
} vanilla_event_t;
#pragma pack(push, 1)
typedef struct { unsigned char bssid[6]; } vanilla_bssid_t;
typedef struct { unsigned char psk[32]; } vanilla_psk_t;
typedef struct {
vanilla_bssid_t bssid;
vanilla_psk_t psk;
} vanilla_connection_t;
typedef struct {
int status;
vanilla_connection_t data;
} vanilla_sync_event_t;
#pragma pack(pop)
/**
* Start listening for gamepad commands
*/
int vanilla_start(uint32_t server_address);
int vanilla_start(uint32_t server_address, vanilla_bssid_t bssid, vanilla_psk_t psk);
int vanilla_sync(uint16_t code, uint32_t server_address);
int vanilla_poll_event(vanilla_event_t *event);
+7 -8
View File
@@ -1,7 +1,10 @@
#ifndef VANILLA_PIPE_DEF_H
#define VANILLA_PIPE_DEF_H
#define VANILLA_PIPE_CMD_PORT 51000
#include <vanilla.h>
#define VANILLA_PIPE_CMD_SERVER_PORT 51000
#define VANILLA_PIPE_CMD_CLIENT_PORT 51100
#define VANILLA_PIPE_CC_SYNC 0x80
#define VANILLA_PIPE_CC_CONNECT 0x81
@@ -11,8 +14,9 @@
#define VANILLA_PIPE_CC_BUSY 0x85
#define VANILLA_PIPE_CC_UNBIND 0x86
#define VANILLA_PIPE_CC_SYNC_SUCCESS 0x87
#define VANILLA_PIPE_CC_CONNECTED 0x88
#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe.%i"
#define VANILLA_PIPE_LOCAL_SOCKET "/tmp/vanilla-pipe_%i.sock"
#pragma pack(push, 1)
typedef struct {
@@ -23,16 +27,11 @@ typedef struct {
int32_t status;
} vanilla_pipe_status_info_t;
typedef struct {
unsigned char bssid[6];
unsigned char psk[32];
} vanilla_pipe_connection_info_t;
typedef struct {
uint8_t control_code;
union{
vanilla_pipe_sync_info_t sync;
vanilla_pipe_connection_info_t connection;
vanilla_connection_t connection;
vanilla_pipe_status_info_t status;
};
} vanilla_pipe_command_t;
+132 -134
View File
@@ -33,16 +33,18 @@ pthread_mutex_t action_mutex;
int running = 0;
int main_loop = 0;
typedef union {
struct sockaddr_in in;
struct sockaddr_un un;
} sockaddr_u;
typedef struct {
int from_socket;
in_port_t from_port;
int to_socket;
in_addr_t to_address;
in_port_t to_port;
sockaddr_u to_address;
size_t to_address_size;
} relay_ports;
struct in_addr client_address = {0};
struct sync_args {
const char *wireless_interface;
const char *wireless_config;
@@ -53,11 +55,15 @@ struct sync_args {
struct wpa_ctrl *ctrl;
int local;
int skt;
sockaddr_u client;
size_t client_size;
};
struct relay_info {
const char *wireless_interface;
int local;
sockaddr_u client;
size_t client_size;
in_port_t port;
};
@@ -532,60 +538,56 @@ int enable_networkmanager_on_device(const char *wireless_interface)
void *do_relay(void *data)
{
relay_ports *ports = (relay_ports *) data;
char buf[2048];
char buf[4096];
ssize_t read_size;
while (!is_interrupted() && client_address.s_addr != 0) {
while (!is_interrupted()) {
read_size = recv(ports->from_socket, buf, sizeof(buf), 0);
if (read_size <= 0) {
continue;
}
struct sockaddr_in forward = {0};
forward.sin_family = AF_INET;
forward.sin_addr.s_addr = ports->to_address;
forward.sin_port = htons(ports->to_port);
char ip[20];
inet_ntop(AF_INET, &forward.sin_addr, ip, sizeof(ip));
// printf("received packet on port %i, forwarding to address %s:%i\n", ports->from_port, ip, ports->to_port);
sendto(ports->to_socket, buf, read_size, 0, (const struct sockaddr *) &forward, sizeof(forward));
if (sendto(ports->to_socket, buf, read_size, 0, (const struct sockaddr *) &ports->to_address, ports->to_address_size) == -1) {
if (ports->to_address_size == sizeof(struct sockaddr_un)) {
print_info("FAILED TO SENDTO \"%s\" (%i)", ports->to_address.un.sun_path, errno);
} else if (ports->to_address_size == sizeof(struct sockaddr_in)) {
char ip[20];
inet_ntop(AF_INET, &ports->to_address.in.sin_addr, ip, sizeof(ip));
print_info("FAILED TO SENDTO %s:%u (%i)", ip, ports->to_address.in.sin_port, errno);
} else {
print_info("FAILED TO SENDTO - INVALID SIZE: %zu", ports->to_address_size);
}
}
}
return NULL;
}
relay_ports create_ports(int from_socket, in_port_t from_port, int to_socket, in_addr_t to_addr, in_port_t to_port)
relay_ports create_ports(int from_socket, int to_socket, const sockaddr_u *to_addr, size_t to_addr_size)
{
relay_ports ports;
ports.from_socket = from_socket;
ports.from_port = from_port;
ports.to_socket = to_socket;
ports.to_address = to_addr;
ports.to_port = to_port;
ports.to_address = *to_addr;
ports.to_address_size = to_addr_size;
return ports;
}
int open_socket(int local, in_port_t port)
{
struct sockaddr_in in = {0};
struct sockaddr_un un = {0};
const struct sockaddr *sa;
sockaddr_u 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);
skt = socket(AF_UNIX, SOCK_DGRAM, 0);
sa.un.sun_family = AF_UNIX;
snprintf(sa.un.sun_path, sizeof(sa.un.sun_path) - 1, VANILLA_PIPE_LOCAL_SOCKET, port);
sa_size = sizeof(struct sockaddr_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 *) &in;
sa_size = sizeof(in);
sa.in.sin_family = AF_INET;
sa.in.sin_addr.s_addr = INADDR_ANY;
sa.in.sin_port = htons(port);
sa_size = sizeof(struct sockaddr_in);
}
if (skt == -1) {
return -1;
@@ -595,18 +597,12 @@ int open_socket(int local, in_port_t port)
tv.tv_usec = 250000;
setsockopt(skt, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
if (bind(skt, sa, sa_size) == -1) {
if (bind(skt, (const struct sockaddr *) &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;
}
@@ -625,27 +621,45 @@ 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(info->local, port + 100);
int from_frontend = open_socket(info->local, port - 100);
if (from_frontend == -1) {
goto close_console_connection;
}
struct sockaddr_in console_addr = {0};
console_addr.sin_family = AF_INET;
console_addr.sin_addr.s_addr = inet_addr("192.168.1.10");
console_addr.sin_port = htons(port - 100);
sockaddr_u frontend_addr;
size_t frontend_addr_size;
if (info->local) {
memset(&frontend_addr.un, 0, sizeof(frontend_addr.un));
frontend_addr.un.sun_family = AF_UNIX;
snprintf(frontend_addr.un.sun_path, sizeof(frontend_addr.un.sun_path), VANILLA_PIPE_LOCAL_SOCKET, port);
frontend_addr_size = sizeof(frontend_addr.un);
} else {
memset(&frontend_addr.in, 0, sizeof(frontend_addr.in));
frontend_addr.in.sin_family = AF_INET;
frontend_addr.in.sin_addr = info->client.in.sin_addr;
frontend_addr.in.sin_port = htons(port);
frontend_addr_size = sizeof(frontend_addr.in);
}
// print_info("ENTERING MAIN LOOP");
while (!is_interrupted()) {
if (client_address.s_addr != 0) {
print_info("STARTED RELAYS");
relay_ports console_to_frontend = create_ports(from_console, port, from_frontend, client_address.s_addr, port + 200);
relay_ports frontend_to_console = create_ports(from_frontend, port + 100, from_console, inet_addr("192.168.1.10"), port - 100);
print_info("STARTED RELAYS");
relay_ports console_to_frontend = create_ports(from_console, from_frontend, &frontend_addr, frontend_addr_size);
relay_ports frontend_to_console = create_ports(from_frontend, from_console, (sockaddr_u *) &console_addr, sizeof(struct sockaddr_in));
pthread_t a_thread, b_thread;
pthread_create(&a_thread, NULL, do_relay, &console_to_frontend);
pthread_create(&b_thread, NULL, do_relay, &frontend_to_console);
pthread_t a_thread, b_thread;
pthread_create(&a_thread, NULL, do_relay, &console_to_frontend);
pthread_create(&b_thread, NULL, do_relay, &frontend_to_console);
pthread_join(a_thread, NULL);
pthread_join(b_thread, NULL);
pthread_join(a_thread, NULL);
pthread_join(b_thread, NULL);
print_info("STOPPED RELAYS");
}
print_info("STOPPED RELAYS");
// print_info("RELAY EXITED");
}
@@ -662,7 +676,7 @@ close:
return THREADRESULT(ret);
}
void create_all_relays(int local, const char *wireless_interface)
void create_all_relays(int skt, int local, const char *wireless_interface, sockaddr_u client, size_t client_size)
{
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;
@@ -670,6 +684,8 @@ void create_all_relays(int local, const char *wireless_interface)
// 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.client = aud_info.client = msg_info.client = cmd_info.client = hid_info.client = client;
vid_info.client_size = aud_info.client_size = msg_info.client_size = cmd_info.client_size = hid_info.client_size = client_size;
vid_info.port = PORT_VID;
aud_info.port = PORT_AUD;
@@ -683,6 +699,11 @@ void create_all_relays(int local, const char *wireless_interface)
pthread_create(&cmd_thread, NULL, open_relay, &cmd_info);
pthread_create(&hid_thread, NULL, open_relay, &hid_info);
// Notify client that we are connected
vanilla_pipe_command_t cmd;
cmd.control_code = VANILLA_PIPE_CC_CONNECTED;
sendto(skt, &cmd, sizeof(cmd.control_code), 0, (const struct sockaddr *) &client, client_size);
pthread_join(vid_thread, NULL);
pthread_join(aud_thread, NULL);
pthread_join(msg_thread, NULL);
@@ -776,15 +797,14 @@ void bytes_to_str(unsigned char *data, size_t data_size, const char *separator,
size_t output_advance = 0;
size_t separator_size = separator ? strlen(separator) : 0;
for (size_t i = 0; i < data_size; i++) {
unsigned char byte = data[i];
sprintf(output + output_advance, "%x", byte & 0xFF);
output_advance += 2;
if (separator_size > 0) {
memcpy(output + output_advance, separator, separator_size);
if (separator_size > 0 && i > 0) {
strncpy(output + output_advance, separator, separator_size);
output_advance += separator_size;
}
unsigned char byte = data[i];
sprintf(output + output_advance, "%02x", byte & 0xFF);
output_advance += 2;
}
output[output_advance] = 0;
}
int create_connect_config(const char *filename, unsigned char *bssid, unsigned char *psk)
@@ -820,10 +840,9 @@ int create_connect_config(const char *filename, unsigned char *bssid, unsigned c
memcpy(ssid_str, "WiiU", 4);
// FIXME: Hardcoded sizes
bytes_to_str(bssid, 6, 0, ssid_str + 4);
bytes_to_str(bssid, 6, ":", bssid_str);
bytes_to_str(psk, 32, 0, psk_str);
bytes_to_str(bssid, sizeof(vanilla_bssid_t), 0, ssid_str + 4);
bytes_to_str(bssid, sizeof(vanilla_bssid_t), ":", bssid_str);
bytes_to_str(psk, sizeof(vanilla_psk_t), 0, psk_str);
fprintf(out_file, template, bssid_str, ssid_str, psk_str);
fclose(out_file);
@@ -852,7 +871,7 @@ void *sync_with_console_internal(void *data)
vanilla_pipe_command_t cmd;
cmd.control_code = VANILLA_PIPE_CC_PING;
if (send(args->skt, &cmd, sizeof(cmd.control_code), MSG_NOSIGNAL) == -1) {
if (sendto(args->skt, &cmd, sizeof(cmd.control_code), 0, (const struct sockaddr *) &args->client, args->client_size) == -1) {
// Client has probably disconnected
interrupt();
goto exit_loop;
@@ -882,7 +901,7 @@ void *sync_with_console_internal(void *data)
while (line) {
if (is_interrupted()) goto exit_loop;
if (strstr(line, "WiiU")) {
if (strstr(line, "WiiU") && strstr(line, "_STA1")) {
print_info("FOUND WII U, TESTING WPS PIN");
// Make copy of bssid for later
@@ -942,7 +961,7 @@ void *sync_with_console_internal(void *data)
char buf[150];
while (len = read_line_from_file(in_file, buf, sizeof(buf))) {
if (memcmp("\tpsk=", buf, 5) == 0) {
str_to_bytes(buf + 5, 0, cmd.connection.psk, sizeof(cmd.connection.psk));
str_to_bytes(buf + 5, 0, cmd.connection.psk.psk, sizeof(cmd.connection.psk.psk));
break;
}
}
@@ -950,9 +969,9 @@ void *sync_with_console_internal(void *data)
fclose(in_file);
// Convert BSSID from string to bytes
str_to_bytes(bssid, 1, cmd.connection.bssid, sizeof(cmd.connection.bssid));
str_to_bytes(bssid, 1, cmd.connection.bssid.bssid, sizeof(cmd.connection.bssid.bssid));
write(args->skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection));
sendto(args->skt, &cmd, sizeof(cmd.control_code) + sizeof(cmd.connection), 0, (const struct sockaddr *) &args->client, args->client_size);
ret = VANILLA_SUCCESS;
} else {
@@ -1010,7 +1029,7 @@ void *do_connect(void *data)
print_info("DHCP ESTABLISHED");
}
create_all_relays(args->local, args->wireless_interface);
create_all_relays(args->skt, args->local, args->wireless_interface, args->client, args->client_size);
int kill_ret = kill(dhclient_pid, SIGTERM);
print_info("KILLING DHCLIENT %i", dhclient_pid);
@@ -1058,7 +1077,7 @@ void vanilla_listen(int local, const char *wireless_interface)
// Ensure local domain sockets can be written to by everyone
umask(0000);
int skt = open_socket(local, VANILLA_PIPE_CMD_PORT);
int skt = open_socket(local, VANILLA_PIPE_CMD_SERVER_PORT);
if (skt == -1) {
pprint("Failed to open server socket\n");
return;
@@ -1066,8 +1085,7 @@ void vanilla_listen(int local, const char *wireless_interface)
vanilla_pipe_command_t cmd;
struct sockaddr_in addr = {0};
socklen_t addr_size = sizeof(addr);
sockaddr_u addr;
pthread_mutex_init(&running_mutex, NULL);
pthread_mutex_init(&action_mutex, NULL);
@@ -1088,75 +1106,55 @@ void vanilla_listen(int local, const char *wireless_interface)
while (main_loop) {
pthread_mutex_unlock(&main_loop_mutex);
int data_socket = accept(skt, 0, 0);
if (data_socket == -1) {
if (errno != EAGAIN)
print_info("Failed to accept: %i", errno);
goto repeat_loop;
socklen_t addr_size = sizeof(addr);
ssize_t r = recvfrom(skt, &cmd, sizeof(cmd), 0, (struct sockaddr *) &addr, &addr_size);
// ssize_t r = read(skt, &cmd, sizeof(cmd));
if (r <= 0) {
continue;
}
pthread_mutex_lock(&main_loop_mutex);
if (cmd.control_code == VANILLA_PIPE_CC_SYNC || cmd.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->local = local;
args->skt = skt;
args->client = addr;
args->client_size = addr_size;
while (main_loop) {
pthread_mutex_unlock(&main_loop_mutex);
// ssize_t r = recvfrom(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, &addr_size);
ssize_t r = read(data_socket, &cmd, sizeof(cmd));
if (r <= 0) {
break;
}
if (cmd.control_code == VANILLA_PIPE_CC_SYNC || cmd.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->local = local;
args->skt = data_socket;
if (cmd.control_code == VANILLA_PIPE_CC_SYNC) {
args->code = ntohs(cmd.sync.code);
args->start_routine = vanilla_sync_with_console;
} else {
memcpy(args->bssid, cmd.connection.bssid, sizeof(cmd.connection.bssid));
memcpy(args->psk, cmd.connection.psk, sizeof(cmd.connection.psk));
args->start_routine = vanilla_connect_to_console;
}
client_address = addr.sin_addr;
// Acknowledge
cmd.control_code = VANILLA_PIPE_CC_BIND_ACK;
// sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr));
write(data_socket, &cmd, sizeof(cmd.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);
}
if (cmd.control_code == VANILLA_PIPE_CC_SYNC) {
args->code = ntohs(cmd.sync.code);
args->start_routine = vanilla_sync_with_console;
} else {
// Busy
cmd.control_code = VANILLA_PIPE_CC_BUSY;
// sendto(skt, &control_code, sizeof(control_code), 0, (struct sockaddr *) &addr, sizeof(addr));
write(data_socket, &cmd, sizeof(cmd.control_code));
memcpy(args->bssid, cmd.connection.bssid.bssid, sizeof(cmd.connection.bssid.bssid));
memcpy(args->psk, cmd.connection.psk.psk, sizeof(cmd.connection.psk.psk));
args->start_routine = vanilla_connect_to_console;
}
// Acknowledge
cmd.control_code = VANILLA_PIPE_CC_BIND_ACK;
if (sendto(skt, &cmd, sizeof(cmd.control_code), 0, (const struct sockaddr *) &addr, addr_size) == -1) {
print_info("FAILED TO SEND ACK: %i", errno);
}
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
cmd.control_code = VANILLA_PIPE_CC_BUSY;
if (sendto(skt, &cmd, sizeof(cmd.control_code), 0, (const struct sockaddr *) &addr, addr_size) == -1) {
print_info("FAILED TO SEND BUSY: %i", errno);
}
} else if (cmd.control_code == VANILLA_PIPE_CC_UNBIND) {
print_info("RECEIVED UNBIND SIGNAL");
interrupt();
}
repeat_read_loop:
pthread_mutex_lock(&main_loop_mutex);
} else if (cmd.control_code == VANILLA_PIPE_CC_UNBIND) {
print_info("RECEIVED UNBIND SIGNAL");
interrupt();
}
if (data_socket != -1) {
close(data_socket);
data_socket = -1;
}
pthread_mutex_unlock(&main_loop_mutex);
repeat_loop:
pthread_mutex_lock(&main_loop_mutex);
}
+10
View File
@@ -2,14 +2,23 @@ find_package(SDL2 REQUIRED)
find_package(SDL2_ttf REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil avfilter swscale)
find_package(libxml2 REQUIRED)
add_executable(vanilla-pi
config.c
drm.c
lang.c
game/game_decode.c
game/game_main.c
main2.c
menu/menu.c
menu/menu_common.c
menu/menu_delete.c
menu/menu_edit.c
menu/menu_game.c
menu/menu_gamepad.c
menu/menu_main.c
menu/menu_settings.c
menu/menu_sudo.c
menu/menu_sync.c
ui/ui.c
@@ -40,6 +49,7 @@ target_link_libraries(vanilla-pi PRIVATE
FFmpeg::avcodec
FFmpeg::avutil
FFmpeg::swscale
LibXml2::LibXml2
drm
m
+181
View File
@@ -0,0 +1,181 @@
#include "config.h"
#include <libxml/xmlreader.h>
#include <libxml/xmlwriter.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
vpi_config_t vpi_config;
int vpi_config_filename(char *out, size_t out_size)
{
return snprintf(out, out_size, "%s/.config/vanilla.conf", getenv("HOME"));
}
void hex_to_string(char *output, uint8_t *data, size_t count)
{
for (size_t i = 0; i < count; i++) {
sprintf(output + i * 2, "%02x", data[i] & 0xFF);
}
}
void string_to_hex(unsigned char *output, size_t length, const char *input)
{
char c[3];
c[2] = 0;
for (size_t i = 0; i < length; i++) {
memcpy(c, input + i * 2, 2);
output[i] = strtol(c, 0, 16);
}
}
void vpi_config_save()
{
char config_fn[1024];
vpi_config_filename(config_fn, sizeof(config_fn));
xmlTextWriterPtr writer = xmlNewTextWriterFilename(config_fn, 0);
if (!writer) {
fprintf(stderr, "Error: xmlNewTextWriterFilename\n");
return;
}
char buf[0x100];
xmlTextWriterStartDocument(writer, 0, 0, 0);
xmlTextWriterSetIndent(writer, 1);
xmlTextWriterSetIndentString(writer, BAD_CAST "\t");
xmlTextWriterStartElement(writer, BAD_CAST "vanilla");
xmlTextWriterStartElement(writer, BAD_CAST "consoles");
for (uint8_t i = 0; i < vpi_config.connected_console_count; i++) {
vpi_console_entry_t *entry = &vpi_config.connected_console_entries[i];
xmlTextWriterStartElement(writer, BAD_CAST "console");
xmlTextWriterWriteElement(writer, BAD_CAST "name", entry->name);
hex_to_string(buf, entry->bssid.bssid, sizeof(vanilla_bssid_t));
xmlTextWriterWriteElement(writer, BAD_CAST "bssid", buf);
hex_to_string(buf, entry->psk.psk, sizeof(vanilla_psk_t));
xmlTextWriterWriteElement(writer, BAD_CAST "psk", buf);
xmlTextWriterEndElement(writer); // console
}
xmlTextWriterEndElement(writer); // consoles
xmlTextWriterEndElement(writer); // vanilla
xmlTextWriterEndDocument(writer);
xmlFreeTextWriter(writer);
}
void vpi_config_init()
{
LIBXML_TEST_VERSION;
// Set defaults
memset(&vpi_config, 0, sizeof(vpi_config));
vpi_config.server_address = VANILLA_ADDRESS_LOCAL;
// Load from file
char config_fn[1024];
vpi_config_filename(config_fn, sizeof(config_fn));
xmlDocPtr doc = xmlReadFile(config_fn, 0, 0);
if (doc) {
xmlNodePtr root = xmlDocGetRootElement(doc);
if (root) {
xmlNodePtr child = root->children;
while (child) {
if (child->type == XML_ELEMENT_NODE) {
if (!strcmp(child->name, "consoles")) {
// Firstly, count the number of consoles
xmlNodePtr console = child->children;
while (console) {
if (console->type == XML_ELEMENT_NODE && !strcmp(console->name, "console")) {
vpi_config.connected_console_count++;
}
console = console->next;
}
// Allocate memory for console entries
vpi_config.connected_console_entries = malloc(vpi_config.connected_console_count * sizeof(vpi_console_entry_t));
vpi_console_entry_t *entry = vpi_config.connected_console_entries;
console = child->children;
while (console) {
if (console->type == XML_ELEMENT_NODE && !strcmp(console->name, "console")) {
xmlNodePtr console_info = console->children;
while (console_info) {
if (!strcmp(console_info->name, "name")) {
strncpy(entry->name, console_info->children->content, sizeof(entry->name));
} else if (!strcmp(console_info->name, "bssid")) {
string_to_hex(entry->bssid.bssid, sizeof(entry->bssid), console_info->children->content);
} else if (!strcmp(console_info->name, "psk")) {
string_to_hex(entry->psk.psk, sizeof(entry->psk), console_info->children->content);
}
console_info = console_info->next;
}
entry++;
}
console = console->next;
}
}
}
child = child->next;
}
}
xmlFreeDoc(doc);
}
}
void vpi_config_free()
{
free(vpi_config.connected_console_entries);
}
void vpi_config_add_console(vpi_console_entry_t *entry)
{
if (vpi_config.connected_console_count == UINT8_MAX) {
// Nothing to do
return;
}
vpi_console_entry_t *old_entries = vpi_config.connected_console_entries;
vpi_config.connected_console_entries = malloc((vpi_config.connected_console_count + 1) * sizeof(vpi_console_entry_t));
if (vpi_config.connected_console_count > 0)
memcpy(vpi_config.connected_console_entries, old_entries, vpi_config.connected_console_count * sizeof(vpi_console_entry_t));
memcpy(vpi_config.connected_console_entries + vpi_config.connected_console_count, entry, sizeof(vpi_console_entry_t));
vpi_config.connected_console_count++;
if (old_entries)
free(old_entries);
vpi_config_save();
}
void vpi_config_remove_console(uint8_t index)
{
if (index >= vpi_config.connected_console_count) {
// Nothing to do
return;
}
vpi_console_entry_t *old_entries = vpi_config.connected_console_entries;
if (vpi_config.connected_console_count != 1) {
vpi_config.connected_console_entries = malloc((vpi_config.connected_console_count - 1) * sizeof(vpi_console_entry_t));
// Copy first half
memcpy(vpi_config.connected_console_entries, old_entries, index * sizeof(vpi_console_entry_t));
// Copy second half
memcpy(vpi_config.connected_console_entries + index, old_entries + index + 1, (vpi_config.connected_console_count - index - 1) * sizeof(vpi_console_entry_t));
}
vpi_config.connected_console_count--;
free(old_entries);
vpi_config_save();
}
+28
View File
@@ -0,0 +1,28 @@
#ifndef VANILLA_PI_CONFIG_H
#define VANILLA_PI_CONFIG_
#include <stdint.h>
#include <vanilla.h>
#define VPI_CONSOLE_MAX_NAME 256
typedef struct {
char name[VPI_CONSOLE_MAX_NAME];
vanilla_bssid_t bssid;
vanilla_psk_t psk;
} vpi_console_entry_t;
typedef struct {
uint8_t connected_console_count;
vpi_console_entry_t *connected_console_entries;
uint32_t server_address;
} vpi_config_t;
extern vpi_config_t vpi_config;
void vpi_config_init();
void vpi_config_free();
void vpi_config_add_console(vpi_console_entry_t *entry);
void vpi_config_remove_console(uint8_t index);
#endif // VANILLA_PI_CONFIG_H
+6
View File
@@ -0,0 +1,6 @@
#ifndef VANILLA_PI_DEF_H
#define VANILLA_PI_DEF_H
#define VANILLA_PI_ERR_DECODER -1001
#endif // VANILLA_PI_DEF_H
+196
View File
@@ -0,0 +1,196 @@
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <pthread.h>
#include <vanilla.h>
#include "def.h"
#include "game_main.h"
AVFrame *vpi_present_frame = 0;
static AVFrame *decoding_frame;
pthread_mutex_t vpi_decoding_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t decoding_wait_cond = PTHREAD_COND_INITIALIZER;
static AVCodecContext *video_codec_ctx;
static AVPacket *video_packet;
pthread_mutex_t vpi_decode_loop_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t vpi_decode_loop_cond = PTHREAD_COND_INITIALIZER;
int vpi_decode_loop_running = 0;
uint8_t vpi_decode_ready = 0;
uint8_t vpi_decode_data[65536];
size_t vpi_decode_size = 0;
enum AVPixelFormat get_format(AVCodecContext* ctx, const enum AVPixelFormat *pix_fmt)
{
while (*pix_fmt != AV_PIX_FMT_NONE) {
if (*pix_fmt == AV_PIX_FMT_DRM_PRIME) {
return AV_PIX_FMT_DRM_PRIME;
}
pix_fmt++;
}
return AV_PIX_FMT_NONE;
}
int decode()
{
int err;
int ret = 1;
// Retrieve frame from decoder
while (1) {
err = avcodec_receive_frame(video_codec_ctx, decoding_frame);
if (err == AVERROR(EAGAIN)) {
// Decoder wants another packet before it can output a frame. Silently exit.
break;
} else if (err < 0) {
fprintf(stderr, "Failed to receive frame from decoder: %i\n", err);
ret = 0;
break;
} else if (!decoding_frame->data[0]) {
fprintf(stderr, "WE GOT A NULL DATA[0] STRAIGHT FROM THE DECODER?????\n");
abort();
} else if ((decoding_frame->flags & AV_FRAME_FLAG_CORRUPT) != 0) {
fprintf(stderr, "GOT A CORRUPT FRAME??????\n");
abort();
} else {
pthread_mutex_lock(&vpi_decoding_mutex);
// Swap refs from decoding_frame to present_frame
av_frame_unref(vpi_present_frame);
av_frame_move_ref(vpi_present_frame, decoding_frame);
pthread_cond_broadcast(&decoding_wait_cond);
pthread_mutex_unlock(&vpi_decoding_mutex);
}
}
return ret;
}
void *vpi_decode_loop(void *)
{
int ret = VANILLA_PI_ERR_DECODER;
int ffmpeg_err;
// Initialize FFmpeg
int using_v4l2m2m = 0;
const AVCodec *codec = 0;
if (using_v4l2m2m) {
codec = avcodec_find_decoder_by_name("h264_v4l2m2m");
}
if (!codec) {
codec = avcodec_find_decoder(AV_CODEC_ID_H264);
using_v4l2m2m = 0;
}
if (!codec) {
fprintf(stderr, "No decoder was available\n");
goto exit;
}
video_codec_ctx = avcodec_alloc_context3(codec);
if (!video_codec_ctx) {
fprintf(stderr, "Failed to allocate codec context\n");
goto free_context;
}
if (using_v4l2m2m) {
ffmpeg_err = av_hwdevice_ctx_create(&video_codec_ctx->hw_device_ctx, AV_HWDEVICE_TYPE_DRM, "/dev/dri/card0", NULL, 0);
if (ffmpeg_err < 0) {
fprintf(stderr, "Failed to create hwdevice context: %s (%i)\n", av_err2str(ffmpeg_err), ffmpeg_err);
goto free_context;
}
// MAKE SURE WE GET DRM PRIME FRAMES BY OVERRIDING THE GET_FORMAT FUNCTION
video_codec_ctx->get_format = get_format;
}
ffmpeg_err = avcodec_open2(video_codec_ctx, codec, NULL);
if (ffmpeg_err < 0) {
fprintf(stderr, "Failed to open decoder: %i\n", ffmpeg_err);
goto free_context;
}
decoding_frame = av_frame_alloc();
if (!decoding_frame) {
fprintf(stderr, "Failed to allocate AVFrame\n");
goto free_context;
}
vpi_present_frame = av_frame_alloc();
if (!vpi_present_frame) {
fprintf(stderr, "Failed to allocate AVFrame\n");
goto free_decode_frame;
}
video_packet = av_packet_alloc();
if (!video_packet) {
fprintf(stderr, "Failed to allocate AVPacket\n");
goto free_present_frame;
}
AVFrame *frame = av_frame_alloc();
pthread_mutex_lock(&vpi_decode_loop_mutex);
while (vpi_decode_loop_running) {
while (vpi_decode_loop_running && !vpi_decode_ready) {
pthread_cond_wait(&vpi_decode_loop_cond, &vpi_decode_loop_mutex);
}
if (!vpi_decode_loop_running) {
break;
}
// Send packet to decoder
video_packet->data = vpi_decode_data;
video_packet->size = vpi_decode_size;
vpi_decode_ready = 0;
int err = avcodec_send_packet(video_codec_ctx, video_packet);
pthread_mutex_unlock(&vpi_decode_loop_mutex);
if (err < 0) {
fprintf(stderr, "Failed to send packet to decoder: %s (%i)\n", av_err2str(err), err);
// return 0;
vanilla_request_idr();
} else {
decode();
}
pthread_mutex_lock(&vpi_decode_loop_mutex);
}
pthread_mutex_unlock(&vpi_decode_loop_mutex);
ret = VANILLA_SUCCESS;
// TEMP: Signals to DRM thread
// pthread_mutex_lock(&decoding_mutex);
// running = 0;
// pthread_cond_broadcast(&decoding_wait_cond);
// pthread_mutex_unlock(&decoding_mutex);
av_frame_free(&frame);
free_packet:
av_packet_free(&video_packet);
free_present_frame:
av_frame_free(&vpi_present_frame);
free_decode_frame:
av_frame_free(&decoding_frame);
free_context:
avcodec_free_context(&video_codec_ctx);
exit:
if (ret != VANILLA_SUCCESS) {
vpi_game_set_error(ret);
}
return 0;
}
+17
View File
@@ -0,0 +1,17 @@
#ifndef VPI_GAME_DECODE_H
#define VPI_GAME_DECODE_H
#include <libavutil/frame.h>
extern pthread_mutex_t vpi_decode_loop_mutex;
extern pthread_cond_t vpi_decode_loop_cond;
extern int vpi_decode_loop_running;
extern uint8_t vpi_decode_data[65536];
extern uint8_t vpi_decode_ready;
extern size_t vpi_decode_size;
extern AVFrame *vpi_present_frame;
extern pthread_mutex_t vpi_decoding_mutex;
void *vpi_decode_loop(void *);
#endif // VPI_GAME_DECODE_H
+10
View File
@@ -0,0 +1,10 @@
#ifndef GAME_DECODE_MEDIACODEC_H
#define GAME_DECODE_MEDIACODEC_H
#include <NdkMediaCodec.h>
/**
* Decoding information for Android
*/
#endif // GAME_DECODE_MEDIACODEC_H
+84
View File
@@ -0,0 +1,84 @@
// TEMP: Remove this...
#include <libavutil/avutil.h>
#include <pthread.h>
#include <vanilla.h>
#include "game_decode.h"
#include "ui/ui.h"
static pthread_t vpi_event_handler_thread;
static int vpi_game_error_value;
static pthread_mutex_t vpi_error_mutex = PTHREAD_MUTEX_INITIALIZER;
int vpi_game_error()
{
int r;
pthread_mutex_lock(&vpi_error_mutex);
r = vpi_game_error_value;
pthread_mutex_unlock(&vpi_error_mutex);
return r;
}
void vpi_game_set_error(int r)
{
pthread_mutex_lock(&vpi_error_mutex);
vpi_game_error_value = r;
pthread_mutex_unlock(&vpi_error_mutex);
}
void *vpi_event_handler(void *data)
{
vanilla_event_t event;
vpi_decode_loop_running = 1;
pthread_t decode_thread;
pthread_create(&decode_thread, 0, vpi_decode_loop, 0);
vui_context_t *vui = (vui_context_t *) data;
while (vanilla_wait_event(&event)) {
int stop = 0;
if (event.type == VANILLA_EVENT_VIDEO) {
pthread_mutex_lock(&vpi_decode_loop_mutex);
vpi_decode_size = event.size;
memcpy(vpi_decode_data, event.data, event.size);
vpi_decode_ready = 1;
pthread_cond_broadcast(&vpi_decode_loop_cond);
pthread_mutex_unlock(&vpi_decode_loop_mutex);
} else if (event.type == VANILLA_EVENT_AUDIO) {
vui_audio_push(vui, event.data, event.size);
} else if (event.type == VANILLA_EVENT_VIBRATE) {
vui_vibrate_set(vui, event.data[0]);
} else if (event.type == VANILLA_EVENT_ERROR) {
vpi_game_set_error(*(int *)event.data);
stop = 1;
}
vanilla_free_event(&event);
if (stop) {
vanilla_stop();
break;
}
}
pthread_mutex_lock(&vpi_decode_loop_mutex);
vpi_decode_loop_running = 0;
pthread_cond_broadcast(&vpi_decode_loop_cond);
pthread_mutex_unlock(&vpi_decode_loop_mutex);
pthread_join(decode_thread, 0);
return 0;
}
void vpi_game_start(vui_context_t *vui)
{
vpi_game_error_value = VANILLA_SUCCESS;
pthread_create(&vpi_event_handler_thread, 0, vpi_event_handler, vui);
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef VPI_GAME_MAIN_H
#define VPI_GAME_MAIN_H
#include "ui/ui.h"
void vpi_game_start(vui_context_t *vui);
int vpi_game_error();
void vpi_game_set_error(int r);
#endif // VPI_GAME_MAIN_H
+13
View File
@@ -13,6 +13,19 @@ static const char *lang_str[__VPI_LANG_T_COUNT] = {
"Sync Failed",
"Vanilla needs root permission to configure the wireless interface for connection.",
"Please enter your \"sudo\" password here.",
"Error",
"No consoles synced",
"Connecting to \"%s\"...",
"Are you sure you want to delete \"%s\"?",
"Successfully deleted \"%s\"",
"Rename",
"Delete",
"Back",
"More",
"Gamepad",
"Audio",
"Connection",
"Webcam",
};
const char *lang(vpi_lang_t id)
+13
View File
@@ -14,6 +14,19 @@ typedef enum {
VPI_LANG_SYNC_FAILED,
VPI_LANG_SUDO_HELP_1,
VPI_LANG_SUDO_HELP_2,
VPI_LANG_ERROR,
VPI_LANG_NO_CONSOLES_SYNCED,
VPI_LANG_CONNECTING_TO,
VPI_LANG_DELETE_CONFIRM,
VPI_LANG_DELETE_SUCCESS,
VPI_LANG_RENAME,
VPI_LANG_DELETE,
VPI_LANG_BACK,
VPI_LANG_MORE,
VPI_LANG_GAMEPAD,
VPI_LANG_AUDIO,
VPI_LANG_CONNECTION,
VPI_LANG_WEBCAM,
__VPI_LANG_T_COUNT
} vpi_lang_t;
-8
View File
@@ -24,7 +24,6 @@ AVFrame *decoding_frame;
pthread_mutex_t decoding_mutex;
pthread_cond_t decoding_wait_cond;
AVCodecContext *video_codec_ctx;
AVCodecParserContext *video_parser;
AVPacket *video_packet;
int running = 0;
@@ -386,12 +385,6 @@ int main(int argc, const char **argv)
return 1;
}
video_parser = av_parser_init(codec->id);
if (!video_parser) {
fprintf(stderr, "Failed to create codec parser\n");
exit(1);
}
// Install logging debugger
vanilla_install_logger(logger);
@@ -558,7 +551,6 @@ int main(int argc, const char **argv)
pthread_cond_destroy(&decoding_wait_cond);
pthread_mutex_destroy(&decoding_mutex);
av_parser_close(video_parser);
av_frame_free(&present_frame);
av_frame_free(&decoding_frame);
av_packet_free(&video_packet);
+6
View File
@@ -1,6 +1,7 @@
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "menu/menu.h"
#include "ui/ui.h"
#include "ui/ui_sdl.h"
@@ -17,6 +18,9 @@ int main(int argc, const char **argv)
}
}
// Load config
vpi_config_init();
// Initialize UI system
vui_context_t *vui = vui_alloc(SCREEN_WIDTH, SCREEN_HEIGHT);
@@ -39,5 +43,7 @@ exit:
vui_free(vui);
vpi_config_free();
return ret;
}
+1 -1
View File
@@ -12,5 +12,5 @@ void vpi_menu_create_background(vui_context_t *vui, int layer, vui_rect_t *bkg_r
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);
vui_rect_create(vui, bkg_rect->x, bkg_rect->y, bkg_rect->w, bkg_rect->h, scrh*1/10, vui_color_create(0, 0, 0, 0.66f), layer);
}
+85
View File
@@ -0,0 +1,85 @@
#include "menu_delete.h"
#include <stdio.h>
#include <string.h>
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_edit.h"
#include "menu_main.h"
#include "ui/ui_anim.h"
static int fglayer;
void vpi_menu_delete_return_to_edit(vui_context_t *vui, int btn, void *v)
{
vui_transition_fade_layer_out(vui, fglayer, vpi_menu_edit_return, v);
}
void vpi_menu_delete_return_to_main(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_main, (void *) (intptr_t) 1);
}
void vpi_menu_delete_status(vui_context_t *vui, void *v)
{
vui_reset(vui);
int bglayer = vui_layer_create(vui);
fglayer = vui_layer_create(vui);
uint8_t console = (uintptr_t) v;
vui_rect_t bkg_rect;
int margin;
vpi_menu_create_background(vui, bglayer, &bkg_rect, &margin);
char cname[VPI_CONSOLE_MAX_NAME];
strncpy(cname, vpi_config.connected_console_entries[console].name, sizeof(cname));
vpi_config_remove_console(console);
char buf[256];
snprintf(buf, sizeof(buf), lang(VPI_LANG_DELETE_SUCCESS), cname);
vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*1/5, bkg_rect.w, bkg_rect.h, buf, vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, fglayer);
printf("%s\n", buf);
static const int ok_btn_w = BTN_SZ * 3;
vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - (ok_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, fglayer, vpi_menu_delete_return_to_main, v);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
void vpi_menu_delete_ok(vui_context_t *vui, int btn, void *v)
{
vui_transition_fade_layer_out(vui, fglayer, vpi_menu_delete_status, v);
}
void vpi_menu_delete(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);
uint8_t console = (uintptr_t) v;
char buf[256];
snprintf(buf, sizeof(buf), lang(VPI_LANG_DELETE_CONFIRM), vpi_config.connected_console_entries[console].name);
vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*1/5, bkg_rect.w, bkg_rect.h, buf, vui_color_create(1,1,1,1), VUI_FONT_SIZE_NORMAL, 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, fglayer, vpi_menu_delete_ok, v);
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, fglayer, vpi_menu_delete_return_to_edit, (void *) (intptr_t) bglayer);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_DELETE_H
#define VANILLA_PI_MENU_DELETE_H
#include "ui/ui.h"
void vpi_menu_delete(vui_context_t *vui, void *v);
#endif // VANILLA_PI_MENU_DELETE_H
+60
View File
@@ -0,0 +1,60 @@
#include "menu_edit.h"
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_delete.h"
#include "menu_main.h"
#include "ui/ui_anim.h"
static int bglayer;
static int fglayer;
static void delete_btn_pressed(vui_context_t *vui, int btn, void *v)
{
vui_transition_fade_layer_out(vui, fglayer, vpi_menu_delete, v);
}
static void return_to_main(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_main, (void *) (intptr_t) 1);
}
void vpi_menu_edit_nofade(vui_context_t *vui, void *v)
{
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);
uint8_t console = (uintptr_t) v;
vui_label_create(vui, bkg_rect.x, bkg_rect.y + bkg_rect.h*1/5, bkg_rect.w, bkg_rect.h, vpi_config.connected_console_entries[console].name, 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 * 1 / 2;
vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - btn_w/2, btn_y + BTN_SZ*0, btn_w, BTN_SZ, lang(VPI_LANG_RENAME), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, 0, 0);
vui_button_create(vui, bkg_rect.x + bkg_rect.w/2 - btn_w/2, btn_y + BTN_SZ*1, btn_w, BTN_SZ, lang(VPI_LANG_DELETE), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, delete_btn_pressed, v);
// Back button
vui_button_create(vui, 0, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_BACK), 0, VUI_BUTTON_STYLE_CORNER, fglayer, return_to_main, (void *) (intptr_t) bglayer);
}
void vpi_menu_edit(vui_context_t *vui, void *v)
{
vpi_menu_edit_nofade(vui, v);
vui_transition_fade_layer_in(vui, bglayer, 0, 0);
}
void vpi_menu_edit_return(vui_context_t *vui, void *v)
{
vpi_menu_edit_nofade(vui, v);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef VANILLA_PI_MENU_EDIT_H
#define VANILLA_PI_MENU_EDIT_H
#include "ui/ui.h"
void vpi_menu_edit_return(vui_context_t *vui, void *v);
void vpi_menu_edit(vui_context_t *vui, void *v);
#endif // VANILLA_PI_MENU_EDIT_H
+81
View File
@@ -0,0 +1,81 @@
#include "menu_game.h"
#include <stdio.h>
#include <vanilla.h>
#include "config.h"
#include "lang.h"
#include "game/game_main.h"
#include "menu_common.h"
#include "menu_main.h"
#include "ui/ui_anim.h"
void back_to_main_menu(vui_context_t *vui, int button, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_main, (void *) (intptr_t) 1);
}
void show_error(vui_context_t *vui, void *v)
{
vui_reset(vui);
vui_enable_background(vui, 1);
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
int layer = vui_layer_create(vui);
vui_label_create(vui, 0, scrh * 1 / 6, scrw, scrh, lang(VPI_LANG_ERROR), vui_color_create(0.25f,0.25f,0.25f,1), VUI_FONT_SIZE_NORMAL, layer);
char buf[10];
snprintf(buf, sizeof(buf), "%i", (int) (intptr_t) v);
vui_label_create(vui, 0, scrh * 3 / 6, scrw, scrh, buf, vui_color_create(1,0,0,1), VUI_FONT_SIZE_NORMAL, layer);
const int ok_btn_w = BTN_SZ*2;
vui_button_create(vui, scrw/2 - ok_btn_w/2, scrh * 3 / 4, ok_btn_w, BTN_SZ, lang(VPI_LANG_OK_BTN), 0, VUI_BUTTON_STYLE_BUTTON, layer, back_to_main_menu, (void *) (intptr_t) layer);
vui_transition_fade_black_out(vui, 0, 0);
}
void vpi_display_update(vui_context_t *vui, int64_t time, void *v)
{
int backend_err = vpi_game_error();
if (backend_err != VANILLA_SUCCESS) {
vui_game_mode_set(vui, 0);
show_error(vui, (void*)(intptr_t) backend_err);
return;
}
}
void vpi_menu_game(vui_context_t *vui, void *v)
{
int console = (intptr_t) v;
vui_reset(vui);
vui_enable_background(vui, 0);
vpi_console_entry_t *entry = vpi_config.connected_console_entries + console;
int r = vanilla_start(vpi_config.server_address, entry->bssid, entry->psk);
if (r != VANILLA_SUCCESS) {
show_error(vui, (void*)(intptr_t) r);
} else {
char buf[100];
snprintf(buf, sizeof(buf), lang(VPI_LANG_CONNECTING_TO), entry->name);
int fglayer = vui_layer_create(vui);
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
vui_label_create(vui, 0, scrh * 2 / 5, scrw, scrh, buf, vui_color_create(0.5f,0.5f,0.5f,1), VUI_FONT_SIZE_NORMAL, fglayer);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
vpi_game_start(vui);
vui_game_mode_set(vui, 1);
vui_start_passive_animation(vui, vpi_display_update, 0);
}
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_GAME_H
#define VANILLA_PI_MENU_GAME_H
#include "ui/ui.h"
void vpi_menu_game(vui_context_t *vui, void *v);
#endif // VANILLA_PI_MENU_GAME_H
+34
View File
@@ -0,0 +1,34 @@
#include "menu_gamepad.h"
#include "lang.h"
#include "menu/menu_common.h"
#include "menu/menu_settings.h"
#include "ui/ui_anim.h"
static void return_to_settings(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_settings, 0);
}
void vpi_menu_gamepad(vui_context_t *vui, void *v)
{
vui_reset(vui);
int layer = vui_layer_create(vui);
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
int img_w = scrw * 8 / 10;
int img_h = scrh * 8 / 10;
vui_image_create(vui, scrw/2-img_w/2, scrh/2-img_h/2, img_w, img_h, "gamepad.svg", layer);
// Back button
vui_button_create(vui, 0, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_BACK), 0, VUI_BUTTON_STYLE_CORNER, layer, return_to_settings, (void *) (intptr_t) layer);
// More button
vui_button_create(vui, scrw-BTN_SZ, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_MORE), 0, VUI_BUTTON_STYLE_CORNER, layer, return_to_settings, (void *) (intptr_t) layer);
vui_transition_fade_layer_in(vui, layer, 0, 0);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_GAMEPAD_H
#define VANILLA_PI_MENU_GAMEPAD_H
#include "ui/ui.h"
void vpi_menu_gamepad(vui_context_t *vui, void *v);
#endif // VANILLA_PI_MENU_GAMEPAD_H
+96 -16
View File
@@ -2,8 +2,12 @@
#include <stdio.h>
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_edit.h"
#include "menu_game.h"
#include "menu_settings.h"
#include "menu_sync.h"
#include "ui/ui_anim.h"
@@ -16,6 +20,14 @@ static int main_menu_current = 0;
static int console_menu_layer = 0;
static int main_menu_left_arrow_btn;
static int main_menu_right_arrow_btn;
static int main_menu_no_console_lbl;
static int main_menu_connect_btn;
static int main_menu_edit_btn;
void vpi_menu_main_sync_action(vui_context_t *vui, int btn, void *v)
{
vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vpi_menu_sync, 0);
@@ -24,11 +36,24 @@ void vpi_menu_main_sync_action(vui_context_t *vui, int btn, void *v)
void vpi_menu_main_populate(vui_context_t *vui)
{
for (int i = 0; i < MAIN_MENU_ENTRIES; i++) {
int b = main_menu_btns[i];
char buf[64];
snprintf(buf, sizeof(buf), "Wii U %i", main_menu_current + i + 1);
vui_button_update_text(vui, b, buf);
int console_index = main_menu_current + i;
int btn = main_menu_btns[i];
int valid_console = (console_index < vpi_config.connected_console_count);
vui_button_update_visible(vui, btn, valid_console);
if (valid_console) {
vui_button_update_text(vui, btn, vpi_config.connected_console_entries[console_index].name);
}
vui_button_update_checked(vui, btn, 0);
}
vui_button_update_visible(vui, main_menu_left_arrow_btn, main_menu_current > 0);
vui_button_update_visible(vui, main_menu_right_arrow_btn, main_menu_current + MAIN_MENU_ENTRIES < vpi_config.connected_console_count);
vui_label_update_visible(vui, main_menu_no_console_lbl, vpi_config.connected_console_count == 0);
vui_button_update_enabled(vui, main_menu_connect_btn, 0);
vui_button_update_enabled(vui, main_menu_edit_btn, 0);
}
void vpi_menu_main_finish_transition(vui_context_t *vui, void *v)
@@ -47,11 +72,6 @@ void vpi_menu_main_finish_transition(vui_context_t *vui, void *v)
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, vpi_menu_main_finish_transition, (void *) (intptr_t) (layer | to << 8));
}
@@ -65,10 +85,66 @@ void vpi_menu_main_right_arrow(vui_context_t *vui, int btn, void *v)
vpi_menu_main_start_transition(vui, (intptr_t) v, main_menu_current + MAIN_MENU_ENTRIES);
}
void connect_to_console_from_btn(vui_context_t *vui, int console)
{
vui_transition_fade_black_in(vui, vpi_menu_game, (void *) (intptr_t) console);
}
void vpi_menu_main_connect_console(vui_context_t *vui, int btn, void *v)
{
if (vui_button_get_checked(vui, btn)) {
int index = (intptr_t) v;
int console = main_menu_current + index;
connect_to_console_from_btn(vui, console);
} else {
for (int i = 0; i < MAIN_MENU_ENTRIES; i++) {
vui_button_update_checked(vui, main_menu_btns[i], 0);
}
vui_button_update_checked(vui, btn, 1);
vui_button_update_enabled(vui, main_menu_connect_btn, 1);
vui_button_update_enabled(vui, main_menu_edit_btn, 1);
}
}
void vpi_menu_main_connect_button_action(vui_context_t *vui, int btn, void *v)
{
for (int i = 0; i < MAIN_MENU_ENTRIES; i++) {
if (vui_button_get_checked(vui, main_menu_btns[i])) {
connect_to_console_from_btn(vui, main_menu_current + i);
break;
}
}
}
void vpi_menu_main_edit_button_action(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
int console = -1;
for (int i = 0; i < MAIN_MENU_ENTRIES; i++) {
if (vui_button_get_checked(vui, main_menu_btns[i])) {
console = main_menu_current + i;
break;
}
}
if (console != -1)
vui_transition_fade_layer_out(vui, layer, vpi_menu_edit, (void *) (intptr_t) console);
}
void vpi_menu_main_settings_button_action(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_settings, 0);
}
void vpi_menu_main(vui_context_t *vui, void *v)
{
vui_reset(vui);
vui_enable_background(vui, 1);
int SCREEN_WIDTH, SCREEN_HEIGHT;
vui_get_screen_size(vui, &SCREEN_WIDTH, &SCREEN_HEIGHT);
@@ -79,26 +155,30 @@ void vpi_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, vpi_menu_main_left_arrow, (void *) (intptr_t) console_menu_layer);
main_menu_left_arrow_btn = 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, vpi_menu_main_right_arrow, (void *) (intptr_t) console_menu_layer);
main_menu_right_arrow_btn = 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, 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, lang(VPI_LANG_CONNECT_BTN), NULL, VUI_BUTTON_STYLE_BUTTON, layer, NULL, NULL);
main_menu_connect_btn = 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, vpi_menu_main_connect_button_action, NULL);
// Delete button
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);
// Edit/settings corner button
int corner_btn_sz = BTN_SZ;
main_menu_edit_btn = vui_button_create(vui, 0, 0, corner_btn_sz, corner_btn_sz, lang(VPI_LANG_EDIT_BTN), "edit.svg", VUI_BUTTON_STYLE_CORNER, layer, vpi_menu_main_edit_button_action, (void *) (intptr_t) layer);
vui_button_create(vui, SCREEN_WIDTH-corner_btn_sz, 0, corner_btn_sz, corner_btn_sz, lang(VPI_LANG_SETTINGS_BTN), "settings.png", VUI_BUTTON_STYLE_CORNER, layer, vpi_menu_main_settings_button_action, (void *) (intptr_t) layer);
// No entries text
main_menu_no_console_lbl = vui_label_create(vui, 0, SCREEN_HEIGHT * 2 / 5, SCREEN_WIDTH, SCREEN_HEIGHT, lang(VPI_LANG_NO_CONSOLES_SYNCED), vui_color_create(0,0,0,0.5f), VUI_FONT_SIZE_SMALL, layer);
// Console list
int list_item_width = SCREEN_WIDTH - BTN_SZ - BTN_SZ;
int list_item_height = (SCREEN_HEIGHT - BTN_SZ - BTN_SZ) / MAIN_MENU_ENTRIES;
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);
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, vpi_menu_main_connect_console, (void *) (intptr_t) i);
}
vpi_menu_main_populate(vui);
+73
View File
@@ -0,0 +1,73 @@
#include "menu_settings.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_gamepad.h"
#include "menu_main.h"
#include "ui/ui_anim.h"
static void return_to_main(vui_context_t *vui, int btn, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_main, (void *) (intptr_t) 1);
}
static void transition_to_gamepad(vui_context_t *vui, int button, void *v)
{
int layer = (intptr_t) v;
vui_transition_fade_layer_out(vui, layer, vpi_menu_gamepad, 0);
}
void vpi_menu_settings(vui_context_t *vui, void *v)
{
vui_reset(vui);
int fglayer = vui_layer_create(vui);
uint8_t console = (uintptr_t) v;
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
static const int SETTINGS_NAMES[] = {
VPI_LANG_GAMEPAD,
VPI_LANG_CONNECTION,
VPI_LANG_AUDIO,
VPI_LANG_WEBCAM,
};
// Highlight currently implemented functionality
static const int SETTINGS_ENABLED[] = {
1,
1,
0,
0,
};
// Highlight currently implemented functionality
static const vui_button_callback_t SETTINGS_ACTION[] = {
transition_to_gamepad,
0,
0,
0,
};
const int settings_cols = 2;
const int settings_rows = 2;
const int settings_w = scrw/3;
const int settings_h = scrh/3;
const int settings_x = scrw/2 - (settings_w*settings_cols)/2;
const int settings_y = scrh/2 - (settings_h*settings_rows)/2;
for (int y = 0; y < settings_rows; y++) {
for (int x = 0; x < settings_cols; x++) {
int index = y * settings_cols + x;
int b = vui_button_create(vui, settings_x + settings_w * x, settings_y + settings_h * y, settings_w, settings_h, lang(SETTINGS_NAMES[index]), 0, VUI_BUTTON_STYLE_BUTTON, fglayer, SETTINGS_ACTION[index], (void *) (intptr_t) fglayer);
vui_button_update_enabled(vui, b, SETTINGS_ENABLED[index]);
}
}
// Back button
vui_button_create(vui, 0, 0, BTN_SZ, BTN_SZ, lang(VPI_LANG_BACK), 0, VUI_BUTTON_STYLE_CORNER, fglayer, return_to_main, (void *) (intptr_t) fglayer);
vui_transition_fade_layer_in(vui, fglayer, 0, 0);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef VANILLA_PI_MENU_SETTINGS_H
#define VANILLA_PI_MENU_SETTINGS_H
#include "ui/ui.h"
void vpi_menu_settings(vui_context_t *vui, void *v);
#endif // VANILLA_PI_MENU_SETTINGS_H
+61 -23
View File
@@ -3,9 +3,11 @@
#include <math.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <vanilla.h>
#include "config.h"
#include "lang.h"
#include "menu_common.h"
#include "menu_main.h"
@@ -21,6 +23,8 @@ static vui_rect_t sync_entry_rect;
static int sync_entry_images[SYNC_BTN_COUNT];
static int sync_bglayer;
static int sync_fglayer;
static int sync_cancel_btn;
static int sync_bksp_btn;
static const char *suit_icons_black[4] = {
"spade_black.svg",
@@ -36,6 +40,11 @@ static const char *suit_icons_white[4] = {
"club_white.svg",
};
void vpi_sync_menu_back_action(vui_context_t *vui, int btn, void *v)
{
vui_transition_fade_layer_out(vui, (int) (intptr_t) v, vpi_menu_main, 0);
}
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);
@@ -54,6 +63,8 @@ void sync_show_success(vui_context_t *vui, void *data)
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);
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, vpi_sync_menu_back_action, (void *) (intptr_t) bglayer);
}
void sync_show_error(vui_context_t *vui, void *data)
@@ -70,9 +81,9 @@ void sync_show_error(vui_context_t *vui, void *data)
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;
int status = (int) (intptr_t) data;
char buf[20];
sprintf(buf, "%i", ret);
sprintf(buf, "%i", status);
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);
@@ -115,20 +126,44 @@ void sync_animation_step(vui_context_t *ctx, int64_t time, void *userdata)
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);
}
vanilla_event_t event;
while (vanilla_poll_event(&event)) {
if (event.type == VANILLA_EVENT_ERROR) {
int err = * (int *) event.data;
// Transition to error screen showing error code
vui_transition_fade_layer_out(ctx, sync_fglayer, sync_show_error, (void *) (intptr_t) err);
}
// }
if (event.type == VANILLA_EVENT_SYNC) {
vanilla_sync_event_t *sync = (vanilla_sync_event_t *) event.data;
// Save this as a new console and transition to success screen
int found = 0;
for (uint8_t i = 0; i < vpi_config.connected_console_count; i++) {
if (!memcmp(vpi_config.connected_console_entries[i].bssid.bssid, sync->data.bssid.bssid, sizeof(vanilla_bssid_t))) {
// We don't bother checking PSK because BSSID is already unique and the PSK should not have changed
found = 1;
break;
}
}
if (!found) {
vpi_console_entry_t console;
snprintf(console.name, sizeof(console.name), "Wii U %i", vpi_config.connected_console_count + 1);
console.bssid = sync->data.bssid;
console.psk = sync->data.psk;
vpi_config_add_console(&console);
}
vui_transition_fade_layer_out(ctx, sync_fglayer, sync_show_success, sync);
vanilla_free_event(&event);
vanilla_stop();
} else {
vanilla_free_event(&event);
}
}
}
void cancel_sync(vui_context_t *vui, int button, void *v)
@@ -160,7 +195,7 @@ void start_syncing(vui_context_t *vui, void *v)
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);
int ret = vanilla_sync(code, vpi_config.server_address);
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);
@@ -184,11 +219,6 @@ void transition_to_sync_window(vui_context_t *vui, void *v)
vui_transition_fade_layer_out(vui, sync_fglayer, start_syncing_or_sudo, 0);
}
void vpi_sync_menu_back_action(vui_context_t *vui, int btn, void *v)
{
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)
{
// Prevent overflowing string
@@ -218,6 +248,8 @@ void sync_btn_clicked(vui_context_t *vui, int button, void *data)
if (sync_str_len == SYNC_BTN_COUNT) {
vui_timer(vui, 250000, transition_to_sync_window, 0);
}
vui_button_set_cancel(vui, sync_bksp_btn);
}
}
@@ -231,6 +263,10 @@ void bksp_btn_clicked(vui_context_t *vui, int button, void *data)
sync_str_len--;
vui_image_destroy(vui, sync_entry_images[sync_str_len]);
sync_entry_images[sync_str_len] = -1;
if (sync_str_len == 0) {
vui_button_set_cancel(vui, sync_cancel_btn);
}
}
void create_sync_btn(vui_context_t *vui, int x, int data, int layer, int origin_x, int origin_y)
@@ -286,10 +322,12 @@ void vpi_menu_sync(vui_context_t *vui, void *d)
vui_rect_create(vui, sync_entry_rect.x, sync_entry_rect.y, sync_entry_rect.w, sync_entry_rect.h, 5, vui_color_create(0,0,0,0.66f), sync_fglayer);
// Create backspace button
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);
sync_bksp_btn = 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, lang(VPI_LANG_CANCEL_BTN), 0, VUI_BUTTON_STYLE_CORNER, sync_fglayer, vpi_sync_menu_back_action, (void *) (intptr_t) sync_bglayer);
sync_cancel_btn = 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, d ? sync_fglayer : sync_bglayer, 0, 0);
vui_button_set_cancel(vui, sync_cancel_btn);
}
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 20 20">
<title>
edit
</title>
<path fill="#404040" d="M16.77 8l1.94-2a1 1 0 0 0 0-1.41l-3.34-3.3a1 1 0 0 0-1.41 0L12 3.23zm-5.81-3.71L1 14.25V19h4.75l9.96-9.96-4.75-4.75z"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

+2796
View File
File diff suppressed because it is too large. Load diff

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 323 KiB

+271 -27
View File
@@ -1,8 +1,11 @@
#include "ui.h"
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <vanilla.h>
#include "ui_anim.h"
#include "ui_priv.h"
@@ -16,6 +19,9 @@ vui_context_t *vui_alloc(int width, int height)
vui->screen_width = width;
vui->screen_height = height;
vui->background_image[0] = 0;
vui->background_enabled = 0;
vui->audio_handler = 0;
vui->vibrate_handler = 0;
return vui;
}
@@ -30,12 +36,15 @@ int vui_reset(vui_context_t *ctx)
ctx->button_count = 0;
ctx->label_count = 0;
ctx->rect_count = 0;
ctx->game_mode = 0;
ctx->passive_animation_count = 0;
memset(ctx->images, 0, sizeof(ctx->images));
ctx->animation_enabled = 0;
ctx->button_active = -1;
ctx->layers = 1;
ctx->layer_opacity[0] = 1;
ctx->selected_button = -1;
ctx->cancel_button = -1;
}
void vui_get_screen_size(vui_context_t *ctx, int *width, int *height)
@@ -44,6 +53,11 @@ void vui_get_screen_size(vui_context_t *ctx, int *width, int *height)
if (height) *height = ctx->screen_height;
}
void vui_enable_background(vui_context_t *ctx, int enabled)
{
ctx->background_enabled = enabled;
}
void vui_set_background(vui_context_t *ctx, const char *background_image)
{
vui_strcpy(ctx->background_image, background_image);
@@ -84,12 +98,44 @@ int vui_button_create(vui_context_t *ctx, int x, int y, int w, int h, const char
vui_button_update_icon(ctx, index, icon);
vui_button_update_style(ctx, index, style);
vui_button_update_click_handler(ctx, index, callback, callback_data);
vui_button_update_visible(ctx, index, 1);
vui_button_update_enabled(ctx, index, 1);
vui_button_update_checked(ctx, index, 0);
ctx->button_count++;
return index;
}
void vui_button_update_visible(vui_context_t *ctx, int index, int visible)
{
vui_button_t *btn = &ctx->buttons[index];
btn->visible = visible;
if (!visible && ctx->selected_button == index) {
ctx->selected_button = -1;
}
}
void vui_button_update_enabled(vui_context_t *ctx, int index, int enabled)
{
vui_button_t *btn = &ctx->buttons[index];
btn->enabled = enabled;
if (!enabled && ctx->selected_button == index) {
ctx->selected_button = -1;
}
}
void vui_button_update_checked(vui_context_t *ctx, int index, int checked)
{
vui_button_t *btn = &ctx->buttons[index];
btn->checked = checked;
}
void vui_button_set_cancel(vui_context_t *ctx, int button)
{
ctx->cancel_button = button;
}
void vui_button_update_click_handler(vui_context_t *ctx, int index, vui_button_callback_t handler, void *userdata)
{
vui_button_t *btn = &ctx->buttons[index];
@@ -106,6 +152,12 @@ void vui_button_get_geometry(vui_context_t *ctx, int index, int *x, int *y, int
*h = btn->sh;
}
int vui_button_get_checked(vui_context_t *ctx, int index)
{
vui_button_t *btn = &ctx->buttons[index];
return btn->checked;
}
void vui_button_update_geometry(vui_context_t *ctx, int index, int x, int y, int w, int h)
{
vui_button_t *btn = &ctx->buttons[index];
@@ -133,11 +185,143 @@ void vui_button_update_style(vui_context_t *ctx, int index, vui_button_style_t s
btn->style = style;
}
void vui_select_direction(vui_context_t *ctx, vui_direction_t dir)
{
int cx, cy;
if (ctx->selected_button == -1) {
int sw, sh;
vui_get_screen_size(ctx, &sw, &sh);
switch (dir) {
case VUI_DIR_LEFT:
cx = sw+sw;
cy = sh/2;
break;
case VUI_DIR_RIGHT:
cx = -sw;
cy = sh/2;
break;
case VUI_DIR_UP:
cx = sw/2;
cy = sh+sh;
break;
case VUI_DIR_DOWN:
cx = sw/2;
cy = -sh;
break;
}
} else {
vui_button_t *sel_btn = &ctx->buttons[ctx->selected_button];
cx = sel_btn->x + sel_btn->w / 2;
cy = sel_btn->y + sel_btn->h / 2;
}
int new_sel = -1;
int diff = INT_MAX;
for (int i = 0; i < ctx->button_count; i++) {
vui_button_t *b = &ctx->buttons[i];
if (b->visible && b->enabled) {
int valid = 0;
// Determine what "direction" this button is in
int ccx = b->x + b->w / 2;
int ccy = b->y + b->h / 2;
int diffx = ccx - cx;
int diffy = ccy - cy;
if (diffx == 0 && diffy == 0) {
// Don't know how to navigate here
continue;
}
vui_direction_t btn_dir;
int btn_dist;
int diffx_abs = abs(diffx);
int diffy_abs = abs(diffy);
if (diffx_abs > diffy_abs) {
btn_dist = diffx_abs;
if (diffx > 0) {
btn_dir = VUI_DIR_RIGHT;
} else {
btn_dir = VUI_DIR_LEFT;
}
} else {
btn_dist = diffy_abs;
if (diffy > 0) {
btn_dir = VUI_DIR_DOWN;
} else {
btn_dir = VUI_DIR_UP;
}
}
if (btn_dir == dir) {
if (btn_dist < diff) {
diff = btn_dist;
new_sel = i;
}
}
}
}
if (new_sel != -1) {
ctx->selected_button = new_sel;
}
}
int point_inside_button(vui_button_t *btn, int x, int y)
{
return x >= btn->x && y >= btn->y && x < btn->x + btn->w && y < btn->y + btn->h;
}
void vui_button_callback(vui_context_t *ctx, void *data)
{
int index = (int) (intptr_t) data;
vui_button_t *btn = &ctx->buttons[index];
if (btn->onclick) {
btn->onclick(ctx, index, btn->onclick_data);
}
}
void press_button(vui_context_t *ctx, int index)
{
if (ctx->button_active != -1) {
// Button already mousedown'd, ignore this
return;
}
if (ctx->animation_enabled) {
return;
}
vui_animation_button_mousedown(ctx, index, 0, 0);
ctx->button_active = index;
}
void release_button(vui_context_t *ctx, int and_click)
{
if (ctx->button_active == -1) {
// No button was mousedown'd, do nothing here
return;
}
vui_button_t *btn = &ctx->buttons[ctx->button_active];
// Only do action if mouse is still inside button's rect
vui_callback_t callback = 0;
void *callback_data = 0;
if (and_click) {
callback = vui_button_callback;
callback_data = (void *) (intptr_t) ctx->button_active;
}
vui_animation_button_mouseup(ctx, ctx->button_active, callback, callback_data);
ctx->button_active = -1;
}
void vui_process_mousedown(vui_context_t *ctx, int x, int y)
{
if (ctx->button_active != -1) {
@@ -149,46 +333,26 @@ void vui_process_mousedown(vui_context_t *ctx, int x, int y)
return;
}
ctx->selected_button = -1;
for (int i = 0; i < ctx->button_count; i++) {
vui_button_t *btn = &ctx->buttons[i];
if (point_inside_button(btn, x, y)) {
vui_animation_button_mousedown(ctx, i, 0, 0);
ctx->button_active = i;
if (btn->visible && btn->enabled && point_inside_button(btn, x, y)) {
press_button(ctx, i);
break;
}
}
}
void vui_button_callback(vui_context_t *ctx, void *data)
{
int index = (int) (intptr_t) data;
vui_button_t *btn = &ctx->buttons[index];
if (btn->onclick) {
btn->onclick(ctx, index, btn->onclick_data);
}
}
void vui_process_mouseup(vui_context_t *ctx, int x, int y)
{
if (ctx->button_active == -1) {
// No button was mousedown'd, do nothing here
return;
}
vui_button_t *btn = &ctx->buttons[ctx->button_active];
// Only do action if mouse is still inside button's rect
vui_callback_t callback = 0;
void *callback_data = 0;
if (point_inside_button(btn, x, y)) {
callback = vui_button_callback;
callback_data = (void *) (intptr_t) ctx->button_active;
}
vui_animation_button_mouseup(ctx, ctx->button_active, callback, callback_data);
ctx->button_active = -1;
release_button(ctx, point_inside_button(&ctx->buttons[ctx->button_active], x, y));
}
void vui_cancel_animation(vui_context_t *ctx)
@@ -272,6 +436,25 @@ void vui_update(vui_context_t *ctx)
}
}
void vui_game_mode_set(vui_context_t *ctx, int enabled)
{
ctx->game_mode = enabled;
}
void vui_audio_push(vui_context_t *ctx, const void *data, size_t size)
{
if (ctx->audio_handler) {
ctx->audio_handler(data, size, ctx->audio_handler_data);
}
}
void vui_vibrate_set(vui_context_t *ctx, uint8_t val)
{
if (ctx->vibrate_handler) {
ctx->vibrate_handler(val, ctx->vibrate_handler_data);
}
}
int vui_layer_create(vui_context_t *ctx)
{
int cur_layer = ctx->layers;
@@ -345,18 +528,25 @@ int vui_label_create(vui_context_t *ctx, int x, int y, int w, int h, const char
lbl->layer = layer;
vui_label_update_text(ctx, index, text);
vui_label_update_visible(ctx, index, 1);
ctx->label_count++;
return index;
}
int vui_label_update_text(vui_context_t *ctx, int index, const char *text)
void 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);
}
void vui_label_update_visible(vui_context_t *ctx, int index, int visible)
{
vui_label_t *lbl = &ctx->labels[index];
lbl->visible = visible;
}
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) {
@@ -415,4 +605,58 @@ int vui_image_create(vui_context_t *ctx, int x, int y, int w, int h, const char
void vui_image_destroy(vui_context_t *ctx, int image)
{
ctx->images[image].valid = 0;
}
void vui_process_keydown(vui_context_t *ctx, int button)
{
switch (button) {
case VANILLA_BTN_LEFT:
case VANILLA_AXIS_L_LEFT:
vui_select_direction(ctx, VUI_DIR_LEFT);
break;
case VANILLA_BTN_RIGHT:
case VANILLA_AXIS_L_RIGHT:
vui_select_direction(ctx, VUI_DIR_RIGHT);
break;
case VANILLA_BTN_UP:
case VANILLA_AXIS_L_UP:
vui_select_direction(ctx, VUI_DIR_UP);
break;
case VANILLA_BTN_DOWN:
case VANILLA_AXIS_L_DOWN:
vui_select_direction(ctx, VUI_DIR_DOWN);
break;
case VANILLA_BTN_A:
if (ctx->selected_button != -1) {
press_button(ctx, ctx->selected_button);
} else if (ctx->button_count == 1) {
press_button(ctx, 0);
} else {
for (int i = 0; i < ctx->button_count; i++) {
vui_button_t *b = &ctx->buttons[i];
if (b->enabled && b->visible) {
ctx->selected_button = i;
break;
}
}
}
break;
case VANILLA_BTN_B:
if (ctx->cancel_button != -1) {
press_button(ctx, ctx->cancel_button);
} else if (ctx->button_count == 1) {
press_button(ctx, 0);
}
break;
}
}
void vui_process_keyup(vui_context_t *ctx, int button)
{
switch (button) {
case VANILLA_BTN_A:
case VANILLA_BTN_B:
release_button(ctx, 1);
break;
}
}
+26 -1
View File
@@ -2,6 +2,7 @@
#define VANILLA_PI_UI_H
#include <stdint.h>
#include <stddef.h>
typedef enum {
VUI_BUTTON_STYLE_NONE,
@@ -35,6 +36,14 @@ typedef void (*vui_callback_t)(vui_context_t *ctx, void *userdata);
typedef void (*vui_button_callback_t)(vui_context_t *ctx, int button, void *userdata);
typedef void (*vui_anim_step_callback_t)(vui_context_t *ctx, int64_t time, void *userdata);
typedef enum {
VUI_DIR_NONE,
VUI_DIR_LEFT,
VUI_DIR_RIGHT,
VUI_DIR_UP,
VUI_DIR_DOWN,
} vui_direction_t;
/**
* Context-related functions
*/
@@ -42,11 +51,18 @@ vui_context_t *vui_alloc(int width, int height);
int vui_reset(vui_context_t *ctx);
void vui_free(vui_context_t *ctx);
void vui_update(vui_context_t *ctx);
void vui_game_mode_set(vui_context_t *ctx, int enabled);
/**
* Audio-related functions
*/
void vui_audio_push(vui_context_t *ctx, const void *data, size_t size);
/**
* Screen-related functions
*/
void vui_get_screen_size(vui_context_t *ctx, int *width, int *height);
void vui_enable_background(vui_context_t *ctx, int enabled);
void vui_set_background(vui_context_t *ctx, const char *background_image);
/**
@@ -62,17 +78,23 @@ int vui_layer_destroy(vui_context_t *ctx);
*/
int vui_button_create(vui_context_t *ctx, int x, int y, int w, int h, const char *text, const char *icon, vui_button_style_t style, int layer, vui_button_callback_t callback, void *callback_data);
void vui_button_get_geometry(vui_context_t *ctx, int button, int *x, int *y, int *w, int *h);
int vui_button_get_checked(vui_context_t *ctx, int button);
void vui_button_update_click_handler(vui_context_t *ctx, int index, vui_button_callback_t handler, void *userdata);
void vui_button_update_geometry(vui_context_t *ctx, int button, int x, int y, int w, int h);
void vui_button_update_icon(vui_context_t *ctx, int button, const char *icon);
void vui_button_update_text(vui_context_t *ctx, int button, const char *text);
void vui_button_update_style(vui_context_t *ctx, int button, vui_button_style_t style);
void vui_button_update_visible(vui_context_t *ctx, int button, int visible);
void vui_button_update_enabled(vui_context_t *ctx, int button, int enabled);
void vui_button_update_checked(vui_context_t *ctx, int button, int checked);
void vui_button_set_cancel(vui_context_t *ctx, int button);
/**
* 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);
void vui_label_update_text(vui_context_t *ctx, int label, const char *text);
void vui_label_update_visible(vui_context_t *ctx, int label, int visible);
/**
* Rect-related functions
@@ -102,5 +124,8 @@ int vui_start_passive_animation(vui_context_t *ctx, vui_anim_step_callback_t ste
*/
void vui_process_mousedown(vui_context_t *ctx, int x, int y);
void vui_process_mouseup(vui_context_t *ctx, int x, int y);
void vui_process_keydown(vui_context_t *ctx, int button);
void vui_process_keyup(vui_context_t *ctx, int button);
void vui_vibrate_set(vui_context_t *ctx, uint8_t val);
#endif // VANILLA_PI_UI_H
+15
View File
@@ -23,6 +23,9 @@ typedef struct {
vui_button_callback_t onclick;
void *onclick_data;
int layer;
int visible;
int enabled;
int checked;
} vui_button_t;
typedef struct {
@@ -34,6 +37,7 @@ typedef struct {
int layer;
vui_color_t color;
vui_font_size_t size;
int visible;
} vui_label_t;
typedef struct {
@@ -76,6 +80,9 @@ typedef struct {
void *complete_data;
} vui_animation_t;
typedef void (*vui_audio_handler_t)(const void *data, size_t size, void *userdata);
typedef void (*vui_vibrate_handler_t)(uint8_t vibrate, void *userdata);
typedef struct vui_context_t {
void *platform_data;
vui_button_t buttons[MAX_BUTTON_COUNT];
@@ -96,6 +103,14 @@ typedef struct vui_context_t {
float layer_opacity[MAX_BUTTON_COUNT];
vui_color_t layer_color[MAX_BUTTON_COUNT];
char background_image[MAX_BUTTON_TEXT];
int background_enabled;
vui_audio_handler_t audio_handler;
void *audio_handler_data;
vui_vibrate_handler_t vibrate_handler;
void *vibrate_handler_data;
int game_mode;
int selected_button;
int cancel_button;
} vui_context_t;
#endif // VANILLA_PI_UI_PRIV_H
+491 -63
View File
@@ -4,20 +4,20 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_ttf.h>
#include <pthread.h>
#include <vanilla.h>
#include "game/game_decode.h"
#include "ui_priv.h"
#include "ui_util.h"
static TTF_Font *sysfont = 0;
static TTF_Font *sysfont_mini = 0;
typedef struct {
SDL_Texture *texture;
int w;
int h;
char text[MAX_BUTTON_TEXT];
char icon[MAX_BUTTON_TEXT];
int checked;
} vui_sdl_cached_texture_t;
typedef struct {
@@ -29,8 +29,110 @@ typedef struct {
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];
TTF_Font *sysfont;
TTF_Font *sysfont_small;
TTF_Font *sysfont_tiny;
SDL_AudioDeviceID audio;
SDL_GameController *controller;
SDL_Texture *game_tex;
AVFrame *frame;
} vui_sdl_context_t;
static int button_map[SDL_CONTROLLER_BUTTON_MAX];
static int axis_map[SDL_CONTROLLER_AXIS_MAX];
static int key_map[SDL_NUM_SCANCODES];
static int vibrate = 0;
void init_gamepad()
{
vibrate = 0;
// Initialize arrays to -1
for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) button_map[i] = -1;
for (int i = 0; i < SDL_CONTROLLER_AXIS_MAX; i++) axis_map[i] = -1;
for (int i = 0; i < SDL_NUM_SCANCODES; i++) key_map[i] = -1;
button_map[SDL_CONTROLLER_BUTTON_A] = VANILLA_BTN_A;
button_map[SDL_CONTROLLER_BUTTON_B] = VANILLA_BTN_B;
button_map[SDL_CONTROLLER_BUTTON_X] = VANILLA_BTN_X;
button_map[SDL_CONTROLLER_BUTTON_Y] = VANILLA_BTN_Y;
button_map[SDL_CONTROLLER_BUTTON_BACK] = VANILLA_BTN_MINUS;
button_map[SDL_CONTROLLER_BUTTON_GUIDE] = VANILLA_BTN_HOME;
button_map[SDL_CONTROLLER_BUTTON_MISC1] = VANILLA_BTN_TV;
button_map[SDL_CONTROLLER_BUTTON_START] = VANILLA_BTN_PLUS;
button_map[SDL_CONTROLLER_BUTTON_LEFTSTICK] = VANILLA_BTN_L3;
button_map[SDL_CONTROLLER_BUTTON_RIGHTSTICK] = VANILLA_BTN_R3;
button_map[SDL_CONTROLLER_BUTTON_LEFTSHOULDER] = VANILLA_BTN_L;
button_map[SDL_CONTROLLER_BUTTON_RIGHTSHOULDER] = VANILLA_BTN_R;
button_map[SDL_CONTROLLER_BUTTON_DPAD_UP] = VANILLA_BTN_UP;
button_map[SDL_CONTROLLER_BUTTON_DPAD_DOWN] = VANILLA_BTN_DOWN;
button_map[SDL_CONTROLLER_BUTTON_DPAD_LEFT] = VANILLA_BTN_LEFT;
button_map[SDL_CONTROLLER_BUTTON_DPAD_RIGHT] = VANILLA_BTN_RIGHT;
axis_map[SDL_CONTROLLER_AXIS_LEFTX] = VANILLA_AXIS_L_X;
axis_map[SDL_CONTROLLER_AXIS_LEFTY] = VANILLA_AXIS_L_Y;
axis_map[SDL_CONTROLLER_AXIS_RIGHTX] = VANILLA_AXIS_R_X;
axis_map[SDL_CONTROLLER_AXIS_RIGHTY] = VANILLA_AXIS_R_Y;
axis_map[SDL_CONTROLLER_AXIS_TRIGGERLEFT] = VANILLA_BTN_ZL;
axis_map[SDL_CONTROLLER_AXIS_TRIGGERRIGHT] = VANILLA_BTN_ZR;
key_map[SDL_SCANCODE_UP] = VANILLA_BTN_UP;
key_map[SDL_SCANCODE_DOWN] = VANILLA_BTN_DOWN;
key_map[SDL_SCANCODE_LEFT] = VANILLA_BTN_LEFT;
key_map[SDL_SCANCODE_RIGHT] = VANILLA_BTN_RIGHT;
key_map[SDL_SCANCODE_Z] = VANILLA_BTN_A;
key_map[SDL_SCANCODE_X] = VANILLA_BTN_B;
key_map[SDL_SCANCODE_C] = VANILLA_BTN_X;
key_map[SDL_SCANCODE_V] = VANILLA_BTN_Y;
key_map[SDL_SCANCODE_RETURN] = VANILLA_BTN_PLUS;
key_map[SDL_SCANCODE_LCTRL] = VANILLA_BTN_MINUS;
key_map[SDL_SCANCODE_H] = VANILLA_BTN_HOME;
key_map[SDL_SCANCODE_Y] = VANILLA_BTN_TV;
key_map[SDL_SCANCODE_W] = VANILLA_AXIS_L_UP;
key_map[SDL_SCANCODE_A] = VANILLA_AXIS_L_LEFT;
key_map[SDL_SCANCODE_S] = VANILLA_AXIS_L_DOWN;
key_map[SDL_SCANCODE_D] = VANILLA_AXIS_L_RIGHT;
key_map[SDL_SCANCODE_E] = VANILLA_BTN_L3;
key_map[SDL_SCANCODE_KP_8] = VANILLA_AXIS_R_UP;
key_map[SDL_SCANCODE_KP_4] = VANILLA_AXIS_R_LEFT;
key_map[SDL_SCANCODE_KP_2] = VANILLA_AXIS_R_DOWN;
key_map[SDL_SCANCODE_KP_6] = VANILLA_AXIS_R_RIGHT;
key_map[SDL_SCANCODE_KP_5] = VANILLA_BTN_R3;
key_map[SDL_SCANCODE_T] = VANILLA_BTN_L;
key_map[SDL_SCANCODE_G] = VANILLA_BTN_ZL;
key_map[SDL_SCANCODE_U] = VANILLA_BTN_R;
key_map[SDL_SCANCODE_J] = VANILLA_BTN_ZR;
}
SDL_GameController *find_valid_controller()
{
for (int i = 0; i < SDL_NumJoysticks(); i++) {
SDL_GameController *c = SDL_GameControllerOpen(i);
if (c) {
return c;
}
}
return NULL;
}
void vui_sdl_audio_handler(const void *data, size_t size, void *userdata)
{
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) userdata;
if (SDL_QueueAudio(sdl_ctx->audio, data, size) < 0) {
printf("Failed to queue audio (data: %p, size: %zu)\n", data, size);
}
}
void vui_sdl_vibrate_handler(uint8_t vibrate, void *userdata)
{
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) userdata;
if (sdl_ctx->controller) {
uint16_t amount = vibrate ? 0xFFFF : 0;
SDL_GameControllerRumble(sdl_ctx->controller, amount, amount, 0);
}
}
int vui_init_sdl(vui_context_t *ctx, int fullscreen)
{
// Initialize SDL
@@ -64,30 +166,65 @@ int vui_init_sdl(vui_context_t *ctx, int fullscreen)
return -1;
}
SDL_AudioSpec desired = {0};
desired.freq = 48000;
desired.format = AUDIO_S16LSB;
desired.channels = 2;
sdl_ctx->audio = SDL_OpenAudioDevice(NULL, 0, &desired, NULL, 0);
if (sdl_ctx->audio) {
SDL_PauseAudioDevice(sdl_ctx->audio, 0);
ctx->audio_handler = vui_sdl_audio_handler;
ctx->audio_handler_data = sdl_ctx;
} else {
printf("Failed to open audio device\n");
}
ctx->vibrate_handler = vui_sdl_vibrate_handler;
ctx->vibrate_handler_data = sdl_ctx;
if (TTF_Init()) {
return -1;
}
const char *font_filename = "/home/matt/src/vanilla/rpi/font/system.ttf";
sysfont = TTF_OpenFont(font_filename, 36);
if (!sysfont) {
sdl_ctx->sysfont = TTF_OpenFont(font_filename, 36);
if (!sdl_ctx->sysfont) {
return -1;
}
sysfont_mini = TTF_OpenFont(font_filename, 24);
if (!sysfont_mini) {
sdl_ctx->sysfont_small = TTF_OpenFont(font_filename, 24);
if (!sdl_ctx->sysfont_small) {
return -1;
}
TTF_SetFontWrappedAlign(sysfont, TTF_WRAPPED_ALIGN_CENTER);
TTF_SetFontWrappedAlign(sysfont_mini, TTF_WRAPPED_ALIGN_CENTER);
sdl_ctx->sysfont_tiny = TTF_OpenFont(font_filename, 16);
if (!sdl_ctx->sysfont_tiny) {
return -1;
}
TTF_SetFontWrappedAlign(sdl_ctx->sysfont, TTF_WRAPPED_ALIGN_CENTER);
TTF_SetFontWrappedAlign(sdl_ctx->sysfont_small, TTF_WRAPPED_ALIGN_CENTER);
TTF_SetFontWrappedAlign(sdl_ctx->sysfont_tiny, TTF_WRAPPED_ALIGN_CENTER);
sdl_ctx->game_tex = SDL_CreateTexture(sdl_ctx->renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, ctx->screen_width, ctx->screen_height);
SDL_SetRenderTarget(sdl_ctx->renderer, sdl_ctx->game_tex);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0, 0, 0, 0);
SDL_RenderClear(sdl_ctx->renderer);
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));
sdl_ctx->controller = find_valid_controller();
sdl_ctx->frame = av_frame_alloc();
// Initialize gamepad lookup tables
init_gamepad();
return 0;
}
@@ -99,17 +236,47 @@ void vui_close_sdl(vui_context_t *ctx)
return;
}
av_frame_free(&sdl_ctx->frame);
if (sdl_ctx->controller) {
SDL_GameControllerClose(sdl_ctx->controller);
}
SDL_DestroyTexture(sdl_ctx->game_tex);
SDL_DestroyTexture(sdl_ctx->background);
for (int i = 0; i < MAX_BUTTON_COUNT; i++) {
if (sdl_ctx->layer_data[i]) {
SDL_DestroyTexture(sdl_ctx->layer_data[i]);
}
}
TTF_CloseFont(sysfont_mini);
TTF_CloseFont(sysfont);
for (int i = 0; i < MAX_BUTTON_COUNT; i++) {
if (sdl_ctx->button_cache[i].texture) {
SDL_DestroyTexture(sdl_ctx->button_cache[i].texture);
}
}
for (int i = 0; i < MAX_BUTTON_COUNT; i++) {
if (sdl_ctx->image_cache[i].texture) {
SDL_DestroyTexture(sdl_ctx->image_cache[i].texture);
}
}
for (int i = 0; i < MAX_BUTTON_COUNT; i++) {
if (sdl_ctx->image_cache[i].texture) {
SDL_DestroyTexture(sdl_ctx->image_cache[i].texture);
}
}
TTF_CloseFont(sdl_ctx->sysfont_tiny);
TTF_CloseFont(sdl_ctx->sysfont_small);
TTF_CloseFont(sdl_ctx->sysfont);
TTF_Quit();
SDL_CloseAudioDevice(sdl_ctx->audio);
SDL_DestroyRenderer(sdl_ctx->renderer);
SDL_DestroyWindow(sdl_ctx->window);
@@ -142,13 +309,27 @@ SDL_Texture *vui_sdl_load_texture(SDL_Renderer *renderer, const char *filename)
return IMG_LoadTexture(renderer, buf);
}
void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
float calculate_x_inset(float y, float h, float radius)
{
float x_inset = 0;
if (y < radius) {
float yb = y - radius;
x_inset = radius - sqrtf(radius*radius - yb*yb);
} else {
if (y >= h - radius) {
float yb = (h - y) - radius;
x_inset = radius - sqrtf(radius*radius - yb*yb);
}
}
return x_inset;
}
void vui_sdl_draw_button(vui_context_t *vui, vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
{
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0, 0, 0, 0);
SDL_RenderClear(sdl_ctx->renderer);
SDL_Color text_color;
TTF_Font *text_font = btn->style != VUI_BUTTON_STYLE_CORNER ? sysfont : sysfont_mini;
SDL_Rect rect;
rect.x = 0;
@@ -182,13 +363,45 @@ void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
0xE0,
0xF0,
};
static const uint8_t button_checked_y_gradient_colors[] = {
0xFF, 0x00, 0x00,
0xFF, 0x80, 0x00,
0xFF, 0xFF, 0x00,
0x00, 0xFF, 0x00,
0x00, 0x00, 0xFF,
};
int scrw, scrh;
vui_get_screen_size(vui, &scrw, &scrh);
// Load icon if exists
int icon_x;
int icon_y;
int icon_size = btn->style == VUI_BUTTON_STYLE_CORNER ? rect.h * 1 / 3 : rect.h * 3 / 4;
SDL_Texture *icon = 0;
if (btn->icon[0]) {
icon = vui_sdl_load_texture(sdl_ctx->renderer, btn->icon);
}
TTF_Font *text_font;
if (btn->style == VUI_BUTTON_STYLE_CORNER) {
if (!icon) {
text_font = sdl_ctx->sysfont_small;
} else {
text_font = sdl_ctx->sysfont_tiny;
}
} else {
text_font = sdl_ctx->sysfont;
}
const int btn_radius = rect.h * 2 / 10;
for (int y = 0; y < rect.h; y++) {
int coord = rect.y + y;
// Determine color
float l = 0;
float lr = 0;
float lg = 0;
float lb = 0;
{
float yf = y / (float) rect.h;
for (size_t j = 1; j < sizeof(button_y_gradient_positions)/sizeof(float); j++) {
@@ -198,9 +411,19 @@ void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
yf -= min;
yf /= max - min;
uint8_t minc = button_y_gradient_colors[j - 1];
uint8_t maxc = button_y_gradient_colors[j];
l = lerp(minc, maxc, yf);
if (btn->checked) {
const uint8_t *minrgb = button_checked_y_gradient_colors + (j - 1) * 3;
const uint8_t *maxrgb = button_checked_y_gradient_colors + j * 3;
lr = lerp(minrgb[0], maxrgb[0], yf);
lg = lerp(minrgb[1], maxrgb[1], yf);
lb = lerp(minrgb[2], maxrgb[2], yf);
} else {
uint8_t minc = button_y_gradient_colors[j - 1];
uint8_t maxc = button_y_gradient_colors[j];
lr = lerp(minc, maxc, yf);
lg = lerp(minc, maxc, yf);
lb = lerp(minc, maxc, yf);
}
break;
}
}
@@ -208,32 +431,29 @@ void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
// Handle rounded corners
float x_inset = 0;
float w_inset = 0;
if (btn->style != VUI_BUTTON_STYLE_CORNER) {
if (y < btn_radius) {
int yb = y - btn_radius;
x_inset = btn_radius - sqrtf(btn_radius*btn_radius - yb*yb);
x_inset = w_inset = calculate_x_inset(y, rect.h, btn_radius);
} else {
float inset_y = y;
if (btn->y < scrh/2) {
inset_y += rect.h;
}
float hor_inset = calculate_x_inset(inset_y, rect.h*2, rect.h);
if (btn->x < scrw/2) {
w_inset = hor_inset;
} else {
if (y >= rect.h - btn_radius) {
int yb = (rect.h - y) - btn_radius;
x_inset = btn_radius - sqrtf(btn_radius*btn_radius - yb*yb);
}
x_inset = hor_inset;
}
}
SDL_SetRenderDrawColor(sdl_ctx->renderer, l, l, l, 0xFF);
SDL_RenderDrawLineF(sdl_ctx->renderer, rect.x + x_inset, coord, rect.x + rect.w - x_inset, coord);
SDL_SetRenderDrawColor(sdl_ctx->renderer, lr, lg, lb, 0xFF);
SDL_RenderDrawLineF(sdl_ctx->renderer, rect.x + x_inset, coord, rect.x + rect.w - w_inset, coord);
}
text_color.r = text_color.g = text_color.b = 0x40;
text_color.a = 0xFF;
// Load icon if exists
int icon_x;
int icon_size = rect.h * 3 / 4;
SDL_Texture *icon = 0;
if (btn->icon[0]) {
icon = vui_sdl_load_texture(sdl_ctx->renderer, btn->icon);
}
// Draw text
if (btn->text[0]) {
@@ -245,21 +465,29 @@ void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
text_rect.w = surface->w;
text_rect.h = surface->h;
if (icon) {
const int btn_padding = 5;
const int btn_padding = 8;
if (icon && btn->style != VUI_BUTTON_STYLE_CORNER) {
icon_x = rect.x + rect.w / 2 - (text_rect.w + icon_size) / 2;
icon_y = rect.y + rect.h/2 - icon_size/2;
text_rect.x = icon_x + icon_size + btn_padding;
} else {
text_rect.x = rect.x + rect.w / 2 - text_rect.w / 2;
}
text_rect.y = rect.y + rect.h / 2 - text_rect.h / 2;
if (icon && btn->style == VUI_BUTTON_STYLE_CORNER) {
text_rect.y = rect.y + rect.h/2 - (text_rect.h + icon_size + btn_padding)/2;
icon_x = rect.x + rect.w/2 - icon_size/2;
icon_y = text_rect.y + text_rect.h + btn_padding;
} else {
text_rect.y = rect.y + rect.h / 2 - text_rect.h / 2;
}
SDL_RenderCopy(sdl_ctx->renderer, texture, NULL, &text_rect);
SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);
} else {
icon_x = rect.x + rect.w/2 - icon_size/2;
icon_y = rect.y + rect.h/2 - icon_size/2;
}
// Draw icon
@@ -267,7 +495,7 @@ void vui_sdl_draw_button(vui_sdl_context_t *sdl_ctx, vui_button_t *btn)
SDL_Rect icon_rect;
icon_rect.w = icon_rect.h = icon_size;
icon_rect.x = icon_x;
icon_rect.y = rect.y + rect.h/2 - icon_rect.h/2;
icon_rect.y = icon_y;
SDL_RenderCopy(sdl_ctx->renderer, icon, 0, &icon_rect);
SDL_DestroyTexture(icon);
@@ -312,7 +540,7 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
}
SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[layer]);
if (layer == 0) {
if (layer == 0 && ctx->background_enabled) {
// Draw background
if (!sdl_ctx->background) {
sdl_ctx->background = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_TARGET, ctx->screen_width, ctx->screen_height);
@@ -323,7 +551,7 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
}
SDL_RenderCopy(renderer, sdl_ctx->background, 0, 0);
} else {
// vui_sdl_set_render_color(renderer, ctx->layer_color[layer]);
vui_sdl_set_render_color(renderer, ctx->layer_color[layer]);
SDL_RenderClear(renderer);
}
}
@@ -340,13 +568,22 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
sr.w = rect->w;
sr.h = rect->h;
vui_sdl_set_render_color(renderer, rect->color);
SDL_RenderFillRect(renderer, &sr);
if (rect->border_radius == 0) {
SDL_RenderFillRect(renderer, &sr);
} else {
for (int y = 0; y < sr.h; y++) {
float x_inset = calculate_x_inset(y, sr.h, rect->border_radius);
float draw_y = sr.y + y;
SDL_RenderDrawLineF(renderer, sr.x + x_inset, draw_y, sr.x + sr.w - x_inset, draw_y);
}
}
}
// Draw labels
for (int i = 0; i < ctx->label_count; i++) {
vui_label_t *lbl = &ctx->labels[i];
if (lbl->text[0]) {
if (lbl->text[0] && lbl->visible) {
SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[lbl->layer]);
SDL_Color c;
@@ -355,7 +592,7 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
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 ? sdl_ctx->sysfont_small : sdl_ctx->sysfont, lbl->text, c, lbl->w);
SDL_Rect sr;
sr.x = 0;
@@ -380,11 +617,15 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
for (int i = 0; i < ctx->button_count; i++) {
vui_button_t *btn = &ctx->buttons[i];
if (!btn->visible) {
continue;
}
SDL_SetRenderTarget(renderer, sdl_ctx->layer_data[btn->layer]);
// Check if button needs to be redrawn
vui_sdl_cached_texture_t *btn_tex = &sdl_ctx->button_cache[i];
if (!btn_tex->texture || btn_tex->w != btn->w || btn_tex->h != btn->h || strcmp(btn_tex->text, btn->text) || strcmp(btn_tex->icon, btn->icon)) {
if (!btn_tex->texture || btn_tex->w != btn->w || btn_tex->h != btn->h || strcmp(btn_tex->text, btn->text) || strcmp(btn_tex->icon, btn->icon) || btn_tex->checked != btn->checked) {
// Must re-draw texture
if (btn_tex->texture && (btn_tex->w != btn->w || btn_tex->h != btn->h)) {
SDL_DestroyTexture(btn_tex->texture);
@@ -397,13 +638,15 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
SDL_Texture *old_target = SDL_GetRenderTarget(renderer);
SDL_SetRenderTarget(renderer, btn_tex->texture);
vui_sdl_draw_button(sdl_ctx, btn);
vui_sdl_draw_button(ctx, sdl_ctx, btn);
SDL_SetRenderTarget(renderer, old_target);
btn_tex->w = btn->w;
btn_tex->h = btn->h;
strcpy(btn_tex->text, btn->text);
strcpy(btn_tex->icon, btn->icon);
btn_tex->checked = btn->checked;
}
// Draw cached texture onto screen
@@ -413,7 +656,60 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
rect.w = btn->sw;
rect.h = btn->sh;
uint8_t f = (btn->enabled ? 1 : 0.5f) * 0xFF;
SDL_SetTextureAlphaMod(btn_tex->texture, f);
SDL_SetTextureColorMod(btn_tex->texture, f, f, f);
SDL_RenderCopy(renderer, btn_tex->texture, 0, &rect);
if (i == ctx->selected_button) {
const int btn_select_short_sz = 8;
const int btn_select_long_sz = 24;
const int tlx = btn->sx;
const int tly = btn->sy;
const int trx = btn->sx + btn->sw;
const int try = tly;
const int blx = tlx;
const int bly = btn->sy + btn->sh;
const int brx = trx;
const int bry = bly;
SDL_SetRenderDrawColor(renderer, 0x17, 0xCB, 0xD1, 0xFF);
SDL_Rect r;
r.x = tlx;
r.y = tly;
r.w = btn_select_long_sz;
r.h = btn_select_short_sz;
SDL_RenderFillRect(renderer, &r);
r.x = trx - btn_select_long_sz;
SDL_RenderFillRect(renderer, &r);
r.y = bly - btn_select_short_sz;
SDL_RenderFillRect(renderer, &r);
r.x = tlx;
SDL_RenderFillRect(renderer, &r);
r.w = btn_select_short_sz;
r.h = btn_select_long_sz;
r.y = tly;
SDL_RenderFillRect(renderer, &r);
r.x = trx - btn_select_short_sz;
SDL_RenderFillRect(renderer, &r);
r.y = bry - btn_select_long_sz;
SDL_RenderFillRect(renderer, &r);
r.x = blx;
SDL_RenderFillRect(renderer, &r);
}
}
// Draw images
@@ -447,6 +743,13 @@ void vui_draw_sdl(vui_context_t *ctx, SDL_Renderer *renderer)
}
}
int32_t pack_float(float f)
{
int32_t x;
memcpy(&x, &f, sizeof(int32_t));
return x;
}
int vui_update_sdl(vui_context_t *vui)
{
vui_sdl_context_t *sdl_ctx = (vui_sdl_context_t *) vui->platform_data;
@@ -455,36 +758,161 @@ int vui_update_sdl(vui_context_t *vui)
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) {
switch (ev.type) {
case SDL_QUIT:
vanilla_stop();
return 0;
} else if (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEBUTTONUP) {
case SDL_MOUSEMOTION:
case SDL_MOUSEBUTTONDOWN:
case SDL_MOUSEBUTTONUP:
// Ensure dst_rect is initialized
if (dst_rect->w > 0 && dst_rect->h > 0) {
// Translate screen coords to logical coords
int tr_x, tr_y;
tr_x = (ev.button.x - dst_rect->x) * vui->screen_width / dst_rect->w;
tr_y = (ev.button.y - dst_rect->y) * vui->screen_height / dst_rect->h;
if (ev.type == SDL_MOUSEBUTTONDOWN)
vui_process_mousedown(vui, tr_x, tr_y);
else
vui_process_mouseup(vui, tr_x, tr_y);
if (vui->game_mode) {
// In game mode, pass clicks directly to Vanilla
int x, y;
if (ev.button.button == SDL_BUTTON_LEFT && (ev.type == SDL_MOUSEBUTTONDOWN || ev.type == SDL_MOUSEMOTION)) {
x = tr_x;
y = tr_y;
} else {
x = -1;
y = -1;
}
vanilla_set_touch(x, y);
} else {
// Otherwise, handle ourselves
if (ev.type == SDL_MOUSEBUTTONDOWN)
vui_process_mousedown(vui, tr_x, tr_y);
else if (ev.type == SDL_MOUSEBUTTONUP)
vui_process_mouseup(vui, tr_x, tr_y);
}
}
break;
case SDL_CONTROLLERDEVICEADDED:
// Attempt to find controller if one doesn't exist already
if (!sdl_ctx->controller) {
sdl_ctx->controller = find_valid_controller();
}
break;
case SDL_CONTROLLERDEVICEREMOVED:
// Handle current controller being removed
if (sdl_ctx->controller && ev.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_ctx->controller))) {
SDL_GameControllerClose(sdl_ctx->controller);
sdl_ctx->controller = find_valid_controller();
}
break;
case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP:
if (sdl_ctx->controller && ev.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_ctx->controller))) {
int vanilla_btn = button_map[ev.cbutton.button];
if (vanilla_btn != -1) {
if (vui->game_mode) {
vanilla_set_button(vanilla_btn, ev.type == SDL_CONTROLLERBUTTONDOWN ? INT16_MAX : 0);
} else if (ev.type == SDL_CONTROLLERBUTTONDOWN) {
vui_process_keydown(vui, vanilla_btn);
} else {
vui_process_keyup(vui, vanilla_btn);
}
}
}
break;
case SDL_CONTROLLERAXISMOTION:
if (sdl_ctx->controller && ev.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(sdl_ctx->controller))) {
int vanilla_axis = axis_map[ev.caxis.axis];
Sint16 axis_value = ev.caxis.value;
if (vanilla_axis != -1) {
if (vui->game_mode) {
vanilla_set_button(vanilla_axis, axis_value);
} else if (vanilla_axis == SDL_CONTROLLER_AXIS_LEFTX) {
if (axis_value < 0)
vui_process_keydown(vui, VANILLA_AXIS_L_LEFT);
else if (axis_value > 0)
vui_process_keydown(vui, VANILLA_AXIS_L_RIGHT);
else {
vui_process_keyup(vui, VANILLA_AXIS_L_LEFT);
vui_process_keyup(vui, VANILLA_AXIS_L_RIGHT);
}
} else if (vanilla_axis == SDL_CONTROLLER_AXIS_LEFTY) {
if (axis_value < 0)
vui_process_keydown(vui, VANILLA_AXIS_L_UP);
else if (axis_value > 0)
vui_process_keydown(vui, VANILLA_AXIS_L_DOWN);
else {
vui_process_keyup(vui, VANILLA_AXIS_L_UP);
vui_process_keyup(vui, VANILLA_AXIS_L_DOWN);
}
}
}
}
break;
case SDL_CONTROLLERSENSORUPDATE:
if (ev.csensor.sensor == SDL_SENSOR_ACCEL) {
vanilla_set_button(VANILLA_SENSOR_ACCEL_X, pack_float(ev.csensor.data[0]));
vanilla_set_button(VANILLA_SENSOR_ACCEL_Y, pack_float(ev.csensor.data[1]));
vanilla_set_button(VANILLA_SENSOR_ACCEL_Z, pack_float(ev.csensor.data[2]));
} else if (ev.csensor.sensor == SDL_SENSOR_GYRO) {
vanilla_set_button(VANILLA_SENSOR_GYRO_PITCH, pack_float(ev.csensor.data[0]));
vanilla_set_button(VANILLA_SENSOR_GYRO_YAW, pack_float(ev.csensor.data[1]));
vanilla_set_button(VANILLA_SENSOR_GYRO_ROLL, pack_float(ev.csensor.data[2]));
}
break;
case SDL_KEYDOWN:
case SDL_KEYUP:
{
int vanilla_btn = key_map[ev.key.keysym.scancode];
if (vanilla_btn != -1) {
if (vui->game_mode) {
vanilla_set_button(vanilla_btn, ev.type == SDL_KEYDOWN ? INT16_MAX : 0);
} else if (ev.type == SDL_KEYDOWN) {
vui_process_keydown(vui, vanilla_btn);
} else {
vui_process_keyup(vui, vanilla_btn);
}
}
break;
}
}
}
vui_update(vui);
// Draw vui to a custom texture
vui_draw_sdl(vui, sdl_ctx->renderer);
SDL_Texture *main_tex;
// Flatten layers
for (int i = vui->layers - 1; i > 0; i--) {
SDL_Texture *bg = sdl_ctx->layer_data[i-1];
SDL_Texture *fg = sdl_ctx->layer_data[i];
if (!vui->game_mode) {
// Draw vui to a custom texture
vui_draw_sdl(vui, sdl_ctx->renderer);
SDL_SetRenderTarget(sdl_ctx->renderer, bg);
SDL_SetTextureColorMod(fg, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF);
SDL_SetTextureAlphaMod(fg, vui->layer_opacity[i] * 0xFF);
SDL_RenderCopy(sdl_ctx->renderer, fg, NULL, NULL);
// Flatten layers
for (int i = vui->layers - 1; i > 0; i--) {
SDL_Texture *bg = sdl_ctx->layer_data[i-1];
SDL_Texture *fg = sdl_ctx->layer_data[i];
SDL_SetRenderTarget(sdl_ctx->renderer, bg);
SDL_SetTextureColorMod(fg, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF, vui->layer_opacity[i] * 0xFF);
SDL_SetTextureAlphaMod(fg, vui->layer_opacity[i] * 0xFF);
SDL_RenderCopy(sdl_ctx->renderer, fg, NULL, NULL);
}
main_tex = sdl_ctx->layer_data[0];
} else {
pthread_mutex_lock(&vpi_decoding_mutex);
if (vpi_present_frame && vpi_present_frame->data[0]) {
av_frame_move_ref(sdl_ctx->frame, vpi_present_frame);
}
pthread_mutex_unlock(&vpi_decoding_mutex);
if (sdl_ctx->frame->data[0]) {
SDL_UpdateYUVTexture(sdl_ctx->game_tex, NULL,
sdl_ctx->frame->data[0], sdl_ctx->frame->linesize[0],
sdl_ctx->frame->data[1], sdl_ctx->frame->linesize[1],
sdl_ctx->frame->data[2], sdl_ctx->frame->linesize[2]);
av_frame_unref(sdl_ctx->frame);
}
main_tex = sdl_ctx->game_tex;
}
// Calculate our destination rectangle
@@ -513,7 +941,7 @@ int vui_update_sdl(vui_context_t *vui)
SDL_SetRenderTarget(sdl_ctx->renderer, NULL);
SDL_SetRenderDrawColor(sdl_ctx->renderer, 0, 0, 0, 0);
SDL_RenderClear(sdl_ctx->renderer);
SDL_RenderCopy(sdl_ctx->renderer, sdl_ctx->layer_data[0], NULL, dst_rect);
SDL_RenderCopy(sdl_ctx->renderer, main_tex, NULL, dst_rect);
// Flip surfaces
SDL_RenderPresent(sdl_ctx->renderer);