Save recording status to file to reload it when gsr overlay restarts

This commit is contained in:
dec05eba
2024-09-22 18:17:46 +02:00
parent 5d6d57b881
commit 61c9b4918e
7 changed files with 270 additions and 66 deletions

View File

@@ -2,6 +2,7 @@
#include "../include/Theme.hpp"
#include "../include/Config.hpp"
#include "../include/Process.hpp"
#include "../include/Utils.hpp"
#include "../include/gui/StaticPage.hpp"
#include "../include/gui/DropdownButton.hpp"
#include "../include/gui/CustomRendererWidget.hpp"
@@ -11,9 +12,9 @@
#include <string.h>
#include <assert.h>
// TODO: Remove
#include <signal.h>
#include <sys/wait.h>
#include <limits.h>
#include <stdexcept>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -196,19 +197,21 @@ namespace gsr {
close_button_widget({0.0f, 0.0f})
{
memset(&window_texture, 0, sizeof(window_texture));
load_program_status();
load_program_pid();
}
Overlay::~Overlay() {
hide();
if(gpu_screen_recorder_process > 0) {
kill(gpu_screen_recorder_process, SIGINT);
int status;
if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
perror("waitpid failed");
/* Ignore... */
}
gpu_screen_recorder_process = -1;
}
// if(gpu_screen_recorder_process > 0) {
// kill(gpu_screen_recorder_process, SIGINT);
// int status;
// if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
// perror("waitpid failed");
// /* Ignore... */
// }
// gpu_screen_recorder_process = -1;
// }
}
void Overlay::on_event(mgl::Event &event, mgl::Window &window) {
@@ -224,6 +227,8 @@ namespace gsr {
}
void Overlay::draw(mgl::Window &window) {
update_gsr_process_status();
if(!visible)
return;
@@ -400,6 +405,9 @@ namespace gsr {
event.mouse_move.x = window.get_mouse_position().x;
event.mouse_move.y = window.get_mouse_position().y;
on_event(event, window);
if(gpu_screen_recorder_process > 0 && recording_status == RecordingStatus::RECORD)
update_ui_recording_started();
}
void Overlay::hide() {
@@ -431,6 +439,185 @@ namespace gsr {
return visible;
}
void Overlay::update_gsr_process_status() {
if(gpu_screen_recorder_process <= 0)
return;
errno = 0;
int status;
if(waitpid(gpu_screen_recorder_process, &status, WNOHANG) == 0) {
// Still running
return;
}
int exit_code = -1;
// The process is no longer a child process since gsr overlay has restarted
if(errno == ECHILD) {
errno = 0;
kill(gpu_screen_recorder_process, 0);
if(errno != ESRCH) {
// Still running
return;
}
// We cant know the exit status, so we assume it succeeded
exit_code = 0;
} else {
if(WIFEXITED(status))
exit_code = WEXITSTATUS(status);
}
gpu_screen_recorder_process = -1;
recording_status = RecordingStatus::NONE;
recording_stopped_remove_runtime_files();
update_ui_recording_stopped();
if(exit_code == 0) {
if(config->record_config.show_video_saved_notifications) {
const std::string tint_color_as_hex = color_to_hex_str(get_theme().tint_color);
const char *notification_args[] = {
"gsr-notify", "--text", "Recording has been saved", "--timeout", "3.0",
"--icon", "record",
"--icon-color", "ffffff", "--bg-color", tint_color_as_hex.c_str(),
nullptr
};
exec_program_daemonized(notification_args);
}
} else {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
const char *notification_args[] = {
"gsr-notify", "--text", "Failed to start/save recording", "--timeout", "3.0",
"--icon", "record",
"--icon-color", "ff0000", "--bg-color", "ff0000",
nullptr
};
exec_program_daemonized(notification_args);
}
}
static RecordingStatus recording_status_from_string(const char *status) {
RecordingStatus recording_status = RecordingStatus::NONE;
if(strcmp(status, "none") == 0)
recording_status = RecordingStatus::NONE;
else if(strcmp(status, "replay") == 0)
recording_status = RecordingStatus::REPLAY;
else if(strcmp(status, "record") == 0)
recording_status = RecordingStatus::RECORD;
else if(strcmp(status, "stream") == 0)
recording_status = RecordingStatus::STREAM;
return recording_status;
}
static const char* recording_status_to_string(RecordingStatus status) {
switch(status) {
case RecordingStatus::NONE: return "none";
case RecordingStatus::REPLAY: return "replay";
case RecordingStatus::RECORD: return "record";
case RecordingStatus::STREAM: return "stream";
}
return "none";
}
void Overlay::load_program_status() {
recording_status = RecordingStatus::NONE;
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
if(!status_filepath)
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-overlay or /tmp/gsr-overlay");
status_filepath.value() += "/status";
std::string file_content;
if(!file_get_content(status_filepath.value().c_str(), file_content))
return;
recording_status = recording_status_from_string(file_content.c_str());
}
void Overlay::save_program_status() {
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
if(!status_filepath)
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-overlay or /tmp/gsr-overlay");
status_filepath.value() += "/status";
if(!file_overwrite(status_filepath.value().c_str(), recording_status_to_string(recording_status)))
fprintf(stderr, "Error: failed to update status to file %s\n", status_filepath.value().c_str());
}
void Overlay::load_program_pid() {
gpu_screen_recorder_process = -1;
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
if(!status_filepath)
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-overlay or /tmp/gsr-overlay");
status_filepath.value() += "/pid";
std::string file_content;
if(!file_get_content(status_filepath.value().c_str(), file_content))
return;
int pid = -1;
if(sscanf(file_content.c_str(), "%d", &pid) != 1) {
fprintf(stderr, "Error: failed to read pid from file %s, content: %s\n", status_filepath.value().c_str(), file_content.c_str());
return;
}
char cmdline_path[256];
snprintf(cmdline_path, sizeof(cmdline_path), "/proc/%d/cmdline", pid);
char program_arg0[PATH_MAX];
program_arg0[0] = '\0';
if(!read_cmdline_arg0(cmdline_path, program_arg0)) {
fprintf(stderr, "Error: failed to parse arg0 from file %s. Was the gpu-screen-recorder process that was started by gsr-overlay closed by another program or the user?\n", cmdline_path);
return;
}
if(strcmp(program_arg0, "gpu-screen-recorder") != 0) {
fprintf(stderr, "Warning: process %d exists but doesn't belong to gpu-screen-recorder (is instead %s). Was the gpu-screen-recorder process that was started by gsr-overlay closed by another program or the user?\n", pid, program_arg0);
return;
}
gpu_screen_recorder_process = pid;
}
void Overlay::save_program_pid() {
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
if(!status_filepath)
throw std::runtime_error("Failed to find/create runtime directory /run/user/.../gsr-overlay or /tmp/gsr-overlay");
status_filepath.value() += "/pid";
char str[32];
snprintf(str, sizeof(str), "%d", (int)gpu_screen_recorder_process);
if(!file_overwrite(status_filepath.value().c_str(), str))
fprintf(stderr, "Error: failed to update pid to file %s\n", status_filepath.value().c_str());
}
void Overlay::recording_stopped_remove_runtime_files() {
std::optional<std::string> status_filepath = get_gsr_runtime_dir();
if(!status_filepath) {
fprintf(stderr, "Error: Failed to find/create runtime directory /run/user/.../gsr-overlay or /tmp/gsr-overlay");
return;
}
const std::string status_file = status_filepath.value() + "/status";
const std::string pid_file = status_filepath.value() + "/pid";
remove(status_file.c_str());
remove(pid_file.c_str());
}
void Overlay::update_ui_recording_started() {
record_dropdown_button_ptr->set_item_label("start", "Stop and save");
record_dropdown_button_ptr->set_activated(true);
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
}
void Overlay::update_ui_recording_stopped() {
record_dropdown_button_ptr->set_item_label("start", "Start");
record_dropdown_button_ptr->set_activated(false);
record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
}
void Overlay::on_press_start_replay(const std::string &id) {
if(id == "settings") {
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, gsr_info, audio_devices, config, &page_stack);
@@ -511,9 +698,9 @@ namespace gsr {
// return;
//exit(0);
gpu_screen_recorder_process = -1;
record_dropdown_button_ptr->set_item_label(id, "Start");
record_dropdown_button_ptr->set_activated(false);
record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
recording_status = RecordingStatus::NONE;
recording_stopped_remove_runtime_files();
update_ui_recording_stopped();
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
if(config->record_config.show_video_saved_notifications) {
@@ -570,9 +757,10 @@ namespace gsr {
if(gpu_screen_recorder_process == -1) {
// TODO: Show notification failed to start
} else {
record_dropdown_button_ptr->set_item_label(id, "Stop and save");
record_dropdown_button_ptr->set_activated(true);
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
recording_status = RecordingStatus::RECORD;
save_program_status();
save_program_pid();
update_ui_recording_started();
}
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.