Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add keymon native udp #1736

Merged
merged 5 commits into from
Dec 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
}
Loading