diff --git a/app/backend.cpp b/app/backend.cpp index 02ad764..ced8de9 100644 --- a/app/backend.cpp +++ b/app/backend.cpp @@ -14,21 +14,25 @@ #include #include -void vanillaEventHandler(void *context, int type, const char *data, size_t dataLength) +void Backend::vanillaEventHandler() { - Backend *backend = static_cast(context); + vanilla_event_t event; - switch (type) { - case VANILLA_EVENT_VIDEO: - emit backend->videoAvailable(QByteArray(data, dataLength)); - break; - case VANILLA_EVENT_AUDIO: - emit backend->audioAvailable(QByteArray(data, dataLength)); - break; - case VANILLA_EVENT_VIBRATE: - emit backend->vibrate(*data); - break; + while (vanilla_wait_event(&event)) { + switch (event.type) { + case VANILLA_EVENT_VIDEO: + emit videoAvailable(QByteArray((const char *) event.data, event.size)); + break; + case VANILLA_EVENT_AUDIO: + emit audioAvailable(QByteArray((const char *) event.data, event.size)); + break; + case VANILLA_EVENT_VIBRATE: + emit vibrate(*event.data); + break; + } } + + // TODO: Add way for this loop to exit, preferably from libvanilla } Backend::Backend(QObject *parent) : QObject(parent) @@ -71,11 +75,12 @@ int Backend::syncInternal(uint16_t code) void Backend::connectToConsole() { connectInternal(); + QtConcurrent::run(&Backend::vanillaEventHandler, this); } int Backend::connectInternal() { - return vanilla_start(vanillaEventHandler, this); + return vanilla_start(0); } void Backend::updateTouch(int x, int y) @@ -174,7 +179,7 @@ int BackendViaInternalPipe::syncInternal(uint16_t code) int BackendViaInternalPipe::connectInternal() { - return vanilla_start_udp(vanillaEventHandler, this, QHostAddress(QHostAddress::LocalHost).toIPv4Address()); + return vanilla_start(QHostAddress(QHostAddress::LocalHost).toIPv4Address()); } BackendViaExternalPipe::BackendViaExternalPipe(const QHostAddress &udpServer, QObject *parent) : Backend(parent) @@ -189,5 +194,5 @@ int BackendViaExternalPipe::syncInternal(uint16_t code) int BackendViaExternalPipe::connectInternal() { - return vanilla_start_udp(vanillaEventHandler, this, m_serverAddress.toIPv4Address()); + return vanilla_start(m_serverAddress.toIPv4Address()); } \ No newline at end of file diff --git a/app/backend.h b/app/backend.h index 7b79e49..2c036ef 100644 --- a/app/backend.h +++ b/app/backend.h @@ -75,6 +75,8 @@ protected: private slots: void syncFutureCompleted(); + void vanillaEventHandler(); + }; class BackendViaInternalPipe : public Backend diff --git a/lib/gamepad/audio.c b/lib/gamepad/audio.c index 455125a..8af80a0 100644 --- a/lib/gamepad/audio.c +++ b/lib/gamepad/audio.c @@ -31,7 +31,7 @@ typedef struct { uint32_t video_format; } AudioPacketVideoFormat; -void handle_audio_packet(vanilla_event_handler_t event_handler, void *context, char *data, size_t len) +void handle_audio_packet(gamepad_context_t *ctx, char *data, size_t len) { for (int byte = 0; byte < len; byte++) { data[byte] = (unsigned char) reverse_bits(data[byte], 8); @@ -56,22 +56,22 @@ void handle_audio_packet(vanilla_event_handler_t event_handler, void *context, c return; } - event_handler(context, VANILLA_EVENT_AUDIO, ap->payload, ap->payload_size); + push_event(ctx, VANILLA_EVENT_AUDIO, ap->payload, ap->payload_size); uint8_t vibrate_val = ap->vibrate; - event_handler(context, VANILLA_EVENT_VIBRATE, &vibrate_val, sizeof(vibrate_val)); + push_event(ctx, VANILLA_EVENT_VIBRATE, &vibrate_val, sizeof(vibrate_val)); } void *listen_audio(void *x) { - struct gamepad_thread_context *info = (struct gamepad_thread_context *) x; + gamepad_context_t *info = (gamepad_context_t *) x; unsigned char data[2048]; ssize_t size; do { size = recv(info->socket_aud, data, sizeof(data), 0); if (size > 0) { if (is_stop_code(data, size)) break; - handle_audio_packet(info->event_handler, info->context, data, size); + handle_audio_packet(info, data, size); } } while (!is_interrupted()); diff --git a/lib/gamepad/command.c b/lib/gamepad/command.c index 9e6590a..afd8cc8 100644 --- a/lib/gamepad/command.c +++ b/lib/gamepad/command.c @@ -420,7 +420,7 @@ void handle_command_packet(int skt, CmdHeader *request) void *listen_command(void *x) { - struct gamepad_thread_context *info = (struct gamepad_thread_context *)x; + gamepad_context_t *info = (gamepad_context_t *)x; unsigned char data[sizeof(CmdHeader) + 2048]; ssize_t size; diff --git a/lib/gamepad/gamepad.c b/lib/gamepad/gamepad.c index c6397e8..7acbb1a 100644 --- a/lib/gamepad/gamepad.c +++ b/lib/gamepad/gamepad.c @@ -178,7 +178,7 @@ exit: return ret; } -int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context, uint32_t server_address) +int connect_as_gamepad_internal(event_loop_t *event_loop, uint32_t server_address) { clear_interrupt(); @@ -199,9 +199,8 @@ int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *con PORT_CMD += 200; } - struct gamepad_thread_context info; - info.event_handler = event_handler; - info.context = context; + gamepad_context_t info; + info.event_loop = event_loop; int ret = VANILLA_ERROR; @@ -270,4 +269,25 @@ exit: int is_stop_code(const char *data, size_t data_length) { return (data_length == sizeof(STOP_CODE) && !memcmp(data, &STOP_CODE, sizeof(STOP_CODE))); +} + +int push_event(gamepad_context_t *ctx, int type, const void *data, size_t size) +{ + pthread_mutex_lock(&ctx->event_loop->mutex); + + vanilla_event_t *ev = &ctx->event_loop->events[ctx->event_loop->new_index % VANILLA_MAX_EVENT_COUNT]; + + ev->type = type; + memcpy(ev->data, data, size); + ev->size = size; + + ctx->event_loop->new_index++; + + // Prevent rollover by skipping oldest event if necessary + if (ctx->event_loop->new_index > ctx->event_loop->used_index - VANILLA_MAX_EVENT_COUNT) { + ctx->event_loop->used_index++; + } + + pthread_cond_broadcast(&ctx->event_loop->waitcond); + pthread_mutex_unlock(&ctx->event_loop->mutex); } \ No newline at end of file diff --git a/lib/gamepad/gamepad.h b/lib/gamepad/gamepad.h index ddcdb36..7cc1945 100644 --- a/lib/gamepad/gamepad.h +++ b/lib/gamepad/gamepad.h @@ -3,6 +3,7 @@ #include "vanilla.h" +#include #include extern uint16_t PORT_MSG; @@ -13,22 +14,31 @@ extern uint16_t PORT_CMD; struct wpa_ctrl; -struct gamepad_thread_context +#define VANILLA_MAX_EVENT_COUNT 20 +typedef struct { - vanilla_event_handler_t event_handler; - void *context; + vanilla_event_t events[VANILLA_MAX_EVENT_COUNT]; + size_t new_index; + size_t used_index; + pthread_mutex_t mutex; + pthread_cond_t waitcond; +} event_loop_t; +typedef struct +{ + event_loop_t *event_loop; int socket_vid; int socket_aud; int socket_hid; int socket_msg; int socket_cmd; -}; +} gamepad_context_t; int sync_internal(uint16_t code, uint32_t server_address); -int connect_as_gamepad_internal(vanilla_event_handler_t event_handler, void *context, uint32_t server_address); +int connect_as_gamepad_internal(event_loop_t *ctx, uint32_t server_address); unsigned int reverse_bits(unsigned int b, int bit_count); void send_to_console(int fd, const void *data, size_t data_size, int port); int is_stop_code(const char *data, size_t data_length); +int push_event(gamepad_context_t *ctx, int type, const void *data, size_t size); #endif // VANILLA_GAMEPAD_H \ No newline at end of file diff --git a/lib/gamepad/input.c b/lib/gamepad/input.c index 737095a..844e75b 100644 --- a/lib/gamepad/input.c +++ b/lib/gamepad/input.c @@ -243,7 +243,7 @@ void send_input(int socket_hid) void *listen_input(void *x) { - struct gamepad_thread_context *info = (struct gamepad_thread_context *) x; + gamepad_context_t *info = (gamepad_context_t *) x; pthread_mutex_init(&button_mtx, NULL); diff --git a/lib/gamepad/video.c b/lib/gamepad/video.c index cbf3cc4..0974bdd 100644 --- a/lib/gamepad/video.c +++ b/lib/gamepad/video.c @@ -46,7 +46,7 @@ void send_idr_request_to_console(int socket_msg) send_to_console(socket_msg, idr_request, sizeof(idr_request), PORT_MSG); } -void handle_video_packet(vanilla_event_handler_t event_handler, void *context, unsigned char *data, size_t size, int socket_msg) +void handle_video_packet(gamepad_context_t *ctx, unsigned char *data, size_t size, int socket_msg) { // TODO: This is all really weird. Copied from drc-sim-c but I feel like there's probably a better way. @@ -176,7 +176,7 @@ void handle_video_packet(vanilla_event_handler_t event_handler, void *context, u nals_current++; } - event_handler(context, VANILLA_EVENT_VIDEO, nals, (nals_current - nals)); + push_event(ctx, VANILLA_EVENT_VIDEO, nals, (nals_current - nals)); free(nals); } else { @@ -188,7 +188,7 @@ void handle_video_packet(vanilla_event_handler_t event_handler, void *context, u void *listen_video(void *x) { // Receive video - struct gamepad_thread_context *info = (struct gamepad_thread_context *) x; + gamepad_context_t *info = (gamepad_context_t *) x; unsigned char data[2048]; ssize_t size; @@ -198,7 +198,7 @@ void *listen_video(void *x) size = recv(info->socket_vid, data, sizeof(data), 0); if (size > 0) { if (is_stop_code(data, size)) break; - handle_video_packet(info->event_handler, info->context, data, size, info->socket_msg); + handle_video_packet(info, data, size, info->socket_msg); } } while (!is_interrupted()); diff --git a/lib/vanilla.c b/lib/vanilla.c index abfe9be..e7d52fb 100644 --- a/lib/vanilla.c +++ b/lib/vanilla.c @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -14,31 +15,40 @@ #include "vanilla.h" pthread_mutex_t main_mutex = PTHREAD_MUTEX_INITIALIZER; +pthread_mutex_t gamepad_mutex = PTHREAD_MUTEX_INITIALIZER; +event_loop_t event_loop = {0}; struct gamepad_data_t { - vanilla_event_handler_t event_handler; - void *context; uint32_t server_address; }; void *start_gamepad(void *arg) { struct gamepad_data_t *data = (struct gamepad_data_t *) arg; - connect_as_gamepad_internal(data->event_handler, data->context, data->server_address); + + // Initialize gamepad + event_loop.new_index = 0; + event_loop.used_index = 0; + pthread_mutex_init(&event_loop.mutex, NULL); + pthread_cond_init(&event_loop.waitcond, NULL); + + connect_as_gamepad_internal(&event_loop, data->server_address); + + pthread_cond_destroy(&event_loop.waitcond); + pthread_mutex_destroy(&event_loop.mutex); + free(data); pthread_mutex_unlock(&main_mutex); return 0; } -int vanilla_start_internal(vanilla_event_handler_t event_handler, void *context, uint32_t server_address) +int vanilla_start_internal(uint32_t server_address) { if (pthread_mutex_trylock(&main_mutex) == 0) { pthread_t other; struct gamepad_data_t *data = malloc(sizeof(struct gamepad_data_t)); - data->event_handler = event_handler; - data->context = context; data->server_address = server_address; pthread_create(&other, NULL, start_gamepad, data); @@ -48,14 +58,9 @@ int vanilla_start_internal(vanilla_event_handler_t event_handler, void *context, } } -int vanilla_start(vanilla_event_handler_t event_handler, void *context) +int vanilla_start(uint32_t server_address) { - return vanilla_start_internal(event_handler, context, 0); -} - -int vanilla_start_udp(vanilla_event_handler_t event_handler, void *context, uint32_t server_address) -{ - return vanilla_start_internal(event_handler, context, server_address); + return vanilla_start_internal(server_address); } void vanilla_stop() @@ -137,4 +142,38 @@ void vanilla_set_battery_status(int battery_status) int vanilla_sync(uint16_t code, uint32_t server_address) { return sync_internal(code, server_address); +} + +int vanilla_get_event(vanilla_event_t *event, int wait) +{ + int ret = 0; + + pthread_mutex_lock(&event_loop.mutex); + + if (wait) { + while (event_loop.used_index == event_loop.new_index) { + pthread_cond_wait(&event_loop.waitcond, &event_loop.mutex); + } + } + + if (event_loop.used_index < event_loop.new_index) { + // Output data to pointer + *event = event_loop.events[event_loop.used_index % VANILLA_MAX_EVENT_COUNT]; + event_loop.used_index++; + ret = 1; + } + + pthread_mutex_unlock(&event_loop.mutex); + + return ret; +} + +int vanilla_poll_event(vanilla_event_t *event) +{ + return vanilla_get_event(event, 0); +} + +int vanilla_wait_event(vanilla_event_t *event) +{ + return vanilla_get_event(event, 1); } \ No newline at end of file diff --git a/lib/vanilla.h b/lib/vanilla.h index ca908b9..e1740a6 100644 --- a/lib/vanilla.h +++ b/lib/vanilla.h @@ -60,6 +60,7 @@ enum VanillaGamepadButtons enum VanillaEvent { + VANILLA_EVENT_NONE, VANILLA_EVENT_VIDEO, VANILLA_EVENT_AUDIO, VANILLA_EVENT_VIBRATE @@ -94,19 +95,22 @@ static const uint8_t VANILLA_PPS_PARAMS[] = { 0x00, 0x00, 0x00, 0x01, 0x68, 0xee, 0x06, 0x0c, 0xe8 }; -/** - * Event handler used by caller to receive events - */ -typedef void (*vanilla_event_handler_t)(void *context, int event_type, const char *data, size_t data_size); +typedef struct +{ + int type; + uint8_t data[2048]; + size_t size; +} vanilla_event_t; /** * Start listening for gamepad commands */ -int vanilla_start(vanilla_event_handler_t event_handler, void *context); -int vanilla_start_udp(vanilla_event_handler_t event_handler, void *context, uint32_t server_address); - +int vanilla_start(uint32_t server_address); int vanilla_sync(uint16_t code, uint32_t server_address); +int vanilla_poll_event(vanilla_event_t *event); +int vanilla_wait_event(vanilla_event_t *event); + /** * Attempt to stop the current action *