mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-05-06 23:06:24 +09:00
Make hotkeys reconfigurable, faster hotkey startup time, fix some keyboard locale issues
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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_action(const std::string &id, GlobalHotkeyCallback callback) {
|
||||
return bound_actions_by_id.insert(std::make_pair(id, std::move(callback))).second;
|
||||
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;
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
179
src/Overlay.cpp
179
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) {
|
||||
window_pos = focused_monitor->position;
|
||||
window_size = focused_monitor->size;
|
||||
} else {
|
||||
window_pos = {0, 0};
|
||||
window_size = {32, 32};
|
||||
}
|
||||
|
||||
// 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;
|
||||
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 = focused_monitor->size / 2;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
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)
|
||||
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;
|
||||
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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -167,33 +390,31 @@ 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));
|
||||
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));
|
||||
}
|
||||
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->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;
|
||||
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();
|
||||
}
|
||||
}
|
||||
96
src/main.cpp
96
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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user