aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.hpp8
-rw-r--r--src/global_shortcuts.c334
-rw-r--r--src/global_shortcuts.h41
-rw-r--r--src/main.cpp806
4 files changed, 861 insertions, 328 deletions
diff --git a/src/config.hpp b/src/config.hpp
index 1bea56c..86fb4de 100644
--- a/src/config.hpp
+++ b/src/config.hpp
@@ -59,13 +59,16 @@ struct StreamingConfig {
TwitchStreamConfig twitch;
CustomStreamConfig custom;
ConfigHotkey start_recording_hotkey;
+ ConfigHotkey stop_recording_hotkey;
};
struct RecordConfig {
std::string save_directory;
std::string container;
ConfigHotkey start_recording_hotkey;
+ ConfigHotkey stop_recording_hotkey;
ConfigHotkey pause_recording_hotkey;
+ ConfigHotkey unpause_recording_hotkey;
};
struct ReplayConfig {
@@ -73,6 +76,7 @@ struct ReplayConfig {
std::string container;
int32_t replay_time = 30;
ConfigHotkey start_recording_hotkey;
+ ConfigHotkey stop_recording_hotkey;
ConfigHotkey save_recording_hotkey;
};
@@ -321,16 +325,20 @@ static std::map<std::string, ConfigValue> get_config_options(Config &config) {
{"streaming.custom.url", {CONFIG_TYPE_STRING, &config.streaming_config.custom.url}},
{"streaming.custom.container", {CONFIG_TYPE_STRING, &config.streaming_config.custom.container}},
{"streaming.start_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.streaming_config.start_recording_hotkey}},
+ {"streaming.stop_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.streaming_config.stop_recording_hotkey}},
{"record.save_directory", {CONFIG_TYPE_STRING, &config.record_config.save_directory}},
{"record.container", {CONFIG_TYPE_STRING, &config.record_config.container}},
{"record.start_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.record_config.start_recording_hotkey}},
+ {"record.stop_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.record_config.stop_recording_hotkey}},
{"record.pause_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.record_config.pause_recording_hotkey}},
+ {"record.unpause_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.record_config.unpause_recording_hotkey}},
{"replay.save_directory", {CONFIG_TYPE_STRING, &config.replay_config.save_directory}},
{"replay.container", {CONFIG_TYPE_STRING, &config.replay_config.container}},
{"replay.time", {CONFIG_TYPE_I32, &config.replay_config.replay_time}},
{"replay.start_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.replay_config.start_recording_hotkey}},
+ {"replay.stop_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.replay_config.stop_recording_hotkey}},
{"replay.save_recording_hotkey", {CONFIG_TYPE_HOTKEY, &config.replay_config.save_recording_hotkey}}
};
}
diff --git a/src/global_shortcuts.c b/src/global_shortcuts.c
new file mode 100644
index 0000000..7037eb8
--- /dev/null
+++ b/src/global_shortcuts.c
@@ -0,0 +1,334 @@
+#include "global_shortcuts.h"
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/random.h>
+#include <gio/gio.h>
+
+/* TODO: Remove G_DBUS_CALL_FLAGS_NO_AUTO_START and G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START? also in gpu screen recorder equivalent */
+/* TODO: More error handling and clean up resources after done */
+/* TODO: Use GArray instead of GVariant where possible */
+
+static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
+ /* TODO: Use other functions on other platforms than linux */
+ if(getrandom(buffer, buffer_size, 0) < buffer_size) {
+ fprintf(stderr, "gsr error: generate_random_characters: failed to get random bytes, error: %s\n", strerror(errno));
+ return false;
+ }
+
+ for(int i = 0; i < buffer_size; ++i) {
+ unsigned char c = *(unsigned char*)&buffer[i];
+ buffer[i] = alphabet[c % alphabet_size];
+ }
+
+ return true;
+}
+
+static void gsr_dbus_portal_get_unique_handle_token(gsr_global_shortcuts *self, char *buffer, int size) {
+ snprintf(buffer, size, "gpu_screen_recorder_gtk_handle_%s_%u", self->random_str, self->handle_counter++);
+}
+
+/* Assumes shortcuts is an array */
+static void handle_shortcuts_data(GVariant *shortcuts, gsr_shortcut_callback callback, void *userdata) {
+ for(guint i = 0; i < g_variant_n_children(shortcuts); ++i) {
+ gchar *shortcut_id = NULL;
+ GVariant *shortcut_values = NULL;
+ g_variant_get_child(shortcuts, i, "(s@a{sv})", &shortcut_id, &shortcut_values);
+
+ if(!shortcut_id || !shortcut_values)
+ continue;
+
+ // gchar *description = NULL;
+ // g_variant_lookup(shortcut_values, "description", "s", &description);
+
+ gchar *trigger_description = NULL;
+ g_variant_lookup(shortcut_values, "trigger_description", "s", &trigger_description);
+
+ gsr_shortcut shortcut;
+ shortcut.id = shortcut_id;
+ shortcut.trigger_description = trigger_description ? trigger_description : "";
+ callback(shortcut, userdata);
+ }
+}
+
+typedef struct {
+ gsr_global_shortcuts *self;
+ gsr_shortcut_callback callback;
+ void *userdata;
+} signal_list_bind_userdata;
+
+static void dbus_signal_list_bind(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, signal_list_bind_userdata *userdata) {
+ (void)proxy;
+ (void)sender_name;
+ if(g_strcmp0(signal_name, "Response") != 0)
+ goto done;
+
+ guint32 response = 0;
+ GVariant *results = NULL;
+ g_variant_get(parameters, "(u@a{sv})", &response, &results);
+
+ if(response != 0 || !results)
+ goto done;
+
+ GVariant *shortcuts = g_variant_lookup_value(results, "shortcuts", G_VARIANT_TYPE("a(sa{sv})"));
+ if(!shortcuts)
+ goto done;
+
+ handle_shortcuts_data(shortcuts, userdata->callback, userdata->userdata);
+
+ done:
+ free(userdata);
+}
+
+typedef struct {
+ gsr_global_shortcuts *self;
+ gsr_deactivated_callback deactivated_callback;
+ gsr_shortcut_callback shortcut_changed_callback;
+ void *userdata;
+} signal_userdata;
+
+static void signal_callback(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer userdata)
+{
+ (void)connection;
+ (void)sender_name;
+ (void)object_path;
+ (void)interface_name;
+ (void)signal_name;
+ (void)parameters;
+ signal_userdata *cu = userdata;
+
+ /* Button released */
+ if(strcmp(signal_name, "Deactivated") == 0) {
+ gchar *session_handle = NULL;
+ gchar *shortcut_id = NULL;
+ gchar *timestamp = NULL;
+ GVariant *options = NULL;
+ g_variant_get(parameters, "(ost@a{sv})", &session_handle, &shortcut_id, &timestamp, &options);
+
+ if(session_handle && shortcut_id && strcmp(session_handle, cu->self->session_handle) == 0)
+ cu->deactivated_callback(shortcut_id, cu->userdata);
+ } else if(strcmp(signal_name, "ShortcutsChanged") == 0) {
+ gchar *session_handle = NULL;
+ GVariant *shortcuts = NULL;
+ g_variant_get(parameters, "(o@a(sa{sv}))", &session_handle, &shortcuts);
+
+ if(session_handle && shortcuts && strcmp(session_handle, cu->self->session_handle) == 0)
+ handle_shortcuts_data(shortcuts, cu->shortcut_changed_callback, cu->userdata);
+ }
+}
+
+typedef struct {
+ gsr_global_shortcuts *self;
+ gsr_init_callback callback;
+ void *userdata;
+} signal_create_session_userdata;
+
+static void dbus_signal_create_session(GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, signal_create_session_userdata *cu) {
+ (void)proxy;
+ (void)sender_name;
+ if(g_strcmp0(signal_name, "Response") != 0)
+ goto done;
+
+ guint32 response = 0;
+ GVariant *results = NULL;
+ g_variant_get(parameters, "(u@a{sv})", &response, &results);
+
+ if(response != 0 || !results)
+ goto done;
+
+ gchar *session_handle = NULL;
+ if(g_variant_lookup(results, "session_handle", "s", &session_handle) && session_handle) {
+ cu->self->session_handle = strdup(session_handle);
+ cu->self->session_created = true;
+ cu->callback(cu->userdata);
+ }
+
+ done:
+ free(cu);
+}
+
+static bool gsr_global_shortcuts_create_session(gsr_global_shortcuts *self, gsr_init_callback callback, void *userdata) {
+ char handle_token[64];
+ gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
+
+ char session_handle_token[64];
+ snprintf(session_handle_token, sizeof(session_handle_token), "gpu_screen_recorder_gtk");
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(handle_token));
+ g_variant_builder_add(&builder, "{sv}", "session_handle_token", g_variant_new_string(session_handle_token));
+ GVariant *aa = g_variant_builder_end(&builder);
+
+ GVariant *ret = g_dbus_connection_call_sync(self->gdbus_con, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.GlobalShortcuts", "CreateSession", g_variant_new_tuple(&aa, 1), NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, NULL);
+
+ if(ret) {
+ const gchar *val = NULL;
+ g_variant_get(ret, "(&o)", &val);
+ if(!val)
+ return false;
+ //g_variant_unref(ret);
+
+ GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ if(!proxy)
+ return false;
+ //g_object_unref(proxy);
+
+ signal_create_session_userdata *cu = malloc(sizeof(signal_create_session_userdata));
+ cu->self = self;
+ cu->callback = callback;
+ cu->userdata = userdata;
+ g_signal_connect(proxy, "g-signal", G_CALLBACK(dbus_signal_create_session), cu);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool gsr_global_shortcuts_init(gsr_global_shortcuts *self, gsr_init_callback callback, void *userdata) {
+ memset(self, 0, sizeof(*self));
+
+ self->random_str[DBUS_RANDOM_STR_SIZE] = '\0';
+ if(!generate_random_characters(self->random_str, DBUS_RANDOM_STR_SIZE, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62)) {
+ fprintf(stderr, "gsr error: gsr_global_shortcuts_init: failed to generate random string\n");
+ return false;
+ }
+
+ self->gdbus_con = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL);
+ if(!self->gdbus_con) {
+ fprintf(stderr, "gsr error: gsr_global_shortcuts_init: g_bus_get_sync failed\n");
+ return false;
+ }
+
+ if(!gsr_global_shortcuts_create_session(self, callback, userdata)) {
+ gsr_global_shortcuts_deinit(self);
+ return false;
+ }
+
+ return true;
+}
+
+void gsr_global_shortcuts_deinit(gsr_global_shortcuts *self) {
+ if(self->gdbus_con) {
+ /* TODO: Re-add this. Right now it causes errors as the connection is already closed, but checking if it's already closed here has no effect */
+ //g_dbus_connection_close(self->gdbus_con, NULL, NULL, NULL);
+ self->gdbus_con = NULL;
+ }
+
+ if(self->session_handle) {
+ free(self->session_handle);
+ self->session_handle = NULL;
+ }
+}
+
+bool gsr_global_shortcuts_list_shortcuts(gsr_global_shortcuts *self, gsr_shortcut_callback callback, void *userdata) {
+ if(!self->session_created)
+ return false;
+
+ char handle_token[64];
+ gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
+
+ GVariant *session_handle_obj = g_variant_new_object_path(self->session_handle);
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(&builder, "{sv}", "handle_token", g_variant_new_string(handle_token));
+ GVariant *aa = g_variant_builder_end(&builder);
+
+ GVariant *args[2] = { session_handle_obj, aa };
+
+ GVariant *ret = g_dbus_connection_call_sync(self->gdbus_con, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.GlobalShortcuts", "ListShortcuts", g_variant_new_tuple(args, 2), NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, 1000, NULL, NULL);
+ if(ret) {
+ const gchar *val = NULL;
+ g_variant_get(ret, "(&o)", &val);
+ if(!val)
+ return false;
+
+ GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ if(!proxy)
+ return false;
+ //g_object_unref(proxy);
+
+ signal_list_bind_userdata *cu = malloc(sizeof(signal_list_bind_userdata));
+ cu->self = self;
+ cu->callback = callback;
+ cu->userdata = userdata;
+ g_signal_connect(proxy, "g-signal", G_CALLBACK(dbus_signal_list_bind), cu);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool gsr_global_shortcuts_bind_shortcuts(gsr_global_shortcuts *self, const gsr_bind_shortcut *shortcuts, int num_shortcuts, gsr_shortcut_callback callback, void *userdata) {
+ if(!self->session_created)
+ return false;
+
+ char handle_token[64];
+ gsr_dbus_portal_get_unique_handle_token(self, handle_token, sizeof(handle_token));
+
+ GVariant *session_handle_obj = g_variant_new_object_path(self->session_handle);
+
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE("a(sa{sv})"));
+
+ for(int i = 0; i < num_shortcuts; ++i) {
+ GVariantBuilder shortcuts_builder;
+ g_variant_builder_init(&shortcuts_builder, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(&shortcuts_builder, "{sv}", "description", g_variant_new_string(shortcuts[i].description));
+ g_variant_builder_add(&shortcuts_builder, "{sv}", "preferred_trigger", g_variant_new_string(shortcuts[i].shortcut.trigger_description));
+ GVariant *shortcuts_data = g_variant_builder_end(&shortcuts_builder);
+ GVariant *ss_l[2] = { g_variant_new_string(shortcuts[i].shortcut.id), shortcuts_data };
+ g_variant_builder_add_value(&builder, g_variant_new_tuple(ss_l, 2));
+ }
+ GVariant *aa = g_variant_builder_end(&builder);
+
+ GVariantBuilder builder_zzz;
+ g_variant_builder_init(&builder_zzz, G_VARIANT_TYPE("a{sv}"));
+ g_variant_builder_add(&builder_zzz, "{sv}", "handle_token", g_variant_new_string(handle_token));
+ GVariant *bb = g_variant_builder_end(&builder_zzz);
+
+ GVariant *parent_window = g_variant_new_string("");
+ GVariant *args[4] = { session_handle_obj, aa, parent_window, bb };
+
+ GVariant *ret = g_dbus_connection_call_sync(self->gdbus_con, "org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop", "org.freedesktop.portal.GlobalShortcuts", "BindShortcuts", g_variant_new_tuple(args, 4), NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, NULL);
+ if(ret) {
+ const gchar *val = NULL;
+ g_variant_get(ret, "(&o)", &val);
+ if(!val)
+ return false;
+
+ GDBusProxy *proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.freedesktop.portal.Desktop", val, "org.freedesktop.portal.Request", NULL, NULL);
+ if(!proxy)
+ return false;
+ //g_object_unref(proxy);
+
+ signal_list_bind_userdata *cu = malloc(sizeof(signal_list_bind_userdata));
+ cu->self = self;
+ cu->callback = callback;
+ cu->userdata = userdata;
+ g_signal_connect(proxy, "g-signal", G_CALLBACK(dbus_signal_list_bind), cu);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool gsr_global_shortcuts_subscribe_activated_signal(gsr_global_shortcuts *self, gsr_deactivated_callback deactivated_callback, gsr_shortcut_callback shortcut_changed_callback, void *userdata) {
+ if(!self->session_created)
+ return false;
+
+ signal_userdata *cu = malloc(sizeof(signal_userdata));
+ cu->self = self;
+ cu->deactivated_callback = deactivated_callback;
+ cu->shortcut_changed_callback = shortcut_changed_callback;
+ cu->userdata = userdata;
+ g_dbus_connection_signal_subscribe(self->gdbus_con, "org.freedesktop.portal.Desktop", "org.freedesktop.portal.GlobalShortcuts", NULL, "/org/freedesktop/portal/desktop", NULL, G_DBUS_SIGNAL_FLAGS_NONE, signal_callback, cu, free);
+ return true;
+}
diff --git a/src/global_shortcuts.h b/src/global_shortcuts.h
new file mode 100644
index 0000000..f27a4de
--- /dev/null
+++ b/src/global_shortcuts.h
@@ -0,0 +1,41 @@
+#ifndef GLOBAL_SHORTCUTS_H
+#define GLOBAL_SHORTCUTS_H
+
+/* Global shortcuts via desktop portal */
+
+#include <stdbool.h>
+#include <gio/gio.h>
+
+#define DBUS_RANDOM_STR_SIZE 16
+
+typedef struct {
+ const char *id;
+ const char *trigger_description;
+} gsr_shortcut;
+
+typedef struct {
+ const char *description;
+ gsr_shortcut shortcut;
+} gsr_bind_shortcut;
+
+typedef void (*gsr_init_callback)(void *userdata);
+typedef void (*gsr_shortcut_callback)(gsr_shortcut shortcut, void *userdata);
+typedef void (*gsr_deactivated_callback)(const char *id, void *userdata);
+
+typedef struct {
+ GDBusConnection *gdbus_con;
+ char *session_handle;
+ bool session_created;
+ char random_str[DBUS_RANDOM_STR_SIZE + 1];
+ unsigned int handle_counter;
+} gsr_global_shortcuts;
+
+bool gsr_global_shortcuts_init(gsr_global_shortcuts *self, gsr_init_callback callback, void *userdata);
+void gsr_global_shortcuts_deinit(gsr_global_shortcuts *self);
+
+bool gsr_global_shortcuts_list_shortcuts(gsr_global_shortcuts *self, gsr_shortcut_callback callback, void *userdata);
+bool gsr_global_shortcuts_bind_shortcuts(gsr_global_shortcuts *self, const gsr_bind_shortcut *shortcuts, int num_shortcuts, gsr_shortcut_callback callback, void *userdata);
+
+bool gsr_global_shortcuts_subscribe_activated_signal(gsr_global_shortcuts *self, gsr_deactivated_callback deactivated_callback, gsr_shortcut_callback shortcut_changed_callback, void *userdata);
+
+#endif /* GLOBAL_SHORTCUTS_H */
diff --git a/src/main.cpp b/src/main.cpp
index ee9fbe2..829320e 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,7 @@
#include "config.hpp"
+extern "C" {
+#include "global_shortcuts.h"
+}
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xlib.h>
@@ -17,6 +20,13 @@
#include <vector>
#include <libayatana-appindicator/app-indicator.h>
+// Start/stop recording also means start/stop streaming and start/stop replay
+#define SHORTCUT_ID_START_RECORDING "gpu_screen_recorder_start_recording"
+#define SHORTCUT_ID_STOP_RECORDING "gpu_screen_recorder_stop_recording"
+#define SHORTCUT_ID_PAUSE_RECORDING "gpu_screen_recorder_pause_recording"
+#define SHORTCUT_ID_UNPAUSE_RECORDING "gpu_screen_recorder_unpause_recording"
+#define SHORTCUT_ID_SAVE_REPLAY "gpu_screen_recorder_save_replay"
+
typedef struct {
Display *display;
GtkApplication *app;
@@ -76,11 +86,15 @@ static GtkSpinButton *replay_time_entry;
static GtkButton *select_window_button;
static GtkWidget *audio_input_used_list;
static GtkWidget *add_audio_input_button;
-static GtkWidget *record_hotkey_button;
-static GtkWidget *pause_unpause_hotkey_button;
-static GtkWidget *replay_start_stop_hotkey_button;
+static GtkWidget *record_start_hotkey_button;
+static GtkWidget *record_stop_hotkey_button;
+static GtkWidget *pause_hotkey_button;
+static GtkWidget *unpause_hotkey_button;
+static GtkWidget *replay_start_hotkey_button;
+static GtkWidget *replay_stop_hotkey_button;
static GtkWidget *replay_save_hotkey_button;
-static GtkWidget *streaming_hotkey_button;
+static GtkWidget *streaming_start_hotkey_button;
+static GtkWidget *streaming_stop_hotkey_button;
static GtkWidget *merge_audio_tracks_button;
static GtkWidget *show_notification_button;
static GtkWidget *record_cursor_button;
@@ -135,6 +149,9 @@ static double notification_start_seconds = 0.0;
static AppIndicator *app_indicator;
+static gsr_global_shortcuts global_shortcuts;
+static bool global_shortcuts_initialized = false;
+
struct AudioInput {
std::string name;
std::string description;
@@ -156,22 +173,48 @@ enum class HotkeyMode {
static HotkeyMode hotkey_mode = HotkeyMode::NoAction;
+typedef gboolean (*hotkey_trigger_handler)(GtkButton *button, gpointer userdata);
struct Hotkey {
uint32_t modkey_mask = 0;
KeySym keysym = None;
GtkWidget *hotkey_entry = nullptr;
GtkWidget *hotkey_active_label = nullptr;
+ ConfigHotkey *config = nullptr;
+ bool grab_success = false;
+ GtkWidget *page = nullptr;
+ bool *state = nullptr;
+ bool expected_state_to_trigger = false;
+ hotkey_trigger_handler trigger_handler = nullptr;
+ GtkButton *associated_button = nullptr;
+ const char *shortcut_id = nullptr;
};
static Hotkey *current_hotkey = nullptr;
static Hotkey pressed_hotkey;
static Hotkey latest_hotkey;
-static Hotkey streaming_hotkey;
-static Hotkey record_hotkey;
-static Hotkey pause_unpause_hotkey;
-static Hotkey replay_start_stop_hotkey;
+static Hotkey streaming_start_hotkey;
+static Hotkey streaming_stop_hotkey;
+static Hotkey record_start_hotkey;
+static Hotkey record_stop_hotkey;
+static Hotkey pause_hotkey;
+static Hotkey unpause_hotkey;
+static Hotkey replay_start_hotkey;
+static Hotkey replay_stop_hotkey;
static Hotkey replay_save_hotkey;
+static Hotkey *hotkeys[] = {
+ &streaming_start_hotkey,
+ &streaming_stop_hotkey,
+ &record_start_hotkey,
+ &record_stop_hotkey,
+ &pause_hotkey,
+ &unpause_hotkey,
+ &replay_start_hotkey,
+ &replay_stop_hotkey,
+ &replay_save_hotkey
+};
+static int num_hotkeys = 9;
+
struct SupportedVideoCodecs {
bool h264 = false;
bool hevc = false;
@@ -645,8 +688,7 @@ static bool is_video_capture_option_enabled(const char *str) {
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND)
enabled = strcmp(str, "window") != 0 && strcmp(str, "focused") != 0;
- const bool is_portal = strcmp(str, "portal") == 0;
- if(is_portal && !gsr_info.supported_capture_options.portal)
+ if(strcmp(str, "portal") == 0 && !gsr_info.supported_capture_options.portal)
enabled = false;
return enabled;
@@ -906,30 +948,19 @@ static void save_configs() {
config.streaming_config.twitch.stream_key = gtk_entry_get_text(twitch_stream_id_entry);
config.streaming_config.custom.url = gtk_entry_get_text(custom_stream_url_entry);
config.streaming_config.custom.container = gtk_combo_box_get_active_id(GTK_COMBO_BOX(custom_stream_container));
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- config.streaming_config.start_recording_hotkey.keysym = streaming_hotkey.keysym;
- config.streaming_config.start_recording_hotkey.modifiers = streaming_hotkey.modkey_mask;
- }
config.record_config.save_directory = gtk_button_get_label(record_file_chooser_button);
config.record_config.container = gtk_combo_box_get_active_id(GTK_COMBO_BOX(record_container));
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- config.record_config.start_recording_hotkey.keysym = record_hotkey.keysym;
- config.record_config.start_recording_hotkey.modifiers = record_hotkey.modkey_mask;
-
- config.record_config.pause_recording_hotkey.keysym = pause_unpause_hotkey.keysym;
- config.record_config.pause_recording_hotkey.modifiers = pause_unpause_hotkey.modkey_mask;
- }
config.replay_config.save_directory = gtk_button_get_label(replay_file_chooser_button);
config.replay_config.container = gtk_combo_box_get_active_id(GTK_COMBO_BOX(replay_container));
config.replay_config.replay_time = gtk_spin_button_get_value_as_int(replay_time_entry);
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- config.replay_config.start_recording_hotkey.keysym = replay_start_stop_hotkey.keysym;
- config.replay_config.start_recording_hotkey.modifiers = replay_start_stop_hotkey.modkey_mask;
- config.replay_config.save_recording_hotkey.keysym = replay_save_hotkey.keysym;
- config.replay_config.save_recording_hotkey.modifiers = replay_save_hotkey.modkey_mask;
+ if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
+ for(int i = 0; i < num_hotkeys; ++i) {
+ hotkeys[i]->config->keysym = hotkeys[i]->keysym;
+ hotkeys[i]->config->modifiers = hotkeys[i]->modkey_mask;
+ }
}
save_config(config);
@@ -1221,8 +1252,10 @@ static bool grab_ungrab_hotkey_combo(Display *display, Hotkey hotkey, bool grab)
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND)
return true;
- if(hotkey.keysym == None && hotkey.modkey_mask == 0)
+ if(hotkey.keysym == None && hotkey.modkey_mask == 0) {
+ fprintf(stderr, "hotkey is empty\n");
return true;
+ }
unsigned int numlockmask = 0;
KeyCode numlock_keycode = XKeysymToKeycode(display, XK_Num_Lock);
@@ -1279,11 +1312,9 @@ static void ungrab_keys(Display *display) {
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND)
return;
- grab_ungrab_hotkey_combo(display, streaming_hotkey, false);
- grab_ungrab_hotkey_combo(display, record_hotkey, false);
- grab_ungrab_hotkey_combo(display, pause_unpause_hotkey, false);
- grab_ungrab_hotkey_combo(display, replay_start_stop_hotkey, false);
- grab_ungrab_hotkey_combo(display, replay_save_hotkey, false);
+ for(int i = 0; i < num_hotkeys; ++i) {
+ grab_ungrab_hotkey_combo(display, *hotkeys[i], false);
+ }
}
static void set_hotkey_text_from_hotkey_data(GtkEntry *entry, Hotkey hotkey) {
@@ -1326,38 +1357,33 @@ static void set_hotkey_text_from_hotkey_data(GtkEntry *entry, Hotkey hotkey) {
gtk_entry_set_text(entry, hotkey_combo_str.c_str());
}
-struct HotkeyResult {
- bool record_hotkey_success = false;
- bool pause_unpause_hotkey_success = false;
- bool streaming_hotkey_success = false;
- bool replay_start_stop_hotkey_success = false;
- bool replay_save_hotkey_success = false;
-};
+static bool replace_grabbed_keys_depending_on_active_page() {
+ if(gsr_info.system_info.display_server == DisplayServer::WAYLAND)
+ return true;
+
+ for(int i = 0; i < num_hotkeys; ++i) {
+ hotkeys[i]->grab_success = false;
+ }
-static HotkeyResult replace_grabbed_keys_depending_on_active_page() {
- HotkeyResult hotkey_result;
ungrab_keys(gdk_x11_get_default_xdisplay());
const GtkWidget *visible_page = gtk_stack_get_visible_child(page_navigation_userdata.stack);
- if(visible_page == page_navigation_userdata.recording_page) {
- bool grab_record_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), record_hotkey, true);
- bool grab_pause_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), pause_unpause_hotkey, true);
- hotkey_mode = HotkeyMode::Record;
-
- hotkey_result.record_hotkey_success = grab_record_success;
- hotkey_result.pause_unpause_hotkey_success = grab_pause_success;
- } else if(visible_page == page_navigation_userdata.streaming_page) {
- bool grab_record_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), streaming_hotkey, true);
- hotkey_mode = HotkeyMode::Record;
- hotkey_result.streaming_hotkey_success = grab_record_success;
- } else if(visible_page == page_navigation_userdata.replay_page) {
- bool grab_record_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), replay_start_stop_hotkey, true);
- bool grab_save_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), replay_save_hotkey, true);
- hotkey_mode = HotkeyMode::Record;
-
- hotkey_result.replay_start_stop_hotkey_success = grab_record_success;
- hotkey_result.replay_save_hotkey_success = grab_save_success;
+
+ bool keys_successfully_grabbed = true;
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(visible_page == hotkeys[i]->page) {
+ hotkey_mode = HotkeyMode::Record;
+ hotkeys[i]->grab_success = grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), *hotkeys[i], true);
+ if(hotkeys[i]->grab_success) {
+ set_hotkey_text_from_hotkey_data(GTK_ENTRY(hotkeys[i]->hotkey_entry), *hotkeys[i]);
+ } else {
+ gtk_entry_set_text(GTK_ENTRY(hotkeys[i]->hotkey_entry), "");
+ hotkeys[i]->keysym = 0;
+ hotkeys[i]->modkey_mask = 0;
+ keys_successfully_grabbed = false;
+ }
+ }
}
- return hotkey_result;
+ return keys_successfully_grabbed;
}
static bool is_monitor_capture_drm() {
@@ -1404,19 +1430,8 @@ static gboolean on_start_replay_click(GtkButton*, gpointer userdata) {
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->replay_page);
app_indicator_set_menu(app_indicator, GTK_MENU(create_systray_menu(page_navigation_userdata->app, SystrayPage::REPLAY)));
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.replay_start_stop_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey.hotkey_entry), "");
- replay_start_stop_hotkey.keysym = 0;
- replay_start_stop_hotkey.modkey_mask = 0;
- }
- if(!hotkey_result.replay_save_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey.hotkey_entry), "");
- replay_save_hotkey.keysym = 0;
- replay_save_hotkey.modkey_mask = 0;
- }
- }
+ if(gsr_info.system_info.display_server != DisplayServer::WAYLAND)
+ replace_grabbed_keys_depending_on_active_page();
return true;
}
@@ -1429,19 +1444,8 @@ static gboolean on_start_recording_click(GtkButton*, gpointer userdata) {
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->recording_page);
app_indicator_set_menu(app_indicator, GTK_MENU(create_systray_menu(page_navigation_userdata->app, SystrayPage::RECORDING)));
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.record_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(record_hotkey.hotkey_entry), "");
- record_hotkey.keysym = 0;
- record_hotkey.modkey_mask = 0;
- }
- if(!hotkey_result.pause_unpause_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(pause_unpause_hotkey.hotkey_entry), "");
- pause_unpause_hotkey.keysym = 0;
- pause_unpause_hotkey.modkey_mask = 0;
- }
- }
+ if(gsr_info.system_info.display_server != DisplayServer::WAYLAND)
+ replace_grabbed_keys_depending_on_active_page();
return true;
}
@@ -1473,14 +1477,8 @@ static gboolean on_start_streaming_click(GtkButton*, gpointer userdata) {
gtk_stack_set_visible_child(page_navigation_userdata->stack, page_navigation_userdata->streaming_page);
app_indicator_set_menu(app_indicator, GTK_MENU(create_systray_menu(page_navigation_userdata->app, SystrayPage::STREAMING)));
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- if(!hotkey_result.streaming_hotkey_success) {
- gtk_entry_set_text(GTK_ENTRY(streaming_hotkey.hotkey_entry), "");
- streaming_hotkey.keysym = 0;
- streaming_hotkey.modkey_mask = 0;
- }
- }
+ if(gsr_info.system_info.display_server != DisplayServer::WAYLAND)
+ replace_grabbed_keys_depending_on_active_page();
return true;
}
@@ -1774,6 +1772,9 @@ static gboolean on_replay_save_button_click(GtkButton*, gpointer userdata) {
}
static gboolean on_pause_unpause_button_click(GtkButton*, gpointer) {
+ if(!recording)
+ return true;
+
if(gpu_screen_recorder_process == -1)
return true;
@@ -2215,16 +2216,12 @@ static bool is_cuda_installed() {
return lib != nullptr;
}
-typedef gboolean (*KeyPressHandler)(GtkButton *button, gpointer userdata);
-static void keypress_toggle_recording(bool recording_state, GtkButton *record_button, KeyPressHandler keypress_handler, GtkApplication *app) {
- if(!gtk_widget_get_sensitive(GTK_WIDGET(record_button)))
- return;
-
- if(!recording_state) {
- keypress_handler(record_button, app);
- } else if(recording_state) {
- keypress_handler(record_button, app);
+static bool is_hotkey_already_bound_to_another_action_on_same_page(const Hotkey *current_hotkey, const Hotkey new_hotkey, GtkWidget *page) {
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(hotkeys[i] != current_hotkey && hotkeys[i]->page == page && hotkeys[i]->keysym == new_hotkey.keysym && hotkeys[i]->modkey_mask == new_hotkey.modkey_mask)
+ return true;
}
+ return false;
}
static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpointer userdata) {
@@ -2244,22 +2241,12 @@ static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpoi
if(hotkey_mode == HotkeyMode::Record && ev->type == KeyRelease) {
const GtkWidget *visible_page = gtk_stack_get_visible_child(page_navigation_userdata->stack);
- if(visible_page == page_navigation_userdata->recording_page) {
- if(key_sym == record_hotkey.keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(record_hotkey.modkey_mask)) {
- keypress_toggle_recording(recording, start_recording_button, on_start_recording_button_click, page_navigation_userdata->app);
- } else if(key_sym == pause_unpause_hotkey.keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(pause_unpause_hotkey.modkey_mask) && recording) {
- on_pause_unpause_button_click(nullptr, page_navigation_userdata->app);
- }
- } else if(visible_page == page_navigation_userdata->streaming_page) {
- if(key_sym == streaming_hotkey.keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(streaming_hotkey.modkey_mask)) {
- keypress_toggle_recording(streaming, start_streaming_button, on_start_streaming_button_click, page_navigation_userdata->app);
- }
- } else if(visible_page == page_navigation_userdata->replay_page) {
- if(key_sym == replay_start_stop_hotkey.keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(replay_start_stop_hotkey.modkey_mask)) {
- keypress_toggle_recording(replaying, start_replay_button, on_start_replay_button_click, page_navigation_userdata->app);
- } else if(key_sym == replay_save_hotkey.keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(replay_save_hotkey.modkey_mask) && replaying && gpu_screen_recorder_process != -1) {
- on_replay_save_button_click(nullptr, page_navigation_userdata->app);
- }
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(visible_page != hotkeys[i]->page)
+ continue;
+
+ if(key_sym == hotkeys[i]->keysym && key_state_without_locks(ev->xkey.state) == key_mod_mask_to_x11_mask(hotkeys[i]->modkey_mask) && *hotkeys[i]->state == hotkeys[i]->expected_state_to_trigger)
+ hotkeys[i]->trigger_handler(hotkeys[i]->associated_button, page_navigation_userdata->app);
}
return GDK_FILTER_CONTINUE;
}
@@ -2279,6 +2266,7 @@ static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpoi
if(ev->type == KeyPress && key_sym == XK_BackSpace) {
if(pressed_hotkey.modkey_mask == None && pressed_hotkey.keysym == None) {
ungrab_keyboard(display);
+ grab_ungrab_hotkey_combo(gdk_x11_get_default_xdisplay(), *current_hotkey, false);
gtk_entry_set_text(GTK_ENTRY(current_hotkey->hotkey_entry), "");
current_hotkey->keysym = None;
current_hotkey->modkey_mask = 0;
@@ -2319,57 +2307,43 @@ static GdkFilterReturn hotkey_filter_callback(GdkXEvent *xevent, GdkEvent*, gpoi
if(pressed_hotkey.modkey_mask == None && pressed_hotkey.keysym == None) {
ungrab_keyboard(display);
- ungrab_keys(gdk_x11_get_default_xdisplay());
-
- bool hotkey_already_used_by_another_hotkey = false;
- if(current_hotkey == &replay_start_stop_hotkey)
- hotkey_already_used_by_another_hotkey = (latest_hotkey.keysym == replay_save_hotkey.keysym && latest_hotkey.modkey_mask == replay_save_hotkey.modkey_mask);
- else if(current_hotkey == &replay_save_hotkey)
- hotkey_already_used_by_another_hotkey = (latest_hotkey.keysym == replay_start_stop_hotkey.keysym && latest_hotkey.modkey_mask == replay_start_stop_hotkey.modkey_mask);
- if(hotkey_already_used_by_another_hotkey) {
- std::string hotkey_text = gtk_entry_get_text(GTK_ENTRY(current_hotkey->hotkey_entry));
- std::string error_text = "Hotkey " + hotkey_text + " can't be used because it's used for something else. Please choose another hotkey.";
+ if(is_hotkey_already_bound_to_another_action_on_same_page(current_hotkey, latest_hotkey, gtk_stack_get_visible_child(page_navigation_userdata->stack))) {
+ const std::string hotkey_text = gtk_entry_get_text(GTK_ENTRY(current_hotkey->hotkey_entry));
+ const std::string error_text = "Hotkey " + hotkey_text + " can't be used because it's used for something else. Please choose another hotkey.";
set_hotkey_text_from_hotkey_data(GTK_ENTRY(current_hotkey->hotkey_entry), *current_hotkey);
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", error_text.c_str());
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
current_hotkey = nullptr;
+ hotkey_mode = HotkeyMode::Record;
return GDK_FILTER_CONTINUE;
}
- Hotkey prev_current_hotkey = *current_hotkey;
+ const Hotkey prev_current_hotkey = *current_hotkey;
current_hotkey->keysym = latest_hotkey.keysym;
current_hotkey->modkey_mask = latest_hotkey.modkey_mask;
- HotkeyResult hotkey_result = replace_grabbed_keys_depending_on_active_page();
- bool hotkey_success = false;
- if(current_hotkey == &record_hotkey)
- hotkey_success = hotkey_result.record_hotkey_success;
- else if(current_hotkey == &pause_unpause_hotkey)
- hotkey_success = hotkey_result.pause_unpause_hotkey_success;
- else if(current_hotkey == &streaming_hotkey)
- hotkey_success = hotkey_result.streaming_hotkey_success;
- else if(current_hotkey == &replay_start_stop_hotkey)
- hotkey_success = hotkey_result.replay_start_stop_hotkey_success;
- else if(current_hotkey == &replay_save_hotkey)
- hotkey_success = hotkey_result.replay_save_hotkey_success;
-
- if(hotkey_success) {
+ if(replace_grabbed_keys_depending_on_active_page()) {
save_configs();
+ current_hotkey = nullptr;
+ hotkey_mode = HotkeyMode::Record;
+ return GDK_FILTER_CONTINUE;
} else {
- *current_hotkey = prev_current_hotkey;
- std::string hotkey_text = gtk_entry_get_text(GTK_ENTRY(current_hotkey->hotkey_entry));
- std::string error_text = "Hotkey " + hotkey_text + " can't be used because it's used by another program. Please choose another hotkey.";
+ const std::string hotkey_text = gtk_entry_get_text(GTK_ENTRY(current_hotkey->hotkey_entry));
+ const std::string error_text = "Hotkey " + hotkey_text + " can't be used because it's used by another program. Please choose another hotkey.";
+
+ current_hotkey->keysym = prev_current_hotkey.keysym;
+ current_hotkey->modkey_mask = prev_current_hotkey.modkey_mask;
set_hotkey_text_from_hotkey_data(GTK_ENTRY(current_hotkey->hotkey_entry), *current_hotkey);
+
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", error_text.c_str());
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- }
- current_hotkey = nullptr;
- return GDK_FILTER_CONTINUE;
+ return GDK_FILTER_CONTINUE;
+ }
}
}
@@ -2380,32 +2354,27 @@ static gboolean on_hotkey_entry_click(GtkWidget *button, gpointer) {
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND)
return true;
- hotkey_mode = HotkeyMode::NewHotkey;
-
pressed_hotkey.hotkey_entry = nullptr;
pressed_hotkey.hotkey_active_label = nullptr;
pressed_hotkey.keysym = None;
pressed_hotkey.modkey_mask = 0;
latest_hotkey = pressed_hotkey;
- if(button == record_hotkey_button) {
- current_hotkey = &record_hotkey;
- } else if(button == pause_unpause_hotkey_button) {
- current_hotkey = &pause_unpause_hotkey;
- } else if(button == streaming_hotkey_button) {
- current_hotkey = &streaming_hotkey;
- } else if(button == replay_start_stop_hotkey_button) {
- current_hotkey = &replay_start_stop_hotkey;
- } else if(button == replay_save_hotkey_button) {
- current_hotkey = &replay_save_hotkey;
- } else {
- current_hotkey = nullptr;
+ current_hotkey = nullptr;
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(button == hotkeys[i]->hotkey_entry) {
+ current_hotkey = hotkeys[i];
+ break;
+ }
}
- if(current_hotkey) {
- gtk_grab_add(current_hotkey->hotkey_entry);
- gtk_widget_set_visible(current_hotkey->hotkey_active_label, true);
- }
+ if(!current_hotkey)
+ return true;
+
+ hotkey_mode = HotkeyMode::NewHotkey;
+
+ gtk_grab_add(current_hotkey->hotkey_entry);
+ gtk_widget_set_visible(current_hotkey->hotkey_active_label, true);
Display *display = gdk_x11_get_default_xdisplay();
XErrorHandler prev_error_handler = XSetErrorHandler(xerror_dummy);
@@ -2642,11 +2611,11 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, "Window (Unavailable on Wayland)", -1);
+ gtk_list_store_set(store, &iter, 0, "Window (Not available on Wayland)", -1);
gtk_list_store_set(store, &iter, 1, "window", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, "Follow focused window (Unavailable on Wayland)", -1);
+ gtk_list_store_set(store, &iter, 0, "Follow focused window (Not available on Wayland)", -1);
gtk_list_store_set(store, &iter, 1, "focused", -1);
} else {
gtk_list_store_append(store, &iter);
@@ -2698,9 +2667,15 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
}
}
- gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_capture_options.portal ? "Desktop portal (Experimental)" : "Desktop portal (Not supported on your system)", -1);
- gtk_list_store_set(store, &iter, 1, "portal", -1);
+ if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_capture_options.portal ? "Desktop portal (Experimental)" : "Desktop portal (Not available on your system)", -1);
+ gtk_list_store_set(store, &iter, 1, "portal", -1);
+ } else {
+ gtk_list_store_append(store, &iter);
+ gtk_list_store_set(store, &iter, 0, "Desktop portal (Not available on X11)", -1);
+ gtk_list_store_set(store, &iter, 1, "portal", -1);
+ }
record_area_selection_menu = GTK_COMBO_BOX(gtk_combo_box_new_with_model(record_area_selection_model));
@@ -2823,32 +2798,32 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
gtk_list_store_set(store, &iter, 1, "auto", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.h264 ? "H264" : "H264 (Not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.h264 ? "H264" : "H264 (Not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "h264", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.hevc ? "HEVC" : "HEVC (Not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.hevc ? "HEVC" : "HEVC (Not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "hevc", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.av1 ? "AV1" : "AV1 (Not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.av1 ? "AV1" : "AV1 (Not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "av1", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.vp8 ? "VP8" : "VP8 (Not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.vp8 ? "VP8" : "VP8 (Not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "vp8", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.vp9 ? "VP9" : "VP9 (Not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.vp9 ? "VP9" : "VP9 (Not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "vp9", -1);
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.hevc ? "HEVC (HDR)" : "HEVC (HDR, not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.hevc ? "HEVC (HDR)" : "HEVC (HDR, not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "hevc_hdr", -1);
gtk_list_store_append(store, &iter);
- gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.av1 ? "AV1 (HDR)" : "AV1 (HDR, not supported on your system)", -1);
+ gtk_list_store_set(store, &iter, 0, gsr_info.supported_video_codecs.av1 ? "AV1 (HDR)" : "AV1 (HDR, not available on your system)", -1);
gtk_list_store_set(store, &iter, 1, "av1_hdr", -1);
} else {
gtk_list_store_append(store, &iter);
@@ -2970,39 +2945,107 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
return GTK_WIDGET(grid);
}
-static void add_wayland_global_hotkeys_ui(GtkGrid *grid, int &row, int width) {
- GtkWidget *label = gtk_label_new("Hotkeys not supported because Wayland doesn't support global hotkeys");
- gtk_widget_set_hexpand(label, true);
- gtk_grid_attach(grid, label, 0, row, width - 1, 1);
+static void replace_meta_with_super(std::string &str) {
+ size_t index = str.find("meta");
+ if(index != std::string::npos)
+ str.replace(index, 4, "Super");
- GtkButton *question_button = GTK_BUTTON(gtk_button_new_with_label("?"));
- gtk_grid_attach(grid, GTK_WIDGET(question_button), width - 1, row, 1, 1);
+ index = str.find("Meta");
+ if(index != std::string::npos)
+ str.replace(index, 4, "Super");
+}
- row++;
+static void shortcut_changed_callback(gsr_shortcut shortcut, void *userdata) {
+ (void)userdata;
+ std::string trigger = shortcut.trigger_description;
+ replace_meta_with_super(trigger);
+ fprintf(stderr, "shortcut: %s, %s\n", shortcut.id, trigger.c_str());
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(strcmp(shortcut.id, hotkeys[i]->shortcut_id) == 0) {
+ gtk_entry_set_text(GTK_ENTRY(hotkeys[i]->hotkey_entry), trigger.c_str());
+ }
+ }
+}
- g_signal_connect(question_button, "clicked", G_CALLBACK(+[](GtkButton *button, gpointer userdata){
- (void)button;
- (void)userdata;
- GtkWidget *dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Wayland (desktop portal) doesn't support global hotkeys in any meaningful manner to most applications, including GPU Screen Recorder.\n"
- "If you want to use global hotkeys in GPU Screen Recorder then either use X11 or bind the following commands in your Wayland desktop environment hotkey settings to keys:\n"
- "Stop recording (saves video as well when not in replay mode):\n"
- " killall -SIGINT gpu-screen-recorder\n"
- "Save a replay:\n"
- " killall -SIGUSR1 gpu-screen-recorder\n"
- "Pause/unpause recording:\n"
- " killall -SIGUSR2 gpu-screen-recorder\n");
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
+static void deactivated_callback(const char *description, void *userdata) {
+ (void)userdata;
+ fprintf(stderr, "deactivated callback: %s, recording: %s\n", description, recording ? "yes" : "no");
+ const GtkWidget *visible_page = gtk_stack_get_visible_child(page_navigation_userdata.stack);
+ for(int i = 0; i < num_hotkeys; ++i) {
+ if(visible_page != hotkeys[i]->page)
+ continue;
- return true;
- }), nullptr);
+ if(strcmp(description, hotkeys[i]->shortcut_id) == 0 && *hotkeys[i]->state == hotkeys[i]->expected_state_to_trigger)
+ hotkeys[i]->trigger_handler(hotkeys[i]->associated_button, page_navigation_userdata.app);
+ }
+}
+
+static gboolean on_register_hotkeys_button_clicked(GtkButton *button, gpointer userdata) {
+ (void)button;
+ (void)userdata;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, width, 1);
+ /*
+ Modifier key names are defined here: https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-names.h.
+ Remove the XKB_MOD_NAME_ prefix from the name and use the remaining part.
+ Key names are defined here: https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h.
+ Remove the XKB_KEY_ (or XKB_KEY_KP_) prefix from the name and user the remaining part.
+ */
+ /* LOGO = Super key */
+ /* Unfortunately global shortcuts cant handle same key for different shortcuts, even though GPU Screen Recorder has page specific hotkeys */
+ const gsr_bind_shortcut shortcuts[5] = {
+ {
+ "Start recording/replay/streaming",
+ { SHORTCUT_ID_START_RECORDING, "LOGO+f1" }
+ },
+ {
+ "Stop recording/replaying/streaming",
+ { SHORTCUT_ID_STOP_RECORDING, "LOGO+f2" }
+ },
+ {
+ "Pause recording",
+ { SHORTCUT_ID_PAUSE_RECORDING, "LOGO+f5" }
+ },
+ {
+ "Unpause recording",
+ { SHORTCUT_ID_UNPAUSE_RECORDING, "LOGO+f6" }
+ },
+ {
+ "Save replay",
+ { SHORTCUT_ID_SAVE_REPLAY, "LOGO+f9" }
+ }
+ };
+
+ if(global_shortcuts_initialized) {
+ if(!gsr_global_shortcuts_bind_shortcuts(&global_shortcuts, shortcuts, 5, shortcut_changed_callback, NULL)) {
+ fprintf(stderr, "gsr error: failed to bind shortcuts\n");
+ }
+ }
+
+ return true;
+}
+
+static void add_wayland_global_hotkeys_ui(GtkGrid *grid, int &row, int width) {
+ GtkGrid *aa_grid = GTK_GRID(gtk_grid_new());
+ gtk_widget_set_halign(GTK_WIDGET(aa_grid), GTK_ALIGN_CENTER);
+ gtk_grid_attach(grid, GTK_WIDGET(aa_grid), 0, row++, width, 1);
+ gtk_grid_set_column_spacing(aa_grid, 10);
+
+ gtk_grid_attach(aa_grid, gtk_label_new("On Wayland hotkeys are managed externally by the Wayland compositor, click here to register hotkeys:"), 0, 0, 1, 1);
+
+ GtkButton *register_hotkeys_button = GTK_BUTTON(gtk_button_new_with_label("Register hotkeys"));
+ gtk_widget_set_hexpand(GTK_WIDGET(register_hotkeys_button), true);
+ //gtk_widget_set_halign(GTK_WIDGET(register_hotkeys_button), GTK_ALIGN_START);
+ g_signal_connect(register_hotkeys_button, "clicked", G_CALLBACK(on_register_hotkeys_button_clicked), nullptr);
+ gtk_grid_attach(aa_grid, GTK_WIDGET(register_hotkeys_button), 1, 0, 1, 1);
+
+ row++;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row, width, 1);
+ row++;
}
static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
int row = 0;
+ const int num_columns = 7;
std::string video_filepath = get_videos_dir();
@@ -3016,52 +3059,45 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
GtkWidget *hotkey_active_label = NULL;
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
- add_wayland_global_hotkeys_ui(grid, row, 5);
+ add_wayland_global_hotkeys_ui(grid, row, num_columns);
} else {
hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey, backspace to remove the hotkey or esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 5, 1);
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, num_columns, 1);
+ }
+
+ {
+ gtk_grid_attach(grid, gtk_label_new("Press"), 0, row, 1, 1);
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ replay_start_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(replay_start_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F1");
+ g_signal_connect(replay_start_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_start_hotkey_button);
+ gtk_grid_attach(grid, replay_start_hotkey_button, 1, row, 1, 1);
- GtkWidget *b = gtk_label_new("to start/stop the replay and");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ gtk_grid_attach(grid, gtk_label_new("to start the replay and"), 2, row, 1, 1);
- GtkWidget *c = gtk_label_new("to save");
- gtk_widget_set_halign(c, GTK_ALIGN_START);
+ replay_stop_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(replay_stop_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F2");
+ g_signal_connect(replay_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_stop_hotkey_button);
+ gtk_grid_attach(grid, replay_stop_hotkey_button, 3, row, 1, 1);
- replay_start_stop_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(replay_start_stop_hotkey_button), "Alt + F1");
- g_signal_connect(replay_start_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_start_stop_hotkey_button);
+ gtk_grid_attach(grid, gtk_label_new("to stop the replay and"), 4, row, 1, 1);
replay_save_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey_button), "Alt + F2");
- gtk_widget_set_halign(replay_save_hotkey_button, GTK_ALIGN_START);
+ gtk_entry_set_text(GTK_ENTRY(replay_save_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F2");
g_signal_connect(replay_save_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), replay_save_hotkey_button);
+ gtk_grid_attach(grid, replay_save_hotkey_button, 5, row, 1, 1);
+
+ gtk_grid_attach(grid, gtk_label_new("to save the replay"), 6, row, 1, 1);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, replay_start_stop_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
- gtk_grid_attach(grid, replay_save_hotkey_button, 3, row, 1, 1);
- gtk_grid_attach(grid, c, 4, row, 1, 1);
++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
}
- replay_start_stop_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
- replay_start_stop_hotkey.keysym = XK_F1;
- replay_start_stop_hotkey.hotkey_entry = replay_start_stop_hotkey_button;
- replay_start_stop_hotkey.hotkey_active_label = hotkey_active_label;
-
- replay_save_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
- replay_save_hotkey.keysym = XK_F2;
- replay_save_hotkey.hotkey_entry = replay_save_hotkey_button;
- replay_save_hotkey.hotkey_active_label = hotkey_active_label;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
GtkWidget *save_icon = gtk_image_new_from_icon_name("document-save", GTK_ICON_SIZE_BUTTON);
GtkGrid *file_chooser_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(file_chooser_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(file_chooser_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(file_chooser_grid, 10);
GtkWidget *file_chooser_label = gtk_label_new("Where do you want to save the replays?");
gtk_grid_attach(file_chooser_grid, file_chooser_label, 0, 0, 1, 1);
@@ -3074,7 +3110,7 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_attach(file_chooser_grid, GTK_WIDGET(replay_file_chooser_button), 1, 0, 1, 1);
GtkGrid *container_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, num_columns, 1);
gtk_grid_attach(container_grid, gtk_label_new("Container: "), 0, 0, 1, 1);
replay_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
for(auto &supported_container : supported_containers) {
@@ -3085,10 +3121,10 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
}
gtk_widget_set_hexpand(GTK_WIDGET(replay_container), true);
gtk_grid_attach(container_grid, GTK_WIDGET(replay_container), 1, 0, 1, 1);
- gtk_combo_box_set_active(GTK_COMBO_BOX(replay_container), 0); // TODO:
+ gtk_combo_box_set_active(GTK_COMBO_BOX(replay_container), 0);
GtkGrid *replay_time_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(replay_time_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(replay_time_grid), 0, row++, num_columns, 1);
gtk_grid_attach(replay_time_grid, gtk_label_new("Replay time in seconds: "), 0, 0, 1, 1);
replay_time_entry = GTK_SPIN_BUTTON(gtk_spin_button_new_with_range(5.0, 1200.0, 1.0));
gtk_spin_button_set_value(replay_time_entry, 30.0);
@@ -3096,7 +3132,7 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_attach(replay_time_grid, GTK_WIDGET(replay_time_entry), 1, 0, 1, 1);
GtkGrid *start_button_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(start_button_grid, 10);
replay_back_button = GTK_BUTTON(gtk_button_new_with_label("Back"));
@@ -3118,10 +3154,10 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
gtk_widget_set_sensitive(GTK_WIDGET(replay_save_button), false);
gtk_grid_attach(start_button_grid, GTK_WIDGET(replay_save_button), 2, 0, 1, 1);
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
replay_bottom_panel_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(replay_bottom_panel_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(replay_bottom_panel_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(replay_bottom_panel_grid, 5);
gtk_widget_set_opacity(GTK_WIDGET(replay_bottom_panel_grid), 0.5);
gtk_widget_set_halign(GTK_WIDGET(replay_bottom_panel_grid), GTK_ALIGN_END);
@@ -3134,11 +3170,48 @@ static GtkWidget* create_replay_page(GtkApplication *app, GtkStack *stack) {
gtk_widget_set_valign(replay_record_time_label, GTK_ALIGN_CENTER);
gtk_grid_attach(replay_bottom_panel_grid, replay_record_time_label, 1, 0, 1, 1);
+ replay_start_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ replay_start_hotkey.keysym = XK_F1;
+ replay_start_hotkey.hotkey_entry = replay_start_hotkey_button;
+ replay_start_hotkey.hotkey_active_label = hotkey_active_label;
+ replay_start_hotkey.config = &config.replay_config.start_recording_hotkey;
+ replay_start_hotkey.page = GTK_WIDGET(grid);
+ replay_start_hotkey.state = &replaying;
+ replay_start_hotkey.expected_state_to_trigger = false;
+ replay_start_hotkey.trigger_handler = on_start_replay_button_click;
+ replay_start_hotkey.associated_button = start_replay_button;
+ replay_start_hotkey.shortcut_id = SHORTCUT_ID_START_RECORDING;
+
+ replay_stop_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ replay_stop_hotkey.keysym = XK_F2;
+ replay_stop_hotkey.hotkey_entry = replay_stop_hotkey_button;
+ replay_stop_hotkey.hotkey_active_label = hotkey_active_label;
+ replay_stop_hotkey.config = &config.replay_config.stop_recording_hotkey;
+ replay_stop_hotkey.page = GTK_WIDGET(grid);
+ replay_stop_hotkey.state = &replaying;
+ replay_stop_hotkey.expected_state_to_trigger = true;
+ replay_stop_hotkey.trigger_handler = on_start_replay_button_click;
+ replay_stop_hotkey.associated_button = start_replay_button;
+ replay_stop_hotkey.shortcut_id = SHORTCUT_ID_STOP_RECORDING;
+
+ replay_save_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ replay_save_hotkey.keysym = XK_F3;
+ replay_save_hotkey.hotkey_entry = replay_save_hotkey_button;
+ replay_save_hotkey.hotkey_active_label = hotkey_active_label;
+ replay_save_hotkey.config = &config.replay_config.save_recording_hotkey;
+ replay_save_hotkey.page = GTK_WIDGET(grid);
+ replay_save_hotkey.state = &replaying;
+ replay_save_hotkey.expected_state_to_trigger = true;
+ replay_save_hotkey.trigger_handler = on_replay_save_button_click;
+ replay_save_hotkey.associated_button = replay_save_button;
+ replay_save_hotkey.shortcut_id = SHORTCUT_ID_SAVE_REPLAY;
+
return GTK_WIDGET(grid);
}
static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
int row = 0;
+ const int num_columns = 5;
std::string video_filepath = get_videos_dir();
@@ -3152,52 +3225,58 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
GtkWidget *hotkey_active_label = NULL;
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
- add_wayland_global_hotkeys_ui(grid, row, 5);
+ add_wayland_global_hotkeys_ui(grid, row, num_columns);
} else {
hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey, backspace to remove the hotkey or esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 5, 1);
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, num_columns, 1);
+ }
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ {
+ gtk_grid_attach(grid, gtk_label_new("Press"), 0, row, 1, 1);
- GtkWidget *b = gtk_label_new("to start/stop recording and");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ record_start_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(record_start_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F1");
+ g_signal_connect(record_start_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), record_start_hotkey_button);
+ gtk_grid_attach(grid, record_start_hotkey_button, 1, row, 1, 1);
- GtkWidget *c = gtk_label_new("to pause/unpause");
- gtk_widget_set_halign(c, GTK_ALIGN_START);
+ gtk_grid_attach(grid, gtk_label_new("to start recording and"), 2, row, 1, 1);
- record_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(record_hotkey_button), "Alt + F1");
- g_signal_connect(record_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), record_hotkey_button);
+ record_stop_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(record_stop_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F2");
+ g_signal_connect(record_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), record_stop_hotkey_button);
+ gtk_grid_attach(grid, record_stop_hotkey_button, 3, row, 1, 1);
- pause_unpause_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(pause_unpause_hotkey_button), "Alt + F2");
- gtk_widget_set_halign(pause_unpause_hotkey_button, GTK_ALIGN_START);
- g_signal_connect(pause_unpause_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), pause_unpause_hotkey_button);
+ gtk_grid_attach(grid, gtk_label_new("to stop recording"), 4, row, 1, 1);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, record_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
- gtk_grid_attach(grid, pause_unpause_hotkey_button, 3, row, 1, 1);
- gtk_grid_attach(grid, c, 4, row, 1, 1);
++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
}
- record_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
- record_hotkey.keysym = XK_F1;
- record_hotkey.hotkey_entry = record_hotkey_button;
- record_hotkey.hotkey_active_label = hotkey_active_label;
+ {
+ gtk_grid_attach(grid, gtk_label_new("Press"), 0, row, 1, 1);
+
+ pause_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(pause_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F5");
+ g_signal_connect(pause_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), pause_hotkey_button);
+ gtk_grid_attach(grid, pause_hotkey_button, 1, row, 1, 1);
- pause_unpause_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
- pause_unpause_hotkey.keysym = XK_F2;
- pause_unpause_hotkey.hotkey_entry = pause_unpause_hotkey_button;
- pause_unpause_hotkey.hotkey_active_label = hotkey_active_label;
+ gtk_grid_attach(grid, gtk_label_new("to pause recording and"), 2, row, 1, 1);
+
+ unpause_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(unpause_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F6");
+ g_signal_connect(unpause_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), unpause_hotkey_button);
+ gtk_grid_attach(grid, unpause_hotkey_button, 3, row, 1, 1);
+
+ gtk_grid_attach(grid, gtk_label_new("to unpause recording"), 4, row, 1, 1);
+
+ ++row;
+ }
+
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
GtkWidget *save_icon = gtk_image_new_from_icon_name("document-save", GTK_ICON_SIZE_BUTTON);
GtkGrid *file_chooser_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(file_chooser_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(file_chooser_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(file_chooser_grid, 10);
GtkWidget *file_chooser_label = gtk_label_new("Where do you want to save the video?");
gtk_grid_attach(file_chooser_grid, GTK_WIDGET(file_chooser_label), 0, 0, 1, 1);
@@ -3210,7 +3289,7 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_attach(file_chooser_grid, GTK_WIDGET(record_file_chooser_button), 1, 0, 1, 1);
GtkGrid *container_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(container_grid), 0, row++, num_columns, 1);
gtk_grid_attach(container_grid, gtk_label_new("Container: "), 0, 0, 1, 1);
record_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
for(auto &supported_container : supported_containers) {
@@ -3221,10 +3300,10 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
}
gtk_widget_set_hexpand(GTK_WIDGET(record_container), true);
gtk_grid_attach(container_grid, GTK_WIDGET(record_container), 1, 0, 1, 1);
- gtk_combo_box_set_active(GTK_COMBO_BOX(record_container), 0); // TODO:
+ gtk_combo_box_set_active(GTK_COMBO_BOX(record_container), 0);
GtkGrid *start_button_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(start_button_grid, 10);
record_back_button = GTK_BUTTON(gtk_button_new_with_label("Back"));
@@ -3246,10 +3325,10 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
gtk_widget_set_sensitive(GTK_WIDGET(pause_recording_button), false);
gtk_grid_attach(start_button_grid, GTK_WIDGET(pause_recording_button), 2, 0, 1, 1);
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 5, 1);
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
recording_bottom_panel_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(recording_bottom_panel_grid), 0, row++, 5, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(recording_bottom_panel_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(recording_bottom_panel_grid, 5);
gtk_widget_set_opacity(GTK_WIDGET(recording_bottom_panel_grid), 0.5);
gtk_widget_set_halign(GTK_WIDGET(recording_bottom_panel_grid), GTK_ALIGN_END);
@@ -3262,11 +3341,60 @@ static GtkWidget* create_recording_page(GtkApplication *app, GtkStack *stack) {
gtk_widget_set_valign(recording_record_time_label, GTK_ALIGN_CENTER);
gtk_grid_attach(recording_bottom_panel_grid, recording_record_time_label, 1, 0, 1, 1);
+ record_start_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ record_start_hotkey.keysym = XK_F1;
+ record_start_hotkey.hotkey_entry = record_start_hotkey_button;
+ record_start_hotkey.hotkey_active_label = hotkey_active_label;
+ record_start_hotkey.config = &config.record_config.start_recording_hotkey;
+ record_start_hotkey.page = GTK_WIDGET(grid);
+ record_start_hotkey.state = &recording;
+ record_start_hotkey.expected_state_to_trigger = false;
+ record_start_hotkey.trigger_handler = on_start_recording_button_click;
+ record_start_hotkey.associated_button = start_recording_button;
+ record_start_hotkey.shortcut_id = SHORTCUT_ID_START_RECORDING;
+
+ record_stop_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ record_stop_hotkey.keysym = XK_F2;
+ record_stop_hotkey.hotkey_entry = record_stop_hotkey_button;
+ record_stop_hotkey.hotkey_active_label = hotkey_active_label;
+ record_stop_hotkey.config = &config.record_config.stop_recording_hotkey;
+ record_stop_hotkey.page = GTK_WIDGET(grid);
+ record_stop_hotkey.state = &recording;
+ record_stop_hotkey.expected_state_to_trigger = true;
+ record_stop_hotkey.trigger_handler = on_start_recording_button_click;
+ record_stop_hotkey.associated_button = start_recording_button;
+ record_stop_hotkey.shortcut_id = SHORTCUT_ID_STOP_RECORDING;
+
+ pause_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ pause_hotkey.keysym = XK_F5;
+ pause_hotkey.hotkey_entry = pause_hotkey_button;
+ pause_hotkey.hotkey_active_label = hotkey_active_label;
+ pause_hotkey.config = &config.record_config.pause_recording_hotkey;
+ pause_hotkey.page = GTK_WIDGET(grid);
+ pause_hotkey.state = &paused;
+ pause_hotkey.expected_state_to_trigger = false;
+ pause_hotkey.trigger_handler = on_pause_unpause_button_click;
+ pause_hotkey.associated_button = pause_recording_button;
+ pause_hotkey.shortcut_id = SHORTCUT_ID_PAUSE_RECORDING;
+
+ unpause_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ unpause_hotkey.keysym = XK_F6;
+ unpause_hotkey.hotkey_entry = unpause_hotkey_button;
+ unpause_hotkey.hotkey_active_label = hotkey_active_label;
+ unpause_hotkey.config = &config.record_config.unpause_recording_hotkey;
+ unpause_hotkey.page = GTK_WIDGET(grid);
+ unpause_hotkey.state = &paused;
+ unpause_hotkey.expected_state_to_trigger = true;
+ unpause_hotkey.trigger_handler = on_pause_unpause_button_click;
+ unpause_hotkey.associated_button = pause_recording_button;
+ unpause_hotkey.shortcut_id = SHORTCUT_ID_UNPAUSE_RECORDING;
+
return GTK_WIDGET(grid);
}
static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
int row = 0;
+ const int num_columns = 5;
GtkGrid *grid = GTK_GRID(gtk_grid_new());
gtk_stack_add_named(stack, GTK_WIDGET(grid), "streaming");
@@ -3278,34 +3406,36 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
GtkWidget *hotkey_active_label = NULL;
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
- add_wayland_global_hotkeys_ui(grid, row, 3);
+ add_wayland_global_hotkeys_ui(grid, row, num_columns);
} else {
hotkey_active_label = gtk_label_new("Press a key combination to set a new hotkey, backspace to remove the hotkey or esc to cancel");
- gtk_grid_attach(grid, hotkey_active_label, 0, row++, 3, 1);
+ gtk_grid_attach(grid, hotkey_active_label, 0, row++, num_columns, 1);
+ }
+
+ {
+ gtk_grid_attach(grid, gtk_label_new("Press"), 0, row, 1, 1);
+
+ streaming_start_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(streaming_start_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F1");
+ g_signal_connect(streaming_start_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), streaming_start_hotkey_button);
+ gtk_grid_attach(grid, streaming_start_hotkey_button, 1, row, 1, 1);
+
+ gtk_grid_attach(grid, gtk_label_new("to start streaming and"), 2, row, 1, 1);
- GtkWidget *a = gtk_label_new("Press");
- gtk_widget_set_halign(a, GTK_ALIGN_END);
+ streaming_stop_hotkey_button = gtk_entry_new();
+ gtk_entry_set_text(GTK_ENTRY(streaming_stop_hotkey_button), gsr_info.system_info.display_server == DisplayServer::WAYLAND ? "" : "Super + F2");
+ g_signal_connect(streaming_stop_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), streaming_stop_hotkey_button);
+ gtk_grid_attach(grid, streaming_stop_hotkey_button, 3, row, 1, 1);
- GtkWidget *b = gtk_label_new("to start/stop streaming");
- gtk_widget_set_halign(b, GTK_ALIGN_START);
+ gtk_grid_attach(grid, gtk_label_new("to stop streaming"), 4, row, 1, 1);
- streaming_hotkey_button = gtk_entry_new();
- gtk_entry_set_text(GTK_ENTRY(streaming_hotkey_button), "Alt + F1");
- g_signal_connect(streaming_hotkey_button, "button-press-event", G_CALLBACK(on_hotkey_entry_click), streaming_hotkey_button);
- gtk_grid_attach(grid, a, 0, row, 1, 1);
- gtk_grid_attach(grid, streaming_hotkey_button, 1, row, 1, 1);
- gtk_grid_attach(grid, b, 2, row, 1, 1);
++row;
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
}
- streaming_hotkey.modkey_mask = modkey_to_mask(XK_Alt_L);
- streaming_hotkey.keysym = XK_F1;
- streaming_hotkey.hotkey_entry = streaming_hotkey_button;
- streaming_hotkey.hotkey_active_label = hotkey_active_label;
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
GtkGrid *stream_service_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(stream_service_grid), 0, row++, 3, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(stream_service_grid), 0, row++, num_columns, 1);
gtk_grid_attach(stream_service_grid, gtk_label_new("Stream service: "), 0, 0, 1, 1);
stream_service_input_menu = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
gtk_combo_box_text_append(stream_service_input_menu, "twitch", "Twitch");
@@ -3317,7 +3447,7 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
gtk_grid_attach(stream_service_grid, GTK_WIDGET(stream_service_input_menu), 1, 0, 1, 1);
GtkGrid *stream_id_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(stream_id_grid), 0, row++, 3, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(stream_id_grid), 0, row++, num_columns, 1);
stream_key_label = GTK_LABEL(gtk_label_new("Stream key: "));
gtk_grid_attach(stream_id_grid, GTK_WIDGET(stream_key_label), 0, 0, 1, 1);
@@ -3335,7 +3465,7 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
}
custom_stream_container_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(custom_stream_container_grid), 0, row++, 3, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(custom_stream_container_grid), 0, row++, num_columns, 1);
gtk_grid_attach(custom_stream_container_grid, gtk_label_new("Container: "), 0, 0, 1, 1);
custom_stream_container = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new());
for(auto &supported_container : supported_containers) {
@@ -3349,7 +3479,7 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
gtk_combo_box_set_active(GTK_COMBO_BOX(custom_stream_container), 1);
GtkGrid *start_button_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, 3, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(start_button_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(start_button_grid, 10);
stream_back_button = GTK_BUTTON(gtk_button_new_with_label("Back"));
@@ -3364,10 +3494,10 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
g_signal_connect(start_streaming_button, "clicked", G_CALLBACK(on_start_streaming_button_click), app);
gtk_grid_attach(start_button_grid, GTK_WIDGET(start_streaming_button), 1, 0, 1, 1);
- gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, 3, 1);
+ gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row++, num_columns, 1);
streaming_bottom_panel_grid = GTK_GRID(gtk_grid_new());
- gtk_grid_attach(grid, GTK_WIDGET(streaming_bottom_panel_grid), 0, row++, 3, 1);
+ gtk_grid_attach(grid, GTK_WIDGET(streaming_bottom_panel_grid), 0, row++, num_columns, 1);
gtk_grid_set_column_spacing(streaming_bottom_panel_grid, 5);
gtk_widget_set_opacity(GTK_WIDGET(streaming_bottom_panel_grid), 0.5);
gtk_widget_set_halign(GTK_WIDGET(streaming_bottom_panel_grid), GTK_ALIGN_END);
@@ -3380,6 +3510,30 @@ static GtkWidget* create_streaming_page(GtkApplication *app, GtkStack *stack) {
gtk_widget_set_valign(streaming_record_time_label, GTK_ALIGN_CENTER);
gtk_grid_attach(streaming_bottom_panel_grid, streaming_record_time_label, 1, 0, 1, 1);
+ streaming_start_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ streaming_start_hotkey.keysym = XK_F1;
+ streaming_start_hotkey.hotkey_entry = streaming_start_hotkey_button;
+ streaming_start_hotkey.hotkey_active_label = hotkey_active_label;
+ streaming_start_hotkey.config = &config.streaming_config.start_recording_hotkey;
+ streaming_start_hotkey.page = GTK_WIDGET(grid);
+ streaming_start_hotkey.state = &streaming;
+ streaming_start_hotkey.expected_state_to_trigger = false;
+ streaming_start_hotkey.trigger_handler = on_start_streaming_button_click;
+ streaming_start_hotkey.associated_button = start_streaming_button;
+ streaming_start_hotkey.shortcut_id = SHORTCUT_ID_START_RECORDING;
+
+ streaming_stop_hotkey.modkey_mask = modkey_to_mask(XK_Super_L);
+ streaming_stop_hotkey.keysym = XK_F2;
+ streaming_stop_hotkey.hotkey_entry = streaming_stop_hotkey_button;
+ streaming_stop_hotkey.hotkey_active_label = hotkey_active_label;
+ streaming_stop_hotkey.config = &config.streaming_config.stop_recording_hotkey;
+ streaming_stop_hotkey.page = GTK_WIDGET(grid);
+ streaming_stop_hotkey.state = &streaming;
+ streaming_stop_hotkey.expected_state_to_trigger = true;
+ streaming_stop_hotkey.trigger_handler = on_start_streaming_button_click;
+ streaming_stop_hotkey.associated_button = start_streaming_button;
+ streaming_stop_hotkey.shortcut_id = SHORTCUT_ID_STOP_RECORDING;
+
return GTK_WIDGET(grid);
}
@@ -3494,7 +3648,7 @@ static void load_config() {
//
} else if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && gsr_info.gpu_info.vendor == GpuVendor::NVIDIA && strcmp(config.main_config.record_area_option.c_str(), "screen") == 0) {
//
- } else if(config.main_config.record_area_option == "portal" && gsr_info.supported_capture_options.portal) {
+ } else if(config.main_config.record_area_option == "portal" && gsr_info.supported_capture_options.portal && gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
//
} else {
bool found_monitor = false;
@@ -3595,48 +3749,29 @@ static void load_config() {
gtk_entry_set_text(twitch_stream_id_entry, config.streaming_config.twitch.stream_key.c_str());
gtk_entry_set_text(custom_stream_url_entry, config.streaming_config.custom.url.c_str());
gtk_combo_box_set_active_id(GTK_COMBO_BOX(custom_stream_container), config.streaming_config.custom.container.c_str());
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && streaming_hotkey_button && !config_empty) {
- streaming_hotkey.keysym = config.streaming_config.start_recording_hotkey.keysym;
- streaming_hotkey.modkey_mask = config.streaming_config.start_recording_hotkey.modifiers;
- set_hotkey_text_from_hotkey_data(GTK_ENTRY(streaming_hotkey_button), streaming_hotkey);
- }
gtk_button_set_label(record_file_chooser_button, config.record_config.save_directory.c_str());
gtk_combo_box_set_active_id(GTK_COMBO_BOX(record_container), config.record_config.container.c_str());
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && record_hotkey_button && !config_empty) {
- record_hotkey.keysym = config.record_config.start_recording_hotkey.keysym;
- record_hotkey.modkey_mask = config.record_config.start_recording_hotkey.modifiers;
- set_hotkey_text_from_hotkey_data(GTK_ENTRY(record_hotkey_button), record_hotkey);
- }
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && pause_unpause_hotkey_button && !config_empty) {
- pause_unpause_hotkey.keysym = config.record_config.pause_recording_hotkey.keysym;
- pause_unpause_hotkey.modkey_mask = config.record_config.pause_recording_hotkey.modifiers;
- set_hotkey_text_from_hotkey_data(GTK_ENTRY(pause_unpause_hotkey_button), pause_unpause_hotkey);
- }
gtk_button_set_label(replay_file_chooser_button, config.replay_config.save_directory.c_str());
gtk_combo_box_set_active_id(GTK_COMBO_BOX(replay_container), config.replay_config.container.c_str());
gtk_spin_button_set_value(replay_time_entry, config.replay_config.replay_time);
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && replay_start_stop_hotkey_button && !config_empty) {
- replay_start_stop_hotkey.keysym = config.replay_config.start_recording_hotkey.keysym;
- replay_start_stop_hotkey.modkey_mask = config.replay_config.start_recording_hotkey.modifiers;
- set_hotkey_text_from_hotkey_data(GTK_ENTRY(replay_start_stop_hotkey_button), replay_start_stop_hotkey);
- }
- if(gsr_info.system_info.display_server != DisplayServer::WAYLAND && replay_save_hotkey_button && !config_empty) {
- replay_save_hotkey.keysym = config.replay_config.save_recording_hotkey.keysym;
- replay_save_hotkey.modkey_mask = config.replay_config.save_recording_hotkey.modifiers;
- set_hotkey_text_from_hotkey_data(GTK_ENTRY(replay_save_hotkey_button), replay_save_hotkey);
- }
gtk_combo_box_set_active_id(GTK_COMBO_BOX(view_combo_box), config.main_config.advanced_view ? "advanced" : "simple");
view_combo_box_change_callback(GTK_COMBO_BOX(view_combo_box), view_combo_box);
if(gsr_info.system_info.display_server != DisplayServer::WAYLAND) {
- gtk_widget_set_visible(record_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(pause_unpause_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(streaming_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(replay_start_stop_hotkey.hotkey_active_label, false);
- gtk_widget_set_visible(replay_save_hotkey.hotkey_active_label, false);
+ if(!config_empty) {
+ for(int i = 0; i < num_hotkeys; ++i) {
+ hotkeys[i]->keysym = hotkeys[i]->config->keysym;
+ hotkeys[i]->modkey_mask = hotkeys[i]->config->modifiers;
+ set_hotkey_text_from_hotkey_data(GTK_ENTRY(hotkeys[i]->hotkey_entry), *hotkeys[i]);
+ }
+ }
+
+ gtk_widget_set_visible(record_start_hotkey.hotkey_active_label, false);
+ gtk_widget_set_visible(streaming_start_hotkey.hotkey_active_label, false);
+ gtk_widget_set_visible(replay_start_hotkey.hotkey_active_label, false);
}
enable_stream_record_button_if_info_filled();
stream_service_item_change_callback(GTK_COMBO_BOX(stream_service_input_menu), nullptr);
@@ -3655,6 +3790,14 @@ static void load_config() {
}
}
+static void init_shortcuts_callback(void *userdata) {
+ (void)userdata;
+ if(!gsr_global_shortcuts_list_shortcuts(&global_shortcuts, shortcut_changed_callback, NULL)) {
+ fprintf(stderr, "gsr error: failed to list shortcuts\n");
+ }
+ gsr_global_shortcuts_subscribe_activated_signal(&global_shortcuts, deactivated_callback, shortcut_changed_callback, NULL);
+}
+
static const char* gpu_vendor_to_name(GpuVendor vendor) {
switch(vendor) {
case GpuVendor::AMD: return "AMD";
@@ -3799,6 +3942,13 @@ static void activate(GtkApplication *app, gpointer) {
gtk_widget_show_all(window);
load_config();
+
+ if(gsr_global_shortcuts_init(&global_shortcuts, init_shortcuts_callback, NULL)) {
+ global_shortcuts_initialized = true;
+ } else {
+ fprintf(stderr, "gsr error: failed to initialize global shortcuts\n");
+ global_shortcuts_initialized = false;
+ }
}
int main(int argc, char **argv) {