implement communication with pipe

also adds support for local Unix domain socket mode
This commit is contained in:
itsmattkc committed 2025-02-09 12:26:58 -08:00
1 parent 03b163cef1
commit 94d86f5bba
29 files changed
+752 -299

No files matched your search

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