From 48ed180d9a6bdf716efe5bfc158f8a5adcfe47eb Mon Sep 17 00:00:00 2001 From: Aemiii91 <44569252+Aemiii91@users.noreply.github.com> Date: Sun, 29 Dec 2024 21:53:23 +0100 Subject: [PATCH] Add keymon native udp (#1736) --- Makefile | 2 +- src/common/utils/retroarch_cmd.c | 47 +++++++++ src/common/utils/retroarch_cmd.h | 21 ++++ src/common/utils/udp.c | 158 +++++++++++++++++++++++++++++++ src/common/utils/udp.h | 10 ++ src/keymon/Makefile | 4 + src/keymon/menuButtonAction.h | 5 +- src/sendUDP/Makefile | 3 + src/sendUDP/sendUDP.c | 74 ++++++++------- third-party/RetroArch-patch | 2 +- 10 files changed, 289 insertions(+), 37 deletions(-) create mode 100644 src/common/utils/retroarch_cmd.c create mode 100644 src/common/utils/retroarch_cmd.h create mode 100644 src/common/utils/udp.c create mode 100644 src/common/utils/udp.h diff --git a/Makefile b/Makefile index 03a1d063b3..11329bf036 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ TARGET=Onion VERSION=4.4.0 -RA_SUBVERSION=1.19.1-1 +RA_SUBVERSION=1.19.1-2 ########################################################### diff --git a/src/common/utils/retroarch_cmd.c b/src/common/utils/retroarch_cmd.c new file mode 100644 index 0000000000..34d2bed0bb --- /dev/null +++ b/src/common/utils/retroarch_cmd.c @@ -0,0 +1,47 @@ +#include "retroarch_cmd.h" + +#include + +#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; +} diff --git a/src/common/utils/retroarch_cmd.h b/src/common/utils/retroarch_cmd.h new file mode 100644 index 0000000000..064a686506 --- /dev/null +++ b/src/common/utils/retroarch_cmd.h @@ -0,0 +1,21 @@ +#ifndef UTILS_RETROARCH_CMD_H__ +#define UTILS_RETROARCH_CMD_H__ + +#include +#include + +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__ \ No newline at end of file diff --git a/src/common/utils/udp.c b/src/common/utils/udp.c new file mode 100644 index 0000000000..b936e01993 --- /dev/null +++ b/src/common/utils/udp.c @@ -0,0 +1,158 @@ +#include "udp.h" + +#include +#include +#include +#include +#include +#include + +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 +} diff --git a/src/common/utils/udp.h b/src/common/utils/udp.h new file mode 100644 index 0000000000..cfaa4ff481 --- /dev/null +++ b/src/common/utils/udp.h @@ -0,0 +1,10 @@ +#ifndef UTILS_UDP_H__ +#define UTILS_UDP_H__ + +#include +#include + +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__ diff --git a/src/keymon/Makefile b/src/keymon/Makefile index 17a8b43058..c634e70ca9 100644 --- a/src/keymon/Makefile +++ b/src/keymon/Makefile @@ -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 diff --git a/src/keymon/menuButtonAction.h b/src/keymon/menuButtonAction.h index 50d3f25a91..0e2da86f45 100644 --- a/src/keymon/menuButtonAction.h +++ b/src/keymon/menuButtonAction.h @@ -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; @@ -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); @@ -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) diff --git a/src/sendUDP/Makefile b/src/sendUDP/Makefile index 7ffbd44a43..2dab81ee01 100644 --- a/src/sendUDP/Makefile +++ b/src/sendUDP/Makefile @@ -1,5 +1,8 @@ include ../common/config.mk +CFILES := $(CFILES) \ + ../common/utils/udp.c + TARGET = sendUDP include ../common/commands.mk diff --git a/src/sendUDP/sendUDP.c b/src/sendUDP/sendUDP.c index 59c5f06559..95f9ff2387 100644 --- a/src/sendUDP/sendUDP.c +++ b/src/sendUDP/sendUDP.c @@ -1,55 +1,63 @@ -#include #include #include #include #include +#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 [ ]\n", argv[0]); + if (argc < 2 || (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))) { + fprintf(stderr, "Usage: %s [-h ] [-p ] [-r ] \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; } diff --git a/third-party/RetroArch-patch b/third-party/RetroArch-patch index 4f130bdb55..aaedd182d1 160000 --- a/third-party/RetroArch-patch +++ b/third-party/RetroArch-patch @@ -1 +1 @@ -Subproject commit 4f130bdb5560e76c7d6191458bf8698b36c522fe +Subproject commit aaedd182d17c001f4a63c48798fdafdf380ed473