aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-01-23 21:23:19 +0100
committerdec05eba <dec05eba@protonmail.com>2025-01-23 21:25:45 +0100
commit1d9d4d6398e989d0628cb4dab0273293b79b4816 (patch)
treed5c4bd8c6f27d84156177831f1c0e6beb0de762a /src
parent47ada4d79844d9a98d9689d0de0c92864e0fc372 (diff)
Make hotkeys reconfigurable, faster hotkey startup time, fix some keyboard locale issues
Diffstat (limited to 'src')
-rw-r--r--src/Config.cpp34
-rw-r--r--src/GlobalHotkeysLinux.cpp140
-rw-r--r--src/GlobalHotkeysX11.cpp39
-rw-r--r--src/Overlay.cpp177
-rw-r--r--src/WindowUtils.cpp100
-rw-r--r--src/gui/GlobalSettingsPage.cpp434
-rw-r--r--src/main.cpp96
7 files changed, 786 insertions, 234 deletions
diff --git a/src/Config.cpp b/src/Config.cpp
index 8dbe32d..16b371f 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -1,21 +1,21 @@
#include "../include/Config.hpp"
#include "../include/Utils.hpp"
#include "../include/GsrInfo.hpp"
+#include "../include/GlobalHotkeys.hpp"
#include <variant>
#include <limits.h>
#include <inttypes.h>
#include <libgen.h>
#include <iostream>
+#include <mglpp/window/Keyboard.hpp>
#define FORMAT_I32 "%" PRIi32
#define FORMAT_I64 "%" PRIi64
#define FORMAT_U32 "%" PRIu32
-#define CONFIG_FILE_VERSION 1
-
namespace gsr {
bool ConfigHotkey::operator==(const ConfigHotkey &other) const {
- return keysym == other.keysym && modifiers == other.modifiers;
+ return key == other.key && modifiers == other.modifiers;
}
bool ConfigHotkey::operator!=(const ConfigHotkey &other) const {
@@ -25,19 +25,26 @@ namespace gsr {
Config::Config(const SupportedCaptureOptions &capture_options) {
const std::string default_save_directory = get_videos_dir();
+ streaming_config.start_stop_hotkey = {mgl::Keyboard::F8, HOTKEY_MOD_LALT};
streaming_config.record_options.video_quality = "custom";
streaming_config.record_options.audio_tracks.push_back("default_output");
streaming_config.record_options.video_bitrate = 15000;
+ record_config.start_stop_hotkey = {mgl::Keyboard::F9, HOTKEY_MOD_LALT};
+ record_config.pause_unpause_hotkey = {mgl::Keyboard::F7, HOTKEY_MOD_LALT};
record_config.save_directory = default_save_directory;
record_config.record_options.audio_tracks.push_back("default_output");
record_config.record_options.video_bitrate = 45000;
+ replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT};
+ replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
replay_config.record_options.video_quality = "custom";
replay_config.save_directory = default_save_directory;
replay_config.record_options.audio_tracks.push_back("default_output");
replay_config.record_options.video_bitrate = 45000;
+ main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT};
+
if(!capture_options.monitors.empty()) {
streaming_config.record_options.record_area_option = capture_options.monitors.front().name;
record_config.record_options.record_area_option = capture_options.monitors.front().name;
@@ -61,6 +68,7 @@ namespace gsr {
{"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},
+ {"main.show_hide_hotkey", &config.main_config.show_hide_hotkey},
{"streaming.record_options.record_area_option", &config.streaming_config.record_options.record_area_option},
{"streaming.record_options.record_area_width", &config.streaming_config.record_options.record_area_width},
@@ -89,7 +97,7 @@ namespace gsr {
{"streaming.twitch.key", &config.streaming_config.twitch.stream_key},
{"streaming.custom.url", &config.streaming_config.custom.url},
{"streaming.custom.container", &config.streaming_config.custom.container},
- {"streaming.start_stop_recording_hotkey", &config.streaming_config.start_stop_recording_hotkey},
+ {"streaming.start_stop_hotkey", &config.streaming_config.start_stop_hotkey},
{"record.record_options.record_area_option", &config.record_config.record_options.record_area_option},
{"record.record_options.record_area_width", &config.record_config.record_options.record_area_width},
@@ -116,8 +124,8 @@ namespace gsr {
{"record.show_video_saved_notifications", &config.record_config.show_video_saved_notifications},
{"record.save_directory", &config.record_config.save_directory},
{"record.container", &config.record_config.container},
- {"record.start_stop_recording_hotkey", &config.record_config.start_stop_recording_hotkey},
- {"record.pause_unpause_recording_hotkey", &config.record_config.pause_unpause_recording_hotkey},
+ {"record.start_stop_hotkey", &config.record_config.start_stop_hotkey},
+ {"record.pause_unpause_hotkey", &config.record_config.pause_unpause_hotkey},
{"replay.record_options.record_area_option", &config.replay_config.record_options.record_area_option},
{"replay.record_options.record_area_width", &config.replay_config.record_options.record_area_width},
@@ -147,8 +155,8 @@ namespace gsr {
{"replay.save_directory", &config.replay_config.save_directory},
{"replay.container", &config.replay_config.container},
{"replay.time", &config.replay_config.replay_time},
- {"replay.start_stop_recording_hotkey", &config.replay_config.start_stop_recording_hotkey},
- {"replay.save_recording_hotkey", &config.replay_config.save_recording_hotkey}
+ {"replay.start_stop_hotkey", &config.replay_config.start_stop_hotkey},
+ {"replay.save_hotkey", &config.replay_config.save_hotkey}
};
}
@@ -229,9 +237,9 @@ namespace gsr {
} else if(std::holds_alternative<ConfigHotkey*>(it->second)) {
std::string value_str(key_value->value);
ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it->second);
- if(sscanf(value_str.c_str(), FORMAT_I64 " " FORMAT_U32, &config_hotkey->keysym, &config_hotkey->modifiers) != 2) {
+ if(sscanf(value_str.c_str(), FORMAT_I64 " " FORMAT_U32, &config_hotkey->key, &config_hotkey->modifiers) != 2) {
fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
- config_hotkey->keysym = 0;
+ config_hotkey->key = 0;
config_hotkey->modifiers = 0;
}
} else if(std::holds_alternative<std::vector<std::string>*>(it->second)) {
@@ -242,7 +250,7 @@ namespace gsr {
return true;
});
- if(config->main_config.config_file_version != CONFIG_FILE_VERSION) {
+ if(config->main_config.config_file_version != GSR_CONFIG_FILE_VERSION) {
fprintf(stderr, "Info: the config file is outdated, resetting it\n");
config = std::nullopt;
}
@@ -251,7 +259,7 @@ namespace gsr {
}
void save_config(Config &config) {
- config.main_config.config_file_version = CONFIG_FILE_VERSION;
+ config.main_config.config_file_version = GSR_CONFIG_FILE_VERSION;
const std::string config_path = get_config_dir() + "/config_ui";
@@ -280,7 +288,7 @@ namespace gsr {
fprintf(file, "%.*s " FORMAT_I32 "\n", (int)it.first.size(), it.first.data(), *std::get<int32_t*>(it.second));
} else if(std::holds_alternative<ConfigHotkey*>(it.second)) {
const ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it.second);
- fprintf(file, "%.*s " FORMAT_I64 " " FORMAT_U32 "\n", (int)it.first.size(), it.first.data(), config_hotkey->keysym, config_hotkey->modifiers);
+ fprintf(file, "%.*s " FORMAT_I64 " " FORMAT_U32 "\n", (int)it.first.size(), it.first.data(), config_hotkey->key, config_hotkey->modifiers);
} else if(std::holds_alternative<std::vector<std::string>*>(it.second)) {
std::vector<std::string> *array = std::get<std::vector<std::string>*>(it.second);
for(const std::string &value : *array) {
diff --git a/src/GlobalHotkeysLinux.cpp b/src/GlobalHotkeysLinux.cpp
index 29cf952..c779777 100644
--- a/src/GlobalHotkeysLinux.cpp
+++ b/src/GlobalHotkeysLinux.cpp
@@ -5,6 +5,12 @@
#include <limits.h>
#include <string.h>
+extern "C" {
+#include <mgl/mgl.h>
+}
+#include <X11/Xlib.h>
+#include <linux/input-event-codes.h>
+
#define PIPE_READ 0
#define PIPE_WRITE 1
@@ -17,16 +23,55 @@ namespace gsr {
return "--all";
}
+ static inline uint8_t x11_keycode_to_linux_keycode(uint8_t code) {
+ return code - 8;
+ }
+
+ static std::vector<uint8_t> modifiers_to_linux_keys(uint32_t modifiers) {
+ std::vector<uint8_t> result;
+ if(modifiers & HOTKEY_MOD_LSHIFT)
+ result.push_back(KEY_LEFTSHIFT);
+ if(modifiers & HOTKEY_MOD_RSHIFT)
+ result.push_back(KEY_RIGHTSHIFT);
+ if(modifiers & HOTKEY_MOD_LCTRL)
+ result.push_back(KEY_LEFTCTRL);
+ if(modifiers & HOTKEY_MOD_RCTRL)
+ result.push_back(KEY_RIGHTCTRL);
+ if(modifiers & HOTKEY_MOD_LALT)
+ result.push_back(KEY_LEFTALT);
+ if(modifiers & HOTKEY_MOD_RALT)
+ result.push_back(KEY_RIGHTALT);
+ if(modifiers & HOTKEY_MOD_LSUPER)
+ result.push_back(KEY_LEFTMETA);
+ if(modifiers & HOTKEY_MOD_RSUPER)
+ result.push_back(KEY_RIGHTMETA);
+ return result;
+ }
+
+ static std::string linux_keys_to_command_string(const uint8_t *keys, size_t size) {
+ std::string result;
+ for(size_t i = 0; i < size; ++i) {
+ if(!result.empty())
+ result += "+";
+ result += std::to_string(keys[i]);
+ }
+ return result;
+ }
+
GlobalHotkeysLinux::GlobalHotkeysLinux(GrabType grab_type) : grab_type(grab_type) {
for(int i = 0; i < 2; ++i) {
- pipes[i] = -1;
+ read_pipes[i] = -1;
+ write_pipes[i] = -1;
}
}
GlobalHotkeysLinux::~GlobalHotkeysLinux() {
for(int i = 0; i < 2; ++i) {
- if(pipes[i] > 0)
- close(pipes[i]);
+ if(read_pipes[i] > 0)
+ close(read_pipes[i]);
+
+ if(write_pipes[i] > 0)
+ close(write_pipes[i]);
}
if(read_file)
@@ -58,21 +103,36 @@ namespace gsr {
if(process_id > 0)
return false;
- if(pipe(pipes) == -1)
+ if(pipe(read_pipes) == -1)
return false;
+ if(pipe(write_pipes) == -1) {
+ for(int i = 0; i < 2; ++i) {
+ close(read_pipes[i]);
+ read_pipes[i] = -1;
+ }
+ return false;
+ }
+
const pid_t pid = vfork();
if(pid == -1) {
perror("Failed to vfork");
for(int i = 0; i < 2; ++i) {
- close(pipes[i]);
- pipes[i] = -1;
+ close(read_pipes[i]);
+ close(write_pipes[i]);
+ read_pipes[i] = -1;
+ write_pipes[i] = -1;
}
return false;
} else if(pid == 0) { /* child */
- dup2(pipes[PIPE_WRITE], STDOUT_FILENO);
+ dup2(read_pipes[PIPE_WRITE], STDOUT_FILENO);
for(int i = 0; i < 2; ++i) {
- close(pipes[i]);
+ close(read_pipes[i]);
+ }
+
+ dup2(write_pipes[PIPE_READ], STDIN_FILENO);
+ for(int i = 0; i < 2; ++i) {
+ close(write_pipes[i]);
}
if(inside_flatpak) {
@@ -87,24 +147,70 @@ namespace gsr {
_exit(127);
} else { /* parent */
process_id = pid;
- close(pipes[PIPE_WRITE]);
- pipes[PIPE_WRITE] = -1;
- const int fdl = fcntl(pipes[PIPE_READ], F_GETFL);
- fcntl(pipes[PIPE_READ], F_SETFL, fdl | O_NONBLOCK);
+ close(read_pipes[PIPE_WRITE]);
+ read_pipes[PIPE_WRITE] = -1;
- read_file = fdopen(pipes[PIPE_READ], "r");
+ close(write_pipes[PIPE_READ]);
+ write_pipes[PIPE_READ] = -1;
+
+ fcntl(read_pipes[PIPE_READ], F_SETFL, fcntl(read_pipes[PIPE_READ], F_GETFL) | O_NONBLOCK);
+ read_file = fdopen(read_pipes[PIPE_READ], "r");
if(read_file)
- pipes[PIPE_READ] = -1;
+ read_pipes[PIPE_READ] = -1;
else
- fprintf(stderr, "fdopen failed, error: %s\n", strerror(errno));
+ fprintf(stderr, "fdopen failed for read, error: %s\n", strerror(errno));
+ }
+
+ return true;
+ }
+
+ bool GlobalHotkeysLinux::bind_key_press(Hotkey hotkey, const std::string &id, GlobalHotkeyCallback callback) {
+ if(bound_actions_by_id.find(id) != bound_actions_by_id.end())
+ return false;
+
+ if(id.find(' ') != std::string::npos || id.find('\n') != std::string::npos) {
+ fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: id \"%s\" contains either space or newline\n", id.c_str());
+ return false;
+ }
+
+ if(hotkey.key == 0) {
+ //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a key\n");
+ return false;
}
+ if(hotkey.modifiers == 0) {
+ //fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: hotkey requires a modifier\n");
+ return false;
+ }
+
+ mgl_context *context = mgl_get_context();
+ Display *display = (Display*)context->connection;
+ const uint8_t keycode = x11_keycode_to_linux_keycode(XKeysymToKeycode(display, hotkey.key));
+ const std::vector<uint8_t> modifiers = modifiers_to_linux_keys(hotkey.modifiers);
+ const std::string modifiers_command = linux_keys_to_command_string(modifiers.data(), modifiers.size());
+
+ char command[256];
+ const int command_size = snprintf(command, sizeof(command), "bind %s %d+%s\n", id.c_str(), (int)keycode, modifiers_command.c_str());
+ if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) {
+ fprintf(stderr, "Error: GlobalHotkeysLinux::bind_key_press: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno));
+ return false;
+ }
+
+ bound_actions_by_id[id] = std::move(callback);
return true;
}
- bool GlobalHotkeysLinux::bind_action(const std::string &id, GlobalHotkeyCallback callback) {
- return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second;
+ void GlobalHotkeysLinux::unbind_all_keys() {
+ if(bound_actions_by_id.empty())
+ return;
+
+ char command[32];
+ const int command_size = snprintf(command, sizeof(command), "unbind_all\n");
+ if(write(write_pipes[PIPE_WRITE], command, command_size) != command_size) {
+ fprintf(stderr, "Error: GlobalHotkeysLinux::unbind_all_keys: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno));
+ }
+ bound_actions_by_id.clear();
}
void GlobalHotkeysLinux::poll_events() {
diff --git a/src/GlobalHotkeysX11.cpp b/src/GlobalHotkeysX11.cpp
index c8d198c..9af2607 100644
--- a/src/GlobalHotkeysX11.cpp
+++ b/src/GlobalHotkeysX11.cpp
@@ -50,6 +50,27 @@ namespace gsr {
return mask;
}
+ static uint32_t modifiers_to_x11_modifiers(uint32_t modifiers) {
+ uint32_t result = 0;
+ if(modifiers & HOTKEY_MOD_LSHIFT)
+ result |= ShiftMask;
+ if(modifiers & HOTKEY_MOD_RSHIFT)
+ result |= ShiftMask;
+ if(modifiers & HOTKEY_MOD_LCTRL)
+ result |= ControlMask;
+ if(modifiers & HOTKEY_MOD_RCTRL)
+ result |= ControlMask;
+ if(modifiers & HOTKEY_MOD_LALT)
+ result |= Mod1Mask;
+ if(modifiers & HOTKEY_MOD_RALT)
+ result |= Mod5Mask;
+ if(modifiers & HOTKEY_MOD_LSUPER)
+ result |= Mod4Mask;
+ if(modifiers & HOTKEY_MOD_RSUPER)
+ result |= Mod4Mask;
+ return result;
+ }
+
GlobalHotkeysX11::GlobalHotkeysX11() {
dpy = XOpenDisplay(NULL);
if(!dpy)
@@ -74,16 +95,17 @@ namespace gsr {
x_failed = false;
XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error);
+ const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers);
unsigned int numlock_mask = x11_get_numlock_mask(dpy);
unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask };
for(int i = 0; i < 4; ++i) {
- XGrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), hotkey.modifiers | modifiers[i], DefaultRootWindow(dpy), False, GrabModeAsync, GrabModeAsync);
+ XGrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy), False, GrabModeAsync, GrabModeAsync);
}
XSync(dpy, False);
if(x_failed) {
for(int i = 0; i < 4; ++i) {
- XUngrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), hotkey.modifiers | modifiers[i], DefaultRootWindow(dpy));
+ XUngrabKey(dpy, XKeysymToKeycode(dpy, hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy));
}
XSync(dpy, False);
XSetErrorHandler(prev_xerror);
@@ -106,10 +128,11 @@ namespace gsr {
x_failed = false;
XErrorHandler prev_xerror = XSetErrorHandler(xerror_grab_error);
+ const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers);
unsigned int numlock_mask = x11_get_numlock_mask(dpy);
unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask };
for(int i = 0; i < 4; ++i) {
- XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), it->second.hotkey.modifiers | modifiers[i], DefaultRootWindow(dpy));
+ XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy));
}
XSync(dpy, False);
@@ -127,8 +150,9 @@ namespace gsr {
unsigned int numlock_mask = x11_get_numlock_mask(dpy);
unsigned int modifiers[] = { 0, LockMask, numlock_mask, numlock_mask|LockMask };
for(auto it = bound_keys_by_id.begin(); it != bound_keys_by_id.end();) {
+ const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(it->second.hotkey.modifiers);
for(int i = 0; i < 4; ++i) {
- XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), it->second.hotkey.modifiers | modifiers[i], DefaultRootWindow(dpy));
+ XUngrabKey(dpy, XKeysymToKeycode(dpy, it->second.hotkey.key), modifiers_x11 | modifiers[i], DefaultRootWindow(dpy));
}
}
bound_keys_by_id.clear();
@@ -145,7 +169,7 @@ namespace gsr {
XNextEvent(dpy, &xev);
if(xev.type == KeyPress) {
const KeySym key_sym = XLookupKeysym(&xev.xkey, 0);
- call_hotkey_callback({ key_sym, xev.xkey.state });
+ call_hotkey_callback({ (uint32_t)key_sym, xev.xkey.state });
}
}
}
@@ -157,7 +181,7 @@ namespace gsr {
// Note: not all keys are mapped in mgl_key_to_key_sym. If more hotkeys are added or changed then add the key mapping there
const KeySym key_sym = mgl_key_to_key_sym(event.key.code);
const uint32_t modifiers = mgl_key_modifiers_to_x11_modifier_mask(event.key);
- return !call_hotkey_callback(Hotkey{key_sym, modifiers});
+ return !call_hotkey_callback(Hotkey{(uint32_t)key_sym, modifiers});
}
static unsigned int key_state_without_locks(unsigned int key_state) {
@@ -165,8 +189,9 @@ namespace gsr {
}
bool GlobalHotkeysX11::call_hotkey_callback(Hotkey hotkey) const {
+ const uint32_t modifiers_x11 = modifiers_to_x11_modifiers(hotkey.modifiers);
for(const auto &[key, val] : bound_keys_by_id) {
- if(val.hotkey.key == hotkey.key && key_state_without_locks(val.hotkey.modifiers) == key_state_without_locks(hotkey.modifiers)) {
+ if(val.hotkey.key == hotkey.key && key_state_without_locks(modifiers_to_x11_modifiers(val.hotkey.modifiers)) == key_state_without_locks(modifiers_x11)) {
val.callback(key);
return true;
}
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 2f1725d..a8490ab 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -336,6 +336,79 @@ namespace gsr {
return true;
}
+ static Hotkey config_hotkey_to_hotkey(ConfigHotkey config_hotkey) {
+ return {
+ (uint32_t)mgl::Keyboard::key_to_x11_keysym((mgl::Keyboard::Key)config_hotkey.key),
+ config_hotkey.modifiers
+ };
+ }
+
+ static void bind_linux_hotkeys(GlobalHotkeysLinux *global_hotkeys, Overlay *overlay) {
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().main_config.show_hide_hotkey),
+ "show_hide", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->toggle_show();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().record_config.start_stop_hotkey),
+ "record", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->toggle_record();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().record_config.pause_unpause_hotkey),
+ "pause", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->toggle_pause();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().streaming_config.start_stop_hotkey),
+ "stream", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->toggle_stream();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.start_stop_hotkey),
+ "replay_start", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->toggle_replay();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.save_hotkey),
+ "replay_save", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay();
+ });
+ }
+
+ static std::unique_ptr<GlobalHotkeysLinux> register_linux_hotkeys(Overlay *overlay, GlobalHotkeysLinux::GrabType grab_type) {
+ auto global_hotkeys = std::make_unique<GlobalHotkeysLinux>(grab_type);
+ if(!global_hotkeys->start())
+ fprintf(stderr, "error: failed to start global hotkeys\n");
+
+ bind_linux_hotkeys(global_hotkeys.get(), overlay);
+ return global_hotkeys;
+ }
+
+ static std::unique_ptr<GlobalHotkeysJoystick> register_joystick_hotkeys(Overlay *overlay) {
+ auto global_hotkeys_js = std::make_unique<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;
+ }
+
Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs) :
resources_path(std::move(resources_path)),
gsr_info(std::move(gsr_info)),
@@ -366,6 +439,20 @@ namespace gsr {
if(config.replay_config.turn_on_replay_automatically_mode == "turn_on_at_system_startup")
on_press_start_replay(true);
+
+ if(config.main_config.hotkeys_enable_option == "enable_hotkeys")
+ global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::ALL);
+ else if(config.main_config.hotkeys_enable_option == "enable_hotkeys_virtual_devices")
+ global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::VIRTUAL);
+
+ if(config.main_config.joystick_hotkeys_enable_option == "enable_hotkeys")
+ global_hotkeys_js = register_joystick_hotkeys(this);
+
+ x11_mapping_display = XOpenDisplay(nullptr);
+ if(x11_mapping_display)
+ XKeysymToKeycode(x11_mapping_display, XK_F1); // If we dont call we will never get a MappingNotify
+ else
+ fprintf(stderr, "Warning: XOpenDisplay failed to mapping notify\n");
}
Overlay::~Overlay() {
@@ -393,6 +480,9 @@ namespace gsr {
close_gpu_screen_recorder_output();
deinit_color_theme();
+
+ if(x11_mapping_display)
+ XCloseDisplay(x11_mapping_display);
}
void Overlay::xi_setup() {
@@ -535,7 +625,32 @@ namespace gsr {
}
}
- void Overlay::handle_events(gsr::GlobalHotkeys *global_hotkeys) {
+ void Overlay::handle_keyboard_mapping_event() {
+ if(!x11_mapping_display)
+ return;
+
+ bool mapping_updated = false;
+ while(XPending(x11_mapping_display)) {
+ XNextEvent(x11_mapping_display, &x11_mapping_xev);
+ if(x11_mapping_xev.type == MappingNotify) {
+ XRefreshKeyboardMapping(&x11_mapping_xev.xmapping);
+ mapping_updated = true;
+ }
+ }
+
+ if(mapping_updated)
+ rebind_all_keyboard_hotkeys();
+ }
+
+ void Overlay::handle_events() {
+ if(global_hotkeys)
+ global_hotkeys->poll_events();
+
+ if(global_hotkeys_js)
+ global_hotkeys_js->poll_events();
+
+ handle_keyboard_mapping_event();
+
if(!visible || !window)
return;
@@ -742,29 +857,30 @@ namespace gsr {
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
- if(is_wlroots) {
+
+ // Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
+ // If the focused window is a wayland application then don't use override redirect and instead create
+ // a fullscreen window for the ui.
+ const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || x11_cursor_window || is_wlroots;
+
+ if(prevent_game_minimizing) {
window_pos = focused_monitor->position;
window_size = focused_monitor->size;
} else {
window_pos = {0, 0};
- window_size = {32, 32};
+ window_size = focused_monitor->size / 2;
}
- // Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
- // If the focused window is a wayland application then don't use override redirect and instead create
- // a fullscreen window for the ui.
- const bool prevent_game_minimizing = gsr_info.system_info.display_server != DisplayServer::WAYLAND || x11_cursor_window;
-
mgl::Window::CreateParams window_create_params;
window_create_params.size = window_size;
- if(is_wlroots || prevent_game_minimizing) {
+ if(prevent_game_minimizing) {
window_create_params.min_size = window_size;
window_create_params.max_size = window_size;
}
- window_create_params.position = window_pos;
+ window_create_params.position = focused_monitor->position + focused_monitor->size / 2 - window_size / 2;
window_create_params.hidden = prevent_game_minimizing;
window_create_params.override_redirect = prevent_game_minimizing;
- window_create_params.background_color = bg_color;
+ window_create_params.background_color = mgl::Color(0, 0, 0, 0);
window_create_params.support_alpha = true;
window_create_params.hide_decorations = true;
// MGL_WINDOW_TYPE_DIALOG is needed for kde plasma wayland in some cases, otherwise the window will pop up on another activity
@@ -786,6 +902,7 @@ namespace gsr {
data = 1;
XChangeProperty(display, window->get_system_handle(), XInternAtom(display, "GAMESCOPE_EXTERNAL_OVERLAY", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
+ const auto original_window_size = window_size;
window_pos = focused_monitor->position;
window_size = focused_monitor->size;
if(!init_theme(resources_path)) {
@@ -795,11 +912,11 @@ namespace gsr {
}
get_theme().set_window_size(window_size);
- if(is_wlroots || prevent_game_minimizing) {
+ if(prevent_game_minimizing) {
window->set_size(window_size);
window->set_size_limits(window_size, window_size);
- window->set_position(window_pos);
}
+ window->set_position(focused_monitor->position + focused_monitor->size / 2 - original_window_size / 2);
mgl_window *win = window->internal_window();
win->cursor_position.x = cursor_position.x - window_pos.x;
@@ -927,7 +1044,8 @@ namespace gsr {
button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
button->set_icon(&get_theme().settings_small_texture);
button->on_click = [&]() {
- auto settings_page = std::make_unique<GlobalSettingsPage>(&gsr_info, config, &page_stack);
+ auto settings_page = std::make_unique<GlobalSettingsPage>(this, &gsr_info, config, &page_stack);
+
settings_page->on_startup_changed = [&](bool enable, int exit_status) {
if(exit_status == 0)
return;
@@ -949,11 +1067,21 @@ namespace gsr {
};
settings_page->on_keyboard_hotkey_changed = [this](const char *hotkey_option) {
- on_keyboard_hotkey_changed(hotkey_option);
+ global_hotkeys.reset();
+ if(strcmp(hotkey_option, "enable_hotkeys") == 0)
+ global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::ALL);
+ else if(strcmp(hotkey_option, "enable_hotkeys_virtual_devices") == 0)
+ global_hotkeys = register_linux_hotkeys(this, GlobalHotkeysLinux::GrabType::VIRTUAL);
+ else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
+ global_hotkeys.reset();
};
settings_page->on_joystick_hotkey_changed = [this](const char *hotkey_option) {
- on_joystick_hotkey_changed(hotkey_option);
+ global_hotkeys_js.reset();
+ if(strcmp(hotkey_option, "enable_hotkeys") == 0)
+ global_hotkeys_js = register_joystick_hotkeys(this);
+ else if(strcmp(hotkey_option, "disable_hotkeys") == 0)
+ global_hotkeys_js.reset();
};
page_stack.push(std::move(settings_page));
@@ -990,7 +1118,8 @@ namespace gsr {
// The focused application can be an xwayland application but the cursor can hover over a wayland application.
// This is even the case when hovering over the titlebar of the xwayland application.
- if(prevent_game_minimizing)
+ const bool fake_cursor = is_wlroots ? x11_cursor_window != None : prevent_game_minimizing;
+ if(fake_cursor)
xi_setup();
//window->set_fullscreen(true);
@@ -1047,6 +1176,8 @@ namespace gsr {
if(paused)
update_ui_recording_paused();
+ // Wayland compositors have retarded fullscreen animations that we cant disable in a proper way
+ // without messing up window position.
show_overlay_timeout_seconds = prevent_game_minimizing ? 0.0 : 0.15;
show_overlay_clock.restart();
draw();
@@ -1233,6 +1364,18 @@ namespace gsr {
return config;
}
+ void Overlay::unbind_all_keyboard_hotkeys() {
+ if(global_hotkeys)
+ global_hotkeys->unbind_all_keys();
+ }
+
+ void Overlay::rebind_all_keyboard_hotkeys() {
+ unbind_all_keyboard_hotkeys();
+ // TODO: Check if type is GlobalHotkeysLinux
+ if(global_hotkeys)
+ bind_linux_hotkeys(static_cast<GlobalHotkeysLinux*>(global_hotkeys.get()), this);
+ }
+
void Overlay::update_notification_process_status() {
if(notification_process <= 0)
return;
diff --git a/src/WindowUtils.cpp b/src/WindowUtils.cpp
index ea9dfbd..d8e3508 100644
--- a/src/WindowUtils.cpp
+++ b/src/WindowUtils.cpp
@@ -98,15 +98,16 @@ namespace gsr {
return found_window;
}
- static Window get_window_at_cursor_position(Display *dpy) {
+ mgl::vec2i get_cursor_position(Display *dpy, Window *window) {
Window root_window = None;
- Window window = None;
+ *window = None;
int dummy_i;
unsigned int dummy_u;
- XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &dummy_i, &dummy_i, &dummy_i, &dummy_i, &dummy_u);
+ mgl::vec2i root_pos;
+ XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u);
if(window)
- window = window_get_target_window_child(dpy, window);
- return window;
+ *window = window_get_target_window_child(dpy, *window);
+ return root_pos;
}
Window get_focused_window(Display *dpy, WindowCaptureType cap_type) {
@@ -136,7 +137,7 @@ namespace gsr {
return focused_window;
}
- focused_window = get_window_at_cursor_position(dpy);
+ get_cursor_position(dpy, &focused_window);
if(focused_window && focused_window != DefaultRootWindow(dpy))
return focused_window;
@@ -235,35 +236,6 @@ namespace gsr {
return result;
}
- mgl::vec2i get_cursor_position(Display *dpy, Window *window) {
- Window root_window = None;
- *window = None;
- int dummy_i;
- unsigned int dummy_u;
- mgl::vec2i root_pos;
- XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u);
-
- // This dumb shit is done to satisfy gnome wayland. Only set |window| if a valid x11 window is focused
- if(window) {
- XWindowAttributes attr;
- if(XGetWindowAttributes(dpy, *window, &attr) && attr.override_redirect)
- *window = None;
-
- int revert_to = 0;
- Window input_focus_window = None;
- if(XGetInputFocus(dpy, &input_focus_window, &revert_to)) {
- if(input_focus_window) {
- if(XGetWindowAttributes(dpy, input_focus_window, &attr) && attr.override_redirect)
- *window = None;
- } else {
- *window = None;
- }
- }
- }
-
- return root_pos;
- }
-
typedef struct {
unsigned long flags;
unsigned long functions;
@@ -334,7 +306,7 @@ namespace gsr {
poll_fd.fd = x_fd;
poll_fd.events = POLLIN;
poll_fd.revents = 0;
- const int fds_ready = poll(&poll_fd, 1, 1000);
+ const int fds_ready = poll(&poll_fd, 1, 200);
if(fds_ready == 0) {
fprintf(stderr, "Error: timed out waiting for ConfigureNotify after XCreateWindow\n");
break;
@@ -342,15 +314,18 @@ namespace gsr {
continue;
}
- XNextEvent(display, &xev);
- if(xev.type == ConfigureNotify && xev.xconfigure.window == window) {
- got_data = xev.xconfigure.x > 0 && xev.xconfigure.y > 0;
- position.x = xev.xconfigure.x + xev.xconfigure.width / 2;
- position.y = xev.xconfigure.y + xev.xconfigure.height / 2;
- break;
+ while(XPending(display)) {
+ XNextEvent(display, &xev);
+ if(xev.type == ConfigureNotify && xev.xconfigure.window == window) {
+ got_data = xev.xconfigure.x > 0 && xev.xconfigure.y > 0;
+ position.x = xev.xconfigure.x + xev.xconfigure.width / 2;
+ position.y = xev.xconfigure.y + xev.xconfigure.height / 2;
+ goto done;
+ }
}
}
+ done:
XDestroyWindow(display, window);
XFlush(display);
@@ -395,7 +370,7 @@ namespace gsr {
poll_fd.fd = x_fd;
poll_fd.events = POLLIN;
poll_fd.revents = 0;
- const int fds_ready = poll(&poll_fd, 1, 1000);
+ const int fds_ready = poll(&poll_fd, 1, 200);
if(fds_ready == 0) {
fprintf(stderr, "Error: timed out waiting for MapNotify/ConfigureNotify after XCreateWindow\n");
break;
@@ -403,27 +378,30 @@ namespace gsr {
continue;
}
- XNextEvent(display, &xev);
- if(xev.type == MapNotify && xev.xmap.window == window) {
- int x = 0;
- int y = 0;
- Window w = None;
- XTranslateCoordinates(display, window, DefaultRootWindow(display), 0, 0, &x, &y, &w);
-
- got_data = x > 0 && y > 0;
- position.x = x + size / 2;
- position.y = y + size / 2;
- if(got_data)
- break;
- } else if(xev.type == ConfigureNotify && xev.xconfigure.window == window) {
- got_data = xev.xconfigure.x > 0 && xev.xconfigure.y > 0;
- position.x = xev.xconfigure.x + xev.xconfigure.width / 2;
- position.y = xev.xconfigure.y + xev.xconfigure.height / 2;
- if(got_data)
- break;
+ while(XPending(display)) {
+ XNextEvent(display, &xev);
+ if(xev.type == MapNotify && xev.xmap.window == window) {
+ int x = 0;
+ int y = 0;
+ Window w = None;
+ XTranslateCoordinates(display, window, DefaultRootWindow(display), 0, 0, &x, &y, &w);
+
+ got_data = x > 0 && y > 0;
+ position.x = x + size / 2;
+ position.y = y + size / 2;
+ if(got_data)
+ goto done;
+ } else if(xev.type == ConfigureNotify && xev.xconfigure.window == window) {
+ got_data = xev.xconfigure.x > 0 && xev.xconfigure.y > 0;
+ position.x = xev.xconfigure.x + xev.xconfigure.width / 2;
+ position.y = xev.xconfigure.y + xev.xconfigure.height / 2;
+ if(got_data)
+ goto done;
+ }
}
}
+ done:
XDestroyWindow(display, window);
XFlush(display);
diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp
index 8a2a162..d4a87c2 100644
--- a/src/gui/GlobalSettingsPage.cpp
+++ b/src/gui/GlobalSettingsPage.cpp
@@ -1,5 +1,7 @@
#include "../../include/gui/GlobalSettingsPage.hpp"
+#include "../../include/Overlay.hpp"
+#include "../../include/GlobalHotkeys.hpp"
#include "../../include/Theme.hpp"
#include "../../include/Process.hpp"
#include "../../include/gui/GsrPage.hpp"
@@ -10,6 +12,16 @@
#include "../../include/gui/Label.hpp"
#include "../../include/gui/RadioButton.hpp"
#include "../../include/gui/LineSeparator.hpp"
+#include "../../include/gui/CustomRendererWidget.hpp"
+
+#include <assert.h>
+#include <X11/Xlib.h>
+extern "C" {
+#include <mgl/mgl.h>
+}
+#include <mglpp/window/Window.hpp>
+#include <mglpp/graphics/Rectangle.hpp>
+#include <mglpp/graphics/Text.hpp>
#ifndef GSR_UI_VERSION
#define GSR_UI_VERSION "unknown"
@@ -40,8 +52,64 @@ namespace gsr {
return "unknown";
}
- GlobalSettingsPage::GlobalSettingsPage(const GsrInfo *gsr_info, Config &config, PageStack *page_stack) :
+ static uint32_t mgl_modifier_to_hotkey_modifier(mgl::Keyboard::Key modifier_key) {
+ switch(modifier_key) {
+ case mgl::Keyboard::LControl: return HOTKEY_MOD_LCTRL;
+ case mgl::Keyboard::LShift: return HOTKEY_MOD_LSHIFT;
+ case mgl::Keyboard::LAlt: return HOTKEY_MOD_LALT;
+ case mgl::Keyboard::LSystem: return HOTKEY_MOD_LSUPER;
+ case mgl::Keyboard::RControl: return HOTKEY_MOD_RCTRL;
+ case mgl::Keyboard::RShift: return HOTKEY_MOD_RSHIFT;
+ case mgl::Keyboard::RAlt: return HOTKEY_MOD_RALT;
+ case mgl::Keyboard::RSystem: return HOTKEY_MOD_RSUPER;
+ default: return 0;
+ }
+ return 0;
+ }
+
+ static std::vector<mgl::Keyboard::Key> hotkey_modifiers_to_mgl_keys(uint32_t modifiers) {
+ std::vector<mgl::Keyboard::Key> result;
+ if(modifiers & HOTKEY_MOD_LCTRL)
+ result.push_back(mgl::Keyboard::LControl);
+ if(modifiers & HOTKEY_MOD_LSHIFT)
+ result.push_back(mgl::Keyboard::LShift);
+ if(modifiers & HOTKEY_MOD_LALT)
+ result.push_back(mgl::Keyboard::LAlt);
+ if(modifiers & HOTKEY_MOD_LSUPER)
+ result.push_back(mgl::Keyboard::LSystem);
+ if(modifiers & HOTKEY_MOD_RCTRL)
+ result.push_back(mgl::Keyboard::RControl);
+ if(modifiers & HOTKEY_MOD_RSHIFT)
+ result.push_back(mgl::Keyboard::RShift);
+ if(modifiers & HOTKEY_MOD_RALT)
+ result.push_back(mgl::Keyboard::RAlt);
+ if(modifiers & HOTKEY_MOD_RSUPER)
+ result.push_back(mgl::Keyboard::RSystem);
+ return result;
+ }
+
+ static std::string config_hotkey_to_string(ConfigHotkey config_hotkey) {
+ std::string result;
+
+ const std::vector<mgl::Keyboard::Key> modifier_keys = hotkey_modifiers_to_mgl_keys(config_hotkey.modifiers);
+ for(const mgl::Keyboard::Key modifier_key : modifier_keys) {
+ if(!result.empty())
+ result += " + ";
+ result += mgl::Keyboard::key_to_string(modifier_key);
+ }
+
+ if(config_hotkey.key != 0) {
+ if(!result.empty())
+ result += " + ";
+ result += mgl::Keyboard::key_to_string((mgl::Keyboard::Key)config_hotkey.key);
+ }
+
+ return result;
+ }
+
+ GlobalSettingsPage::GlobalSettingsPage(Overlay *overlay, const GsrInfo *gsr_info, Config &config, PageStack *page_stack) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
+ overlay(overlay),
config(config),
gsr_info(gsr_info),
page_stack(page_stack)
@@ -57,6 +125,45 @@ namespace gsr {
add_widgets();
load();
+
+ auto hotkey_overlay = std::make_unique<CustomRendererWidget>(get_size());
+ hotkey_overlay->draw_handler = [this](mgl::Window &window, mgl::vec2f, mgl::vec2f) {
+ Button *configure_hotkey_button = configure_hotkey_get_button_by_active_type();
+ if(!configure_hotkey_button)
+ return;
+
+ mgl::Text title_text("Press a key combination to use for the hotkey \"Start/stop recording\":", get_theme().title_font);
+ mgl::Text hotkey_text(configure_hotkey_button->get_text(), get_theme().top_bar_font);
+ mgl::Text description_text("The hotkey has to contain one or more of these keys: Alt, Ctrl, Shift and Super. Press Esc to cancel.", get_theme().body_font);
+ const float text_max_width = std::max(title_text.get_bounds().size.x, std::max(hotkey_text.get_bounds().size.x, description_text.get_bounds().size.x));
+
+ const float padding_horizontal = int(get_theme().window_height * 0.01f);
+ const float padding_vertical = int(get_theme().window_height * 0.01f);
+
+ const mgl::vec2f bg_size = mgl::vec2f(text_max_width + padding_horizontal*2.0f, get_theme().window_height * 0.1f).floor();
+ mgl::Rectangle bg_rect(mgl::vec2f(get_theme().window_width*0.5f - bg_size.x*0.5f, get_theme().window_height*0.5f - bg_size.y*0.5f).floor(), bg_size);
+ bg_rect.set_color(get_color_theme().page_bg_color);
+ window.draw(bg_rect);
+
+ const mgl::vec2f tint_size = mgl::vec2f(bg_size.x, 0.004f * get_theme().window_height).floor();
+ mgl::Rectangle tint_rect(bg_rect.get_position() - mgl::vec2f(0.0f, tint_size.y), tint_size);
+ tint_rect.set_color(get_color_theme().tint_color);
+ window.draw(tint_rect);
+
+ title_text.set_position(mgl::vec2f(bg_rect.get_position() + mgl::vec2f(bg_rect.get_size().x*0.5f - title_text.get_bounds().size.x*0.5f, padding_vertical)).floor());
+ window.draw(title_text);
+
+ //const float description_bottom = description_text.get_position().y + description_text.get_bounds().size.y;
+ //const float remaining_height = (bg_rect.get_position().y + bg_rect.get_size().y) - description_bottom;
+ hotkey_text.set_position(mgl::vec2f(bg_rect.get_position() + bg_rect.get_size()*0.5f - hotkey_text.get_bounds().size*0.5f).floor());
+ window.draw(hotkey_text);
+
+ description_text.set_position(mgl::vec2f(bg_rect.get_position() + mgl::vec2f(bg_rect.get_size().x*0.5f - description_text.get_bounds().size.x*0.5f, bg_rect.get_size().y - description_text.get_bounds().size.y - padding_vertical)).floor());
+ window.draw(description_text);
+ };
+ hotkey_overlay->set_visible(false);
+ hotkey_overlay_ptr = hotkey_overlay.get();
+ add_widget(std::move(hotkey_overlay));
}
std::unique_ptr<Subsection> GlobalSettingsPage::create_appearance_subsection(ScrollablePage *parent_page) {
@@ -134,6 +241,117 @@ namespace gsr {
return enable_hotkeys_radio_button;
}
+ std::unique_ptr<List> GlobalSettingsPage::create_show_hide_hotkey_options() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Show/hide UI:", get_color_theme().text_color));
+ auto show_hide_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ show_hide_button_ptr = show_hide_button.get();
+ list->add_widget(std::move(show_hide_button));
+
+ show_hide_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::SHOW_HIDE);
+ };
+
+
+ return list;
+ }
+
+ std::unique_ptr<List> GlobalSettingsPage::create_replay_hotkey_options() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Turn replay on/off:", get_color_theme().text_color));
+ auto turn_replay_on_off_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ turn_replay_on_off_button_ptr = turn_replay_on_off_button.get();
+ list->add_widget(std::move(turn_replay_on_off_button));
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save replay:", get_color_theme().text_color));
+ auto save_replay_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ save_replay_button_ptr = save_replay_button.get();
+ list->add_widget(std::move(save_replay_button));
+
+ turn_replay_on_off_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::REPLAY_START_STOP);
+ };
+
+ save_replay_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE);
+ };
+
+ return list;
+ }
+
+ std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Start/stop recording:", get_color_theme().text_color));
+ auto start_stop_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ start_stop_recording_button_ptr = start_stop_recording_button.get();
+ list->add_widget(std::move(start_stop_recording_button));
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Pause/unpause recording:", get_color_theme().text_color));
+ auto pause_unpause_recording_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ pause_unpause_recording_button_ptr = pause_unpause_recording_button.get();
+ list->add_widget(std::move(pause_unpause_recording_button));
+
+ start_stop_recording_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::RECORD_START_STOP);
+ };
+
+ pause_unpause_recording_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE);
+ };
+
+ return list;
+ }
+
+ std::unique_ptr<List> GlobalSettingsPage::create_stream_hotkey_options() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Start/stop streaming:", get_color_theme().text_color));
+ auto start_stop_streaming_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ start_stop_streaming_button_ptr = start_stop_streaming_button.get();
+ list->add_widget(std::move(start_stop_streaming_button));
+
+ start_stop_streaming_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::STREAM_START_STOP);
+ };
+
+ return list;
+ }
+
+ std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ // auto clear_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, "Clear hotkeys", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ // clear_hotkeys_button->on_click = [this] {
+ // config.streaming_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
+ // config.record_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
+ // config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0};
+ // config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
+ // config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0};
+ // config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
+ // load_hotkeys();
+ // overlay->rebind_all_keyboard_hotkeys();
+ // };
+ // list->add_widget(std::move(clear_hotkeys_button));
+
+ auto reset_hotkeys_button = std::make_unique<Button>(&get_theme().body_font, "Reset hotkeys to default", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ reset_hotkeys_button->on_click = [this] {
+ config.streaming_config.start_stop_hotkey = {mgl::Keyboard::F8, HOTKEY_MOD_LALT};
+ config.record_config.start_stop_hotkey = {mgl::Keyboard::F9, HOTKEY_MOD_LALT};
+ config.record_config.pause_unpause_hotkey = {mgl::Keyboard::F7, HOTKEY_MOD_LALT};
+ config.replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT};
+ config.replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
+ config.main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT};
+ load_hotkeys();
+ overlay->rebind_all_keyboard_hotkeys();
+ };
+ list->add_widget(std::move(reset_hotkeys_button));
+
+ return list;
+ }
+
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();
@@ -144,7 +362,12 @@ namespace gsr {
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));
+ list_ptr->add_widget(create_show_hide_hotkey_options());
+ list_ptr->add_widget(create_replay_hotkey_options());
+ list_ptr->add_widget(create_record_hotkey_options());
+ list_ptr->add_widget(create_stream_hotkey_options());
+ list_ptr->add_widget(std::make_unique<Label>(&get_theme().body_font, "Double-click the controller share button to save a replay", get_color_theme().text_color));
+ list_ptr->add_widget(create_hotkey_control_buttons());
return subsection;
}
@@ -168,32 +391,30 @@ 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));
+ }
+
+ std::unique_ptr<Subsection> GlobalSettingsPage::create_application_info_subsection(ScrollablePage *parent_page) {
+ const bool inside_flatpak = getenv("FLATPAK_ID") != NULL;
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));
+ char str[128];
+ snprintf(str, sizeof(str), "UI version: %s", GSR_UI_VERSION);
+ list->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->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
}
- return subsection;
+
+ snprintf(str, sizeof(str), "GPU vendor: %s", gpu_vendor_to_string(gsr_info->gpu_info.vendor));
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
+
+ return std::make_unique<Subsection>("Application info", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
}
void GlobalSettingsPage::add_widgets() {
@@ -205,6 +426,7 @@ namespace gsr {
settings_list->add_widget(create_startup_subsection(scrollable_page.get()));
settings_list->add_widget(create_hotkey_subsection(scrollable_page.get()));
settings_list->add_widget(create_application_options_subsection(scrollable_page.get()));
+ settings_list->add_widget(create_application_info_subsection(scrollable_page.get()));
scrollable_page->add_widget(std::move(settings_list));
content_page_ptr->add_widget(std::move(scrollable_page));
@@ -227,12 +449,174 @@ namespace gsr {
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);
+
+ load_hotkeys();
+ }
+
+ void GlobalSettingsPage::load_hotkeys() {
+ turn_replay_on_off_button_ptr->set_text(config_hotkey_to_string(config.replay_config.start_stop_hotkey));
+ save_replay_button_ptr->set_text(config_hotkey_to_string(config.replay_config.save_hotkey));
+
+ start_stop_recording_button_ptr->set_text(config_hotkey_to_string(config.record_config.start_stop_hotkey));
+ pause_unpause_recording_button_ptr->set_text(config_hotkey_to_string(config.record_config.pause_unpause_hotkey));
+
+ start_stop_streaming_button_ptr->set_text(config_hotkey_to_string(config.streaming_config.start_stop_hotkey));
+
+ show_hide_button_ptr->set_text(config_hotkey_to_string(config.main_config.show_hide_hotkey));
}
void GlobalSettingsPage::save() {
+ configure_hotkey_cancel();
config.main_config.tint_color = tint_color_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);
}
+
+ bool GlobalSettingsPage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
+ if(!StaticPage::on_event(event, window, offset))
+ return false;
+
+ if(configure_hotkey_type == ConfigureHotkeyType::NONE)
+ return true;
+
+ Button *configure_hotkey_button = configure_hotkey_get_button_by_active_type();
+ if(!configure_hotkey_button)
+ return true;
+
+ if(event.type == mgl::Event::KeyPressed) {
+ if(event.key.code == mgl::Keyboard::Escape)
+ return false;
+
+ if(mgl::Keyboard::key_is_modifier(event.key.code)) {
+ configure_config_hotkey.modifiers |= mgl_modifier_to_hotkey_modifier(event.key.code);
+ configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey));
+ } else if(configure_config_hotkey.modifiers != 0) {
+ configure_config_hotkey.key = event.key.code;
+ configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey));
+ configure_hotkey_stop_and_save();
+ }
+
+ return false;
+ } else if(event.type == mgl::Event::KeyReleased) {
+ if(event.key.code == mgl::Keyboard::Escape) {
+ configure_hotkey_cancel();
+ return false;
+ }
+
+ if(mgl::Keyboard::key_is_modifier(event.key.code)) {
+ configure_config_hotkey.modifiers &= ~mgl_modifier_to_hotkey_modifier(event.key.code);
+ configure_hotkey_button->set_text(config_hotkey_to_string(configure_config_hotkey));
+ }
+
+ return false;
+ }
+
+ return true;
+ }
+
+ Button* GlobalSettingsPage::configure_hotkey_get_button_by_active_type() {
+ switch(configure_hotkey_type) {
+ case ConfigureHotkeyType::NONE:
+ return nullptr;
+ case ConfigureHotkeyType::REPLAY_START_STOP:
+ return turn_replay_on_off_button_ptr;
+ case ConfigureHotkeyType::REPLAY_SAVE:
+ return save_replay_button_ptr;
+ case ConfigureHotkeyType::RECORD_START_STOP:
+ return start_stop_recording_button_ptr;
+ case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
+ return pause_unpause_recording_button_ptr;
+ case ConfigureHotkeyType::STREAM_START_STOP:
+ return start_stop_streaming_button_ptr;
+ case ConfigureHotkeyType::SHOW_HIDE:
+ return show_hide_button_ptr;
+ }
+ return nullptr;
+ }
+
+ ConfigHotkey* GlobalSettingsPage::configure_hotkey_get_config_by_active_type() {
+ switch(configure_hotkey_type) {
+ case ConfigureHotkeyType::NONE:
+ return nullptr;
+ case ConfigureHotkeyType::REPLAY_START_STOP:
+ return &config.replay_config.start_stop_hotkey;
+ case ConfigureHotkeyType::REPLAY_SAVE:
+ return &config.replay_config.save_hotkey;
+ case ConfigureHotkeyType::RECORD_START_STOP:
+ return &config.record_config.start_stop_hotkey;
+ case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
+ return &config.record_config.pause_unpause_hotkey;
+ case ConfigureHotkeyType::STREAM_START_STOP:
+ return &config.streaming_config.start_stop_hotkey;
+ case ConfigureHotkeyType::SHOW_HIDE:
+ return &config.main_config.show_hide_hotkey;
+ }
+ return nullptr;
+ }
+
+ void GlobalSettingsPage::for_each_config_hotkey(std::function<void(ConfigHotkey *config_hotkey)> callback) {
+ ConfigHotkey *config_hotkeys[] = {
+ &config.replay_config.start_stop_hotkey,
+ &config.replay_config.save_hotkey,
+ &config.record_config.start_stop_hotkey,
+ &config.record_config.pause_unpause_hotkey,
+ &config.streaming_config.start_stop_hotkey,
+ &config.main_config.show_hide_hotkey
+ };
+ for(ConfigHotkey *config_hotkey : config_hotkeys) {
+ callback(config_hotkey);
+ }
+ }
+
+ void GlobalSettingsPage::configure_hotkey_start(ConfigureHotkeyType hotkey_type) {
+ assert(hotkey_type != ConfigureHotkeyType::NONE);
+ configure_config_hotkey = {0, 0};
+ configure_hotkey_type = hotkey_type;
+
+ content_page_ptr->set_visible(false);
+ hotkey_overlay_ptr->set_visible(true);
+ overlay->unbind_all_keyboard_hotkeys();
+ }
+
+ void GlobalSettingsPage::configure_hotkey_cancel() {
+ Button *config_hotkey_button = configure_hotkey_get_button_by_active_type();
+ ConfigHotkey *config_hotkey = configure_hotkey_get_config_by_active_type();
+ if(config_hotkey_button && config_hotkey)
+ config_hotkey_button->set_text(config_hotkey_to_string(*config_hotkey));
+
+ configure_config_hotkey = {0, 0};
+ configure_hotkey_type = ConfigureHotkeyType::NONE;
+ content_page_ptr->set_visible(true);
+ hotkey_overlay_ptr->set_visible(false);
+ overlay->rebind_all_keyboard_hotkeys();
+ }
+
+ void GlobalSettingsPage::configure_hotkey_stop_and_save() {
+ Button *config_hotkey_button = configure_hotkey_get_button_by_active_type();
+ ConfigHotkey *config_hotkey = configure_hotkey_get_config_by_active_type();
+ if(config_hotkey_button && config_hotkey) {
+ bool hotkey_used_by_another_action = false;
+ for_each_config_hotkey([&](ConfigHotkey *config_hotkey_item) {
+ if(config_hotkey_item != config_hotkey && *config_hotkey_item == configure_config_hotkey)
+ hotkey_used_by_another_action = true;
+ });
+
+ if(hotkey_used_by_another_action) {
+ const std::string error_msg = "The hotkey \"" + config_hotkey_to_string(configure_config_hotkey) + " is already used for something else";
+ overlay->show_notification(error_msg.c_str(), 3.0, mgl::Color(255, 0, 0, 255), mgl::Color(255, 0, 0, 255), NotificationType::NONE);
+ config_hotkey_button->set_text(config_hotkey_to_string(*config_hotkey));
+ configure_config_hotkey = {0, 0};
+ return;
+ }
+
+ *config_hotkey = configure_config_hotkey;
+ }
+
+ configure_config_hotkey = {0, 0};
+ configure_hotkey_type = ConfigureHotkeyType::NONE;
+ content_page_ptr->set_visible(true);
+ hotkey_overlay_ptr->set_visible(false);
+ overlay->rebind_all_keyboard_hotkeys();
+ }
} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index efb3583..17a73a5 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,18 +1,14 @@
#include "../include/GsrInfo.hpp"
#include "../include/Overlay.hpp"
-#include "../include/GlobalHotkeysLinux.hpp"
-#include "../include/GlobalHotkeysJoystick.hpp"
#include "../include/gui/Utils.hpp"
#include "../include/Process.hpp"
#include "../include/Rpc.hpp"
#include <unistd.h>
#include <signal.h>
-#include <thread>
#include <string.h>
#include <limits.h>
-#include <X11/keysym.h>
#include <mglpp/mglpp.hpp>
#include <mglpp/system/Clock.hpp>
@@ -41,57 +37,6 @@ static void disable_prime_run() {
unsetenv("DRI_PRIME");
}
-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())
- fprintf(stderr, "error: failed to start global hotkeys\n");
-
- global_hotkeys->bind_action("show_hide", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_show();
- });
-
- global_hotkeys->bind_action("record", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_record();
- });
-
- global_hotkeys->bind_action("pause", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_pause();
- });
-
- global_hotkeys->bind_action("stream", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_stream();
- });
-
- global_hotkeys->bind_action("replay_start", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->toggle_replay();
- });
-
- global_hotkeys->bind_action("replay_save", [overlay](const std::string &id) {
- fprintf(stderr, "pressed %s\n", id.c_str());
- overlay->save_replay();
- });
-
- 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());
@@ -320,34 +265,6 @@ int main(int argc, char **argv) {
rpc_add_commands(rpc.get(), overlay.get());
- std::unique_ptr<gsr::GlobalHotkeys> global_hotkeys = nullptr;
- if(overlay->get_config().main_config.hotkeys_enable_option == "enable_hotkeys")
- global_hotkeys = register_linux_hotkeys(overlay.get(), gsr::GlobalHotkeysLinux::GrabType::ALL);
- 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;
@@ -358,24 +275,15 @@ int main(int argc, char **argv) {
gsr::set_frame_delta_seconds(frame_delta_seconds);
rpc->poll();
-
- if(global_hotkeys)
- global_hotkeys->poll_events();
-
- if(global_hotkeys_js)
- global_hotkeys_js->poll_events();
-
- overlay->handle_events(global_hotkeys.get());
+ overlay->handle_events();
if(!overlay->draw()) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ usleep(100 * 1000); // 100ms
mgl_ping_display_server();
}
}
fprintf(stderr, "Info: shutting down!\n");
rpc.reset();
- global_hotkeys.reset();
- global_hotkeys_js.reset();
overlay.reset();
mgl_deinit();