diff --git a/src/control.cpp b/src/control.cpp new file mode 100644 index 00000000..a7f6cc9b --- /dev/null +++ b/src/control.cpp @@ -0,0 +1,197 @@ +#include +#include +#include +#include "mesa/util/os_socket.h" +#include "overlay.h" + +using namespace std; +static void parse_command(struct instance_data *instance_data, + const char *cmd, unsigned cmdlen, + const char *param, unsigned paramlen) +{ + if (!strncmp(cmd, "hud", cmdlen)) { + _params->no_display = !_params->no_display; + } +} + +#define BUFSIZE 4096 + +/** + * This function will process commands through the control file. + * + * A command starts with a colon, followed by the command, and followed by an + * option '=' and a parameter. It has to end with a semi-colon. A full command + * + parameter looks like: + * + * :cmd=param; + */ +static void process_char(struct instance_data *instance_data, char c) +{ + static char cmd[BUFSIZE]; + static char param[BUFSIZE]; + + static unsigned cmdpos = 0; + static unsigned parampos = 0; + static bool reading_cmd = false; + static bool reading_param = false; + + switch (c) { + case ':': + cmdpos = 0; + parampos = 0; + reading_cmd = true; + reading_param = false; + break; + case ';': + if (!reading_cmd) + break; + cmd[cmdpos++] = '\0'; + param[parampos++] = '\0'; + parse_command(instance_data, cmd, cmdpos, param, parampos); + reading_cmd = false; + reading_param = false; + break; + case '=': + if (!reading_cmd) + break; + reading_param = true; + break; + default: + if (!reading_cmd) + break; + + if (reading_param) { + /* overflow means an invalid parameter */ + if (parampos >= BUFSIZE - 1) { + reading_cmd = false; + reading_param = false; + break; + } + + param[parampos++] = c; + } else { + /* overflow means an invalid command */ + if (cmdpos >= BUFSIZE - 1) { + reading_cmd = false; + break; + } + + cmd[cmdpos++] = c; + } + } +} + +static void control_send(struct instance_data *instance_data, + const char *cmd, unsigned cmdlen, + const char *param, unsigned paramlen) +{ + unsigned msglen = 0; + char buffer[BUFSIZE]; + + assert(cmdlen + paramlen + 3 < BUFSIZE); + + buffer[msglen++] = ':'; + + memcpy(&buffer[msglen], cmd, cmdlen); + msglen += cmdlen; + + if (paramlen > 0) { + buffer[msglen++] = '='; + memcpy(&buffer[msglen], param, paramlen); + msglen += paramlen; + buffer[msglen++] = ';'; + } + + os_socket_send(instance_data->control_client, buffer, msglen, 0); +} + +static void control_send_connection_string(struct device_data *device_data) +{ + struct instance_data *instance_data = device_data->instance; + + const char *controlVersionCmd = "MesaOverlayControlVersion"; + const char *controlVersionString = "1"; + + control_send(instance_data, controlVersionCmd, strlen(controlVersionCmd), + controlVersionString, strlen(controlVersionString)); + + const char *deviceCmd = "DeviceName"; + const char *deviceName = device_data->properties.deviceName; + + control_send(instance_data, deviceCmd, strlen(deviceCmd), + deviceName, strlen(deviceName)); + + const char *mesaVersionCmd = "MesaVersion"; + const char *mesaVersionString = "Mesa"; + + control_send(instance_data, mesaVersionCmd, strlen(mesaVersionCmd), + mesaVersionString, strlen(mesaVersionString)); + +} + +void control_client_check(struct device_data *device_data) +{ + struct instance_data *instance_data = device_data->instance; + + /* Already connected, just return. */ + if (instance_data->control_client >= 0) + return; + + int socket = os_socket_accept(instance_data->params.control); + if (socket == -1) { + if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ECONNABORTED) + fprintf(stderr, "ERROR on socket: %s\n", strerror(errno)); + return; + } + + if (socket >= 0) { + os_socket_block(socket, false); + instance_data->control_client = socket; + control_send_connection_string(device_data); + } +} + +static void control_client_disconnected(struct instance_data *instance_data) +{ + os_socket_close(instance_data->control_client); + instance_data->control_client = -1; +} + +void process_control_socket(struct instance_data *instance_data) +{ + const int client = instance_data->control_client; + if (client >= 0) { + char buf[BUFSIZE]; + + while (true) { + ssize_t n = os_socket_recv(client, buf, BUFSIZE, 0); + + if (n == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* nothing to read, try again later */ + break; + } + + if (errno != ECONNRESET) + fprintf(stderr, "ERROR on connection: %s\n", strerror(errno)); + + control_client_disconnected(instance_data); + } else if (n == 0) { + /* recv() returns 0 when the client disconnects */ + control_client_disconnected(instance_data); + } + + for (ssize_t i = 0; i < n; i++) { + process_char(instance_data, buf[i]); + } + + /* If we try to read BUFSIZE and receive BUFSIZE bytes from the + * socket, there's a good chance that there's still more data to be + * read, so we will try again. Otherwise, simply be done for this + * iteration and try again on the next frame. + */ + if (n < BUFSIZE) + break; + } + } +} \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index dfcca39e..82820b95 100644 --- a/src/meson.build +++ b/src/meson.build @@ -80,6 +80,7 @@ if is_unixy 'real_dlsym.cpp', 'pci_ids.cpp', 'battery.cpp', + 'control.cpp', ) opengl_files = files( diff --git a/src/overlay.h b/src/overlay.h index 24231dc2..99731d30 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -13,6 +13,9 @@ #include "version.h" #include "gpu.h" #include "logging.h" +#include "vk_enum_to_str.h" +#include "notify.h" +#include #ifdef HAVE_DBUS #include "dbus_info.h" extern float g_overflow; @@ -98,6 +101,36 @@ struct LOAD_DATA { unsigned high_load; }; +/* Mapped from VkInstace/VkPhysicalDevice */ +struct instance_data { + struct vk_instance_dispatch_table vtable; + VkInstance instance; + struct overlay_params params; + uint32_t api_version; + string engineName, engineVersion; + enum EngineTypes engine; + notify_thread notifier; + int control_client; +}; + +/* Mapped from VkDevice */ +struct queue_data; +struct device_data { + struct instance_data *instance; + + PFN_vkSetDeviceLoaderData set_device_loader_data; + + struct vk_device_dispatch_table vtable; + VkPhysicalDevice physical_device; + VkDevice device; + + VkPhysicalDeviceProperties properties; + + struct queue_data *graphic_queue; + + std::vector queues; +}; + extern struct fps_limit fps_limit_stats; extern int32_t deviceID; @@ -124,6 +157,8 @@ void center_text(const std::string& text); ImVec4 change_on_load_temp(LOAD_DATA& data, unsigned current); float get_time_stat(void *_data, int _idx); void stop_hw_updater(); +extern void control_client_check(struct device_data *device_data); +extern void process_control_socket(struct instance_data *instance_data); #ifdef HAVE_DBUS void render_mpris_metadata(overlay_params& params, mutexed_metadata& meta, uint64_t frame_timing); diff --git a/src/vulkan.cpp b/src/vulkan.cpp index f069ff6f..cd71e1b2 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -66,35 +66,6 @@ namespace MangoHud { namespace GL { }} #endif -/* Mapped from VkInstace/VkPhysicalDevice */ -struct instance_data { - struct vk_instance_dispatch_table vtable; - VkInstance instance; - struct overlay_params params; - uint32_t api_version; - string engineName, engineVersion; - enum EngineTypes engine; - notify_thread notifier; -}; - -/* Mapped from VkDevice */ -struct queue_data; -struct device_data { - struct instance_data *instance; - - PFN_vkSetDeviceLoaderData set_device_loader_data; - - struct vk_device_dispatch_table vtable; - VkPhysicalDevice physical_device; - VkDevice device; - - VkPhysicalDeviceProperties properties; - - struct queue_data *graphic_queue; - - std::vector queues; -}; - /* Mapped from VkCommandBuffer */ struct queue_data; struct command_buffer_data { @@ -456,11 +427,10 @@ static void snapshot_swapchain_frame(struct swapchain_data *data) update_hud_info(data->sw_stats, instance_data->params, device_data->properties.vendorID); check_keybinds(data->sw_stats, instance_data->params, device_data->properties.vendorID); - // not currently used - // if (instance_data->params.control >= 0) { - // control_client_check(device_data); - // process_control_socket(instance_data); - // } + if (instance_data->params.control >= 0) { + control_client_check(device_data); + process_control_socket(instance_data); + } } static void compute_swapchain_display(struct swapchain_data *data)