mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Copy Config from gpu-screen-recorder-gtk, make it more modern and efficient with string_view and variant, use string_view in gsr info parsing
This commit is contained in:
153
src/Config.cpp
Normal file
153
src/Config.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
#include "../include/Config.hpp"
|
||||
#include <variant>
|
||||
#include <limits.h>
|
||||
#include <inttypes.h>
|
||||
#include <libgen.h>
|
||||
|
||||
namespace gsr {
|
||||
#define FORMAT_I32 "%" PRIi32
|
||||
#define FORMAT_I64 "%" PRIi64
|
||||
#define FORMAT_U32 "%" PRIu32
|
||||
|
||||
using ConfigValue = std::variant<bool*, std::string*, int32_t*, ConfigHotkey*, std::vector<std::string>*>;
|
||||
|
||||
static std::map<std::string_view, ConfigValue> get_config_options(Config &config) {
|
||||
return {
|
||||
{"main.record_area_option", &config.main_config.record_area_option},
|
||||
{"main.record_area_width", &config.main_config.record_area_width},
|
||||
{"main.record_area_height", &config.main_config.record_area_height},
|
||||
{"main.fps", &config.main_config.fps},
|
||||
{"main.merge_audio_tracks", &config.main_config.merge_audio_tracks},
|
||||
{"main.audio_input", &config.main_config.audio_input},
|
||||
{"main.color_range", &config.main_config.color_range},
|
||||
{"main.quality", &config.main_config.quality},
|
||||
{"main.codec", &config.main_config.video_codec},
|
||||
{"main.audio_codec", &config.main_config.audio_codec},
|
||||
{"main.framerate_mode", &config.main_config.framerate_mode},
|
||||
{"main.advanced_view", &config.main_config.advanced_view},
|
||||
{"main.overclock", &config.main_config.overclock},
|
||||
{"main.show_recording_started_notifications", &config.main_config.show_recording_started_notifications},
|
||||
{"main.show_recording_stopped_notifications", &config.main_config.show_recording_stopped_notifications},
|
||||
{"main.show_recording_saved_notifications", &config.main_config.show_recording_saved_notifications},
|
||||
{"main.record_cursor", &config.main_config.record_cursor},
|
||||
{"main.hide_window_when_recording", &config.main_config.hide_window_when_recording},
|
||||
{"main.software_encoding_warning_shown", &config.main_config.software_encoding_warning_shown},
|
||||
{"main.restore_portal_session", &config.main_config.restore_portal_session},
|
||||
|
||||
{"streaming.service", &config.streaming_config.streaming_service},
|
||||
{"streaming.youtube.key", &config.streaming_config.youtube.stream_key},
|
||||
{"streaming.twitch.key", &config.streaming_config.twitch.stream_key},
|
||||
{"streaming.custom.url", &config.streaming_config.custom.url},
|
||||
{"streaming.custom.container", &config.streaming_config.custom.container},
|
||||
{"streaming.start_stop_recording_hotkey", &config.streaming_config.start_stop_recording_hotkey},
|
||||
|
||||
{"record.save_directory", &config.record_config.save_directory},
|
||||
{"record.container", &config.record_config.container},
|
||||
{"record.start_stop_recording_hotkey", &config.record_config.start_stop_recording_hotkey},
|
||||
{"record.pause_unpause_recording_hotkey", &config.record_config.pause_unpause_recording_hotkey},
|
||||
|
||||
{"replay.save_directory", &config.replay_config.save_directory},
|
||||
{"replay.container", &config.replay_config.container},
|
||||
{"replay.time", &config.replay_config.replay_time},
|
||||
{"replay.start_stop_recording_hotkey", &config.replay_config.start_stop_recording_hotkey},
|
||||
{"replay.save_recording_hotkey", &config.replay_config.save_recording_hotkey}
|
||||
};
|
||||
}
|
||||
|
||||
Config read_config(bool &config_empty) {
|
||||
Config config;
|
||||
|
||||
const std::string config_path = get_config_dir() + "/config";
|
||||
std::string file_content;
|
||||
if(!file_get_content(config_path.c_str(), file_content)) {
|
||||
fprintf(stderr, "Warning: Failed to read config file: %s\n", config_path.c_str());
|
||||
config_empty = true;
|
||||
return config;
|
||||
}
|
||||
|
||||
auto config_options = get_config_options(config);
|
||||
|
||||
string_split_char(file_content, '\n', [&](std::string_view line) {
|
||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||
if(!key_value) {
|
||||
fprintf(stderr, "Warning: Invalid config option format: %.*s\n", (int)line.size(), line.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
if(key_value->key.empty() || key_value->value.empty())
|
||||
return true;
|
||||
|
||||
auto it = config_options.find(key_value->key);
|
||||
if(it == config_options.end())
|
||||
return true;
|
||||
|
||||
if(std::holds_alternative<bool*>(it->second)) {
|
||||
*std::get<bool*>(it->second) = key_value->value == "true";
|
||||
} else if(std::holds_alternative<std::string*>(it->second)) {
|
||||
std::get<std::string*>(it->second)->assign(key_value->value.data(), key_value->value.size());
|
||||
} else if(std::holds_alternative<int32_t*>(it->second)) {
|
||||
std::string value_str(key_value->value);
|
||||
int32_t *value = std::get<int32_t*>(it->second);
|
||||
if(sscanf(value_str.c_str(), FORMAT_I32, value) != 1) {
|
||||
fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
|
||||
*value = 0;
|
||||
}
|
||||
} else if(std::holds_alternative<ConfigHotkey*>(it->second)) {
|
||||
std::string value_str(key_value->value);
|
||||
ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it->second);
|
||||
if(sscanf(value_str.c_str(), FORMAT_I64 " " FORMAT_U32, &config_hotkey->keysym, &config_hotkey->modifiers) != 2) {
|
||||
fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
|
||||
config_hotkey->keysym = 0;
|
||||
config_hotkey->modifiers = 0;
|
||||
}
|
||||
} else if(std::holds_alternative<ConfigHotkey*>(it->second)) {
|
||||
std::string array_value(key_value->value);
|
||||
std::get<std::vector<std::string>*>(it->second)->push_back(std::move(array_value));
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
void save_config(Config &config) {
|
||||
const std::string config_path = get_config_dir() + "/config";
|
||||
|
||||
char dir_tmp[PATH_MAX];
|
||||
snprintf(dir_tmp, sizeof(dir_tmp), "%s", config_path.c_str());
|
||||
char *dir = dirname(dir_tmp);
|
||||
|
||||
if(create_directory_recursive(dir) != 0) {
|
||||
fprintf(stderr, "Warning: Failed to create config directory: %s\n", dir);
|
||||
return;
|
||||
}
|
||||
|
||||
FILE *file = fopen(config_path.c_str(), "wb");
|
||||
if(!file) {
|
||||
fprintf(stderr, "Warning: Failed to create config file: %s\n", config_path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto config_options = get_config_options(config);
|
||||
for(auto it : config_options) {
|
||||
if(std::holds_alternative<bool*>(it.second)) {
|
||||
fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), *std::get<bool*>(it.second) ? "true" : "false");
|
||||
} else if(std::holds_alternative<std::string*>(it.second)) {
|
||||
fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), std::get<std::string*>(it.second)->c_str());
|
||||
} else if(std::holds_alternative<int32_t*>(it.second)) {
|
||||
fprintf(file, "%.*s " FORMAT_I32 "\n", (int)it.first.size(), it.first.data(), *std::get<int32_t*>(it.second));
|
||||
} else if(std::holds_alternative<ConfigHotkey*>(it.second)) {
|
||||
const ConfigHotkey *config_hotkey = std::get<ConfigHotkey*>(it.second);
|
||||
fprintf(file, "%.*s " FORMAT_I64 " " FORMAT_U32 "\n", (int)it.first.size(), it.first.data(), config_hotkey->keysym, config_hotkey->modifiers);
|
||||
} else if(std::holds_alternative<ConfigHotkey*>(it.second)) {
|
||||
std::vector<std::string> *array = std::get<std::vector<std::string>*>(it.second);
|
||||
for(const std::string &value : *array) {
|
||||
fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), value.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
126
src/GsrInfo.cpp
126
src/GsrInfo.cpp
@@ -1,57 +1,38 @@
|
||||
#include "../include/GsrInfo.hpp"
|
||||
#include "../include/Utils.hpp"
|
||||
#include <optional>
|
||||
#include <string.h>
|
||||
#include <functional>
|
||||
|
||||
namespace gsr {
|
||||
using StringSplitCallback = std::function<bool(std::string_view line)>;
|
||||
|
||||
static void string_split_char(const std::string &str, char delimiter, StringSplitCallback callback_func) {
|
||||
size_t index = 0;
|
||||
while(index < str.size()) {
|
||||
size_t new_index = str.find(delimiter, index);
|
||||
if(new_index == std::string::npos)
|
||||
new_index = str.size();
|
||||
|
||||
if(!callback_func({str.data() + index, new_index - index}))
|
||||
break;
|
||||
|
||||
index = new_index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_system_info_line(GsrInfo *gsr_info, const std::string &line) {
|
||||
const size_t space_index = line.find(' ');
|
||||
if(space_index == std::string::npos)
|
||||
static void parse_system_info_line(GsrInfo *gsr_info, std::string_view line) {
|
||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||
if(!key_value)
|
||||
return;
|
||||
|
||||
const std::string_view attribute_name = {line.c_str(), space_index};
|
||||
const std::string_view attribute_value = {line.c_str() + space_index + 1, line.size() - (space_index + 1)};
|
||||
if(attribute_name == "display_server") {
|
||||
if(attribute_value == "x11")
|
||||
if(key_value->key == "display_server") {
|
||||
if(key_value->value == "x11")
|
||||
gsr_info->system_info.display_server = DisplayServer::X11;
|
||||
else if(attribute_value == "wayland")
|
||||
else if(key_value->value == "wayland")
|
||||
gsr_info->system_info.display_server = DisplayServer::WAYLAND;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_gpu_info_line(GsrInfo *gsr_info, const std::string &line) {
|
||||
const size_t space_index = line.find(' ');
|
||||
if(space_index == std::string::npos)
|
||||
static void parse_gpu_info_line(GsrInfo *gsr_info, std::string_view line) {
|
||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||
if(!key_value)
|
||||
return;
|
||||
|
||||
const std::string_view attribute_name = {line.c_str(), space_index};
|
||||
const std::string_view attribute_value = {line.c_str() + space_index + 1, line.size() - (space_index + 1)};
|
||||
if(attribute_name == "vendor") {
|
||||
if(attribute_value == "amd")
|
||||
if(key_value->key == "vendor") {
|
||||
if(key_value->value == "amd")
|
||||
gsr_info->gpu_info.vendor = GpuVendor::AMD;
|
||||
else if(attribute_value == "intel")
|
||||
else if(key_value->value == "intel")
|
||||
gsr_info->gpu_info.vendor = GpuVendor::INTEL;
|
||||
else if(attribute_value == "nvidia")
|
||||
else if(key_value->value == "nvidia")
|
||||
gsr_info->gpu_info.vendor = GpuVendor::NVIDIA;
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_video_codecs_line(GsrInfo *gsr_info, const std::string &line) {
|
||||
static void parse_video_codecs_line(GsrInfo *gsr_info, std::string_view line) {
|
||||
if(line == "h264")
|
||||
gsr_info->supported_video_codecs.h264 = true;
|
||||
else if(line == "h264_software")
|
||||
@@ -66,19 +47,23 @@ namespace gsr {
|
||||
gsr_info->supported_video_codecs.vp9 = true;
|
||||
}
|
||||
|
||||
static GsrMonitor capture_option_line_to_monitor(const std::string &line) {
|
||||
size_t space_index = line.find(' ');
|
||||
if(space_index == std::string::npos)
|
||||
return { line, {0, 0} };
|
||||
static std::optional<GsrMonitor> capture_option_line_to_monitor(std::string_view line) {
|
||||
std::optional<GsrMonitor> monitor;
|
||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||
if(!key_value)
|
||||
return monitor;
|
||||
|
||||
mgl::vec2i size = {0, 0};
|
||||
if(sscanf(line.c_str() + space_index + 1, "%dx%d", &size.x, &size.y) != 2)
|
||||
size = {0, 0};
|
||||
char value_buffer[256];
|
||||
snprintf(value_buffer, sizeof(value_buffer), "%.*s", (int)key_value->value.size(), key_value->value.data());
|
||||
|
||||
return { line.substr(0, space_index), size };
|
||||
monitor = GsrMonitor{std::string(key_value->key), mgl::vec2i{0, 0}};
|
||||
if(sscanf(value_buffer, "%dx%d", &monitor->size.x, &monitor->size.y) != 2)
|
||||
monitor->size = {0, 0};
|
||||
|
||||
return monitor;
|
||||
}
|
||||
|
||||
static void parse_capture_options_line(GsrInfo *gsr_info, const std::string &line) {
|
||||
static void parse_capture_options_line(GsrInfo *gsr_info, std::string_view line) {
|
||||
if(line == "window")
|
||||
gsr_info->supported_capture_options.window = true;
|
||||
else if(line == "focused")
|
||||
@@ -87,8 +72,11 @@ namespace gsr {
|
||||
gsr_info->supported_capture_options.screen = true;
|
||||
else if(line == "portal")
|
||||
gsr_info->supported_capture_options.portal = true;
|
||||
else
|
||||
gsr_info->supported_capture_options.monitors.push_back(capture_option_line_to_monitor(line));
|
||||
else {
|
||||
std::optional<GsrMonitor> monitor = capture_option_line_to_monitor(line);
|
||||
if(monitor)
|
||||
gsr_info->supported_capture_options.monitors.push_back(std::move(monitor.value()));
|
||||
}
|
||||
}
|
||||
|
||||
enum class GsrInfoSection {
|
||||
@@ -99,7 +87,7 @@ namespace gsr {
|
||||
CAPTURE_OPTIONS
|
||||
};
|
||||
|
||||
static bool starts_with(const std::string &str, const char *substr) {
|
||||
static bool starts_with(std::string_view str, const char *substr) {
|
||||
size_t len = strlen(substr);
|
||||
return str.size() >= len && memcmp(str.data(), substr, len) == 0;
|
||||
}
|
||||
@@ -123,18 +111,16 @@ namespace gsr {
|
||||
output[bytes_read] = '\0';
|
||||
|
||||
GsrInfoSection section = GsrInfoSection::UNKNOWN;
|
||||
string_split_char(output, '\n', [&](std::string_view line) {
|
||||
const std::string line_str(line.data(), line.size());
|
||||
|
||||
if(starts_with(line_str, "section=")) {
|
||||
const char *section_name = line_str.c_str() + 8;
|
||||
if(strcmp(section_name, "system_info") == 0)
|
||||
string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
|
||||
if(starts_with(line, "section=")) {
|
||||
const std::string_view section_name = line.substr(8);
|
||||
if(section_name == "system_info")
|
||||
section = GsrInfoSection::SYSTEM_INFO;
|
||||
else if(strcmp(section_name, "gpu_info") == 0)
|
||||
else if(section_name == "gpu_info")
|
||||
section = GsrInfoSection::GPU_INFO;
|
||||
else if(strcmp(section_name, "video_codecs") == 0)
|
||||
else if(section_name == "video_codecs")
|
||||
section = GsrInfoSection::VIDEO_CODECS;
|
||||
else if(strcmp(section_name, "capture_options") == 0)
|
||||
else if(section_name == "capture_options")
|
||||
section = GsrInfoSection::CAPTURE_OPTIONS;
|
||||
else
|
||||
section = GsrInfoSection::UNKNOWN;
|
||||
@@ -146,19 +132,19 @@ namespace gsr {
|
||||
break;
|
||||
}
|
||||
case GsrInfoSection::SYSTEM_INFO: {
|
||||
parse_system_info_line(gsr_info, line_str);
|
||||
parse_system_info_line(gsr_info, line);
|
||||
break;
|
||||
}
|
||||
case GsrInfoSection::GPU_INFO: {
|
||||
parse_gpu_info_line(gsr_info, line_str);
|
||||
parse_gpu_info_line(gsr_info, line);
|
||||
break;
|
||||
}
|
||||
case GsrInfoSection::VIDEO_CODECS: {
|
||||
parse_video_codecs_line(gsr_info, line_str);
|
||||
parse_video_codecs_line(gsr_info, line);
|
||||
break;
|
||||
}
|
||||
case GsrInfoSection::CAPTURE_OPTIONS: {
|
||||
parse_capture_options_line(gsr_info, line_str);
|
||||
parse_capture_options_line(gsr_info, line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -179,16 +165,13 @@ namespace gsr {
|
||||
return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
|
||||
}
|
||||
|
||||
static AudioDevice parse_audio_device_line(const std::string &line) {
|
||||
AudioDevice audio_device;
|
||||
const size_t space_index = line.find(' ');
|
||||
if(space_index == std::string::npos)
|
||||
static std::optional<AudioDevice> parse_audio_device_line(std::string_view line) {
|
||||
std::optional<AudioDevice> audio_device;
|
||||
const std::optional<KeyValue> key_value = parse_key_value(line);
|
||||
if(!key_value)
|
||||
return audio_device;
|
||||
|
||||
const std::string_view audio_input_name = {line.c_str(), space_index};
|
||||
const std::string_view audio_input_description = {line.c_str() + space_index + 1, line.size() - (space_index + 1)};
|
||||
audio_device.name.assign(audio_input_name.data(), audio_input_name.size());
|
||||
audio_device.description.assign(audio_input_description.data(), audio_input_description.size());
|
||||
audio_device = AudioDevice{std::string(key_value->key), std::string(key_value->value)};
|
||||
return audio_device;
|
||||
}
|
||||
|
||||
@@ -210,9 +193,10 @@ namespace gsr {
|
||||
}
|
||||
output[bytes_read] = '\0';
|
||||
|
||||
string_split_char(output, '\n', [&](std::string_view line) {
|
||||
const std::string line_str(line.data(), line.size());
|
||||
audio_devices.push_back(parse_audio_device_line(line_str));
|
||||
string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
|
||||
std::optional<AudioDevice> audio_device = parse_audio_device_line(line);
|
||||
if(audio_device)
|
||||
audio_devices.push_back(std::move(audio_device.value()));
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
176
src/Utils.cpp
Normal file
176
src/Utils.cpp
Normal file
@@ -0,0 +1,176 @@
|
||||
#include "../include/Utils.hpp"
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace gsr {
|
||||
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func) {
|
||||
size_t index = 0;
|
||||
while(index < str.size()) {
|
||||
size_t new_index = str.find(delimiter, index);
|
||||
if(new_index == std::string_view::npos)
|
||||
new_index = str.size();
|
||||
|
||||
if(!callback_func(str.substr(index, new_index - index)))
|
||||
break;
|
||||
|
||||
index = new_index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<KeyValue> parse_key_value(std::string_view line) {
|
||||
const size_t space_index = line.find(' ');
|
||||
if(space_index == std::string_view::npos)
|
||||
return std::nullopt;
|
||||
return KeyValue{line.substr(0, space_index), line.substr(space_index + 1)};
|
||||
}
|
||||
|
||||
std::string get_home_dir() {
|
||||
const char *home_dir = getenv("HOME");
|
||||
if(!home_dir) {
|
||||
passwd *pw = getpwuid(getuid());
|
||||
home_dir = pw->pw_dir;
|
||||
}
|
||||
|
||||
if(!home_dir) {
|
||||
fprintf(stderr, "Error: Failed to get home directory of user, using /tmp directory\n");
|
||||
home_dir = "/tmp";
|
||||
}
|
||||
|
||||
return home_dir;
|
||||
}
|
||||
|
||||
std::string get_config_dir() {
|
||||
std::string config_dir;
|
||||
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
|
||||
if(xdg_config_home) {
|
||||
config_dir = xdg_config_home;
|
||||
} else {
|
||||
config_dir = get_home_dir() + "/.config";
|
||||
}
|
||||
config_dir += "/gpu-screen-recorder";
|
||||
return config_dir;
|
||||
}
|
||||
|
||||
// Whoever designed xdg-user-dirs is retarded. Why are some XDG variables environment variables
|
||||
// while others are in this pseudo shell config file ~/.config/user-dirs.dirs
|
||||
std::map<std::string, std::string> get_xdg_variables() {
|
||||
std::string user_dirs_filepath;
|
||||
const char *xdg_config_home = getenv("XDG_CONFIG_HOME");
|
||||
if(xdg_config_home) {
|
||||
user_dirs_filepath = xdg_config_home;
|
||||
} else {
|
||||
user_dirs_filepath = get_home_dir() + "/.config";
|
||||
}
|
||||
|
||||
user_dirs_filepath += "/user-dirs.dirs";
|
||||
|
||||
std::map<std::string, std::string> result;
|
||||
FILE *f = fopen(user_dirs_filepath.c_str(), "rb");
|
||||
if(!f)
|
||||
return result;
|
||||
|
||||
char line[PATH_MAX];
|
||||
while(fgets(line, sizeof(line), f)) {
|
||||
int len = strlen(line);
|
||||
if(len < 2)
|
||||
continue;
|
||||
|
||||
if(line[0] == '#')
|
||||
continue;
|
||||
|
||||
if(line[len - 1] == '\n') {
|
||||
line[len - 1] = '\0';
|
||||
len--;
|
||||
}
|
||||
|
||||
if(line[len - 1] != '"')
|
||||
continue;
|
||||
|
||||
line[len - 1] = '\0';
|
||||
len--;
|
||||
|
||||
const char *sep = strchr(line, '=');
|
||||
if(!sep)
|
||||
continue;
|
||||
|
||||
if(sep[1] != '\"')
|
||||
continue;
|
||||
|
||||
std::string value(sep + 2);
|
||||
if(strncmp(value.c_str(), "$HOME/", 6) == 0)
|
||||
value = get_home_dir() + value.substr(5);
|
||||
|
||||
std::string key(line, sep - line);
|
||||
result[std::move(key)] = std::move(value);
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string get_videos_dir() {
|
||||
auto xdg_vars = get_xdg_variables();
|
||||
std::string xdg_videos_dir = xdg_vars["XDG_VIDEOS_DIR"];
|
||||
if(xdg_videos_dir.empty())
|
||||
xdg_videos_dir = get_home_dir() + "/Videos";
|
||||
return xdg_videos_dir;
|
||||
}
|
||||
|
||||
int create_directory_recursive(char *path) {
|
||||
int path_len = strlen(path);
|
||||
char *p = path;
|
||||
char *end = path + path_len;
|
||||
for(;;) {
|
||||
char *slash_p = strchr(p, '/');
|
||||
|
||||
// Skips first '/', we don't want to try and create the root directory
|
||||
if(slash_p == path) {
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!slash_p)
|
||||
slash_p = end;
|
||||
|
||||
char prev_char = *slash_p;
|
||||
*slash_p = '\0';
|
||||
int err = mkdir(path, S_IRWXU);
|
||||
*slash_p = prev_char;
|
||||
|
||||
if(err == -1 && errno != EEXIST)
|
||||
return err;
|
||||
|
||||
if(slash_p == end)
|
||||
break;
|
||||
else
|
||||
p = slash_p + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool file_get_content(const char *filepath, std::string &file_content) {
|
||||
file_content.clear();
|
||||
bool success = false;
|
||||
|
||||
FILE *file = fopen(filepath, "rb");
|
||||
if(!file)
|
||||
return success;
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
long file_size = ftell(file);
|
||||
if(file_size != -1) {
|
||||
file_content.resize(file_size);
|
||||
fseek(file, 0, SEEK_SET);
|
||||
if((long)fread(&file_content[0], 1, file_size, file) == file_size)
|
||||
success = true;
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
return success;
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,8 @@ namespace gsr {
|
||||
|
||||
bool Button::on_event(mgl::Event &event, mgl::Window&, mgl::vec2f offset) {
|
||||
const mgl::vec2f item_size = get_size().floor();
|
||||
if(event.type == mgl::Event::MouseMoved) {
|
||||
mouse_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_move.x, (float)event.mouse_move.y });
|
||||
} else if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
|
||||
const bool clicked_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });;
|
||||
if(event.type == mgl::Event::MouseButtonPressed && event.mouse_button.button == mgl::Mouse::Left) {
|
||||
const bool clicked_inside = mgl::FloatRect(position + offset, item_size).contains({ (float)event.mouse_button.x, (float)event.mouse_button.y });
|
||||
if(clicked_inside && on_click)
|
||||
on_click();
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ static void add_widgets_to_settings_page(mgl::vec2i window_size, mgl::vec2f sett
|
||||
auto audio_device_list = std::make_unique<gsr::List>(gsr::List::Orientation::HORIZONTAL, gsr::List::Alignment::CENTER);
|
||||
gsr::List *audio_device_list_ptr = audio_device_list.get();
|
||||
{
|
||||
audio_device_list->add_widget(std::make_unique<gsr::Label>(&gsr::get_theme().body_font, "*", gsr::get_theme().text_color));
|
||||
audio_device_list->add_widget(std::make_unique<gsr::Label>(&gsr::get_theme().body_font, " ", gsr::get_theme().text_color));
|
||||
auto audio_device_box = std::make_unique<gsr::ComboBox>(&gsr::get_theme().body_font);
|
||||
for(const auto &audio_device : audio_devices) {
|
||||
audio_device_box->add_item(audio_device.description, audio_device.name);
|
||||
|
||||
Reference in New Issue
Block a user