aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-01-20 23:11:00 +0100
committerdec05eba <dec05eba@protonmail.com>2025-01-20 23:11:00 +0100
commit47ada4d79844d9a98d9689d0de0c92864e0fc372 (patch)
tree1325fd3061e78a194b3db0be848a06928fdacce9 /src
parent92401d8bc8fa3cbc8017936eb1d18280199942e0 (diff)
Add option to save replay with controller (double-click share button), allow prime-run on wayland
Diffstat (limited to 'src')
-rw-r--r--src/Config.cpp1
-rw-r--r--src/GlobalHotkeysJoystick.cpp236
-rw-r--r--src/GlobalHotkeysLinux.cpp4
-rw-r--r--src/GlobalHotkeysX11.cpp3
-rw-r--r--src/Hotplug.cpp81
-rw-r--r--src/Overlay.cpp12
-rw-r--r--src/gui/GlobalSettingsPage.cpp108
-rw-r--r--src/main.cpp121
8 files changed, 469 insertions, 97 deletions
diff --git a/src/Config.cpp b/src/Config.cpp
index 4ad1107..8dbe32d 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -59,6 +59,7 @@ namespace gsr {
{"main.config_file_version", &config.main_config.config_file_version},
{"main.software_encoding_warning_shown", &config.main_config.software_encoding_warning_shown},
{"main.hotkeys_enable_option", &config.main_config.hotkeys_enable_option},
+ {"main.joystick_hotkeys_enable_option", &config.main_config.joystick_hotkeys_enable_option},
{"main.tint_color", &config.main_config.tint_color},
{"streaming.record_options.record_area_option", &config.streaming_config.record_options.record_area_option},
diff --git a/src/GlobalHotkeysJoystick.cpp b/src/GlobalHotkeysJoystick.cpp
new file mode 100644
index 0000000..55c6e43
--- /dev/null
+++ b/src/GlobalHotkeysJoystick.cpp
@@ -0,0 +1,236 @@
+#include "../include/GlobalHotkeysJoystick.hpp"
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+
+namespace gsr {
+ static constexpr double double_click_timeout_seconds = 0.33;
+
+ // Returns -1 on error
+ static int get_js_dev_input_id_from_filepath(const char *dev_input_filepath) {
+ if(strncmp(dev_input_filepath, "/dev/input/js", 13) != 0)
+ return -1;
+
+ int dev_input_id = -1;
+ if(sscanf(dev_input_filepath + 13, "%d", &dev_input_id) == 1)
+ return dev_input_id;
+ return -1;
+ }
+
+ GlobalHotkeysJoystick::~GlobalHotkeysJoystick() {
+ if(event_fd > 0) {
+ const uint64_t exit = 1;
+ write(event_fd, &exit, sizeof(exit));
+ }
+
+ if(read_thread.joinable())
+ read_thread.join();
+
+ if(event_fd > 0)
+ close(event_fd);
+
+ for(int i = 0; i < num_poll_fd; ++i) {
+ close(poll_fd[i].fd);
+ }
+ }
+
+ bool GlobalHotkeysJoystick::start() {
+ if(num_poll_fd > 0)
+ return false;
+
+ event_fd = eventfd(0, 0);
+ if(event_fd <= 0)
+ return false;
+
+ event_index = num_poll_fd;
+ poll_fd[num_poll_fd] = {
+ event_fd,
+ POLLIN,
+ 0
+ };
+ extra_data[num_poll_fd] = {
+ -1
+ };
+ ++num_poll_fd;
+
+ if(!hotplug.start()) {
+ fprintf(stderr, "Warning: failed to setup hotplugging\n");
+ } else {
+ hotplug_poll_index = num_poll_fd;
+ poll_fd[num_poll_fd] = {
+ hotplug.steal_fd(),
+ POLLIN,
+ 0
+ };
+ extra_data[num_poll_fd] = {
+ -1
+ };
+ ++num_poll_fd;
+ }
+
+ char dev_input_path[128];
+ for(int i = 0; i < 8; ++i) {
+ snprintf(dev_input_path, sizeof(dev_input_path), "/dev/input/js%d", i);
+ add_device(dev_input_path, false);
+ }
+
+ if(num_poll_fd == 0)
+ fprintf(stderr, "Info: no joysticks found, assuming they might be connected later\n");
+
+ read_thread = std::thread(&GlobalHotkeysJoystick::read_events, this);
+ return true;
+ }
+
+ bool GlobalHotkeysJoystick::bind_action(const std::string &id, GlobalHotkeyCallback callback) {
+ return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second;
+ }
+
+ void GlobalHotkeysJoystick::poll_events() {
+ if(num_poll_fd == 0)
+ return;
+
+ if(save_replay) {
+ save_replay = false;
+ auto it = bound_actions_by_id.find("save_replay");
+ if(it != bound_actions_by_id.end())
+ it->second("save_replay");
+ }
+ }
+
+ void GlobalHotkeysJoystick::read_events() {
+ js_event event;
+ while(poll(poll_fd, num_poll_fd, -1) > 0) {
+ for(int i = 0; i < num_poll_fd; ++i) {
+ if(poll_fd[i].revents & (POLLHUP|POLLERR|POLLNVAL)) {
+ if(i == event_index)
+ goto done;
+
+ if(remove_poll_fd(i))
+ --i; // This item was removed so we want to repeat the same index to continue to the next item
+
+ continue;
+ }
+
+ if(!(poll_fd[i].revents & POLLIN))
+ continue;
+
+ if(i == event_index) {
+ goto done;
+ } else if(i == hotplug_poll_index) {
+ hotplug.process_event_data(poll_fd[i].fd, [&](HotplugAction hotplug_action, const char *devname) {
+ char dev_input_filepath[1024];
+ snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname);
+ switch(hotplug_action) {
+ case HotplugAction::ADD: {
+ // Cant open the /dev/input device immediately or it fails.
+ // TODO: Remove this hack when a better solution is found.
+ usleep(50 * 1000);
+ add_device(dev_input_filepath);
+ break;
+ }
+ case HotplugAction::REMOVE: {
+ if(remove_device(dev_input_filepath))
+ --i; // This item was removed so we want to repeat the same index to continue to the next item
+ break;
+ }
+ }
+ });
+ } else {
+ process_js_event(poll_fd[i].fd, event);
+ }
+ }
+ }
+
+ done:
+ ;
+ }
+
+ void GlobalHotkeysJoystick::process_js_event(int fd, js_event &event) {
+ if(read(fd, &event, sizeof(event)) != sizeof(event))
+ return;
+
+ if((event.type & JS_EVENT_BUTTON) == 0)
+ return;
+
+ if(event.number == 8 && event.value == 1) {
+ ++num_times_clicked;
+ if(num_times_clicked == 1)
+ double_click_clock.restart();
+ else if(num_times_clicked == 2 && double_click_clock.restart() >= double_click_timeout_seconds)
+ num_times_clicked = 0;
+
+ if(num_times_clicked == 2) {
+ save_replay = true;
+ num_times_clicked = 0;
+ }
+ }
+ }
+
+ bool GlobalHotkeysJoystick::add_device(const char *dev_input_filepath, bool print_error) {
+ if(num_poll_fd >= max_js_poll_fd) {
+ fprintf(stderr, "Warning: failed to add joystick device %s, too many joysticks have been added\n", dev_input_filepath);
+ return false;
+ }
+
+ const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath);
+ if(dev_input_id == -1)
+ return false;
+
+ const int fd = open(dev_input_filepath, O_RDONLY);
+ if(fd <= 0) {
+ if(print_error)
+ fprintf(stderr, "Error: failed to add joystick %s, error: %s\n", dev_input_filepath, strerror(errno));
+ return false;
+ }
+
+ poll_fd[num_poll_fd] = {
+ fd,
+ POLLIN,
+ 0
+ };
+
+ extra_data[num_poll_fd] = {
+ dev_input_id
+ };
+
+ ++num_poll_fd;
+ fprintf(stderr, "Info: added joystick: %s\n", dev_input_filepath);
+ return true;
+ }
+
+ bool GlobalHotkeysJoystick::remove_device(const char *dev_input_filepath) {
+ const int dev_input_id = get_js_dev_input_id_from_filepath(dev_input_filepath);
+ if(dev_input_id == -1)
+ return false;
+
+ const int poll_fd_index = get_poll_fd_index_by_dev_input_id(dev_input_id);
+ if(poll_fd_index == -1)
+ return false;
+
+ fprintf(stderr, "Info: removed joystick: %s\n", dev_input_filepath);
+ return remove_poll_fd(poll_fd_index);
+ }
+
+ bool GlobalHotkeysJoystick::remove_poll_fd(int index) {
+ if(index < 0 || index >= num_poll_fd)
+ return false;
+
+ close(poll_fd[index].fd);
+ for(int i = index + 1; i < num_poll_fd; ++i) {
+ poll_fd[i - 1] = poll_fd[i];
+ extra_data[i - 1] = extra_data[i];
+ }
+ --num_poll_fd;
+ return true;
+ }
+
+ int GlobalHotkeysJoystick::get_poll_fd_index_by_dev_input_id(int dev_input_id) const {
+ for(int i = 0; i < num_poll_fd; ++i) {
+ if(dev_input_id == extra_data[i].dev_input_id)
+ return i;
+ }
+ return -1;
+ }
+}
diff --git a/src/GlobalHotkeysLinux.cpp b/src/GlobalHotkeysLinux.cpp
index 418e317..29cf952 100644
--- a/src/GlobalHotkeysLinux.cpp
+++ b/src/GlobalHotkeysLinux.cpp
@@ -109,12 +109,12 @@ namespace gsr {
void GlobalHotkeysLinux::poll_events() {
if(process_id <= 0) {
- fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, process has not been started yet. Use GlobalHotkeysLinux::start to start the process first\n");
+ //fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, process has not been started yet. Use GlobalHotkeysLinux::start to start the process first\n");
return;
}
if(!read_file) {
- fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, read file hasn't opened\n");
+ //fprintf(stderr, "error: GlobalHotkeysLinux::poll_events failed, read file hasn't opened\n");
return;
}
diff --git a/src/GlobalHotkeysX11.cpp b/src/GlobalHotkeysX11.cpp
index 2943397..c8d198c 100644
--- a/src/GlobalHotkeysX11.cpp
+++ b/src/GlobalHotkeysX11.cpp
@@ -138,6 +138,9 @@ namespace gsr {
}
void GlobalHotkeysX11::poll_events() {
+ if(!dpy)
+ return;
+
while(XPending(dpy)) {
XNextEvent(dpy, &xev);
if(xev.type == KeyPress) {
diff --git a/src/Hotplug.cpp b/src/Hotplug.cpp
new file mode 100644
index 0000000..84ed5bb
--- /dev/null
+++ b/src/Hotplug.cpp
@@ -0,0 +1,81 @@
+#include "../include/Hotplug.hpp"
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+namespace gsr {
+ Hotplug::~Hotplug() {
+ if(fd > 0)
+ close(fd);
+ }
+
+ bool Hotplug::start() {
+ if(started)
+ return false;
+
+ struct sockaddr_nl nls = {
+ AF_NETLINK,
+ 0,
+ (unsigned int)getpid(),
+ (unsigned int)-1
+ };
+
+ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
+ if(fd == -1)
+ return false; /* Not root user */
+
+ if(bind(fd, (const struct sockaddr*)&nls, sizeof(struct sockaddr_nl))) {
+ close(fd);
+ fd = -1;
+ return false;
+ }
+
+ started = true;
+ return true;
+ }
+
+ int Hotplug::steal_fd() {
+ const int val = fd;
+ fd = -1;
+ return val;
+ }
+
+ void Hotplug::process_event_data(int fd, const HotplugEventCallback &callback) {
+ const int bytes_read = read(fd, event_data, sizeof(event_data));
+ if(bytes_read <= 0)
+ return;
+
+ /* Hotplug data ends with a newline and a null terminator */
+ int data_index = 0;
+ while(data_index < bytes_read) {
+ parse_netlink_data(event_data + data_index, callback);
+ data_index += strlen(event_data + data_index) + 1; /* Skip null terminator as well */
+ }
+ }
+
+ /* TODO: This assumes SUBSYSTEM= is output before DEVNAME=, is that always true? */
+ void Hotplug::parse_netlink_data(const char *line, const HotplugEventCallback &callback) {
+ const char *at_symbol = strchr(line, '@');
+ if(at_symbol) {
+ event_is_add = strncmp(line, "add@", 4) == 0;
+ event_is_remove = strncmp(line, "remove@", 7) == 0;
+ subsystem_is_input = false;
+ } else if(event_is_add || event_is_remove) {
+ if(strcmp(line, "SUBSYSTEM=input") == 0)
+ subsystem_is_input = true;
+
+ if(subsystem_is_input && strncmp(line, "DEVNAME=", 8) == 0) {
+ if(event_is_add)
+ callback(HotplugAction::ADD, line+8);
+ else if(event_is_remove)
+ callback(HotplugAction::REMOVE, line+8);
+
+ event_is_add = false;
+ event_is_remove = false;
+ }
+ }
+ }
+}
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 9e866fd..2f1725d 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -942,10 +942,20 @@ namespace gsr {
show_notification("Failed to remove GPU Screen Recorder from system startup", 3.0, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
}
};
- settings_page->on_click_exit_program_button = [&](const char *reason) {
+
+ settings_page->on_click_exit_program_button = [this](const char *reason) {
do_exit = true;
exit_reason = reason;
};
+
+ settings_page->on_keyboard_hotkey_changed = [this](const char *hotkey_option) {
+ on_keyboard_hotkey_changed(hotkey_option);
+ };
+
+ settings_page->on_joystick_hotkey_changed = [this](const char *hotkey_option) {
+ on_joystick_hotkey_changed(hotkey_option);
+ };
+
page_stack.push(std::move(settings_page));
};
front_page_ptr->add_widget(std::move(button));
diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp
index d3d440d..8a2a162 100644
--- a/src/gui/GlobalSettingsPage.cpp
+++ b/src/gui/GlobalSettingsPage.cpp
@@ -9,6 +9,15 @@
#include "../../include/gui/List.hpp"
#include "../../include/gui/Label.hpp"
#include "../../include/gui/RadioButton.hpp"
+#include "../../include/gui/LineSeparator.hpp"
+
+#ifndef GSR_UI_VERSION
+#define GSR_UI_VERSION "unknown"
+#endif
+
+#ifndef GSR_FLATPAK_VERSION
+#define GSR_FLATPAK_VERSION "unknown"
+#endif
namespace gsr {
static const char* gpu_vendor_to_color_name(GpuVendor vendor) {
@@ -21,6 +30,16 @@ namespace gsr {
return "amd";
}
+ static const char* gpu_vendor_to_string(GpuVendor vendor) {
+ switch(vendor) {
+ case GpuVendor::UNKNOWN: return "Unknown";
+ case GpuVendor::AMD: return "AMD";
+ case GpuVendor::INTEL: return "Intel";
+ case GpuVendor::NVIDIA: return "NVIDIA";
+ }
+ return "unknown";
+ }
+
GlobalSettingsPage::GlobalSettingsPage(const GsrInfo *gsr_info, Config &config, PageStack *page_stack) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
config(config),
@@ -88,29 +107,45 @@ namespace gsr {
return std::make_unique<Subsection>("Startup", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
}
- std::unique_ptr<Subsection> GlobalSettingsPage::create_hotkey_subsection(ScrollablePage *parent_page) {
- const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
-
- auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL);
- enable_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
- enable_hotkeys_radio_button->add_item("Enable hotkeys", "enable_hotkeys");
- if(!inside_flatpak)
- enable_hotkeys_radio_button->add_item("Disable hotkeys", "disable_hotkeys");
+ std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_keyboard_hotkeys_button() {
+ auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
+ enable_keyboard_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
+ enable_hotkeys_radio_button->add_item("Yes", "enable_hotkeys");
+ enable_hotkeys_radio_button->add_item("No", "disable_hotkeys");
enable_hotkeys_radio_button->add_item("Only grab virtual devices (supports input remapping software)", "enable_hotkeys_virtual_devices");
enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
- if(!on_click_exit_program_button)
- return true;
-
- if(id == "enable_hotkeys")
- on_click_exit_program_button("restart");
- else if(id == "disable_hotkeys")
- on_click_exit_program_button("restart");
- else if(id == "enable_hotkeys_virtual_devices")
- on_click_exit_program_button("restart");
+ if(on_keyboard_hotkey_changed)
+ on_keyboard_hotkey_changed(id.c_str());
+ return true;
+ };
+ return enable_hotkeys_radio_button;
+ }
+ std::unique_ptr<RadioButton> GlobalSettingsPage::create_enable_joystick_hotkeys_button() {
+ auto enable_hotkeys_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
+ enable_joystick_hotkeys_radio_button_ptr = enable_hotkeys_radio_button.get();
+ enable_hotkeys_radio_button->add_item("Yes", "enable_hotkeys");
+ enable_hotkeys_radio_button->add_item("No", "disable_hotkeys");
+ enable_hotkeys_radio_button->on_selection_changed = [&](const std::string&, const std::string &id) {
+ if(on_joystick_hotkey_changed)
+ on_joystick_hotkey_changed(id.c_str());
return true;
};
- return std::make_unique<Subsection>("Hotkeys", std::move(enable_hotkeys_radio_button), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
+ return enable_hotkeys_radio_button;
+ }
+
+ std::unique_ptr<Subsection> GlobalSettingsPage::create_hotkey_subsection(ScrollablePage *parent_page) {
+ auto list = std::make_unique<List>(List::Orientation::VERTICAL);
+ List *list_ptr = list.get();
+ auto subsection = std::make_unique<Subsection>("Hotkeys", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
+
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Enable keyboard hotkeys?", get_color_theme().text_color));
+ list_ptr->add_widget(create_enable_keyboard_hotkeys_button());
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Enable controller hotkeys?", get_color_theme().text_color));
+ list_ptr->add_widget(create_enable_joystick_hotkeys_button());
+ list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Double-click the share button to save a replay", get_color_theme().text_color));
+ return subsection;
}
std::unique_ptr<Button> GlobalSettingsPage::create_exit_program_button() {
@@ -133,11 +168,32 @@ namespace gsr {
std::unique_ptr<Subsection> GlobalSettingsPage::create_application_options_subsection(ScrollablePage *parent_page) {
const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
- auto list = std::make_unique<List>(List::Orientation::HORIZONTAL);
- list->add_widget(create_exit_program_button());
- if(inside_flatpak)
- list->add_widget(create_go_back_to_old_ui_button());
- return std::make_unique<Subsection>("Application options", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
+ auto list = std::make_unique<List>(List::Orientation::VERTICAL);
+ List *list_ptr = list.get();
+ auto subsection = std::make_unique<Subsection>("Application options", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
+
+ {
+ auto buttons_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
+ buttons_list->add_widget(create_exit_program_button());
+ if(inside_flatpak)
+ buttons_list->add_widget(create_go_back_to_old_ui_button());
+ list_ptr->add_widget(std::move(buttons_list));
+ }
+ list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
+ {
+ char str[256];
+ snprintf(str, sizeof(str), "UI version: %s", GSR_UI_VERSION);
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
+
+ if(inside_flatpak) {
+ snprintf(str, sizeof(str), "Flatpak version: %s", GSR_FLATPAK_VERSION);
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
+ }
+
+ snprintf(str, sizeof(str), "GPU vendor: %s", gpu_vendor_to_string(gsr_info->gpu_info.vendor));
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
+ }
+ return subsection;
}
void GlobalSettingsPage::add_widgets() {
@@ -169,12 +225,14 @@ namespace gsr {
const int exit_status = exec_program_on_host_get_stdout(args, stdout_str);
startup_radio_button_ptr->set_selected_item(exit_status == 0 ? "start_on_system_startup" : "dont_start_on_system_startup", false, false);
- enable_hotkeys_radio_button_ptr->set_selected_item(config.main_config.hotkeys_enable_option, false, false);
+ enable_keyboard_hotkeys_radio_button_ptr->set_selected_item(config.main_config.hotkeys_enable_option, false, false);
+ enable_joystick_hotkeys_radio_button_ptr->set_selected_item(config.main_config.joystick_hotkeys_enable_option, false, false);
}
void GlobalSettingsPage::save() {
config.main_config.tint_color = tint_color_radio_button_ptr->get_selected_id();
- config.main_config.hotkeys_enable_option = enable_hotkeys_radio_button_ptr->get_selected_id();
+ config.main_config.hotkeys_enable_option = enable_keyboard_hotkeys_radio_button_ptr->get_selected_id();
+ config.main_config.joystick_hotkeys_enable_option = enable_joystick_hotkeys_radio_button_ptr->get_selected_id();
save_config(config);
}
} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index dd507fb..efb3583 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,7 +1,7 @@
#include "../include/GsrInfo.hpp"
#include "../include/Overlay.hpp"
-#include "../include/GlobalHotkeysX11.hpp"
#include "../include/GlobalHotkeysLinux.hpp"
+#include "../include/GlobalHotkeysJoystick.hpp"
#include "../include/gui/Utils.hpp"
#include "../include/Process.hpp"
#include "../include/Rpc.hpp"
@@ -41,62 +41,6 @@ static void disable_prime_run() {
unsetenv("DRI_PRIME");
}
-static std::unique_ptr<gsr::GlobalHotkeysX11> register_x11_hotkeys(gsr::Overlay *overlay) {
- auto global_hotkeys = std::make_unique<gsr::GlobalHotkeysX11>();
- const bool show_hotkey_registered = global_hotkeys->bind_key_press({ XK_z, Mod1Mask }, "show_hide", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_show();
- });
-
- const bool record_hotkey_registered = global_hotkeys->bind_key_press({ XK_F9, Mod1Mask }, "record", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_record();
- });
-
- const bool pause_hotkey_registered = global_hotkeys->bind_key_press({ XK_F7, Mod1Mask }, "pause", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_pause();
- });
-
- const bool stream_hotkey_registered = global_hotkeys->bind_key_press({ XK_F8, Mod1Mask }, "stream", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_stream();
- });
-
- const bool replay_hotkey_registered = global_hotkeys->bind_key_press({ XK_F10, ShiftMask | Mod1Mask }, "replay_start", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_replay();
- });
-
- const bool replay_save_hotkey_registered = global_hotkeys->bind_key_press({ XK_F10, Mod1Mask }, "replay_save", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->save_replay();
- });
-
- if(!show_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+z for showing the overlay because the hotkey is registered by another program\n");
-
- if(!record_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+f9 for recording because the hotkey is registered by another program\n");
-
- if(!pause_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+f7 for pausing because the hotkey is registered by another program\n");
-
- if(!stream_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+f8 for streaming because the hotkey is registered by another program\n");
-
- if(!replay_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+shift+f10 for starting replay because the hotkey is registered by another program\n");
-
- if(!replay_save_hotkey_registered)
- fprintf(stderr, "error: failed to register hotkey alt+f10 for saving replay because the hotkey is registered by another program\n");
-
- if(!show_hotkey_registered || !record_hotkey_registered || !pause_hotkey_registered || !stream_hotkey_registered || !replay_hotkey_registered || !replay_save_hotkey_registered)
- return nullptr;
-
- return global_hotkeys;
-}
-
static std::unique_ptr<gsr::GlobalHotkeysLinux> register_linux_hotkeys(gsr::Overlay *overlay, gsr::GlobalHotkeysLinux::GrabType grab_type) {
auto global_hotkeys = std::make_unique<gsr::GlobalHotkeysLinux>(grab_type);
if(!global_hotkeys->start())
@@ -135,6 +79,19 @@ static std::unique_ptr<gsr::GlobalHotkeysLinux> register_linux_hotkeys(gsr::Over
return global_hotkeys;
}
+static std::unique_ptr<gsr::GlobalHotkeysJoystick> register_joystick_hotkeys(gsr::Overlay *overlay) {
+ auto global_hotkeys_js = std::make_unique<gsr::GlobalHotkeysJoystick>();
+ if(!global_hotkeys_js->start())
+ fprintf(stderr, "Warning: failed to start joystick hotkeys\n");
+
+ global_hotkeys_js->bind_action("save_replay", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay();
+ });
+
+ return global_hotkeys_js;
+}
+
static void rpc_add_commands(gsr::Rpc *rpc, gsr::Overlay *overlay) {
rpc->add_handler("show_ui", [overlay](const std::string &name) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
@@ -292,9 +249,6 @@ int main(int argc, char **argv) {
return 1;
}
- // Cant get window texture when prime-run is used
- disable_prime_run();
-
// Stop nvidia driver from buffering frames
setenv("__GL_MaxFramesAllowed", "1", true);
// If this is set to 1 then cuGraphicsGLRegisterImage will fail for egl context with error: invalid OpenGL or DirectX context,
@@ -307,11 +261,6 @@ int main(int argc, char **argv) {
signal(SIGINT, sigint_handler);
- if(mgl_init() != 0) {
- fprintf(stderr, "Error: failed to initialize mgl. Failed to either connect to the X11 server or setup opengl\n");
- exit(1);
- }
-
gsr::GsrInfo gsr_info;
// TODO: Show the error in ui
gsr::GsrInfoExitStatus gsr_info_exit_status = gsr::get_gpu_screen_recorder_info(&gsr_info);
@@ -321,8 +270,17 @@ int main(int argc, char **argv) {
}
const gsr::DisplayServer display_server = gsr_info.system_info.display_server;
- if(display_server == gsr::DisplayServer::WAYLAND)
- fprintf(stderr, "Warning: Wayland support is experimental and requires XWayland. Things may not work as expected.\n");
+ if(display_server == gsr::DisplayServer::WAYLAND) {
+ fprintf(stderr, "Warning: Wayland doesn't support this program properly and XWayland is required. Things may not work as expected. Use X11 if you experience issues.\n");
+ } else {
+ // Cant get window texture when prime-run is used
+ disable_prime_run();
+ }
+
+ if(mgl_init() != 0) {
+ fprintf(stderr, "Error: failed to initialize mgl. Failed to either connect to the X11 server or setup opengl\n");
+ exit(1);
+ }
gsr::SupportedCaptureOptions capture_options = gsr::get_supported_capture_options(gsr_info);
@@ -368,6 +326,28 @@ int main(int argc, char **argv) {
else if(overlay->get_config().main_config.hotkeys_enable_option == "enable_hotkeys_virtual_devices")
global_hotkeys = register_linux_hotkeys(overlay.get(), gsr::GlobalHotkeysLinux::GrabType::VIRTUAL);
+ overlay->on_keyboard_hotkey_changed = [&](const char *hotkey_option) {
+ global_hotkeys.reset();
+ if(strcmp(hotkey_option, "enable_hotkeys") == 0)
+ global_hotkeys = register_linux_hotkeys(overlay.get(), gsr::GlobalHotkeysLinux::GrabType::ALL);
+ else if(strcmp(hotkey_option, "enable_hotkeys_virtual_devices") == 0)
+ global_hotkeys = register_linux_hotkeys(overlay.get(), gsr::GlobalHotkeysLinux::GrabType::VIRTUAL);
+ else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
+ global_hotkeys.reset();
+ };
+
+ std::unique_ptr<gsr::GlobalHotkeysJoystick> global_hotkeys_js = nullptr;
+ if(overlay->get_config().main_config.joystick_hotkeys_enable_option == "enable_hotkeys")
+ global_hotkeys_js = register_joystick_hotkeys(overlay.get());
+
+ overlay->on_joystick_hotkey_changed = [&](const char *hotkey_option) {
+ global_hotkeys_js.reset();
+ if(strcmp(hotkey_option, "enable_hotkeys") == 0)
+ global_hotkeys_js = register_joystick_hotkeys(overlay.get());
+ else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
+ global_hotkeys_js.reset();
+ };
+
// TODO: Add hotkeys in Overlay when using x11 global hotkeys. The hotkeys in Overlay should duplicate each key that is used for x11 global hotkeys.
std::string exit_reason;
@@ -382,6 +362,9 @@ int main(int argc, char **argv) {
if(global_hotkeys)
global_hotkeys->poll_events();
+ if(global_hotkeys_js)
+ global_hotkeys_js->poll_events();
+
overlay->handle_events(global_hotkeys.get());
if(!overlay->draw()) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
@@ -391,8 +374,8 @@ int main(int argc, char **argv) {
fprintf(stderr, "Info: shutting down!\n");
rpc.reset();
- if(global_hotkeys)
- global_hotkeys.reset();
+ global_hotkeys.reset();
+ global_hotkeys_js.reset();
overlay.reset();
mgl_deinit();