aboutsummaryrefslogtreecommitdiff
path: root/src/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.cpp')
-rw-r--r--src/main.cpp446
1 files changed, 238 insertions, 208 deletions
diff --git a/src/main.cpp b/src/main.cpp
index f515e2f..91f7c3d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -20,7 +20,7 @@ extern "C" {
#include <vector>
#include <libayatana-appindicator/app-indicator.h>
-#define GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION 4
+#define GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION 6
#ifndef GSR_VERSION
#define GSR_VERSION "unknown"
@@ -99,7 +99,7 @@ static GtkWidget *replay_start_stop_hotkey_button;
static GtkWidget *replay_save_hotkey_button;
static GtkWidget *streaming_start_stop_hotkey_button;
static GtkWidget *record_app_audio_inverted_button;
-static GtkWidget *merge_audio_tracks_button;
+static GtkWidget *split_audio_button;
static GtkFrame *notifications_frame;
static GtkWidget *show_recording_started_notification_button;
static GtkWidget *show_recording_stopped_notification_button;
@@ -169,6 +169,7 @@ static AppIndicator *app_indicator;
static gsr_global_shortcuts global_shortcuts;
static bool global_shortcuts_initialized = false;
+static bool global_shortcuts_received = false;
struct AudioInput {
std::string name;
@@ -264,7 +265,8 @@ enum class GpuVendor {
UNKNOWN,
AMD,
INTEL,
- NVIDIA
+ NVIDIA,
+ BROADCOM
};
struct GpuInfo {
@@ -899,7 +901,7 @@ static void save_configs() {
config.main_config.video_height = gtk_spin_button_get_value_as_int(video_height_entry);
config.main_config.fps = gtk_spin_button_get_value_as_int(fps_entry);
config.main_config.video_bitrate = gtk_spin_button_get_value_as_int(video_bitrate_entry);
- config.main_config.merge_audio_tracks = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button));
+ config.main_config.merge_audio_tracks = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(split_audio_button));
config.main_config.record_app_audio_inverted = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button));
config.main_config.change_video_resolution = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(change_video_resolution_button));
@@ -1399,6 +1401,9 @@ static bool is_monitor_capture_drm() {
}
static bool show_pkexec_flatpak_error_if_needed() {
+ if(!flatpak)
+ return false;
+
const std::string window_str = record_area_selection_menu_get_active_id();
if(is_monitor_capture_drm() && window_str != "window" && window_str != "focused" && window_str != "portal") {
if(!is_pkexec_installed()) {
@@ -1409,7 +1414,7 @@ static bool show_pkexec_flatpak_error_if_needed() {
return true;
}
- if(flatpak && !flatpak_is_installed_as_system()) {
+ if(!flatpak_is_installed_as_system()) {
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"GPU Screen Recorder needs to be installed system-wide to record your monitor on Wayland when not using the portal option. You can run this command to install GPU Screen recorder system-wide:\n"
@@ -1456,11 +1461,80 @@ static void show_bugged_driver_warning() {
}
}
+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");
+
+ index = str.find("Meta");
+ if(index != std::string::npos)
+ str.replace(index, 4, "Super");
+}
+
+static void shortcut_changed_callback(gsr_shortcut shortcut, void *userdata) {
+ (void)userdata;
+ global_shortcuts_received = true;
+ std::string trigger = shortcut.trigger_description;
+ replace_meta_with_super(trigger);
+ 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());
+ }
+ }
+}
+
+static gboolean on_register_hotkeys_button_clicked(GtkButton *button, gpointer userdata) {
+ (void)button;
+ (void)userdata;
+
+ /*
+ 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.
+ */
+ /* Unfortunately global shortcuts cant handle same key for different shortcuts, even though GPU Screen Recorder has page specific hotkeys */
+ const gsr_bind_shortcut shortcuts[3] = {
+ {
+ "Start/stop recording/replay/streaming",
+ { SHORTCUT_ID_START_STOP_RECORDING, "ALT+1" }
+ },
+ {
+ "Pause/unpause recording",
+ { SHORTCUT_ID_PAUSE_UNPAUSE_RECORDING, "ALT+2" }
+ },
+ {
+ "Save replay",
+ { SHORTCUT_ID_SAVE_REPLAY, "ALT+3" }
+ }
+ };
+
+ if(global_shortcuts_initialized) {
+ if(!gsr_global_shortcuts_bind_shortcuts(&global_shortcuts, shortcuts, 3, shortcut_changed_callback, NULL)) {
+ fprintf(stderr, "gsr error: failed to bind shortcuts\n");
+ }
+ }
+
+ return true;
+}
+
+static void register_global_shortcuts_once() {
+ static bool registered = false;
+ // On KDE plasma the shortcut menu popup will show up everytime this is used, so we dont want to call it everytime.
+ // On Hyprland the global shortcut desktop portal is broken on older versions and crashes the desktop portal.
+ // On GNOME this needs to be called everytime to register the shortcuts. The shortcut popup menu will show the first time only.
+ if(wayland_compositor == WaylandCompositor::UNKNOWN && !registered && !global_shortcuts_received) {
+ registered = true;
+ on_register_hotkeys_button_clicked(nullptr, nullptr);
+ }
+}
+
static gboolean on_start_replay_click(GtkButton*, gpointer userdata) {
if(show_pkexec_flatpak_error_if_needed())
return true;
show_bugged_driver_warning();
+ register_global_shortcuts_once();
PageNavigationUserdata *_page_navigation_userdata = (PageNavigationUserdata*)userdata;
gtk_stack_set_visible_child(_page_navigation_userdata->stack, _page_navigation_userdata->replay_page);
@@ -1477,6 +1551,7 @@ static gboolean on_start_recording_click(GtkButton*, gpointer userdata) {
return true;
show_bugged_driver_warning();
+ register_global_shortcuts_once();
PageNavigationUserdata *_page_navigation_userdata = (PageNavigationUserdata*)userdata;
gtk_stack_set_visible_child(_page_navigation_userdata->stack, _page_navigation_userdata->recording_page);
@@ -1499,6 +1574,7 @@ static gboolean on_start_streaming_click(GtkButton*, gpointer userdata) {
return true;
show_bugged_driver_warning();
+ register_global_shortcuts_once();
int num_audio_tracks = 0;
gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), [](GtkWidget *widget, gpointer userdata) {
@@ -1508,7 +1584,7 @@ static gboolean on_start_streaming_click(GtkButton*, gpointer userdata) {
++num_audio_tracks;
}, &num_audio_tracks);
- if(num_audio_tracks > 1 && !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button))) {
+ if(num_audio_tracks > 1 && gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(split_audio_button))) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
"Streaming doesn't work with more than 1 audio track. Please remove all audio tracks or only use 1 audio track or select to merge audio tracks.");
gtk_dialog_run(GTK_DIALOG(dialog));
@@ -1603,13 +1679,15 @@ static bool starts_with(const std::string &str, const char *substr) {
struct AudioTracksUserdata {
std::vector<std::string> &result;
bool application_audio_invert;
+ int num_app_audio = 0;
};
static std::vector<std::string> create_audio_tracks_real_names(std::string &merge_audio_tracks) {
std::vector<std::string> result;
AudioTracksUserdata audio_tracks_userdata {
result,
- (bool)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button))
+ (bool)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button)),
+ 0
};
gtk_container_foreach(GTK_CONTAINER(audio_devices_items_box), [](GtkWidget *widget, gpointer userdata) {
@@ -1627,6 +1705,7 @@ static std::vector<std::string> create_audio_tracks_real_names(std::string &merg
std::string audio_input_name = audio_tracks_userdata.application_audio_invert ? "app-inverse:" : "app:";
audio_input_name += gtk_combo_box_get_active_id(GTK_COMBO_BOX(row_item_widget));
audio_tracks_userdata.result.push_back(std::move(audio_input_name));
+ ++audio_tracks_userdata.num_app_audio;
} else if(strcmp(audio_track_type, "app-custom") == 0) {
if(!gsr_info.system_info.supports_app_audio)
return;
@@ -1634,9 +1713,13 @@ static std::vector<std::string> create_audio_tracks_real_names(std::string &merg
std::string audio_input_name = audio_tracks_userdata.application_audio_invert ? "app-inverse:" : "app:";
audio_input_name += gtk_entry_get_text(GTK_ENTRY(row_item_widget));
audio_tracks_userdata.result.push_back(std::move(audio_input_name));
+ ++audio_tracks_userdata.num_app_audio;
}
}, &audio_tracks_userdata);
+ if(audio_tracks_userdata.num_app_audio == 0 && audio_tracks_userdata.application_audio_invert)
+ audio_tracks_userdata.result.push_back("app-inverse:");
+
merge_audio_tracks.clear();
for(size_t i = 0; i < result.size(); ++i) {
if(i > 0)
@@ -1648,7 +1731,7 @@ static std::vector<std::string> create_audio_tracks_real_names(std::string &merg
}
static void add_audio_command_line_args(std::vector<const char*> &args, const std::vector<std::string> &audio_tracks, const std::string &merge_audio_tracks) {
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button))) {
+ if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(split_audio_button))) {
if(!merge_audio_tracks.empty())
args.insert(args.end(), { "-a", merge_audio_tracks.c_str() });
} else {
@@ -1711,6 +1794,16 @@ static void debug_print_args(const char **args) {
fprintf(stderr, "\n");
}
+static bool validate_window(GtkApplication *app, Window window) {
+ XWindowAttributes attr;
+ if(XGetWindowAttributes(dpy, window, &attr)) {
+ return true;
+ } else {
+ show_notification(app, "GPU Screen Recorder", "The window you are trying to record no longer exists", G_NOTIFICATION_PRIORITY_URGENT);
+ return false;
+ }
+}
+
static gboolean on_start_replay_button_click(GtkButton *button, gpointer userdata) {
GtkApplication *app = (GtkApplication*)userdata;
const gchar *dir = gtk_button_get_label(replay_file_chooser_button);
@@ -1734,8 +1827,7 @@ static gboolean on_start_replay_button_click(GtkButton *button, gpointer userdat
app_indicator_set_icon_full(app_indicator, get_tray_idle_icon_name(), "Idle");
if(exit_status == 10) {
- show_notification(app, "GPU Screen Recorder",
- "You need to have pkexec installed and a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
+ show_notification(app, "GPU Screen Recorder", "You need to have pkexec installed and have a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 50) {
show_notification(app, "GPU Screen Recorder", "Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 60) {
@@ -1772,6 +1864,8 @@ static gboolean on_start_replay_button_click(GtkButton *button, gpointer userdat
return true;
}
window_str = std::to_string(select_window_userdata.selected_window);
+ if(!validate_window(app, select_window_userdata.selected_window))
+ return true;
} else if(window_str == "focused") {
follow_focused = true;
}
@@ -1939,8 +2033,7 @@ static gboolean on_start_recording_button_click(GtkButton *button, gpointer user
app_indicator_set_icon_full(app_indicator, get_tray_idle_icon_name(), "Idle");
if(exit_status == 10) {
- show_notification(app, "GPU Screen Recorder",
- "You need to have pkexec installed and a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
+ show_notification(app, "GPU Screen Recorder", "You need to have pkexec installed and have a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 50) {
show_notification(app, "GPU Screen Recorder", "Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 60) {
@@ -1968,6 +2061,8 @@ static gboolean on_start_recording_button_click(GtkButton *button, gpointer user
return true;
}
window_str = std::to_string(select_window_userdata.selected_window);
+ if(!validate_window(app, select_window_userdata.selected_window))
+ return true;
} else if(window_str == "focused") {
follow_focused = true;
}
@@ -2102,8 +2197,7 @@ static gboolean on_start_streaming_button_click(GtkButton *button, gpointer user
app_indicator_set_icon_full(app_indicator, get_tray_idle_icon_name(), "Idle");
if(exit_status == 10) {
- show_notification(app, "GPU Screen Recorder",
- "You need to have pkexec installed and a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
+ show_notification(app, "GPU Screen Recorder", "You need to have pkexec installed and have a polkit agent running to record your monitor", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 50) {
show_notification(app, "GPU Screen Recorder", "Desktop portal capture failed. Either you canceled the desktop portal or your Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system", G_NOTIFICATION_PRIORITY_URGENT);
} else if(exit_status == 60) {
@@ -2130,6 +2224,8 @@ static gboolean on_start_streaming_button_click(GtkButton *button, gpointer user
return true;
}
window_str = std::to_string(select_window_userdata.selected_window);
+ if(!validate_window(app, select_window_userdata.selected_window))
+ return true;
} else if(window_str == "focused") {
follow_focused = true;
}
@@ -2310,6 +2406,7 @@ static void view_combo_box_change_callback(GtkComboBox *widget, gpointer userdat
gtk_widget_set_visible(GTK_WIDGET(framerate_mode_grid), advanced_view);
gtk_widget_set_visible(GTK_WIDGET(overclock_grid), advanced_view && gsr_info.gpu_info.vendor == GpuVendor::NVIDIA && gsr_info.system_info.display_server != DisplayServer::WAYLAND);
gtk_widget_set_visible(GTK_WIDGET(notifications_frame), advanced_view);
+ gtk_widget_set_visible(GTK_WIDGET(split_audio_button), advanced_view);
}
static void quality_combo_box_change_callback(GtkComboBox *widget, gpointer userdata) {
@@ -2570,6 +2667,8 @@ static void parse_gpu_info_line(GsrInfo *_gsr_info, const std::string &line) {
_gsr_info->gpu_info.vendor = GpuVendor::INTEL;
else if(attribute_value == "nvidia")
_gsr_info->gpu_info.vendor = GpuVendor::NVIDIA;
+ else if(attribute_value == "broadcom")
+ _gsr_info->gpu_info.vendor = GpuVendor::BROADCOM;
}
}
@@ -2615,7 +2714,7 @@ static void parse_capture_options_line(GsrInfo *_gsr_info, const std::string &li
_gsr_info->supported_capture_options.focused = true;
else if(line == "portal")
_gsr_info->supported_capture_options.portal = true;
- else
+ else if(line != "region") // We dont support region capture in the gtk application
_gsr_info->supported_capture_options.monitors.push_back(capture_option_line_to_monitor(line));
}
@@ -2722,8 +2821,8 @@ static void video_codec_set_sensitive(GtkCellLayout *cell_layout, GtkCellRendere
g_free(id);
}
-static void launch_gsr_ui(bool show_ui) {
- const char *args[] = { "gsr-ui", show_ui ? "launch-show" : "launch-hide", nullptr };
+static void launch_gsr_ui(bool launched_by_daemon) {
+ const char *args[] = { "gsr-ui", launched_by_daemon ? "launch-daemon" : "launch-show", nullptr };
execvp(args[0], (char* const*)args);
// TODO: This is incorrect because window wont be defined here if this is called from startup.
// This is fine for not because this is only called inside the flatpak where gsr-ui is always available.
@@ -2734,6 +2833,39 @@ static void launch_gsr_ui(bool show_ui) {
gtk_widget_destroy(dialog);
}
+static bool kms_server_proxy_setup_gsr_ui(const char *msg) {
+ GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", msg);
+ const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ switch(response) {
+ case GTK_RESPONSE_YES:
+ break;
+ case GTK_RESPONSE_NO:
+ default: {
+ config.main_config.use_new_ui = false;
+ save_config(config);
+ return false;
+ }
+ }
+
+ const int exit_code = system("flatpak-spawn --host -- /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy setup-gsr-ui");
+ if(exit_code != 0) {
+ GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Failed to setup the new UI. You either cancelled the installation or you don't have pkexec installed and a polkit agent running.");
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
+
+ config.main_config.use_new_ui = false;
+ save_configs();
+ return false;
+ }
+
+ config.main_config.use_new_ui = true;
+ config.main_config.installed_gsr_global_hotkeys_version = GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION;
+ save_config(config);
+ return true;
+}
+
static gboolean on_click_switch_to_new_ui(GtkButton*, gpointer) {
if(!dpy) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -2764,10 +2896,14 @@ static gboolean on_click_switch_to_new_ui(GtkButton*, gpointer) {
GtkWidget *dialog = gtk_message_dialog_new_with_markup(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
"You are about to try out the new UI, which is a ShadowPlay-like fullscreen UI. It runs in the background and you have to show/hide it by pressing Left Alt+Z.\n"
- "This new UI is still experimental and you may experience issues depending on your system. You can switch back to the old UI at any time by opening the UI and clicking on the settings button and clicking on the \"Go back to the old UI\" button.\n"
+ "This new UI is still experimental and you may experience issues depending on your system, especially on Wayland since Wayland doesn't support this software properly.\n"
+ "You can switch back to the old UI at any time by opening the UI and clicking on the settings button and clicking on the \"Go back to the old UI\" button.\n"
"\n"
"This new UI comes with new features, such as being able to automatically launch it on system startup by enabling it in settings, and hotkey support on any Wayland compositor.\n"
"\n"
+ "If you are using keyboard remapping software such as keyd then make sure it ignores \"gsr-ui virtual keyboard\" (dec0:5eba device id), or it will prevent you from using your keyboard.\n"
+ "You can go back to the old UI by pressing (left) ctrl+shift+alt+esc if this happens.\n"
+ "\n"
"If you are using an NVIDIA GPU then you may experience issue with recording/replay if a suspend happens while recording/using replay. This is an NVIDIA driver issue and it also happens in the old UI.\n"
"See this for a workaround: <a href=\"https://wiki.archlinux.org/title/NVIDIA/Tips_and_tricks#Preserve_video_memory_after_suspend\">Arch Wiki - Preserve video memory after suspend</a>.\n"
"\n"
@@ -2783,40 +2919,18 @@ static gboolean on_click_switch_to_new_ui(GtkButton*, gpointer) {
return true;
}
- dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
+ const bool kms_server_setup_finished = kms_server_proxy_setup_gsr_ui(
"The new UI needs root privileges to finish setup to make global hotkeys and recording work on any system. The new UI will also be added to system startup.\n"
"\n"
"Are you sure you want to continue?");
- response = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-
- switch(response) {
- case GTK_RESPONSE_YES:
- break;
- case GTK_RESPONSE_NO:
- default:
- return true;
- }
-
- const int exit_code = system("flatpak-spawn --host -- /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy setup-gsr-ui");
- if(exit_code != 0) {
- GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
- "Failed to setup the new UI. You either cancelled the installation or you don't have pkexec installed and a polkit agent running.");
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
+ if(!kms_server_setup_finished)
return true;
- }
-
- config.main_config.use_new_ui = true;
- config.main_config.installed_gsr_global_hotkeys_version = GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION;
- save_configs();
bool service_install_successful = (system(
"data_home=$(flatpak-spawn --host -- /bin/sh -c 'echo \"${XDG_DATA_HOME:-$HOME/.local/share}\"') && "
"flatpak-spawn --host -- install -Dm644 /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/share/gpu-screen-recorder/gpu-screen-recorder-ui.service \"$data_home/systemd/user/gpu-screen-recorder-ui.service\"") == 0);
service_install_successful &= (system("flatpak-spawn --host -- systemctl --user daemon-reload") == 0);
service_install_successful &= (system("flatpak-spawn --host -- systemctl enable --user gpu-screen-recorder-ui") == 0);
- service_install_successful &= (system("flatpak-spawn --host -- systemctl start --user gpu-screen-recorder-ui") == 0);
if(!service_install_successful) {
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
"Failed to add GPU Screen Recorder to system startup. If you want the new UI to start on system startup then you need to add this command to system startup:\n"
@@ -2826,9 +2940,7 @@ static gboolean on_click_switch_to_new_ui(GtkButton*, gpointer) {
gtk_widget_destroy(dialog);
}
- if(!service_install_successful)
- launch_gsr_ui(true);
-
+ launch_gsr_ui(false);
g_application_quit(G_APPLICATION(select_window_userdata.app));
return true;
}
@@ -3115,10 +3227,10 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
gtk_grid_attach(audio_devices_grid, GTK_WIDGET(audio_devices_items_box), 0, audio_devices_row++, 2, 1);
}
- merge_audio_tracks_button = gtk_check_button_new_with_label("Merge audio tracks");
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button), true);
- gtk_widget_set_halign(merge_audio_tracks_button, GTK_ALIGN_START);
- gtk_grid_attach(audio_grid, merge_audio_tracks_button, 0, audio_input_area_row++, 2, 1);
+ split_audio_button = gtk_check_button_new_with_label("Split each device/app audio into separate audio tracks");
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(split_audio_button), false);
+ gtk_widget_set_halign(split_audio_button, GTK_ALIGN_START);
+ gtk_grid_attach(audio_grid, split_audio_button, 0, audio_input_area_row++, 2, 1);
record_app_audio_inverted_button = gtk_check_button_new_with_label("Record audio from all applications except the selected ones");
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button), false);
@@ -3375,27 +3487,6 @@ static GtkWidget* create_common_settings_page(GtkStack *stack, GtkApplication *a
return GTK_WIDGET(main_grid);
}
-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");
-
- index = str.find("Meta");
- if(index != std::string::npos)
- str.replace(index, 4, "Super");
-}
-
-static void shortcut_changed_callback(gsr_shortcut shortcut, void *userdata) {
- (void)userdata;
- std::string trigger = shortcut.trigger_description;
- replace_meta_with_super(trigger);
- 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());
- }
- }
-}
-
static void deactivated_callback(const char *description, void *userdata) {
(void)userdata;
const GtkWidget *visible_page = gtk_stack_get_visible_child(page_navigation_userdata.stack);
@@ -3408,54 +3499,23 @@ static void deactivated_callback(const char *description, void *userdata) {
}
}
-static gboolean on_register_hotkeys_button_clicked(GtkButton *button, gpointer userdata) {
- (void)button;
- (void)userdata;
-
- /*
- 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.
- */
- /* Unfortunately global shortcuts cant handle same key for different shortcuts, even though GPU Screen Recorder has page specific hotkeys */
- const gsr_bind_shortcut shortcuts[3] = {
- {
- "Start/stop recording/replay/streaming",
- { SHORTCUT_ID_START_STOP_RECORDING, "ALT+1" }
- },
- {
- "Pause/unpause recording",
- { SHORTCUT_ID_PAUSE_UNPAUSE_RECORDING, "ALT+2" }
- },
- {
- "Save replay",
- { SHORTCUT_ID_SAVE_REPLAY, "ALT+3" }
- }
- };
-
- if(global_shortcuts_initialized) {
- if(!gsr_global_shortcuts_bind_shortcuts(&global_shortcuts, shortcuts, 3, 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 change hotkeys:"), 0, 0, 1, 1);
+ if(wayland_compositor == WaylandCompositor::KDE) {
+ gtk_grid_attach(aa_grid, gtk_label_new("Hotkeys are managed externally on KDE Plasma Wayland. Click here to change hotkeys:"), 0, 0, 1, 1);
- GtkButton *register_hotkeys_button = GTK_BUTTON(gtk_button_new_with_label("Change 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);
+ GtkButton *register_hotkeys_button = GTK_BUTTON(gtk_button_new_with_label("Change 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);
+ } else {
+ gtk_grid_attach(aa_grid, gtk_label_new("Hotkeys are managed externally on Wayland. Go into your system application/hotkey settings to change hotkeys."), 0, 0, 1, 1);
+ }
row++;
gtk_grid_attach(grid, gtk_separator_new(GTK_ORIENTATION_HORIZONTAL), 0, row, width, 1);
@@ -4125,7 +4185,7 @@ static void load_config() {
gtk_spin_button_set_value(video_height_entry, config.main_config.video_height);
gtk_spin_button_set_value(fps_entry, config.main_config.fps);
gtk_spin_button_set_value(video_bitrate_entry, config.main_config.video_bitrate);
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(merge_audio_tracks_button), config.main_config.merge_audio_tracks);
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(split_audio_button), !config.main_config.merge_audio_tracks);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(record_app_audio_inverted_button), config.main_config.record_app_audio_inverted);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(change_video_resolution_button), config.main_config.change_video_resolution);
@@ -4218,7 +4278,8 @@ static void load_config() {
"Unable to find a hardware video encoder on your system, using software video encoder instead (slow!). If you know that your system supports H264/HEVC hardware video encoding and "
"you are using the flatpak version of GPU Screen Recorder then try installing mesa-extra freedesktop runtime by running this command:\n"
"flatpak install --system org.freedesktop.Platform.GL.default//23.08-extra\n"
- "and then restart GPU Screen Recorder. If that doesn't work then you may have to install another mesa package for your distro.\n"
+ "and then restart GPU Screen Recorder. If that doesn't work then you may have to install another mesa package for your distro if you are using AMD.\n"
+ "If you are using NVIDIA then you might need to run the \"nvidia-smi\" command first before starting GPU Screen Recorder if NVIDIA is incorrectly setup on your distro.\n"
"If you are using a distro such as manjaro which disables hardware accelerated video encoding then you can also try the <a href=\"https://flathub.org/apps/com.dec05eba.gpu_screen_recorder\">flatpak version of GPU Screen Recorder</a> instead which doesn't have this issue.");
set_dialog_selectable(dialog);
gtk_dialog_run(GTK_DIALOG(dialog));
@@ -4269,19 +4330,16 @@ static void init_shortcuts_callback(bool success, void *userdata) {
static const char* gpu_vendor_to_name(GpuVendor vendor) {
switch(vendor) {
- case GpuVendor::UNKNOWN: return "Unknown";
- case GpuVendor::AMD: return "AMD";
- case GpuVendor::INTEL: return "Intel";
- case GpuVendor::NVIDIA: return "NVIDIA";
+ case GpuVendor::UNKNOWN: return "Unknown";
+ case GpuVendor::AMD: return "AMD";
+ case GpuVendor::INTEL: return "Intel";
+ case GpuVendor::NVIDIA: return "NVIDIA";
+ case GpuVendor::BROADCOM: return "Broadcom";
}
return "";
}
-static void activate(GtkApplication *app, gpointer) {
- flatpak = is_inside_flatpak();
- nvfbc_installed = gsr_info.system_info.display_server != DisplayServer::WAYLAND && is_nv_fbc_installed();
- page_navigation_userdata.app = app;
-
+static bool gsr_startup_validation() {
if(gsr_info_exit_status == GsrInfoExitStatus::FAILED_TO_RUN_COMMAND) {
const char *cmd = flatpak ? "flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4" : "gpu-screen-recorder -w screen -f 60 -o video.mp4";
GtkWidget *dialog = gtk_message_dialog_new_with_markup(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -4291,8 +4349,7 @@ static void activate(GtkApplication *app, gpointer) {
set_dialog_selectable(dialog);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(gsr_info_exit_status == GsrInfoExitStatus::OPENGL_FAILED) {
@@ -4303,8 +4360,7 @@ static void activate(GtkApplication *app, gpointer) {
set_dialog_selectable(dialog);
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(gsr_info_exit_status == GsrInfoExitStatus::NO_DRM_CARD) {
@@ -4312,8 +4368,7 @@ static void activate(GtkApplication *app, gpointer) {
"Failed to find a valid DRM card. If you are running GPU Screen Recorder with prime-run then try running without it.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(gsr_info.system_info.display_server == DisplayServer::UNKNOWN) {
@@ -4321,8 +4376,7 @@ static void activate(GtkApplication *app, gpointer) {
"Neither X11 nor Wayland is running.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(gsr_info.system_info.display_server == DisplayServer::X11 && !dpy) {
@@ -4330,8 +4384,7 @@ static void activate(GtkApplication *app, gpointer) {
"Failed to connect to the X11 server");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(gsr_info.gpu_info.vendor == GpuVendor::NVIDIA) {
@@ -4340,8 +4393,7 @@ static void activate(GtkApplication *app, gpointer) {
"CUDA is not installed on your system. GPU Screen Recorder requires CUDA to be installed to work with a NVIDIA GPU.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
if(!is_nvenc_installed()) {
@@ -4349,12 +4401,22 @@ static void activate(GtkApplication *app, gpointer) {
"NVENC is not installed on your system. GPU Screen Recorder requires NVENC to be installed to work with a NVIDIA GPU.");
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
- g_application_quit(G_APPLICATION(app));
- return;
+ return false;
}
}
- std::string window_title = "GPU Screen Recorder v" + std::string(GSR_VERSION) + " | Running on ";
+ return true;
+}
+
+static void activate(GtkApplication *app, gpointer) {
+ if(!gsr_startup_validation()) {
+ g_application_quit(G_APPLICATION(app));
+ return;
+ }
+
+ page_navigation_userdata.app = app;
+
+ std::string window_title = "GPU Screen Recorder | Running on ";
window_title += gpu_vendor_to_name(gsr_info.gpu_info.vendor);
window = gtk_application_window_new(app);
@@ -4377,6 +4439,14 @@ static void activate(GtkApplication *app, gpointer) {
gtk_window_set_default_icon_name(icon_name);
gtk_window_set_icon_name(GTK_WINDOW(window), icon_name);
+ if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
+ if(gdk_wayland_display_query_registry(gdk_display_get_default(), "hyprland_global_shortcuts_manager_v1")) {
+ wayland_compositor = WaylandCompositor::HYPRLAND;
+ } else if(gdk_wayland_display_query_registry(gdk_display_get_default(), "org_kde_plasma_shell")) {
+ wayland_compositor = WaylandCompositor::KDE;
+ }
+ }
+
select_window_userdata.app = app;
audio_inputs = get_audio_devices();
application_audio = get_application_audio();
@@ -4426,12 +4496,6 @@ static void activate(GtkApplication *app, gpointer) {
load_config();
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
- if(gdk_wayland_display_query_registry(gdk_display_get_default(), "hyprland_global_shortcuts_manager_v1")) {
- wayland_compositor = WaylandCompositor::HYPRLAND;
- } else if(gdk_wayland_display_query_registry(gdk_display_get_default(), "org_kde_plasma_shell")) {
- wayland_compositor = WaylandCompositor::KDE;
- }
-
init_shortcuts_callback(false, nullptr);
// TODO:
// Disable global hotkeys on Hyprland for now. It crashes the hyprland desktop portal.
@@ -4440,7 +4504,7 @@ static void activate(GtkApplication *app, gpointer) {
// the desktop portal is restarted (when the computer is restarted for example).
if(wayland_compositor == WaylandCompositor::HYPRLAND) {
- const char *hotkeys_not_supported_text = "Global hotkeys have been disabled on your system because of a Hyprland bug.\nUse X11 or KDE Plasma on Wayland if you want to use hotkeys.";
+ const char *hotkeys_not_supported_text = "Hotkeys have been disabled on your system because of a Hyprland bug.\nUse X11 or KDE Plasma on Wayland if you want to use hotkeys.";
gtk_label_set_text(GTK_LABEL(recording_hotkeys_not_supported_label), hotkeys_not_supported_text);
gtk_label_set_text(GTK_LABEL(replay_hotkeys_not_supported_label), hotkeys_not_supported_text);
gtk_label_set_text(GTK_LABEL(streaming_hotkeys_not_supported_label), hotkeys_not_supported_text);
@@ -4452,57 +4516,9 @@ static void activate(GtkApplication *app, gpointer) {
}
}
-static bool kms_server_proxy_setup_gsr_ui(const char *msg) {
- GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, "%s", msg);
- const gint response = gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-
- switch(response) {
- case GTK_RESPONSE_YES:
- break;
- case GTK_RESPONSE_NO:
- default: {
- config.main_config.use_new_ui = false;
- save_config(config);
- return false;
- }
- }
-
- const int exit_code = system("flatpak-spawn --host -- /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy setup-gsr-ui");
- if(exit_code != 0) {
- GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "Failed to setup the new UI. You either cancelled the installation or you don't have pkexec installed and a polkit agent running.");
- gtk_dialog_run(GTK_DIALOG(dialog));
- gtk_widget_destroy(dialog);
-
- config.main_config.use_new_ui = false;
- save_configs();
- return false;
- }
-
- config.main_config.use_new_ui = true;
- config.main_config.installed_gsr_global_hotkeys_version = GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION;
- save_config(config);
- return true;
-}
-
-static bool is_gsr_global_hotkeys_installed() {
- const char *user_homepath = getenv("HOME");
- if(!user_homepath)
- user_homepath = "/tmp";
-
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "%s/.local/share/gpu-screen-recorder/gsr-global-hotkeys", user_homepath);
- return access(path, F_OK) == 0;
-}
-
static bool is_kms_server_proxy_installed() {
- const char *user_homepath = getenv("HOME");
- if(!user_homepath)
- user_homepath = "/tmp";
-
- char path[PATH_MAX];
- snprintf(path, sizeof(path), "%s/.local/share/gpu-screen-recorder/kms-server-proxy-1", user_homepath);
- return access(path, F_OK) == 0;
+ const int exit_code = system("flatpak-spawn --host -- /var/lib/flatpak/app/com.dec05eba.gpu_screen_recorder/current/active/files/bin/kms-server-proxy is-setup");
+ return exit_code == 0;
}
static void gtk_activate_handler_run_and_quit(GtkApplication *app, gpointer userdata) {
@@ -4534,13 +4550,18 @@ static void startup_new_ui(bool launched_by_daemon) {
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
});
-
+
config.main_config.use_new_ui = false;
save_config(config);
return;
}
}
+ start_gtk_run_handler([]() {
+ if(!gsr_startup_validation())
+ exit(1);
+ });
+
if(!flatpak_is_installed_as_system()) {
start_gtk_run_handler([]() {
GtkWidget *dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
@@ -4550,7 +4571,7 @@ static void startup_new_ui(bool launched_by_daemon) {
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_destroy(dialog);
});
- return;
+ exit(1);
}
if(config.main_config.installed_gsr_global_hotkeys_version != GSR_CURRENT_GLOBAL_HOTKEYS_CODE_VERSION) {
@@ -4558,13 +4579,14 @@ static void startup_new_ui(bool launched_by_daemon) {
start_gtk_run_handler([&finished]() {
finished = kms_server_proxy_setup_gsr_ui(
"An update is available. The new GPU Screen Recorder UI needs root privileges to finish update to make global hotkeys and recording work on any system.\n"
+ "You will need to restart the application to apply the update.\n"
"\n"
"Are you sure you want to continue?");
});
if(!finished)
return;
- } else if(!is_gsr_global_hotkeys_installed() || !is_kms_server_proxy_installed()) {
+ } else if(!is_kms_server_proxy_installed()) {
bool finished = false;
start_gtk_run_handler([&finished]() {
finished = kms_server_proxy_setup_gsr_ui(
@@ -4580,15 +4602,15 @@ static void startup_new_ui(bool launched_by_daemon) {
if(dpy)
XCloseDisplay(dpy);
- launch_gsr_ui(!launched_by_daemon);
+ launch_gsr_ui(launched_by_daemon);
exit(0);
}
int main(int argc, char **argv) {
setlocale(LC_ALL, "C");
- const bool use_old_ui_opt = argc == 2 && strcmp(argv[1], "use-old-ui") == 0;
- const bool launched_by_daemon_opt = argc == 2 && strcmp(argv[1], "gsr-ui") == 0;
+ const bool use_old_ui_opt = argc >= 2 && strcmp(argv[1], "use-old-ui") == 0;
+ const bool launched_by_daemon_opt = argc >= 2 && strcmp(argv[1], "gsr-ui") == 0;
argc = 1;
if(geteuid() == 0) {
@@ -4601,17 +4623,12 @@ int main(int argc, char **argv) {
config_empty = false;
config = read_config(config_empty);
- if(use_old_ui_opt) {
- system("flatpak-spawn --host -- systemctl disable --now --user gpu-screen-recorder-ui");
- config.main_config.use_new_ui = false;
- save_config(config);
+ if(!dpy && launched_by_daemon_opt && config.main_config.use_new_ui) {
+ fprintf(stderr, "Error: failed to connect to the X11 server, assuming no graphical session has started yet\n");
+ exit(1);
}
- if(config.main_config.use_new_ui)
- startup_new_ui(launched_by_daemon_opt);
-
gsr_info_exit_status = get_gpu_screen_recorder_info(&gsr_info);
-
if(gsr_info_exit_status == GsrInfoExitStatus::OK) {
if(gsr_info.system_info.display_server == DisplayServer::WAYLAND) {
setenv("GDK_BACKEND", "wayland", true);
@@ -4620,6 +4637,19 @@ int main(int argc, char **argv) {
}
}
+ flatpak = is_inside_flatpak();
+ nvfbc_installed = gsr_info.system_info.display_server != DisplayServer::WAYLAND && is_nv_fbc_installed();
+
+ if(use_old_ui_opt) {
+ system("flatpak-spawn --host -- systemctl disable --user gpu-screen-recorder-ui");
+ system("flatpak-spawn --host -- systemctl stop --user gpu-screen-recorder-ui");
+ config.main_config.use_new_ui = false;
+ save_config(config);
+ }
+
+ if(config.main_config.use_new_ui)
+ startup_new_ui(launched_by_daemon_opt);
+
char app_id[] = "com.dec05eba.gpu_screen_recorder";
// Gtk sets wayland app id / x11 wm class from the binary name, so we override it here.
// This is needed for the correct window icon on wayland (app id needs to match the desktop file name).