mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-05-07 15:19:56 +09:00
Save recording status to file to reload it when gsr overlay restarts
This commit is contained in:
4
TODO
4
TODO
@@ -42,3 +42,7 @@ Save gsr info data to file to quick re-open.
|
|||||||
Support wayland (excluding gnome, or force xwayland on gnome).
|
Support wayland (excluding gnome, or force xwayland on gnome).
|
||||||
|
|
||||||
Restart replay on system start if monitor resolution changes.
|
Restart replay on system start if monitor resolution changes.
|
||||||
|
|
||||||
|
Show warning when selecting hevc/av1 on amd because of amd driver/ffmpeg bug.
|
||||||
|
|
||||||
|
Update gsr info and validate saved config when monitors update. The selected monitor/audio may no longer be available.
|
||||||
@@ -15,6 +15,13 @@
|
|||||||
namespace gsr {
|
namespace gsr {
|
||||||
class DropdownButton;
|
class DropdownButton;
|
||||||
|
|
||||||
|
enum class RecordingStatus {
|
||||||
|
NONE,
|
||||||
|
REPLAY,
|
||||||
|
RECORD,
|
||||||
|
STREAM
|
||||||
|
};
|
||||||
|
|
||||||
class Overlay {
|
class Overlay {
|
||||||
public:
|
public:
|
||||||
Overlay(mgl::Window &window, std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs, mgl::Color bg_color);
|
Overlay(mgl::Window &window, std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs, mgl::Color bg_color);
|
||||||
@@ -30,6 +37,17 @@ namespace gsr {
|
|||||||
void toggle_show();
|
void toggle_show();
|
||||||
bool is_open() const;
|
bool is_open() const;
|
||||||
private:
|
private:
|
||||||
|
void update_gsr_process_status();
|
||||||
|
|
||||||
|
void load_program_status();
|
||||||
|
void save_program_status();
|
||||||
|
void load_program_pid();
|
||||||
|
void save_program_pid();
|
||||||
|
void recording_stopped_remove_runtime_files();
|
||||||
|
|
||||||
|
void update_ui_recording_started();
|
||||||
|
void update_ui_recording_stopped();
|
||||||
|
|
||||||
void on_press_start_replay(const std::string &id);
|
void on_press_start_replay(const std::string &id);
|
||||||
void on_press_start_record(const std::string &id);
|
void on_press_start_record(const std::string &id);
|
||||||
void on_press_start_stream(const std::string &id);
|
void on_press_start_stream(const std::string &id);
|
||||||
@@ -60,5 +78,7 @@ namespace gsr {
|
|||||||
DropdownButton *replay_dropdown_button_ptr = nullptr;
|
DropdownButton *replay_dropdown_button_ptr = nullptr;
|
||||||
DropdownButton *record_dropdown_button_ptr = nullptr;
|
DropdownButton *record_dropdown_button_ptr = nullptr;
|
||||||
DropdownButton *stream_dropdown_button_ptr = nullptr;
|
DropdownButton *stream_dropdown_button_ptr = nullptr;
|
||||||
|
|
||||||
|
RecordingStatus recording_status = RecordingStatus::NONE;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -14,5 +14,6 @@ namespace gsr {
|
|||||||
bool exec_program_daemonized(const char **args);
|
bool exec_program_daemonized(const char **args);
|
||||||
// Arguments ending with NULL
|
// Arguments ending with NULL
|
||||||
pid_t exec_program(const char **args);
|
pid_t exec_program(const char **args);
|
||||||
bool is_gpu_screen_recorder_running(pid_t &gsr_pid, GsrMode &mode);
|
// |output_buffer| should be at least PATH_MAX in size
|
||||||
|
bool read_cmdline_arg0(const char *filepath, char *output_buffer);
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
namespace gsr {
|
namespace gsr {
|
||||||
struct KeyValue {
|
struct KeyValue {
|
||||||
@@ -23,10 +24,14 @@ namespace gsr {
|
|||||||
std::map<std::string, std::string> get_xdg_variables();
|
std::map<std::string, std::string> get_xdg_variables();
|
||||||
|
|
||||||
std::string get_videos_dir();
|
std::string get_videos_dir();
|
||||||
|
// Returns 0 on success
|
||||||
int create_directory_recursive(char *path);
|
int create_directory_recursive(char *path);
|
||||||
bool file_get_content(const char *filepath, std::string &file_content);
|
bool file_get_content(const char *filepath, std::string &file_content);
|
||||||
|
bool file_overwrite(const char *filepath, const std::string &data);
|
||||||
|
|
||||||
// Returns the path to the parent directory (ignoring trailing /)
|
// Returns the path to the parent directory (ignoring trailing /)
|
||||||
// of "." if there is no parent directory and the directory path is relative
|
// of "." if there is no parent directory and the directory path is relative
|
||||||
std::string get_parent_directory(std::string_view directory);
|
std::string get_parent_directory(std::string_view directory);
|
||||||
|
|
||||||
|
std::optional<std::string> get_gsr_runtime_dir();
|
||||||
}
|
}
|
||||||
222
src/Overlay.cpp
222
src/Overlay.cpp
@@ -2,6 +2,7 @@
|
|||||||
#include "../include/Theme.hpp"
|
#include "../include/Theme.hpp"
|
||||||
#include "../include/Config.hpp"
|
#include "../include/Config.hpp"
|
||||||
#include "../include/Process.hpp"
|
#include "../include/Process.hpp"
|
||||||
|
#include "../include/Utils.hpp"
|
||||||
#include "../include/gui/StaticPage.hpp"
|
#include "../include/gui/StaticPage.hpp"
|
||||||
#include "../include/gui/DropdownButton.hpp"
|
#include "../include/gui/DropdownButton.hpp"
|
||||||
#include "../include/gui/CustomRendererWidget.hpp"
|
#include "../include/gui/CustomRendererWidget.hpp"
|
||||||
@@ -11,9 +12,9 @@
|
|||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
// TODO: Remove
|
|
||||||
#include <signal.h>
|
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
@@ -196,19 +197,21 @@ namespace gsr {
|
|||||||
close_button_widget({0.0f, 0.0f})
|
close_button_widget({0.0f, 0.0f})
|
||||||
{
|
{
|
||||||
memset(&window_texture, 0, sizeof(window_texture));
|
memset(&window_texture, 0, sizeof(window_texture));
|
||||||
|
load_program_status();
|
||||||
|
load_program_pid();
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlay::~Overlay() {
|
Overlay::~Overlay() {
|
||||||
hide();
|
hide();
|
||||||
if(gpu_screen_recorder_process > 0) {
|
// if(gpu_screen_recorder_process > 0) {
|
||||||
kill(gpu_screen_recorder_process, SIGINT);
|
// kill(gpu_screen_recorder_process, SIGINT);
|
||||||
int status;
|
// int status;
|
||||||
if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
|
// if(waitpid(gpu_screen_recorder_process, &status, 0) == -1) {
|
||||||
perror("waitpid failed");
|
// perror("waitpid failed");
|
||||||
/* Ignore... */
|
// /* Ignore... */
|
||||||
}
|
// }
|
||||||
gpu_screen_recorder_process = -1;
|
// gpu_screen_recorder_process = -1;
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::on_event(mgl::Event &event, mgl::Window &window) {
|
void Overlay::on_event(mgl::Event &event, mgl::Window &window) {
|
||||||
@@ -224,6 +227,8 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::draw(mgl::Window &window) {
|
void Overlay::draw(mgl::Window &window) {
|
||||||
|
update_gsr_process_status();
|
||||||
|
|
||||||
if(!visible)
|
if(!visible)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -400,6 +405,9 @@ namespace gsr {
|
|||||||
event.mouse_move.x = window.get_mouse_position().x;
|
event.mouse_move.x = window.get_mouse_position().x;
|
||||||
event.mouse_move.y = window.get_mouse_position().y;
|
event.mouse_move.y = window.get_mouse_position().y;
|
||||||
on_event(event, window);
|
on_event(event, window);
|
||||||
|
|
||||||
|
if(gpu_screen_recorder_process > 0 && recording_status == RecordingStatus::RECORD)
|
||||||
|
update_ui_recording_started();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Overlay::hide() {
|
void Overlay::hide() {
|
||||||
@@ -431,6 +439,185 @@ namespace gsr {
|
|||||||
return visible;
|
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) {
|
void Overlay::on_press_start_replay(const std::string &id) {
|
||||||
if(id == "settings") {
|
if(id == "settings") {
|
||||||
auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, gsr_info, audio_devices, config, &page_stack);
|
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;
|
// return;
|
||||||
//exit(0);
|
//exit(0);
|
||||||
gpu_screen_recorder_process = -1;
|
gpu_screen_recorder_process = -1;
|
||||||
record_dropdown_button_ptr->set_item_label(id, "Start");
|
recording_status = RecordingStatus::NONE;
|
||||||
record_dropdown_button_ptr->set_activated(false);
|
recording_stopped_remove_runtime_files();
|
||||||
record_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
|
update_ui_recording_stopped();
|
||||||
|
|
||||||
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
|
// 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) {
|
if(config->record_config.show_video_saved_notifications) {
|
||||||
@@ -570,9 +757,10 @@ namespace gsr {
|
|||||||
if(gpu_screen_recorder_process == -1) {
|
if(gpu_screen_recorder_process == -1) {
|
||||||
// TODO: Show notification failed to start
|
// TODO: Show notification failed to start
|
||||||
} else {
|
} else {
|
||||||
record_dropdown_button_ptr->set_item_label(id, "Stop and save");
|
recording_status = RecordingStatus::RECORD;
|
||||||
record_dropdown_button_ptr->set_activated(true);
|
save_program_status();
|
||||||
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
|
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.
|
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
|
||||||
|
|||||||
@@ -58,17 +58,9 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_number(const char *str) {
|
bool read_cmdline_arg0(const char *filepath, char *output_buffer) {
|
||||||
while(*str) {
|
output_buffer[0] = '\0';
|
||||||
char c = *str;
|
|
||||||
if(c < '0' || c > '9')
|
|
||||||
return false;
|
|
||||||
++str;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool read_cmdline(const char *filepath, char *output_buffer) {
|
|
||||||
const char *arg0_end = NULL;
|
const char *arg0_end = NULL;
|
||||||
int fd = open(filepath, O_RDONLY);
|
int fd = open(filepath, O_RDONLY);
|
||||||
if(fd == -1)
|
if(fd == -1)
|
||||||
@@ -92,41 +84,4 @@ namespace gsr {
|
|||||||
close(fd);
|
close(fd);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static pid_t pidof(const char *process_name) {
|
|
||||||
pid_t result = -1;
|
|
||||||
DIR *dir = opendir("/proc");
|
|
||||||
if(!dir)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
char cmdline_filepath[PATH_MAX];
|
|
||||||
char arg0[PATH_MAX];
|
|
||||||
|
|
||||||
struct dirent *entry;
|
|
||||||
while((entry = readdir(dir)) != NULL) {
|
|
||||||
if(!is_number(entry->d_name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
snprintf(cmdline_filepath, sizeof(cmdline_filepath), "/proc/%s/cmdline", entry->d_name);
|
|
||||||
if(read_cmdline(cmdline_filepath, arg0) && strcmp(process_name, arg0) == 0) {
|
|
||||||
result = atoi(entry->d_name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
closedir(dir);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_gpu_screen_recorder_running(pid_t &gsr_pid, GsrMode &mode) {
|
|
||||||
// TODO: Set |mode| by checking cmdline
|
|
||||||
gsr_pid = pidof("gpu-screen-recorder");
|
|
||||||
if(gsr_pid == -1) {
|
|
||||||
mode = GsrMode::Unknown;
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
mode = GsrMode::Record;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -167,6 +167,20 @@ namespace gsr {
|
|||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool file_overwrite(const char *filepath, const std::string &data) {
|
||||||
|
bool success = false;
|
||||||
|
|
||||||
|
FILE *file = fopen(filepath, "wb");
|
||||||
|
if(!file)
|
||||||
|
return success;
|
||||||
|
|
||||||
|
if(fwrite(data.data(), 1, data.size(), file) == data.size())
|
||||||
|
success = true;
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
std::string get_parent_directory(std::string_view directory) {
|
std::string get_parent_directory(std::string_view directory) {
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
@@ -184,4 +198,21 @@ namespace gsr {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> get_gsr_runtime_dir() {
|
||||||
|
std::optional<std::string> result;
|
||||||
|
char runtime_dir_path[256];
|
||||||
|
snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/run/user/%u", (unsigned int)getuid());
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
if(stat(runtime_dir_path, &st) == -1 || !S_ISDIR(st.st_mode))
|
||||||
|
snprintf(runtime_dir_path, sizeof(runtime_dir_path), "/tmp");
|
||||||
|
|
||||||
|
strcat(runtime_dir_path, "/gsr-overlay");
|
||||||
|
if(create_directory_recursive(runtime_dir_path) != 0)
|
||||||
|
return result;
|
||||||
|
|
||||||
|
result = runtime_dir_path;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user