diff --git a/Makefile b/Makefile index 018b70d5ff..392593d13f 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,11 @@ ifeq ($(HAVE_BSV_MOVIE), 1) endif ifeq ($(HAVE_NETPLAY), 1) - OBJ += netplay.o network_cmd.o + OBJ += netplay.o +endif + +ifeq ($(HAVE_COMMAND), 1) + OBJ += command.o endif ifeq ($(HAVE_RSOUND), 1) diff --git a/Makefile.win b/Makefile.win index ab98c666bf..9211afe4dd 100644 --- a/Makefile.win +++ b/Makefile.win @@ -116,7 +116,7 @@ endif ifeq ($(HAVE_NETPLAY), 1) DEFINES += -DHAVE_NETPLAY -DHAVE_NETWORK_CMD - OBJ += netplay.o network_cmd.o + OBJ += netplay.o command.o LIBS += -lws2_32 endif diff --git a/network_cmd.c b/command.c similarity index 69% rename from network_cmd.c rename to command.c index 0ea6955f07..8e06017b7c 100644 --- a/network_cmd.c +++ b/command.c @@ -13,9 +13,13 @@ * If not, see . */ +#include "command.h" + +#ifdef HAVE_NETWORK_CMD #include "netplay_compat.h" #include "netplay.h" -#include "network_cmd.h" +#endif + #include "driver.h" #include "general.h" #include "compat/strl.h" @@ -24,10 +28,20 @@ #include #define DEFAULT_NETWORK_CMD_PORT 55355 +#define STDIN_BUF_SIZE 4096 -struct network_cmd +struct rarch_cmd { - int fd; +#ifdef HAVE_STDIN_CMD + bool stdin_enable; + char stdin_buf[STDIN_BUF_SIZE]; + size_t stdin_buf_ptr; +#endif + +#ifdef HAVE_NETWORK_CMD + int net_fd; +#endif + bool state[RARCH_BIND_LIST_END]; }; @@ -41,18 +55,19 @@ static bool socket_nonblock(int fd) #endif } -network_cmd_t *network_cmd_new(uint16_t port) +rarch_cmd_t *rarch_cmd_new(bool stdin_enable, bool network_enable, uint16_t port) { - if (!netplay_init_network()) + rarch_cmd_t *handle = (rarch_cmd_t*)calloc(1, sizeof(*handle)); + if (!handle) return NULL; - network_cmd_t *handle = (network_cmd_t*)calloc(1, sizeof(*handle)); - if (!handle) +#ifdef HAVE_NETWORK_CMD + if (network_enable && !netplay_init_network()) return NULL; RARCH_LOG("Bringing up command interface on port %hu.\n", (unsigned short)port); - handle->fd = -1; + handle->net_fd = -1; struct addrinfo hints, *res = NULL; memset(&hints, 0, sizeof(hints)); @@ -71,34 +86,49 @@ network_cmd_t *network_cmd_new(uint16_t port) if (getaddrinfo(NULL, port_buf, &hints, &res) < 0) goto error; - handle->fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (handle->fd < 0) + handle->net_fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (handle->net_fd < 0) goto error; - if (!socket_nonblock(handle->fd)) + if (!socket_nonblock(handle->net_fd)) goto error; - setsockopt(handle->fd, SOL_SOCKET, SO_REUSEADDR, CONST_CAST &yes, sizeof(int)); - if (bind(handle->fd, res->ai_addr, res->ai_addrlen) < 0) + setsockopt(handle->net_fd, SOL_SOCKET, SO_REUSEADDR, CONST_CAST &yes, sizeof(int)); + if (bind(handle->net_fd, res->ai_addr, res->ai_addrlen) < 0) { RARCH_ERR("Failed to bind socket.\n"); goto error; } +#else + (void)network_enable; + (void)port; +#endif + +#if defined(HAVE_STDIN_CMD) && !defined(_WIN32) + if (stdin_enable && !socket_nonblock(STDIN_FILENO)) + goto error; + + handle->stdin_enable = stdin_enable; +#else + (void)stdin_enable; +#endif freeaddrinfo(res); return handle; error: +#ifdef HAVE_NETWORK_CMD if (res) freeaddrinfo(res); - network_cmd_free(handle); +#endif + rarch_cmd_free(handle); return NULL; } -void network_cmd_free(network_cmd_t *handle) +void rarch_cmd_free(rarch_cmd_t *handle) { - if (handle->fd >= 0) - close(handle->fd); + if (handle->net_fd >= 0) + close(handle->net_fd); free(handle); } @@ -137,7 +167,7 @@ static const struct cmd_map map[] = { { "SLOWMOTION", RARCH_SLOWMOTION }, }; -static void parse_sub_msg(network_cmd_t *handle, const char *tok) +static void parse_sub_msg(rarch_cmd_t *handle, const char *tok) { for (unsigned i = 0; i < sizeof(map) / sizeof(map[0]); i++) { @@ -151,7 +181,7 @@ static void parse_sub_msg(network_cmd_t *handle, const char *tok) RARCH_WARN("Unrecognized command \"%s\" received.\n", tok); } -static void parse_msg(network_cmd_t *handle, char *buf) +static void parse_msg(rarch_cmd_t *handle, char *buf) { char *save; const char *tok = strtok_r(buf, "\n", &save); @@ -162,36 +192,38 @@ static void parse_msg(network_cmd_t *handle, char *buf) } } -void network_cmd_set(network_cmd_t *handle, unsigned id) +void rarch_cmd_set(rarch_cmd_t *handle, unsigned id) { if (id < RARCH_BIND_LIST_END) handle->state[id] = true; } -bool network_cmd_get(network_cmd_t *handle, unsigned id) +bool rarch_cmd_get(rarch_cmd_t *handle, unsigned id) { return id < RARCH_BIND_LIST_END && handle->state[id]; } -void network_cmd_pre_frame(network_cmd_t *handle) +#ifdef HAVE_NETWORK_CMD +static void network_cmd_pre_frame(rarch_cmd_t *handle) { - memset(handle->state, 0, sizeof(handle->state)); + if (handle->net_fd < 0) + return; fd_set fds; FD_ZERO(&fds); - FD_SET(handle->fd, &fds); + FD_SET(handle->net_fd, &fds); struct timeval tmp_tv = {0}; - if (select(handle->fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) + if (select(handle->net_fd + 1, &fds, NULL, NULL, &tmp_tv) <= 0) return; - if (!FD_ISSET(handle->fd, &fds)) + if (!FD_ISSET(handle->net_fd, &fds)) return; for (;;) { char buf[1024]; - ssize_t ret = recvfrom(handle->fd, buf, sizeof(buf) - 1, 0, NULL, NULL); + ssize_t ret = recvfrom(handle->net_fd, buf, sizeof(buf) - 1, 0, NULL, NULL); if (ret <= 0) break; @@ -199,7 +231,71 @@ void network_cmd_pre_frame(network_cmd_t *handle) parse_msg(handle, buf); } } +#endif +#ifdef HAVE_STDIN_CMD + +#ifdef _WIN32 +#else +static size_t read_stdin(char *buf, size_t size) +{ + size_t has_read = 0; + while (size) + { + ssize_t ret = read(STDIN_FILENO, buf, size); + + if (ret <= 0) + break; + + buf += ret; + has_read += ret; + size -= ret; + } + + return has_read; +} +#endif + +static void stdin_cmd_pre_frame(rarch_cmd_t *handle) +{ + if (!handle->stdin_enable) + return; + + size_t ret = read_stdin(handle->stdin_buf, STDIN_BUF_SIZE - handle->stdin_buf_ptr - 1); + if (ret == 0) + return; + + handle->stdin_buf_ptr += ret; + handle->stdin_buf[handle->stdin_buf_ptr] = '\0'; + + char *last_newline = strrchr(handle->stdin_buf, '\n'); + if (!last_newline) + return; + + *last_newline++ = '\0'; + ptrdiff_t msg_len = last_newline - handle->stdin_buf; + + parse_msg(handle, handle->stdin_buf); + + memmove(handle->stdin_buf, last_newline, handle->stdin_buf_ptr - msg_len); + handle->stdin_buf_ptr -= msg_len; +} +#endif + +void rarch_cmd_pre_frame(rarch_cmd_t *handle) +{ + memset(handle->state, 0, sizeof(handle->state)); + +#ifdef HAVE_NETWORK_CMD + network_cmd_pre_frame(handle); +#endif + +#ifdef HAVE_STDIN_CMD + stdin_cmd_pre_frame(handle); +#endif +} + +#ifdef HAVE_NETWORK_CMD static bool send_udp_packet(const char *host, uint16_t port, const char *msg) { struct addrinfo hints, *res = NULL; @@ -311,4 +407,6 @@ bool network_cmd_send(const char *cmd_) g_extern.verbose = old_verbose; return ret; } +#endif + diff --git a/config.def.h b/config.def.h index 86016c0bed..bbc4c351e7 100644 --- a/config.def.h +++ b/config.def.h @@ -293,9 +293,10 @@ static const bool savestate_auto_save = false; // Slowmotion ratio. static const float slowmotion_ratio = 3.0; -// Enable network command interface +// Enable stdin/network command interface static const bool network_cmd_enable = false; static const uint16_t network_cmd_port = 55355; +static const bool stdin_cmd_enable = false; //////////////////// diff --git a/driver.h b/driver.h index 3399e22196..6c6c5c7c96 100644 --- a/driver.h +++ b/driver.h @@ -28,8 +28,8 @@ #include "config.h" #endif -#ifdef HAVE_NETWORK_CMD -#include "network_cmd.h" +#ifdef HAVE_COMMAND +#include "command.h" #endif #define AUDIO_CHUNK_SIZE_BLOCKING 64 @@ -198,9 +198,10 @@ typedef struct driver void *video_data; void *input_data; -#ifdef HAVE_NETWORK_CMD - network_cmd_t *network_cmd; +#ifdef HAVE_COMMAND + rarch_cmd_t *command; #endif + bool stdin_claimed; } driver_t; void init_drivers(void); @@ -289,9 +290,9 @@ extern const input_driver_t input_null; static inline bool input_key_pressed_func(int key) { bool ret = driver.input->key_pressed(driver.input_data, key); -#ifdef HAVE_NETWORK_CMD - if (!ret && driver.network_cmd) - ret = network_cmd_get(driver.network_cmd, key); +#ifdef HAVE_COMMAND + if (!ret && driver.command) + ret = rarch_cmd_get(driver.command, key); #endif return ret; } diff --git a/general.h b/general.h index f1c7ded0bd..7f922dcaab 100644 --- a/general.h +++ b/general.h @@ -55,8 +55,8 @@ #include "netplay.h" #endif -#ifdef HAVE_NETWORK_CMD -#include "network_cmd.h" +#ifdef HAVE_COMMAND +#include "command.h" #endif #include "audio/resampler.h" @@ -186,6 +186,7 @@ struct settings bool network_cmd_enable; uint16_t network_cmd_port; + bool stdin_cmd_enable; }; // Settings and/or global state that is specific to a console-style implementation. diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c index eb65791b2f..95f144aa0b 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -223,6 +223,7 @@ static void *linuxraw_input_init(void) init_lut(); linuxraw->sdl->use_keyboard = false; + driver.stdin_claimed = true; // We need to disable use of stdin command interface if stdin is supposed to be used for input. return linuxraw; } diff --git a/network_cmd.h b/network_cmd.h deleted file mode 100644 index 267bcdeaf6..0000000000 --- a/network_cmd.h +++ /dev/null @@ -1,34 +0,0 @@ -/* RetroArch - A frontend for libretro. - * Copyright (C) 2010-2012 - Hans-Kristian Arntzen - * - * RetroArch is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software Found- - * ation, either version 3 of the License, or (at your option) any later version. - * - * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with RetroArch. - * If not, see . - */ - -#ifndef NETWORK_CMD_H__ -#define NETWORK_CMD_H__ - -#include -#include "boolean.h" - -typedef struct network_cmd network_cmd_t; - -network_cmd_t *network_cmd_new(uint16_t port); -void network_cmd_free(network_cmd_t *handle); - -void network_cmd_pre_frame(network_cmd_t *handle); -void network_cmd_set(network_cmd_t *handle, unsigned id); -bool network_cmd_get(network_cmd_t *handle, unsigned id); - -bool network_cmd_send(const char *cmd); - -#endif - diff --git a/qb/config.libs.sh b/qb/config.libs.sh index 945b9310bd..2ef1bf1b45 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -49,6 +49,14 @@ else HAVE_NETWORK_CMD='no' fi +check_lib STDIN_CMD -lc fcntl + +if [ "$HAVE_NETWORK_CMD" = "yes" ] || [ "$HAVE_STDIN_CMD" = "yes" ]; then + HAVE_COMMAND='yes' +else + HAVE_COMMAND='no' +fi + check_lib GETOPT_LONG -lc getopt_long if [ "$HAVE_DYLIB" = 'no' ] && [ "$HAVE_DYNAMIC" = 'yes' ]; then @@ -140,6 +148,6 @@ check_pkgconf PYTHON python3 add_define_make OS "$OS" # Creates config.mk and config.h. -VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC FIXED_POINT BSV_MOVIE RPI" +VARS="ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL DYLIB GETOPT_LONG THREADS CG XML SDL_IMAGE LIBPNG DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE CONFIGFILE FREETYPE XVIDEO X11 XEXT NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 X264RGB SINC FIXED_POINT BSV_MOVIE RPI" create_config_make config.mk $VARS create_config_header config.h $VARS diff --git a/retroarch.c b/retroarch.c index 57ae1d50d0..67b7309515 100644 --- a/retroarch.c +++ b/retroarch.c @@ -694,6 +694,8 @@ static void verify_stdin_paths(void) rarch_fail(1, "verify_stdin_paths()"); } #endif + + driver.stdin_claimed = true; } static void parse_input(int argc, char *argv[]) @@ -1407,23 +1409,31 @@ static void deinit_netplay(void) } #endif -#ifdef HAVE_NETWORK_CMD -static void init_network_cmd(void) +#ifdef HAVE_COMMAND +static void init_command(void) { - if (!g_settings.network_cmd_enable) + if (!g_settings.stdin_cmd_enable && !g_settings.network_cmd_enable) return; - driver.network_cmd = network_cmd_new(g_settings.network_cmd_port); - if (!driver.network_cmd) - RARCH_ERR("Failed to initialize network command interface.\n"); + if (g_settings.stdin_cmd_enable && driver.stdin_claimed) + { + RARCH_WARN("stdin command interface is desired, but input driver has already claimed stdin." + "Cannot use this command interface.\n"); + } + + driver.command = rarch_cmd_new(g_settings.stdin_cmd_enable && !driver.stdin_claimed, + g_settings.network_cmd_enable, g_settings.network_cmd_port); + + if (!driver.command) + RARCH_ERR("Failed to initialize command interface.\n"); } -static void deinit_network_cmd(void) +static void deinit_command(void) { - if (driver.network_cmd) + if (driver.command) { - network_cmd_free(driver.network_cmd); - driver.network_cmd = NULL; + rarch_cmd_free(driver.command); + driver.command = NULL; } } #endif @@ -2460,12 +2470,13 @@ int rarch_main_init(int argc, char *argv[]) #ifdef HAVE_NETPLAY init_netplay(); #endif -#ifdef HAVE_NETWORK_CMD - init_network_cmd(); -#endif init_drivers(); +#ifdef HAVE_COMMAND + init_command(); +#endif + #ifdef HAVE_NETPLAY if (!g_extern.netplay) #endif @@ -2537,9 +2548,9 @@ bool rarch_main_iterate(void) !video_alive_func()) return false; -#ifdef HAVE_NETWORK_CMD - if (driver.network_cmd) - network_cmd_pre_frame(driver.network_cmd); +#ifdef HAVE_COMMAND + if (driver.command) + rarch_cmd_pre_frame(driver.command); #endif // Checks for stuff like fullscreen, save states, etc. @@ -2596,8 +2607,8 @@ void rarch_main_deinit(void) #ifdef HAVE_NETPLAY deinit_netplay(); #endif -#ifdef HAVE_NETWORK_CMD - deinit_network_cmd(); +#ifdef HAVE_COMMAND + deinit_command(); #endif #ifdef HAVE_THREADS diff --git a/retroarch.cfg b/retroarch.cfg index 274141ea65..c552d2919b 100644 --- a/retroarch.cfg +++ b/retroarch.cfg @@ -380,7 +380,8 @@ # Slowmotion ratio. When slowmotion, game will slow down by factor. # slowmotion_ratio = 3.0 -# Enable network command interface. +# Enable stdin/network command interface. # network_cmd_enable = false # network_cmd_port = 55355 +# stdin_cmd_enable = false diff --git a/settings.c b/settings.c index ebc9bc7da7..da7c95ab25 100644 --- a/settings.c +++ b/settings.c @@ -202,6 +202,7 @@ void config_set_defaults(void) g_settings.savestate_auto_save = savestate_auto_save; g_settings.network_cmd_enable = network_cmd_enable; g_settings.network_cmd_port = network_cmd_port; + g_settings.stdin_cmd_enable = stdin_cmd_enable; rarch_assert(sizeof(g_settings.input.binds[0]) >= sizeof(retro_keybinds_1)); rarch_assert(sizeof(g_settings.input.binds[1]) >= sizeof(retro_keybinds_rest)); @@ -473,6 +474,7 @@ bool config_load_file(const char *path) CONFIG_GET_BOOL(network_cmd_enable, "network_cmd_enable"); CONFIG_GET_INT(network_cmd_port, "network_cmd_port"); + CONFIG_GET_BOOL(stdin_cmd_enable, "stdin_cmd_enable"); if (config_get_string(conf, "environment_variables", &g_extern.system.environment))