aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-04-23 00:59:17 +0200
committerdec05eba <dec05eba@protonmail.com>2025-04-23 00:59:17 +0200
commit736f2f30956369754f6c8eeb49e7972720777cc9 (patch)
tree1e0201740a6652cd02f0821ad599563e15448718
parent719236d4f41735a96e5a0707c7d964335907ffee (diff)
Allow recording while using replay/streaming and option to save 1 min or 10 minHEADmaster
-rw-r--r--TODO2
-rw-r--r--include/Config.hpp2
-rw-r--r--include/Overlay.hpp10
-rw-r--r--include/gui/GlobalSettingsPage.hpp5
-rw-r--r--src/Config.cpp8
-rw-r--r--src/Overlay.cpp259
-rw-r--r--src/gui/DropdownButton.cpp4
-rw-r--r--src/gui/GlobalSettingsPage.cpp43
-rw-r--r--src/main.cpp14
9 files changed, 275 insertions, 72 deletions
diff --git a/TODO b/TODO
index 2148846..2e562b5 100644
--- a/TODO
+++ b/TODO
@@ -14,8 +14,6 @@ Add nvidia overclock option.
Add support for window selection in capture.
-Add option to record the focused monitor. This works on wayland too when using kms capture since we can get cursor position without root and see which monitor (crtc) the cursor is on. Or use create_window_get_center_position.
-
Filechooser should have the option to select list view, search bar and common folders/mounted drives on the left side for quick navigation. Also a button to create a new directory.
Restart replay on system start if monitor resolution changes.
diff --git a/include/Config.hpp b/include/Config.hpp
index bbb5381..c8c6650 100644
--- a/include/Config.hpp
+++ b/include/Config.hpp
@@ -119,6 +119,8 @@ namespace gsr {
int32_t replay_time = 60;
ConfigHotkey start_stop_hotkey;
ConfigHotkey save_hotkey;
+ ConfigHotkey save_1_min_hotkey;
+ ConfigHotkey save_10_min_hotkey;
};
struct ScreenshotConfig {
diff --git a/include/Overlay.hpp b/include/Overlay.hpp
index ac9d2b1..8dd246a 100644
--- a/include/Overlay.hpp
+++ b/include/Overlay.hpp
@@ -59,6 +59,8 @@ namespace gsr {
void toggle_stream();
void toggle_replay();
void save_replay();
+ void save_replay_1_min();
+ void save_replay_10_min();
void take_screenshot();
void take_screenshot_region();
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type, const char *capture_target = nullptr);
@@ -87,7 +89,7 @@ namespace gsr {
void update_notification_process_status();
void save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type);
void on_replay_saved(const char *replay_saved_filepath);
- void update_gsr_replay_save();
+ void process_gsr_output();
void update_gsr_process_status();
void update_gsr_screenshot_process_status();
@@ -96,7 +98,7 @@ namespace gsr {
void update_power_supply_status();
void update_system_startup_status();
- void on_stop_recording(int exit_code);
+ void on_stop_recording(int exit_code, const std::string &video_filepath);
void update_ui_recording_paused();
void update_ui_recording_unpaused();
@@ -110,7 +112,10 @@ namespace gsr {
void update_ui_replay_started();
void update_ui_replay_stopped();
+ void prepare_gsr_output_for_reading();
void on_press_save_replay();
+ void on_press_save_replay_1_min_replay();
+ void on_press_save_replay_10_min_replay();
bool on_press_start_replay(bool disable_notification, bool finished_region_selection);
void on_press_start_record(bool finished_region_selection);
void on_press_start_stream(bool finished_region_selection);
@@ -211,7 +216,6 @@ namespace gsr {
std::function<void()> on_region_selected;
std::string recording_capture_target;
- std::string replay_capture_target;
std::string screenshot_capture_target;
std::unique_ptr<CursorTracker> cursor_tracker;
diff --git a/include/gui/GlobalSettingsPage.hpp b/include/gui/GlobalSettingsPage.hpp
index 5df5b9c..d96397d 100644
--- a/include/gui/GlobalSettingsPage.hpp
+++ b/include/gui/GlobalSettingsPage.hpp
@@ -22,6 +22,8 @@ namespace gsr {
NONE,
REPLAY_START_STOP,
REPLAY_SAVE,
+ REPLAY_SAVE_1_MIN,
+ REPLAY_SAVE_10_MIN,
RECORD_START_STOP,
RECORD_PAUSE_UNPAUSE,
STREAM_START_STOP,
@@ -56,6 +58,7 @@ namespace gsr {
std::unique_ptr<RadioButton> create_enable_joystick_hotkeys_button();
std::unique_ptr<List> create_show_hide_hotkey_options();
std::unique_ptr<List> create_replay_hotkey_options();
+ std::unique_ptr<List> create_replay_partial_save_hotkey_options();
std::unique_ptr<List> create_record_hotkey_options();
std::unique_ptr<List> create_stream_hotkey_options();
std::unique_ptr<List> create_screenshot_hotkey_options();
@@ -89,6 +92,8 @@ namespace gsr {
Button *turn_replay_on_off_button_ptr = nullptr;
Button *save_replay_button_ptr = nullptr;
+ Button *save_replay_1_min_button_ptr = nullptr;
+ Button *save_replay_10_min_button_ptr = nullptr;
Button *start_stop_recording_button_ptr = nullptr;
Button *pause_unpause_recording_button_ptr = nullptr;
Button *start_stop_streaming_button_ptr = nullptr;
diff --git a/src/Config.cpp b/src/Config.cpp
index 3832c83..e920bf0 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -84,8 +84,8 @@ namespace gsr {
modifier_str = mgl::Keyboard::key_to_string(modifier_key);
if(!modifier_side) {
- string_remove_all(modifier_str, "Left");
- string_remove_all(modifier_str, "Right");
+ string_remove_all(modifier_str, "Left ");
+ string_remove_all(modifier_str, "Right ");
}
result += modifier_str;
}
@@ -148,6 +148,8 @@ namespace gsr {
replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT};
replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
+ replay_config.save_1_min_hotkey = {mgl::Keyboard::F11, HOTKEY_MOD_LALT};
+ replay_config.save_10_min_hotkey = {mgl::Keyboard::F12, HOTKEY_MOD_LALT};
screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Printscreen, 0};
screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LCTRL};
@@ -264,6 +266,8 @@ namespace gsr {
{"replay.time", &config.replay_config.replay_time},
{"replay.start_stop_hotkey", &config.replay_config.start_stop_hotkey},
{"replay.save_hotkey", &config.replay_config.save_hotkey},
+ {"replay.save_1_min_hotkey", &config.replay_config.save_1_min_hotkey},
+ {"replay.save_10_min_hotkey", &config.replay_config.save_10_min_hotkey},
{"screenshot.record_area_option", &config.screenshot_config.record_area_option},
{"screenshot.image_width", &config.screenshot_config.image_width},
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 4d8790b..a6f8a69 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -47,7 +47,7 @@ namespace gsr {
static const double force_window_on_top_timeout_seconds = 1.0;
static const double replay_status_update_check_timeout_seconds = 1.5;
static const double replay_saving_notification_timeout_seconds = 0.5;
- static const double notification_timeout_seconds = 2.0;
+ static const double notification_timeout_seconds = 2.5;
static const double notification_error_timeout_seconds = 5.0;
static const double cursor_tracker_update_timeout_sec = 0.1;
@@ -326,6 +326,20 @@ namespace gsr {
});
global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.save_1_min_hotkey),
+ "replay_save_1_min", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_1_min();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.save_10_min_hotkey),
+ "replay_save_10_min", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_10_min();
+ });
+
+ global_hotkeys->bind_key_press(
config_hotkey_to_hotkey(overlay->get_config().screenshot_config.take_screenshot_hotkey),
"take_screenshot", [overlay](const std::string &id) {
fprintf(stderr, "pressed %s\n", id.c_str());
@@ -688,7 +702,7 @@ namespace gsr {
remove_widgets_to_be_removed();
update_notification_process_status();
- update_gsr_replay_save();
+ process_gsr_output();
update_gsr_process_status();
update_gsr_screenshot_process_status();
replay_status_update_status();
@@ -697,7 +711,7 @@ namespace gsr {
start_region_capture = false;
hide();
if(!region_selector.start(get_color_theme().tint_color)) {
- show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::NONE);
+ show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
on_region_selected = nullptr;
}
}
@@ -1051,9 +1065,15 @@ namespace gsr {
replay_dropdown_button_ptr = button.get();
button->add_item("Turn on", "start", config.replay_config.start_stop_hotkey.to_string(false, false));
button->add_item("Save", "save", config.replay_config.save_hotkey.to_string(false, false));
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ button->add_item("Save 1 min", "save_1_min", config.replay_config.save_1_min_hotkey.to_string(false, false));
+ button->add_item("Save 10 min", "save_10_min", config.replay_config.save_10_min_hotkey.to_string(false, false));
+ }
button->add_item("Settings", "settings");
button->set_item_icon("start", &get_theme().play_texture);
button->set_item_icon("save", &get_theme().save_texture);
+ button->set_item_icon("save_1_min", &get_theme().save_texture);
+ button->set_item_icon("save_10_min", &get_theme().save_texture);
button->set_item_icon("settings", &get_theme().settings_small_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
@@ -1066,11 +1086,17 @@ namespace gsr {
page_stack.push(std::move(replay_settings_page));
} else if(id == "save") {
on_press_save_replay();
+ } else if(id == "save_1_min") {
+ on_press_save_replay_1_min_replay();
+ } else if(id == "save_10_min") {
+ on_press_save_replay_10_min_replay();
} else if(id == "start") {
on_press_start_replay(false, false);
}
};
button->set_item_enabled("save", false);
+ button->set_item_enabled("save_1_min", false);
+ button->set_item_enabled("save_10_min", false);
main_buttons_list->add_widget(std::move(button));
}
{
@@ -1370,6 +1396,14 @@ namespace gsr {
on_press_save_replay();
}
+ void Overlay::save_replay_1_min() {
+ on_press_save_replay_1_min_replay();
+ }
+
+ void Overlay::save_replay_10_min() {
+ on_press_save_replay_10_min_replay();
+ }
+
void Overlay::take_screenshot() {
on_press_take_screenshot(false, false);
}
@@ -1596,12 +1630,12 @@ namespace gsr {
if(!config.replay_config.show_replay_saved_notifications)
return;
- if(is_capture_target_monitor(replay_capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Saved a replay of this monitor to '%s'", filename.c_str());
else
- snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
- capture_target = replay_capture_target.c_str();
+ capture_target = recording_capture_target.c_str();
break;
}
case NotificationType::SCREENSHOT: {
@@ -1623,6 +1657,16 @@ namespace gsr {
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, notification_type, capture_target);
}
+ static NotificationType recording_status_to_notification_type(RecordingStatus recording_status) {
+ switch(recording_status) {
+ case RecordingStatus::NONE: return NotificationType::NONE;
+ case RecordingStatus::REPLAY: return NotificationType::REPLAY;
+ case RecordingStatus::RECORD: return NotificationType::RECORD;
+ case RecordingStatus::STREAM: return NotificationType::STREAM;
+ }
+ return NotificationType::NONE;
+ }
+
void Overlay::on_replay_saved(const char *replay_saved_filepath) {
replay_save_show_notification = false;
if(config.replay_config.save_video_in_game_folder) {
@@ -1630,15 +1674,15 @@ namespace gsr {
} else {
const std::string filename = filepath_get_filename(replay_saved_filepath);
char msg[512];
- if(is_capture_target_monitor(replay_capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Saved a replay of this monitor to '%s'", filename.c_str());
else
- snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
- show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Saved a replay of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
+ show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
}
}
- void Overlay::update_gsr_replay_save() {
+ void Overlay::process_gsr_output() {
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
replay_save_show_notification = false;
show_notification("Saving replay, this might take some time", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
@@ -1646,15 +1690,36 @@ namespace gsr {
if(gpu_screen_recorder_process_output_file) {
char buffer[1024];
- char *replay_saved_filepath = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
- if(!replay_saved_filepath || replay_saved_filepath[0] == '\0')
+ char *line = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
+ if(!line || line[0] == '\0')
return;
- const int line_len = strlen(replay_saved_filepath);
- if(replay_saved_filepath[line_len - 1] == '\n')
- replay_saved_filepath[line_len - 1] = '\0';
+ const int line_len = strlen(line);
+ if(line[line_len - 1] == '\n')
+ line[line_len - 1] = '\0';
+
+ if(starts_with({line, (size_t)line_len}, "Error: ")) {
+ show_notification(line + 7, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), recording_status_to_notification_type(recording_status));
+ return;
+ }
- on_replay_saved(replay_saved_filepath);
+ const std::string video_filepath = filepath_get_filename(line);
+ if(starts_with(video_filepath, "Video_")) {
+ on_stop_recording(0, video_filepath);
+ return;
+ }
+
+ switch(recording_status) {
+ case RecordingStatus::NONE:
+ break;
+ case RecordingStatus::REPLAY:
+ on_replay_saved(line);
+ break;
+ case RecordingStatus::RECORD:
+ break;
+ case RecordingStatus::STREAM:
+ break;
+ }
} else if(gpu_screen_recorder_process_output_fd > 0) {
char buffer[1024];
read(gpu_screen_recorder_process_output_fd, buffer, sizeof(buffer));
@@ -1693,7 +1758,7 @@ namespace gsr {
}
case RecordingStatus::RECORD: {
update_ui_recording_stopped();
- on_stop_recording(exit_code);
+ on_stop_recording(exit_code, record_filepath);
break;
}
case RecordingStatus::STREAM: {
@@ -1829,12 +1894,12 @@ namespace gsr {
on_press_start_replay(true, false);
}
- void Overlay::on_stop_recording(int exit_code) {
+ void Overlay::on_stop_recording(int exit_code, const std::string &video_filepath) {
if(exit_code == 0) {
if(config.record_config.save_video_in_game_folder) {
- save_video_in_current_game_directory(record_filepath.c_str(), NotificationType::RECORD);
+ save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD);
} else {
- const std::string filename = filepath_get_filename(record_filepath.c_str());
+ const std::string filename = filepath_get_filename(video_filepath.c_str());
char msg[512];
if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Saved a recording of this monitor to '%s'", filename.c_str());
@@ -1846,6 +1911,7 @@ namespace gsr {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
show_notification("Failed to start/save recording. Verify if settings are correct", notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
}
+ update_ui_recording_stopped();
}
void Overlay::update_ui_recording_paused() {
@@ -1874,7 +1940,7 @@ namespace gsr {
record_dropdown_button_ptr->set_activated(true);
record_dropdown_button_ptr->set_description("Recording");
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
- record_dropdown_button_ptr->set_item_enabled("pause", true);
+ record_dropdown_button_ptr->set_item_enabled("pause", recording_status == RecordingStatus::RECORD);
}
void Overlay::update_ui_recording_stopped() {
@@ -1910,6 +1976,7 @@ namespace gsr {
stream_dropdown_button_ptr->set_activated(false);
stream_dropdown_button_ptr->set_description("Not streaming");
stream_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
+ update_ui_recording_stopped();
}
void Overlay::update_ui_replay_started() {
@@ -1921,6 +1988,8 @@ namespace gsr {
replay_dropdown_button_ptr->set_description("On");
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
replay_dropdown_button_ptr->set_item_enabled("save", true);
+ replay_dropdown_button_ptr->set_item_enabled("save_1_min", true);
+ replay_dropdown_button_ptr->set_item_enabled("save_10_min", true);
}
void Overlay::update_ui_replay_stopped() {
@@ -1932,6 +2001,9 @@ namespace gsr {
replay_dropdown_button_ptr->set_description("Off");
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
replay_dropdown_button_ptr->set_item_enabled("save", false);
+ replay_dropdown_button_ptr->set_item_enabled("save_1_min", false);
+ replay_dropdown_button_ptr->set_item_enabled("save_10_min", false);
+ update_ui_recording_stopped();
}
static std::string get_date_str() {
@@ -2060,6 +2132,17 @@ namespace gsr {
}
}
+ void Overlay::prepare_gsr_output_for_reading() {
+ if(gpu_screen_recorder_process_output_fd <= 0)
+ return;
+
+ const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
+ fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
+ gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
+ if(gpu_screen_recorder_process_output_file)
+ gpu_screen_recorder_process_output_fd = -1;
+ }
+
void Overlay::on_press_save_replay() {
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
return;
@@ -2069,6 +2152,24 @@ namespace gsr {
kill(gpu_screen_recorder_process, SIGUSR1);
}
+ void Overlay::on_press_save_replay_1_min_replay() {
+ if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
+ return;
+
+ replay_save_show_notification = true;
+ replay_save_clock.restart();
+ kill(gpu_screen_recorder_process, SIGRTMIN+3);
+ }
+
+ void Overlay::on_press_save_replay_10_min_replay() {
+ if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
+ return;
+
+ replay_save_show_notification = true;
+ replay_save_clock.restart();
+ kill(gpu_screen_recorder_process, SIGRTMIN+5);
+ }
+
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_region_selection) {
if(region_selector.is_started())
return false;
@@ -2078,10 +2179,10 @@ namespace gsr {
case RecordingStatus::REPLAY:
break;
case RecordingStatus::RECORD:
- show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
+ show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return false;
case RecordingStatus::STREAM:
- show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
+ show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
return false;
}
@@ -2089,9 +2190,6 @@ namespace gsr {
replay_save_show_notification = false;
try_replay_startup = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
-
close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
@@ -2114,11 +2212,11 @@ namespace gsr {
}
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
- replay_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
- if(!validate_capture_target(replay_capture_target, capture_options)) {
+ recording_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
+ if(!validate_capture_target(recording_capture_target, capture_options)) {
char err_msg[256];
- snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", replay_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::REPLAY);
+ snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
return false;
}
@@ -2153,7 +2251,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
std::vector<const char*> args = {
- "gpu-screen-recorder", "-w", replay_capture_target.c_str(),
+ "gpu-screen-recorder", "-w", recording_capture_target.c_str(),
"-c", config.replay_config.container.c_str(),
"-ac", config.replay_config.record_options.audio_codec.c_str(),
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
@@ -2175,21 +2273,23 @@ namespace gsr {
char region_str[128];
add_common_gpu_screen_recorder_args(args, config.replay_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ args.push_back("-ro");
+ args.push_back(config.record_config.save_directory.c_str());
+ }
+
args.push_back(nullptr);
gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start replay", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
+ return false;
} else {
recording_status = RecordingStatus::REPLAY;
update_ui_replay_started();
}
- const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
- fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
- gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
- if(gpu_screen_recorder_process_output_file)
- gpu_screen_recorder_process_output_fd = -1;
+ prepare_gsr_output_for_reading();
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
@@ -2202,11 +2302,11 @@ namespace gsr {
// to see when the program has exit.
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(replay_capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Started replaying this monitor");
else
- snprintf(msg, sizeof(msg), "Started replaying %s", replay_capture_target.c_str());
- show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started replaying %s", recording_capture_target.c_str());
+ show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
}
return true;
@@ -2220,18 +2320,37 @@ namespace gsr {
case RecordingStatus::NONE:
case RecordingStatus::RECORD:
break;
- case RecordingStatus::REPLAY:
- show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
+ case RecordingStatus::REPLAY: {
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ if(gpu_screen_recorder_process > 0) {
+ if(config.record_config.show_recording_started_notifications)
+ show_notification("Started recording in the replay session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
+ update_ui_recording_started();
+ kill(gpu_screen_recorder_process, SIGRTMIN);
+ }
+ } else {
+ show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::REPLAY);
+ }
return;
- case RecordingStatus::STREAM:
- show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
+ }
+ case RecordingStatus::STREAM: {
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ if(gpu_screen_recorder_process > 0) {
+ if(config.record_config.show_recording_started_notifications)
+ show_notification("Started recording in the streaming session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
+ update_ui_recording_started();
+ kill(gpu_screen_recorder_process, SIGRTMIN);
+ }
+ } else {
+ show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::STREAM);
+ }
return;
+ }
}
paused = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
+ close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
kill(gpu_screen_recorder_process, SIGINT);
@@ -2243,7 +2362,7 @@ namespace gsr {
int exit_code = -1;
if(WIFEXITED(status))
exit_code = WEXITSTATUS(status);
- on_stop_recording(exit_code);
+ on_stop_recording(exit_code, record_filepath);
}
gpu_screen_recorder_process = -1;
@@ -2258,7 +2377,7 @@ namespace gsr {
if(!validate_capture_target(config.record_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::RECORD);
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return;
}
@@ -2313,14 +2432,17 @@ namespace gsr {
args.push_back(nullptr);
record_filepath = output_file;
- gpu_screen_recorder_process = exec_program(args.data(), nullptr);
+ gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start recording", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
+ return;
} else {
recording_status = RecordingStatus::RECORD;
update_ui_recording_started();
}
+ prepare_gsr_output_for_reading();
+
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
@@ -2378,17 +2500,16 @@ namespace gsr {
case RecordingStatus::STREAM:
break;
case RecordingStatus::REPLAY:
- show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
+ show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
return;
case RecordingStatus::RECORD:
- show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
+ show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return;
}
paused = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
+ close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
kill(gpu_screen_recorder_process, SIGINT);
@@ -2409,11 +2530,11 @@ namespace gsr {
}
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
- const std::string capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
+ recording_capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
if(!validate_capture_target(config.streaming_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
- snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::STREAM);
+ snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
return;
}
@@ -2456,7 +2577,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
std::vector<const char*> args = {
- "gpu-screen-recorder", "-w", capture_target.c_str(),
+ "gpu-screen-recorder", "-w", recording_capture_target.c_str(),
"-c", container.c_str(),
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
@@ -2471,16 +2592,24 @@ namespace gsr {
char region_str[128];
add_common_gpu_screen_recorder_args(args, config.streaming_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ args.push_back("-ro");
+ args.push_back(config.record_config.save_directory.c_str());
+ }
+
args.push_back(nullptr);
- gpu_screen_recorder_process = exec_program(args.data(), nullptr);
+ gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start streaming", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
+ return;
} else {
recording_status = RecordingStatus::STREAM;
update_ui_streaming_started();
}
+ prepare_gsr_output_for_reading();
+
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
@@ -2492,11 +2621,11 @@ namespace gsr {
// to see when the program has exit.
if(config.streaming_config.show_streaming_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Started streaming this monitor");
else
- snprintf(msg, sizeof(msg), "Started streaming %s", capture_target.c_str());
- show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started streaming %s", recording_capture_target.c_str());
+ show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, recording_capture_target.c_str());
}
}
@@ -2516,7 +2645,7 @@ namespace gsr {
if(!validate_capture_target(record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", screenshot_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::SCREENSHOT);
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
return;
}
@@ -2562,7 +2691,7 @@ namespace gsr {
screenshot_filepath = output_file;
gpu_screen_recorder_screenshot_process = exec_program(args.data(), nullptr);
if(gpu_screen_recorder_screenshot_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to take a screenshot", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
}
}
diff --git a/src/gui/DropdownButton.cpp b/src/gui/DropdownButton.cpp
index 6721840..788f56a 100644
--- a/src/gui/DropdownButton.cpp
+++ b/src/gui/DropdownButton.cpp
@@ -181,6 +181,10 @@ namespace gsr {
}
void DropdownButton::add_item(const std::string &text, const std::string &id, const std::string &description) {
+ for(auto &item : items) {
+ if(item.id == id)
+ return;
+ }
items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id});
dirty = true;
}
diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp
index fd4b6b8..109e72d 100644
--- a/src/gui/GlobalSettingsPage.cpp
+++ b/src/gui/GlobalSettingsPage.cpp
@@ -256,6 +256,30 @@ namespace gsr {
return list;
}
+ std::unique_ptr<List> GlobalSettingsPage::create_replay_partial_save_hotkey_options() {
+ auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 1 minute replay:", get_color_theme().text_color));
+ auto save_replay_1_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ save_replay_1_min_button_ptr = save_replay_1_min_button.get();
+ list->add_widget(std::move(save_replay_1_min_button));
+
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 10 minute replay:", get_color_theme().text_color));
+ auto save_replay_10_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
+ save_replay_10_min_button_ptr = save_replay_10_min_button.get();
+ list->add_widget(std::move(save_replay_10_min_button));
+
+ save_replay_1_min_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_1_MIN);
+ };
+
+ save_replay_10_min_button_ptr->on_click = [this] {
+ configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_10_MIN);
+ };
+
+ return list;
+ }
+
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
@@ -335,6 +359,8 @@ namespace gsr {
config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0};
+ config.replay_config.save_1_min_hotkey = {mgl::Keyboard::Unknown, 0};
+ config.replay_config.save_10_min_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0};
config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
@@ -374,6 +400,7 @@ namespace gsr {
list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
list_ptr->add_widget(create_show_hide_hotkey_options());
list_ptr->add_widget(create_replay_hotkey_options());
+ list_ptr->add_widget(create_replay_partial_save_hotkey_options());
list_ptr->add_widget(create_record_hotkey_options());
list_ptr->add_widget(create_stream_hotkey_options());
list_ptr->add_widget(create_screenshot_hotkey_options());
@@ -490,6 +517,8 @@ namespace gsr {
void GlobalSettingsPage::load_hotkeys() {
turn_replay_on_off_button_ptr->set_text(config.replay_config.start_stop_hotkey.to_string());
save_replay_button_ptr->set_text(config.replay_config.save_hotkey.to_string());
+ save_replay_1_min_button_ptr->set_text(config.replay_config.save_1_min_hotkey.to_string());
+ save_replay_10_min_button_ptr->set_text(config.replay_config.save_10_min_hotkey.to_string());
start_stop_recording_button_ptr->set_text(config.record_config.start_stop_hotkey.to_string());
pause_unpause_recording_button_ptr->set_text(config.record_config.pause_unpause_hotkey.to_string());
@@ -567,6 +596,10 @@ namespace gsr {
return turn_replay_on_off_button_ptr;
case ConfigureHotkeyType::REPLAY_SAVE:
return save_replay_button_ptr;
+ case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
+ return save_replay_1_min_button_ptr;
+ case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
+ return save_replay_10_min_button_ptr;
case ConfigureHotkeyType::RECORD_START_STOP:
return start_stop_recording_button_ptr;
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
@@ -591,6 +624,10 @@ namespace gsr {
return &config.replay_config.start_stop_hotkey;
case ConfigureHotkeyType::REPLAY_SAVE:
return &config.replay_config.save_hotkey;
+ case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
+ return &config.replay_config.save_1_min_hotkey;
+ case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
+ return &config.replay_config.save_10_min_hotkey;
case ConfigureHotkeyType::RECORD_START_STOP:
return &config.record_config.start_stop_hotkey;
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
@@ -643,6 +680,12 @@ namespace gsr {
case ConfigureHotkeyType::REPLAY_SAVE:
hotkey_configure_action_name = "Save replay";
break;
+ case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
+ hotkey_configure_action_name = "Save 1 minute replay";
+ break;
+ case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
+ hotkey_configure_action_name = "Save 10 minute replay";
+ break;
case ConfigureHotkeyType::RECORD_START_STOP:
hotkey_configure_action_name = "Start/stop recording";
break;
diff --git a/src/main.cpp b/src/main.cpp
index 9cb0ede..fefa832 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -30,6 +30,10 @@ static void sigint_handler(int signal) {
running = 0;
}
+static void signal_ignore(int) {
+
+}
+
static void disable_prime_run() {
unsetenv("__NV_PRIME_RENDER_OFFLOAD");
unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
@@ -218,6 +222,16 @@ int main(int argc, char **argv) {
unsetenv("vblank_mode");
signal(SIGINT, sigint_handler);
+ signal(SIGTERM, sigint_handler);
+ signal(SIGUSR1, signal_ignore);
+ signal(SIGUSR2, signal_ignore);
+ signal(SIGRTMIN, signal_ignore);
+ signal(SIGRTMIN+1, signal_ignore);
+ signal(SIGRTMIN+2, signal_ignore);
+ signal(SIGRTMIN+3, signal_ignore);
+ signal(SIGRTMIN+4, signal_ignore);
+ signal(SIGRTMIN+5, signal_ignore);
+ signal(SIGRTMIN+6, signal_ignore);
gsr::GsrInfo gsr_info;
// TODO: Show the error in ui