mirror of
https://github.com/vanilla-wiiu/vanilla.git
synced 2026-10-28 09:43:51 +00:00
implement actual gameplay
This commit is contained in:
1 parent
db9cd2f134
commit
9b370947e1
44 files changed
+5024
-449
No files matched your search
@@ -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" ]
|
||||
@@ -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());
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 *) ∈
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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
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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user