initial event loop implementation

This commit is contained in:
MattKC 2024-10-14 03:09:20 -07:00
parent 92b5ffb5b1
commit 39fb9da8ae
10 changed files with 135 additions and 55 deletions

View file

@ -14,21 +14,25 @@
#include <unistd.h>
#include <vanilla.h>
void vanillaEventHandler(void *context, int type, const char *data, size_t dataLength)
void Backend::vanillaEventHandler()
{
Backend *backend = static_cast<Backend*>(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());
}

View file

@ -75,6 +75,8 @@ protected:
private slots:
void syncFutureCompleted();
void vanillaEventHandler();
};
class BackendViaInternalPipe : public Backend

View file

@ -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());

View file

@ -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;

View file

@ -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);
}

View file

@ -3,6 +3,7 @@
#include "vanilla.h"
#include <pthread.h>
#include <stdint.h>
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

View file

@ -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);

View file

@ -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());

View file

@ -2,6 +2,7 @@
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -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);
}

View file

@ -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
*