Skip to content

Commit

Permalink
Add keymon native udp (#1736)
Browse files Browse the repository at this point in the history
  • Loading branch information
Aemiii91 authored Dec 29, 2024
1 parent 2ee5e85 commit 48ed180
Show file tree
Hide file tree
Showing 10 changed files with 289 additions and 37 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

TARGET=Onion
VERSION=4.4.0
RA_SUBVERSION=1.19.1-1
RA_SUBVERSION=1.19.1-2

###########################################################

Expand Down
47 changes: 47 additions & 0 deletions src/common/utils/retroarch_cmd.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "retroarch_cmd.h"

#include <stdio.h>

#include "udp.h"

static const char *LOCALHOST_IP = "127.0.0.1";
static const int RETROARCH_CMD_UDP_PORT = 55355;

int retroarch_cmd(const char *cmd)
{
return udp_send(LOCALHOST_IP, RETROARCH_CMD_UDP_PORT, cmd);
}

int retroarch_get(const char *cmd, char *response, size_t response_size)
{
return udp_send_receive(LOCALHOST_IP, RETROARCH_CMD_UDP_PORT, cmd, response, response_size);
}

int retroarch_quit(void)
{
return retroarch_cmd("QUIT");
}

int retroarch_toggleMenu(void)
{
return retroarch_cmd("MENU_TOGGLE");
}

int retroarch_getInfo(RetroArchInfo_t *info)
{
char response[128];
if (retroarch_get("GET_INFO", response, sizeof(response)) == -1) {
return -1;
}

// Parse response "GET_INFO %d %d NO" or "GET_INFO %d %d %d"
int parsed = sscanf(response, "GET_INFO %u %u %d", &info->max_disk_slots, &info->disk_slot, &info->state_slot);

if (parsed < 2) {
return -1;
}

info->has_state_slot = parsed == 3;

return 0;
}
21 changes: 21 additions & 0 deletions src/common/utils/retroarch_cmd.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#ifndef UTILS_RETROARCH_CMD_H__
#define UTILS_RETROARCH_CMD_H__

#include <stdbool.h>
#include <stddef.h>

typedef struct RetroArchInfo {
unsigned int max_disk_slots;
unsigned int disk_slot;
int state_slot;
bool has_state_slot;
} RetroArchInfo_t;

int retroarch_cmd(const char *cmd); // Send RetroArch UDP command
int retroarch_get(const char *cmd, char *response, size_t response_size); // Get RetroArch UDP command response

int retroarch_quit(void); // RetroArch QUIT
int retroarch_toggleMenu(void); // RetroArch MENU_TOGGLE
int retroarch_getInfo(RetroArchInfo_t *info); // Get RetroArch info

#endif // UTILS_RETROARCH_CMD_H__
158 changes: 158 additions & 0 deletions src/common/utils/udp.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
#include "udp.h"

#include <arpa/inet.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>

static const int PING_TIMEOUT_MS = 100;
static const int MAX_RETRIES = 3;
static const int RETRY_DELAY_MS = 500;
static const int RECV_TIMEOUT_MS = 60000;

static int _init_socket(const char *ipAddress, int port, int *socket_fd, struct sockaddr_in *server_address)
{
*socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (*socket_fd == -1) {
perror("Socket creation failed");
return -1;
}

memset(server_address, 0, sizeof(*server_address));
server_address->sin_family = AF_INET;
server_address->sin_port = htons(port);
if (inet_pton(AF_INET, ipAddress, &server_address->sin_addr) <= 0) {
perror("Invalid IP address");
close(*socket_fd);
return -1;
}

return 0;
}

static int _ping_socket(int socket_fd, struct sockaddr_in *server_address)
{
const char *ping_message = "VERSION";
char response[32];
struct timeval timeout;
timeout.tv_sec = PING_TIMEOUT_MS / 1000;
timeout.tv_usec = (PING_TIMEOUT_MS % 1000) * 1000;

// Set receive timeout
if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("Failed to set socket receive timeout");
return -1;
}

// Send ping message
if (sendto(socket_fd, ping_message, strlen(ping_message), 0, (struct sockaddr *)server_address, sizeof(*server_address)) == -1) {
perror("Failed to send ping");
return -1;
}

// Wait for response
ssize_t bytes_received = recvfrom(socket_fd, response, sizeof(response), 0, NULL, NULL);
if (bytes_received == -1) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
fprintf(stderr, "Ping timeout\n");
}
else {
perror("Failed to receive ping response");
}
return -1;
}

return 0;
}

static int _udp_send(const char *ipAddress, int port, const char *message)
{
int socket_fd;
struct sockaddr_in server_address;

if (_init_socket(ipAddress, port, &socket_fd, &server_address) == -1) {
return -1;
}

if (_ping_socket(socket_fd, &server_address) == -1) {
close(socket_fd);
return -1;
}

if (sendto(socket_fd, message, strlen(message), 0, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
perror("Failed to send data");
close(socket_fd);
return -1;
}

close(socket_fd);
return 0;
}

static int _udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size)
{
int socket_fd;
struct sockaddr_in server_address;

if (_init_socket(ipAddress, port, &socket_fd, &server_address) == -1) {
return -1;
}

if (_ping_socket(socket_fd, &server_address) == -1) {
close(socket_fd);
return -1;
}

if (sendto(socket_fd, message, strlen(message), 0, (struct sockaddr *)&server_address, sizeof(server_address)) == -1) {
perror("Failed to send data");
close(socket_fd);
return -1;
}

// Set a longer timeout for receiving the response
struct timeval timeout;
timeout.tv_sec = RECV_TIMEOUT_MS / 1000;
timeout.tv_usec = (RECV_TIMEOUT_MS % 1000) * 1000;

if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
perror("Failed to set socket receive timeout");
close(socket_fd);
return -1;
}

ssize_t bytes_received = recvfrom(socket_fd, response, response_size, 0, NULL, NULL);
if (bytes_received == -1) {
perror("Failed to receive data");
close(socket_fd);
return -1;
}

response[bytes_received] = '\0'; // Null-terminate the received string

close(socket_fd);
return 0;
}

int udp_send(const char *ipAddress, int port, const char *message)
{
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
if (_udp_send(ipAddress, port, message) == 0) {
return 0; // Success
}
usleep(RETRY_DELAY_MS * 1000); // Delay before retrying
}
return -1; // Failed after retries
}

int udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size)
{
for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
if (_udp_send_receive(ipAddress, port, message, response, response_size) == 0) {
return 0; // Success
}
usleep(RETRY_DELAY_MS * 1000); // Delay before retrying
}
return -1; // Failed after retries
}
10 changes: 10 additions & 0 deletions src/common/utils/udp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef UTILS_UDP_H__
#define UTILS_UDP_H__

#include <stdbool.h>
#include <stddef.h>

int udp_send(const char *ipAddress, int port, const char *message);
int udp_send_receive(const char *ipAddress, int port, const char *message, char *response, size_t response_size);

#endif // UTILS_UDP_H__
4 changes: 4 additions & 0 deletions src/keymon/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ INCLUDE_SHMVAR=1
INCLUDE_CJSON=1
include ../common/config.mk

CFILES := $(CFILES) \
../common/utils/udp.c \
../common/utils/retroarch_cmd.c

TARGET = keymon
CFLAGS := $(CFLAGS) -Os -ffunction-sections -fdata-sections
LDFLAGS := $(LDFLAGS) -lpthread -lpng -Wl,--gc-sections
Expand Down
5 changes: 3 additions & 2 deletions src/keymon/menuButtonAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include "../tweaks/tools_defs.h"
#include "./input_fd.h"
#include "utils/retroarch_cmd.h"

static SystemState menu_last_state = MODE_UNKNOWN;
static int menu_last_pressed = 0;
Expand Down Expand Up @@ -83,7 +84,7 @@ bool terminate_retroarch(void)
// send signal
kill(pid, SIGCONT);
usleep(100000);
system("sendUDP QUIT");
retroarch_quit();
// wait for terminate
sprintf(fname, "/proc/%d", pid);

Expand Down Expand Up @@ -180,7 +181,7 @@ void action_RA_quickSwitch(void)

void action_RA_toggleMenu(void)
{
system("sendUDP MENU_TOGGLE");
retroarch_toggleMenu();
}

void action_drastic_gameSwitcher(void)
Expand Down
3 changes: 3 additions & 0 deletions src/sendUDP/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
include ../common/config.mk

CFILES := $(CFILES) \
../common/utils/udp.c

TARGET = sendUDP

include ../common/commands.mk
Expand Down
74 changes: 41 additions & 33 deletions src/sendUDP/sendUDP.c
Original file line number Diff line number Diff line change
@@ -1,55 +1,63 @@
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "utils/udp.h"

int main(int argc, char *argv[])
{
char *ipAddress = "127.0.0.1"; // localhost
int port = 55355; // default RetroArch CMD port
char *message; // the message to send
char *message;
size_t response_size = 0;

if (argc < 2) {
fprintf(stderr, "Usage: %s <MESSAGE> [<IP> <PORT>]\n", argv[0]);
if (argc < 2 || (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))) {
fprintf(stderr, "Usage: %s [-h <ipAddress>] [-p <port>] [-r <response_size>] <message>\n", argv[0]);
exit(EXIT_FAILURE);
}

message = argv[1];

if (argc == 4) {
ipAddress = argv[2];
port = atoi(argv[3]);
}

int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (socket_fd == -1) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-h") == 0) {
if (i + 1 < argc) {
ipAddress = argv[i + 1];
i++;
}
}
else if (strcmp(argv[i], "-p") == 0) {
if (i + 1 < argc) {
port = atoi(argv[i + 1]);
i++;
}
}
else if (strcmp(argv[i], "-r") == 0) {
if (i + 1 < argc) {
response_size = atoi(argv[i + 1]);
i++;
}
}
else {
message = argv[i];
}
}

struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(port);
if (inet_pton(AF_INET, ipAddress, &server_address.sin_addr) <= 0) {
perror("Invalid IP address");
close(socket_fd);
exit(EXIT_FAILURE);
}
if (response_size > 0) {
char *response = (char *)malloc(response_size);
if (response == NULL) {
perror("Failed to allocate memory for response");
exit(EXIT_FAILURE);
}

ssize_t bytes_sent = sendto(socket_fd, message, strlen(message), 0,
(struct sockaddr *)&server_address, sizeof(server_address));
if (udp_send_receive(ipAddress, port, message, response, response_size) == -1) {
exit(EXIT_FAILURE);
}

if (bytes_sent == -1) {
perror("Failed to send data");
close(socket_fd);
printf("%s\n", response);
free(response);
}
else if (udp_send(ipAddress, port, message) == -1) {
exit(EXIT_FAILURE);
}

printf("Sent %d bytes to %s:%d\n", bytes_sent, ipAddress, port);

close(socket_fd);

return EXIT_SUCCESS;
}

0 comments on commit 48ed180

Please sign in to comment.