From d64e698eb1ed2988a8211d0b7d18a6ad8ef2d77d Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 22 Jul 2025 01:10:58 +0200 Subject: [PATCH] Show recording/replay duration in notification --- TODO | 4 +- include/Overlay.hpp | 7 +++ src/Overlay.cpp | 121 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 105 insertions(+), 27 deletions(-) diff --git a/TODO b/TODO index a36231f..fc03a7f 100644 --- a/TODO +++ b/TODO @@ -206,4 +206,6 @@ Disable hotkeys if virtual keyboard is found (either at startup or after running Support localization. -Add option to not capture cursor in screenshot when doing region/window capture. \ No newline at end of file +Add option to not capture cursor in screenshot when doing region/window capture. + +Window selection doesn't work when a window is fullscreen on x11. \ No newline at end of file diff --git a/include/Overlay.hpp b/include/Overlay.hpp index 72e03f8..353993c 100644 --- a/include/Overlay.hpp +++ b/include/Overlay.hpp @@ -94,6 +94,7 @@ namespace gsr { void close_gpu_screen_recorder_output(); + double get_time_passed_in_replay_buffer_seconds(); 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); @@ -220,6 +221,12 @@ namespace gsr { bool try_replay_startup = true; bool replay_recording = false; int replay_save_duration_min = 0; + double replay_buffer_save_duration_sec = 0.0; + mgl::Clock replay_duration_clock; + double replay_saved_duration_sec = 0.0; + bool replay_restart_on_save = false; + + mgl::Clock recording_duration_clock; AudioPlayer audio_player; diff --git a/src/Overlay.cpp b/src/Overlay.cpp index 036d5dd..7acdb20 100644 --- a/src/Overlay.cpp +++ b/src/Overlay.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -49,7 +50,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.5; + static const double notification_timeout_seconds = 3.0; static const double notification_error_timeout_seconds = 5.0; static const double cursor_tracker_update_timeout_sec = 0.1; @@ -1573,7 +1574,7 @@ namespace gsr { return strcmp(capture_target, "window") != 0 && strcmp(capture_target, "focused") != 0 && strcmp(capture_target, "region") != 0 && strcmp(capture_target, "portal") != 0 && contains_non_hex_number(capture_target); } - static std::string capture_target_get_notification_name(const char *capture_target) { + static std::string capture_target_get_notification_name(const char *capture_target, bool save) { std::string result; if(is_capture_target_monitor(capture_target)) { result = "this monitor"; @@ -1585,9 +1586,11 @@ namespace gsr { sscanf(capture_target, "%" PRIi64, &window_id); const std::optional window_title = get_window_title(display, window_id); - if(window_title) { + if(save) { + result = "window"; + } else if(window_title) { result = strip(window_title.value()); - truncate_string(result, 20); + truncate_string(result, 30); result = "window \"" + result + "\""; } else { result = std::string("window ") + capture_target; @@ -1774,6 +1777,45 @@ namespace gsr { return result; } + static std::string to_duration_string(double duration_sec) { + int seconds = ceil(duration_sec); + + const int hours = seconds / 60 / 60; + seconds -= (hours * 60 * 60); + + const int minutes = seconds / 60; + seconds -= (minutes * 60); + + std::string result; + if(hours > 0) + result += std::to_string(hours) + " hour" + (hours == 1 ? "" : "s"); + + if(minutes > 0) { + if(!result.empty()) + result += " "; + result += std::to_string(minutes) + " minute" + (minutes == 1 ? "" : "s"); + } + + if(seconds > 0 || (hours == 0 && minutes == 0)) { + if(!result.empty()) + result += " "; + result += std::to_string(seconds) + " second" + (seconds == 1 ? "" : "s"); + } + + fprintf(stderr, "to duration string: %f, %d, %d, %d\n", duration_sec, seconds, minutes, hours); + + return result; + } + + double Overlay::get_time_passed_in_replay_buffer_seconds() { + double replay_duration_sec = replay_saved_duration_sec; + if(replay_duration_sec > replay_buffer_save_duration_sec) + replay_duration_sec = replay_buffer_save_duration_sec; + if(replay_save_duration_min > 0 && replay_duration_sec > replay_save_duration_min * 60) + replay_duration_sec = replay_save_duration_min * 60; + return replay_duration_sec; + } + void Overlay::save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type) { mgl_context *context = mgl_get_context(); Display *display = (Display*)context->connection; @@ -1794,7 +1836,7 @@ namespace gsr { const std::string new_video_filepath = video_directory + "/" + video_filename; rename(video_filepath, new_video_filepath.c_str()); - truncate_string(focused_window_name, 20); + truncate_string(focused_window_name, 40); const char *capture_target = nullptr; char msg[512]; @@ -1803,7 +1845,10 @@ namespace gsr { if(!config.record_config.show_video_saved_notifications) return; - snprintf(msg, sizeof(msg), "Saved a recording of %s to \"%s\"", capture_target_get_notification_name(recording_capture_target.c_str()).c_str(), focused_window_name.c_str()); + const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds()); + snprintf(msg, sizeof(msg), "Saved a %s recording of %s\nto \"%s\"", + duration_str.c_str(), + capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str()); capture_target = recording_capture_target.c_str(); break; } @@ -1811,13 +1856,10 @@ namespace gsr { if(!config.replay_config.show_replay_saved_notifications) return; - char duration[32]; - if(replay_save_duration_min > 0) - snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min); - else - snprintf(duration, sizeof(duration), " "); - - snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to \"%s\"", duration, capture_target_get_notification_name(recording_capture_target.c_str()).c_str(), focused_window_name.c_str()); + const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds()); + snprintf(msg, sizeof(msg), "Saved a %s replay of %s\nto \"%s\"", + duration_str.c_str(), + capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str()); capture_target = recording_capture_target.c_str(); break; } @@ -1825,7 +1867,8 @@ namespace gsr { if(!config.screenshot_config.show_screenshot_saved_notifications) return; - snprintf(msg, sizeof(msg), "Saved a screenshot of %s to \"%s\"", capture_target_get_notification_name(screenshot_capture_target.c_str()).c_str(), focused_window_name.c_str()); + snprintf(msg, sizeof(msg), "Saved a screenshot of %s\nto \"%s\"", + capture_target_get_notification_name(screenshot_capture_target.c_str(), true).c_str(), focused_window_name.c_str()); capture_target = screenshot_capture_target.c_str(); break; } @@ -1851,14 +1894,12 @@ namespace gsr { if(config.replay_config.save_video_in_game_folder) { save_video_in_current_game_directory(replay_saved_filepath, NotificationType::REPLAY); } else if(config.replay_config.show_replay_saved_notifications) { - char duration[32]; - if(replay_save_duration_min > 0) - snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min); - else - snprintf(duration, sizeof(duration), " "); + const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds()); char msg[512]; - snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, capture_target_get_notification_name(recording_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Saved a %s replay of %s", + duration_str.c_str(), + capture_target_get_notification_name(recording_capture_target.c_str(), true).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()); } } @@ -2006,7 +2047,8 @@ namespace gsr { save_video_in_current_game_directory(screenshot_filepath.c_str(), NotificationType::SCREENSHOT); } else if(config.screenshot_config.show_screenshot_saved_notifications) { char msg[512]; - snprintf(msg, sizeof(msg), "Saved a screenshot of %s", capture_target_get_notification_name(screenshot_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Saved a screenshot of %s", + capture_target_get_notification_name(screenshot_capture_target.c_str(), true).c_str()); show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::SCREENSHOT, screenshot_capture_target.c_str()); } } else { @@ -2104,8 +2146,12 @@ namespace gsr { if(config.record_config.save_video_in_game_folder) { save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD); } else if(config.record_config.show_video_saved_notifications) { + const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds()); + char msg[512]; - snprintf(msg, sizeof(msg), "Saved a recording of %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Saved a %s recording of %s", + duration_str.c_str(), + capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str()); show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str()); } } else { @@ -2394,6 +2440,9 @@ namespace gsr { replay_save_duration_min = 0; replay_save_show_notification = true; replay_save_clock.restart(); + replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds(); + if(replay_restart_on_save) + replay_duration_clock.restart(); kill(gpu_screen_recorder_process, SIGUSR1); } @@ -2404,6 +2453,7 @@ namespace gsr { replay_save_duration_min = 1; replay_save_show_notification = true; replay_save_clock.restart(); + replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds(); kill(gpu_screen_recorder_process, SIGRTMIN+3); } @@ -2414,6 +2464,7 @@ namespace gsr { replay_save_duration_min = 10; replay_save_show_notification = true; replay_save_clock.restart(); + replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds(); kill(gpu_screen_recorder_process, SIGRTMIN+5); } @@ -2564,6 +2615,9 @@ namespace gsr { if(config.replay_config.restart_replay_on_save && gsr_info.system_info.gsr_version >= GsrVersion{5, 0, 3}) { args.push_back("-restart-replay-on-save"); args.push_back("yes"); + replay_restart_on_save = true; + } else { + replay_restart_on_save = false; } if(gsr_info.system_info.gsr_version >= GsrVersion{5, 5, 0}) { @@ -2603,13 +2657,17 @@ namespace gsr { // to see when the program has exit. if(!disable_notification && config.replay_config.show_replay_started_notifications) { char msg[256]; - snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).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()); } if(config.replay_config.record_options.record_area_option == "portal") hide_ui = true; + // TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has + // selected what to capture and accepted it. + replay_duration_clock.restart(); + replay_buffer_save_duration_sec = config.replay_config.replay_time; return true; } @@ -2630,6 +2688,10 @@ namespace gsr { 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(); + + // TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has + // selected what to capture and accepted it. + recording_duration_clock.restart(); } replay_recording = true; kill(gpu_screen_recorder_process, SIGRTMIN); @@ -2647,6 +2709,10 @@ namespace gsr { 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(); + + // TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has + // selected what to capture and accepted it. + recording_duration_clock.restart(); } replay_recording = true; kill(gpu_screen_recorder_process, SIGRTMIN); @@ -2766,12 +2832,16 @@ namespace gsr { // 1... if(config.record_config.show_recording_started_notifications) { char msg[256]; - snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).c_str()); show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str()); } if(config.record_config.record_options.record_area_option == "portal") hide_ui = true; + + // TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has + // selected what to capture and accepted it. + recording_duration_clock.restart(); } static std::string streaming_get_url(const Config &config) { @@ -2943,7 +3013,7 @@ namespace gsr { // to see when the program has exit. if(config.streaming_config.show_streaming_started_notifications) { char msg[256]; - snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str()); + snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).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()); } @@ -2987,7 +3057,6 @@ namespace gsr { if(record_area_option == "region" && !finished_selection) { start_region_capture = true; on_region_selected = [this, force_type]() { - usleep(200 * 1000); // Hack: wait 0.2 seconds before taking a screenshot to allow user to move cursor away. TODO: Remove this on_press_take_screenshot(true, force_type); }; return;