aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Config.cpp15
-rw-r--r--src/GsrInfo.cpp167
-rw-r--r--src/Overlay.cpp105
-rw-r--r--src/Process.cpp48
-rw-r--r--src/Theme.cpp48
-rw-r--r--src/gui/Button.cpp44
-rw-r--r--src/gui/GlobalSettingsPage.cpp83
-rw-r--r--src/gui/SettingsPage.cpp146
-rw-r--r--src/gui/Utils.cpp17
-rw-r--r--src/main.cpp11
10 files changed, 481 insertions, 203 deletions
diff --git a/src/Config.cpp b/src/Config.cpp
index 112688a..4deaaf4 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -13,7 +13,7 @@
#define CONFIG_FILE_VERSION 1
namespace gsr {
- Config::Config(const GsrInfo &gsr_info) {
+ Config::Config(const SupportedCaptureOptions &capture_options) {
const std::string default_save_directory = get_videos_dir();
streaming_config.record_options.video_quality = "custom";
@@ -29,10 +29,10 @@ namespace gsr {
replay_config.record_options.audio_tracks.push_back("default_output");
replay_config.record_options.video_bitrate = 45000;
- if(!gsr_info.supported_capture_options.monitors.empty()) {
- streaming_config.record_options.record_area_option = gsr_info.supported_capture_options.monitors.front().name;
- record_config.record_options.record_area_option = gsr_info.supported_capture_options.monitors.front().name;
- replay_config.record_options.record_area_option = gsr_info.supported_capture_options.monitors.front().name;
+ if(!capture_options.monitors.empty()) {
+ streaming_config.record_options.record_area_option = capture_options.monitors.front().name;
+ record_config.record_options.record_area_option = capture_options.monitors.front().name;
+ replay_config.record_options.record_area_option = capture_options.monitors.front().name;
}
}
@@ -49,6 +49,7 @@ namespace gsr {
return {
{"main.config_file_version", &config.main_config.config_file_version},
{"main.software_encoding_warning_shown", &config.main_config.software_encoding_warning_shown},
+ {"main.tint_color", &config.main_config.tint_color},
{"streaming.record_options.record_area_option", &config.streaming_config.record_options.record_area_option},
{"streaming.record_options.record_area_width", &config.streaming_config.record_options.record_area_width},
@@ -140,7 +141,7 @@ namespace gsr {
};
}
- std::optional<Config> read_config(const GsrInfo &gsr_info) {
+ std::optional<Config> read_config(const SupportedCaptureOptions &capture_options) {
std::optional<Config> config;
const std::string config_path = get_config_dir() + "/config_ui";
@@ -150,7 +151,7 @@ namespace gsr {
return config;
}
- config = Config(gsr_info);
+ config = Config(capture_options);
config->streaming_config.record_options.audio_tracks.clear();
config->record_config.record_options.audio_tracks.clear();
config->replay_config.record_options.audio_tracks.clear();
diff --git a/src/GsrInfo.cpp b/src/GsrInfo.cpp
index 276870b..c35ccfb 100644
--- a/src/GsrInfo.cpp
+++ b/src/GsrInfo.cpp
@@ -1,5 +1,7 @@
#include "../include/GsrInfo.hpp"
#include "../include/Utils.hpp"
+#include "../include/Process.hpp"
+
#include <optional>
#include <string.h>
@@ -38,6 +40,8 @@ namespace gsr {
gsr_info->gpu_info.vendor = GpuVendor::INTEL;
else if(key_value->value == "nvidia")
gsr_info->gpu_info.vendor = GpuVendor::NVIDIA;
+ } else if(key_value->key == "card_path") {
+ gsr_info->gpu_info.card_path = key_value->value;
}
}
@@ -64,38 +68,6 @@ namespace gsr {
gsr_info->supported_video_codecs.vp9 = true;
}
- 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;
-
- char value_buffer[256];
- snprintf(value_buffer, sizeof(value_buffer), "%.*s", (int)key_value->value.size(), key_value->value.data());
-
- 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, std::string_view line) {
- if(line == "window")
- gsr_info->supported_capture_options.window = true;
- else if(line == "focused")
- gsr_info->supported_capture_options.focused = true;
- else if(line == "screen")
- gsr_info->supported_capture_options.screen = true;
- else if(line == "portal")
- gsr_info->supported_capture_options.portal = true;
- 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 {
UNKNOWN,
SYSTEM_INFO,
@@ -112,23 +84,19 @@ namespace gsr {
GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info) {
*gsr_info = GsrInfo{};
- FILE *f = popen("gpu-screen-recorder --info", "r");
- if(!f) {
- fprintf(stderr, "error: 'gpu-screen-recorder --info' failed\n");
- return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
- }
-
- char output[8192];
- ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
- if(bytes_read < 0 || ferror(f)) {
- fprintf(stderr, "error: failed to read 'gpu-screen-recorder --info' output\n");
- pclose(f);
- return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ std::string stdout_str;
+ const char *args[] = { "gpu-screen-recorder", "--info", nullptr };
+ const int exit_status = exec_program_get_stdout(args, stdout_str);
+ switch(exit_status) {
+ case 0: break;
+ case 14: return GsrInfoExitStatus::BROKEN_DRIVERS;
+ case 22: return GsrInfoExitStatus::OPENGL_FAILED;
+ case 23: return GsrInfoExitStatus::NO_DRM_CARD;
+ default: return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
}
- output[bytes_read] = '\0';
GsrInfoSection section = GsrInfoSection::UNKNOWN;
- string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
+ string_split_char(stdout_str, '\n', [&](std::string_view line) {
if(starts_with(line, "section=")) {
const std::string_view section_name = line.substr(8);
if(section_name == "system_info")
@@ -161,7 +129,7 @@ namespace gsr {
break;
}
case GsrInfoSection::CAPTURE_OPTIONS: {
- parse_capture_options_line(gsr_info, line);
+ // Intentionally ignore, get capture options with get_supported_capture_options instead
break;
}
}
@@ -169,18 +137,7 @@ namespace gsr {
return true;
});
- int status = pclose(f);
- if(WIFEXITED(status)) {
- switch(WEXITSTATUS(status)) {
- case 0: return GsrInfoExitStatus::OK;
- case 14: return GsrInfoExitStatus::BROKEN_DRIVERS;
- case 22: return GsrInfoExitStatus::OPENGL_FAILED;
- case 23: return GsrInfoExitStatus::NO_DRM_CARD;
- default: return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
- }
- }
-
- return GsrInfoExitStatus::FAILED_TO_RUN_COMMAND;
+ return GsrInfoExitStatus::OK;
}
static std::optional<AudioDevice> parse_audio_device_line(std::string_view line) {
@@ -196,22 +153,14 @@ namespace gsr {
std::vector<AudioDevice> get_audio_devices() {
std::vector<AudioDevice> audio_devices;
- FILE *f = popen("gpu-screen-recorder --list-audio-devices", "r");
- if(!f) {
+ std::string stdout_str;
+ const char *args[] = { "gpu-screen-recorder", "--list-audio-devices", nullptr };
+ if(exec_program_get_stdout(args, stdout_str) != 0) {
fprintf(stderr, "error: 'gpu-screen-recorder --list-audio-devices' failed\n");
return audio_devices;
}
- char output[16384];
- ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
- if(bytes_read < 0 || ferror(f)) {
- fprintf(stderr, "error: failed to read 'gpu-screen-recorder --list-audio-devices' output\n");
- pclose(f);
- return audio_devices;
- }
- output[bytes_read] = '\0';
-
- string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
+ string_split_char(stdout_str, '\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()));
@@ -224,26 +173,78 @@ namespace gsr {
std::vector<std::string> get_application_audio() {
std::vector<std::string> application_audio;
- FILE *f = popen("gpu-screen-recorder --list-application-audio", "r");
- if(!f) {
+ std::string stdout_str;
+ const char *args[] = { "gpu-screen-recorder", "--list-application-audio", nullptr };
+ if(exec_program_get_stdout(args, stdout_str) != 0) {
fprintf(stderr, "error: 'gpu-screen-recorder --list-application-audio' failed\n");
return application_audio;
}
- char output[16384];
- ssize_t bytes_read = fread(output, 1, sizeof(output) - 1, f);
- if(bytes_read < 0 || ferror(f)) {
- fprintf(stderr, "error: failed to read 'gpu-screen-recorder --list-application-audio' output\n");
- pclose(f);
- return application_audio;
- }
- output[bytes_read] = '\0';
-
- string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
+ string_split_char(stdout_str, '\n', [&](std::string_view line) {
application_audio.emplace_back(line);
return true;
});
return application_audio;
}
+
+ 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;
+
+ char value_buffer[256];
+ snprintf(value_buffer, sizeof(value_buffer), "%.*s", (int)key_value->value.size(), key_value->value.data());
+
+ 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(SupportedCaptureOptions &capture_options, std::string_view line) {
+ if(line == "window")
+ capture_options.window = true;
+ else if(line == "focused")
+ capture_options.focused = true;
+ else if(line == "screen")
+ capture_options.screen = true;
+ else if(line == "portal")
+ capture_options.portal = true;
+ else {
+ std::optional<GsrMonitor> monitor = capture_option_line_to_monitor(line);
+ if(monitor)
+ capture_options.monitors.push_back(std::move(monitor.value()));
+ }
+ }
+
+ static const char* gpu_vendor_to_string(GpuVendor vendor) {
+ switch(vendor) {
+ case GpuVendor::UNKNOWN: return "unknown";
+ case GpuVendor::AMD: return "amd";
+ case GpuVendor::INTEL: return "intel";
+ case GpuVendor::NVIDIA: return "nvidia";
+ }
+ return "unknown";
+ }
+
+ SupportedCaptureOptions get_supported_capture_options(const GsrInfo &gsr_info) {
+ SupportedCaptureOptions capture_options;
+
+ std::string stdout_str;
+ const char *args[] = { "gpu-screen-recorder", "--list-capture-options", gsr_info.gpu_info.card_path.c_str(), gpu_vendor_to_string(gsr_info.gpu_info.vendor), nullptr };
+ if(exec_program_get_stdout(args, stdout_str) != 0) {
+ fprintf(stderr, "error: 'gpu-screen-recorder --list-capture-options' failed\n");
+ return capture_options;
+ }
+
+ string_split_char(stdout_str, '\n', [&](std::string_view line) {
+ parse_capture_options_line(capture_options, line);
+ return true;
+ });
+
+ return capture_options;
+ }
}
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 5a2783b..88625d6 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -7,8 +7,10 @@
#include "../include/gui/DropdownButton.hpp"
#include "../include/gui/CustomRendererWidget.hpp"
#include "../include/gui/SettingsPage.hpp"
+#include "../include/gui/GlobalSettingsPage.hpp"
#include "../include/gui/Utils.hpp"
#include "../include/gui/PageStack.hpp"
+#include "../include/gui/GsrPage.hpp"
#include "../include/WindowUtils.hpp"
#include "../include/GlobalHotkeys.hpp"
@@ -17,6 +19,7 @@
#include <sys/wait.h>
#include <limits.h>
#include <fcntl.h>
+#include <poll.h>
#include <stdexcept>
#include <X11/Xlib.h>
@@ -297,10 +300,11 @@ namespace gsr {
}
static mgl::vec2i create_window_get_center_position(Display *display) {
- const int screen = DefaultScreen(display);
+ const int size = 16;
XSetWindowAttributes window_attr;
window_attr.event_mask = StructureNotifyMask;
- const Window window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, 32, 32, 0, DefaultDepth(display, screen), InputOutput, DefaultVisual(display, screen), CWEventMask, &window_attr);
+ window_attr.background_pixel = 0;
+ const Window window = XCreateWindow(display, DefaultRootWindow(display), 0, 0, size, size, 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixel | CWEventMask, &window_attr);
if(!window)
return {0, 0};
@@ -319,12 +323,36 @@ namespace gsr {
const unsigned long opacity = (unsigned long)(0xFFFFFFFFul * alpha);
XChangeProperty(display, window, net_wm_window_opacity, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&opacity, 1L);
+ XSizeHints *size_hints = XAllocSizeHints();
+ size_hints->width = size;
+ size_hints->min_width = size;
+ size_hints->max_width = size;
+ size_hints->height = size;
+ size_hints->min_height = size;
+ size_hints->max_height = size;
+ size_hints->flags = PSize | PMinSize | PMaxSize;
+ XSetWMNormalHints(display, window, size_hints);
+ XFree(size_hints);
+
XMapWindow(display, window);
XFlush(display);
+ const int x_fd = XConnectionNumber(display);
mgl::vec2i window_pos;
XEvent xev;
while(true) {
+ struct pollfd poll_fd;
+ poll_fd.fd = x_fd;
+ poll_fd.events = POLLIN;
+ poll_fd.revents = 0;
+ const int fds_ready = poll(&poll_fd, 1, 1000);
+ if(fds_ready == 0) {
+ fprintf(stderr, "Error: timed out waiting for ConfigureNotify after XCreateWindow\n");
+ break;
+ } else if(fds_ready == -1 || !(poll_fd.revents & POLLIN)) {
+ continue;
+ }
+
XNextEvent(display, &xev);
if(xev.type == ConfigureNotify) {
window_pos.x = xev.xconfigure.x + xev.xconfigure.width / 2;
@@ -394,11 +422,11 @@ namespace gsr {
return true;
}
- Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs) :
+ Overlay::Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs) :
resources_path(std::move(resources_path)),
- gsr_info(gsr_info),
+ gsr_info(std::move(gsr_info)),
egl_funcs(egl_funcs),
- config(gsr_info),
+ config(capture_options),
bg_screenshot_overlay({0.0f, 0.0f}),
top_bar_background({0.0f, 0.0f}),
close_button_widget({0.0f, 0.0f})
@@ -417,11 +445,11 @@ namespace gsr {
memset(&window_texture, 0, sizeof(window_texture));
- std::optional<Config> new_config = read_config(gsr_info);
+ std::optional<Config> new_config = read_config(capture_options);
if(new_config)
config = std::move(new_config.value());
- init_color_theme(gsr_info);
+ init_color_theme(config, this->gsr_info);
power_supply_online_filepath = get_power_supply_online_filepath();
@@ -864,6 +892,7 @@ namespace gsr {
const int button_width = button_height;
auto main_buttons_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
+ List * main_buttons_list_ptr = main_buttons_list.get();
main_buttons_list->set_spacing(0.0f);
{
auto button = std::make_unique<DropdownButton>(&get_theme().title_font, &get_theme().body_font, "Instant Replay", "Off", &get_theme().replay_button_texture,
@@ -876,7 +905,7 @@ namespace gsr {
button->set_item_icon("save", &get_theme().save_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
- auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, gsr_info, config, &page_stack);
+ auto replay_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::REPLAY, &gsr_info, config, &page_stack);
page_stack.push(std::move(replay_settings_page));
} else if(id == "save") {
on_press_save_replay();
@@ -897,7 +926,7 @@ namespace gsr {
button->set_item_icon("pause", &get_theme().pause_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
- auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, gsr_info, config, &page_stack);
+ auto record_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::RECORD, &gsr_info, config, &page_stack);
page_stack.push(std::move(record_settings_page));
} else if(id == "pause") {
toggle_pause();
@@ -916,7 +945,7 @@ namespace gsr {
button->set_item_icon("start", &get_theme().play_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
- auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, gsr_info, config, &page_stack);
+ auto stream_settings_page = std::make_unique<SettingsPage>(SettingsPage::Type::STREAM, &gsr_info, config, &page_stack);
page_stack.push(std::move(stream_settings_page));
} else if(id == "start") {
on_press_start_stream();
@@ -929,6 +958,20 @@ namespace gsr {
main_buttons_list->set_position((mgl::vec2f(window_size.x * 0.5f, window_size.y * 0.25f) - main_buttons_list_size * 0.5f).floor());
front_page_ptr->add_widget(std::move(main_buttons_list));
+ {
+ const mgl::vec2f main_buttons_size = main_buttons_list_ptr->get_size();
+ const int settings_button_size = main_buttons_size.y * 0.2f;
+ auto button = std::make_unique<Button>(&get_theme().title_font, "", mgl::vec2f(settings_button_size, settings_button_size), mgl::Color(0, 0, 0, 180));
+ button->set_position((main_buttons_list_ptr->get_position() + main_buttons_size - mgl::vec2f(0.0f, settings_button_size) + mgl::vec2f(settings_button_size * 0.333f, 0.0f)).floor());
+ button->set_bg_hover_color(mgl::Color(0, 0, 0, 255));
+ button->set_icon(&get_theme().settings_small_texture);
+ button->on_click = [&]() {
+ auto settings_page = std::make_unique<GlobalSettingsPage>(&gsr_info, config, &page_stack);
+ page_stack.push(std::move(settings_page));
+ };
+ front_page_ptr->add_widget(std::move(button));
+ }
+
close_button_widget.draw_handler = [&](mgl::Window &window, mgl::vec2f pos, mgl::vec2f size) {
const int border_size = std::max(1.0f, 0.0015f * get_theme().window_height);
const float padding_size = std::max(1.0f, 0.003f * get_theme().window_height);
@@ -986,8 +1029,7 @@ namespace gsr {
// XFlush(display);
// }
- if(!prevent_game_minimizing)
- window->set_fullscreen(true);
+ window->set_fullscreen(true);
visible = true;
@@ -1526,6 +1568,24 @@ namespace gsr {
}
}
+ static bool validate_capture_target(const GsrInfo &gsr_info, const std::string &capture_target) {
+ const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
+ // TODO: Also check x11 window when enabled (check if capture_target is a decminal/hex number)
+ if(capture_target == "focused" && !capture_options.focused) {
+ return false;
+ } else if(capture_target == "screen" && !capture_options.screen) {
+ return false;
+ } else if(capture_target == "portal" && !capture_options.portal) {
+ return false;
+ } else {
+ for(const GsrMonitor &monitor : capture_options.monitors) {
+ if(capture_target == monitor.name)
+ return true;
+ }
+ return false;
+ }
+ }
+
void Overlay::on_press_save_replay() {
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
return;
@@ -1571,6 +1631,13 @@ namespace gsr {
return;
}
+ if(!validate_capture_target(gsr_info, config.replay_config.record_options.record_area_option)) {
+ 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", config.replay_config.record_options.record_area_option.c_str());
+ show_notification(err_msg, 3.0, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::REPLAY);
+ return;
+ }
+
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.replay_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
@@ -1678,6 +1745,13 @@ namespace gsr {
return;
}
+ if(!validate_capture_target(gsr_info, config.record_config.record_options.record_area_option)) {
+ 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", config.record_config.record_options.record_area_option.c_str());
+ show_notification(err_msg, 3.0, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::RECORD);
+ return;
+ }
+
record_filepath.clear();
// TODO: Validate input, fallback to valid values
@@ -1807,6 +1881,13 @@ namespace gsr {
return;
}
+ if(!validate_capture_target(gsr_info, config.streaming_config.record_options.record_area_option)) {
+ 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", config.streaming_config.record_options.record_area_option.c_str());
+ show_notification(err_msg, 3.0, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::STREAM);
+ return;
+ }
+
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.streaming_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.streaming_config.record_options.video_bitrate);
diff --git a/src/Process.cpp b/src/Process.cpp
index e5886c0..a9c5103 100644
--- a/src/Process.cpp
+++ b/src/Process.cpp
@@ -29,7 +29,7 @@ namespace gsr {
debug_print_args(args);
- pid_t pid = vfork();
+ const pid_t pid = vfork();
if(pid == -1) {
perror("Failed to vfork");
return false;
@@ -38,7 +38,7 @@ namespace gsr {
signal(SIGHUP, SIG_IGN);
// Daemonize child to make the parent the init process which will reap the zombie child
- pid_t second_child = vfork();
+ const pid_t second_child = vfork();
if(second_child == 0) { // child
execvp(args[0], (char* const*)args);
perror("execvp");
@@ -68,7 +68,7 @@ namespace gsr {
debug_print_args(args);
- pid_t pid = vfork();
+ const pid_t pid = vfork();
if(pid == -1) {
close(fds[PIPE_READ]);
close(fds[PIPE_WRITE]);
@@ -92,6 +92,48 @@ namespace gsr {
}
}
+ int exec_program_get_stdout(const char **args, std::string &result) {
+ result.clear();
+ int read_fd = -1;
+ const pid_t process_id = exec_program(args, &read_fd);
+ if(process_id == -1)
+ return -1;
+
+ int exit_status = 0;
+ char buffer[8192];
+ for(;;) {
+ ssize_t bytes_read = read(read_fd, buffer, sizeof(buffer));
+ if(bytes_read == 0) {
+ break;
+ } else if(bytes_read == -1) {
+ fprintf(stderr, "Failed to read from pipe to program %s, error: %s\n", args[0], strerror(errno));
+ exit_status = -1;
+ break;
+ }
+
+ buffer[bytes_read] = '\0';
+ result.append(buffer, bytes_read);
+ }
+
+ if(exit_status != 0)
+ kill(process_id, SIGKILL);
+
+ int status = 0;
+ if(waitpid(process_id, &status, 0) == -1) {
+ perror("waitpid failed");
+ exit_status = -1;
+ }
+
+ if(!WIFEXITED(status))
+ exit_status = -1;
+
+ if(exit_status == 0)
+ exit_status = WEXITSTATUS(status);
+
+ close(read_fd);
+ return exit_status;
+ }
+
bool read_cmdline_arg0(const char *filepath, char *output_buffer) {
output_buffer[0] = '\0';
diff --git a/src/Theme.cpp b/src/Theme.cpp
index a88aa1e..a6d1050 100644
--- a/src/Theme.cpp
+++ b/src/Theme.cpp
@@ -1,4 +1,5 @@
#include "../include/Theme.hpp"
+#include "../include/Config.hpp"
#include "../include/GsrInfo.hpp"
#include <assert.h>
@@ -7,6 +8,27 @@ namespace gsr {
static Theme *theme = nullptr;
static ColorTheme *color_theme = nullptr;
+ static mgl::Color gpu_vendor_to_color(GpuVendor vendor) {
+ switch(vendor) {
+ case GpuVendor::UNKNOWN: return mgl::Color(221, 0, 49);
+ case GpuVendor::AMD: return mgl::Color(221, 0, 49);
+ case GpuVendor::INTEL: return mgl::Color(8, 109, 183);
+ case GpuVendor::NVIDIA: return mgl::Color(118, 185, 0);
+ }
+ return mgl::Color(221, 0, 49);
+ }
+
+ static mgl::Color color_name_to_color(const std::string &color_name) {
+ GpuVendor vendor = GpuVendor::UNKNOWN;
+ if(color_name == "amd")
+ vendor = GpuVendor::AMD;
+ else if(color_name == "intel")
+ vendor = GpuVendor::INTEL;
+ else if(color_name == "nvidia")
+ vendor = GpuVendor::NVIDIA;
+ return gpu_vendor_to_color(vendor);
+ }
+
bool Theme::set_window_size(mgl::vec2i window_size) {
if(std::abs(window_size.x - window_width) < 0.1f && std::abs(window_size.y - window_height) < 0.1f)
return true;
@@ -44,6 +66,9 @@ namespace gsr {
if(!theme->settings_texture.load_from_file((resources_path + "images/settings.png").c_str()))
goto error;
+ if(!theme->settings_small_texture.load_from_file((resources_path + "images/settings_small.png").c_str()))
+ goto error;
+
if(!theme->folder_texture.load_from_file((resources_path + "images/folder.png").c_str()))
goto error;
@@ -102,29 +127,16 @@ namespace gsr {
return *theme;
}
- bool init_color_theme(const GsrInfo &gsr_info) {
+ bool init_color_theme(const Config &config, const GsrInfo &gsr_info) {
if(color_theme)
return true;
color_theme = new ColorTheme();
- switch(gsr_info.gpu_info.vendor) {
- case GpuVendor::UNKNOWN: {
- break;
- }
- case GpuVendor::AMD: {
- color_theme->tint_color = mgl::Color(221, 0, 49);
- break;
- }
- case GpuVendor::INTEL: {
- color_theme->tint_color = mgl::Color(8, 109, 183);
- break;
- }
- case GpuVendor::NVIDIA: {
- color_theme->tint_color = mgl::Color(118, 185, 0);
- break;
- }
- }
+ if(config.main_config.tint_color.empty())
+ color_theme->tint_color = gpu_vendor_to_color(gsr_info.gpu_info.vendor);
+ else
+ color_theme->tint_color = color_name_to_color(config.main_config.tint_color);
return true;
}
diff --git a/src/gui/Button.cpp b/src/gui/Button.cpp
index fbf5cdd..54d1854 100644
--- a/src/gui/Button.cpp
+++ b/src/gui/Button.cpp
@@ -12,7 +12,15 @@ namespace gsr {
static const float padding_left_scale = 0.007f;
static const float padding_right_scale = 0.007f;
- Button::Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color) : size(size), bg_color(bg_color), text(text, *font) {
+ // These are relative to the button size
+ static const float padding_top_icon_scale = 0.25f;
+ static const float padding_bottom_icon_scale = 0.25f;
+ static const float padding_left_icon_scale = 0.25f;
+ static const float padding_right_icon_scale = 0.25f;
+
+ Button::Button(mgl::Font *font, const char *text, mgl::vec2f size, mgl::Color bg_color) :
+ size(size), bg_color(bg_color), bg_hover_color(bg_color), text(text, *font)
+ {
}
@@ -37,17 +45,23 @@ namespace gsr {
return;
const mgl::vec2f draw_pos = position + offset;
-
const mgl::vec2f item_size = get_size().floor();
+ const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
+
mgl::Rectangle background(item_size);
background.set_position(draw_pos.floor());
- background.set_color(bg_color);
+ background.set_color(mouse_inside ? bg_hover_color : bg_color);
window.draw(background);
text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor());
window.draw(text);
- const bool mouse_inside = mgl::FloatRect(draw_pos, item_size).contains(window.get_mouse_position().to_vec2f()) && !has_parent_with_selected_child_widget();
+ if(sprite.get_texture() && sprite.get_texture()->is_valid()) {
+ scale_sprite_to_button_size();
+ sprite.set_position((background.get_position() + background.get_size() * 0.5f - sprite.get_size() * 0.5f).floor());
+ window.draw(sprite);
+ }
+
if(mouse_inside) {
const mgl::Color outline_color = (bg_color == get_color_theme().tint_color) ? mgl::Color(255, 255, 255) : get_color_theme().tint_color;
draw_rectangle_outline(window, draw_pos, item_size, outline_color, std::max(1.0f, border_scale * get_theme().window_height));
@@ -76,6 +90,14 @@ namespace gsr {
border_scale = scale;
}
+ void Button::set_bg_hover_color(mgl::Color color) {
+ bg_hover_color = color;
+ }
+
+ void Button::set_icon(mgl::Texture *texture) {
+ sprite.set_texture(texture);
+ }
+
const std::string& Button::get_text() const {
return text.get_string();
}
@@ -83,4 +105,18 @@ namespace gsr {
void Button::set_text(std::string str) {
text.set_string(std::move(str));
}
+
+ void Button::scale_sprite_to_button_size() {
+ if(!sprite.get_texture() || !sprite.get_texture()->is_valid())
+ return;
+
+ const mgl::vec2f button_size = get_size();
+ const int padding_icon_top = padding_top_icon_scale * button_size.y;
+ const int padding_icon_bottom = padding_bottom_icon_scale * button_size.y;
+ const int padding_icon_left = padding_left_icon_scale * button_size.y;
+ const int padding_icon_right = padding_right_icon_scale * button_size.y;
+
+ const mgl::vec2f desired_size = button_size - mgl::vec2f(padding_icon_left + padding_icon_right, padding_icon_top + padding_icon_bottom);
+ sprite.set_size(scale_keep_aspect_ratio(sprite.get_texture()->get_size().to_vec2f(), desired_size).floor());
+ }
} \ No newline at end of file
diff --git a/src/gui/GlobalSettingsPage.cpp b/src/gui/GlobalSettingsPage.cpp
new file mode 100644
index 0000000..6c25663
--- /dev/null
+++ b/src/gui/GlobalSettingsPage.cpp
@@ -0,0 +1,83 @@
+#include "../../include/gui/GlobalSettingsPage.hpp"
+
+#include "../../include/Theme.hpp"
+#include "../../include/gui/GsrPage.hpp"
+#include "../../include/gui/PageStack.hpp"
+#include "../../include/gui/ScrollablePage.hpp"
+#include "../../include/gui/Subsection.hpp"
+#include "../../include/gui/List.hpp"
+#include "../../include/gui/Label.hpp"
+#include "../../include/gui/RadioButton.hpp"
+
+namespace gsr {
+ static const char* gpu_vendor_to_color_name(GpuVendor vendor) {
+ switch(vendor) {
+ case GpuVendor::UNKNOWN: return "amd";
+ case GpuVendor::AMD: return "amd";
+ case GpuVendor::INTEL: return "intel";
+ case GpuVendor::NVIDIA: return "nvidia";
+ }
+ return "amd";
+ }
+
+ GlobalSettingsPage::GlobalSettingsPage(const GsrInfo *gsr_info, Config &config, PageStack *page_stack) :
+ StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
+ config(config),
+ gsr_info(gsr_info),
+ page_stack(page_stack)
+ {
+ auto content_page = std::make_unique<GsrPage>();
+ content_page->add_button("Back", "back", get_color_theme().page_bg_color);
+ content_page->on_click = [page_stack](const std::string &id) {
+ if(id == "back")
+ page_stack->pop();
+ };
+ content_page_ptr = content_page.get();
+ add_widget(std::move(content_page));
+
+ add_widgets();
+ load();
+ }
+
+ std::unique_ptr<Subsection> GlobalSettingsPage::create_appearance_subsection(ScrollablePage *parent_page) {
+ auto list = std::make_unique<List>(List::Orientation::VERTICAL);
+ list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Tint color", get_color_theme().text_color));
+ auto tint_color_radio_button = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::HORIZONTAL);
+ tint_color_radio_button_ptr = tint_color_radio_button.get();
+ tint_color_radio_button->add_item("Red", "amd");
+ tint_color_radio_button->add_item("Green", "nvidia");
+ tint_color_radio_button->add_item("blue", "intel");
+ tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) {
+ if(id == "amd")
+ get_color_theme().tint_color = mgl::Color(221, 0, 49);
+ else if(id == "nvidia")
+ get_color_theme().tint_color = mgl::Color(118, 185, 0);
+ else if(id == "intel")
+ get_color_theme().tint_color = mgl::Color(8, 109, 183);
+ };
+ list->add_widget(std::move(tint_color_radio_button));
+ return std::make_unique<Subsection>("Appearance", std::move(list), mgl::vec2f(parent_page->get_inner_size().x, 0.0f));
+ }
+
+ void GlobalSettingsPage::add_widgets() {
+ auto scrollable_page = std::make_unique<ScrollablePage>(content_page_ptr->get_inner_size());
+ scrollable_page->add_widget(create_appearance_subsection(scrollable_page.get()));
+ content_page_ptr->add_widget(std::move(scrollable_page));
+ }
+
+ void GlobalSettingsPage::on_navigate_away_from_page() {
+ save();
+ }
+
+ void GlobalSettingsPage::load() {
+ if(config.main_config.tint_color.empty())
+ tint_color_radio_button_ptr->set_selected_item(gpu_vendor_to_color_name(gsr_info->gpu_info.vendor));
+ else
+ tint_color_radio_button_ptr->set_selected_item(config.main_config.tint_color);
+ }
+
+ void GlobalSettingsPage::save() {
+ config.main_config.tint_color = tint_color_radio_button_ptr->get_selected_id();
+ save_config(config);
+ }
+} \ No newline at end of file
diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp
index 79f6c52..28821d1 100644
--- a/src/gui/SettingsPage.cpp
+++ b/src/gui/SettingsPage.cpp
@@ -22,15 +22,16 @@ namespace gsr {
APPLICATION_CUSTOM
};
- SettingsPage::SettingsPage(Type type, const GsrInfo &gsr_info, Config &config, PageStack *page_stack) :
+ SettingsPage::SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack) :
StaticPage(mgl::vec2f(get_theme().window_width, get_theme().window_height).floor()),
type(type),
config(config),
- page_stack(page_stack),
- settings_title_text("Settings", get_theme().title_font)
+ gsr_info(gsr_info),
+ page_stack(page_stack)
{
audio_devices = get_audio_devices();
application_audio = get_application_audio();
+ capture_options = get_supported_capture_options(*gsr_info);
auto content_page = std::make_unique<GsrPage>();
content_page->add_button("Back", "back", get_color_theme().page_bg_color);
@@ -41,9 +42,9 @@ namespace gsr {
content_page_ptr = content_page.get();
add_widget(std::move(content_page));
- add_widgets(gsr_info);
- add_page_specific_widgets(gsr_info);
- load(gsr_info);
+ add_widgets();
+ add_page_specific_widgets();
+ load();
}
std::unique_ptr<RadioButton> SettingsPage::create_view_radio_button() {
@@ -55,31 +56,32 @@ namespace gsr {
return view_radio_button;
}
- std::unique_ptr<ComboBox> SettingsPage::create_record_area_box(const GsrInfo &gsr_info) {
+ std::unique_ptr<ComboBox> SettingsPage::create_record_area_box() {
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
// TODO: Show options not supported but disable them
// TODO: Enable this
- //if(gsr_info.supported_capture_options.window)
+ //if(capture_options.window)
// record_area_box->add_item("Window", "window");
- if(gsr_info.supported_capture_options.focused)
+ if(capture_options.focused)
record_area_box->add_item("Follow focused window", "focused");
- if(gsr_info.supported_capture_options.screen)
- record_area_box->add_item("All monitors", "screen");
- for(const auto &monitor : gsr_info.supported_capture_options.monitors) {
+ // Do we really need this? it's only available on nvidia x11
+ //if(capture_options.screen)
+ // record_area_box->add_item("All monitors", "screen");
+ for(const auto &monitor : capture_options.monitors) {
char name[256];
snprintf(name, sizeof(name), "Monitor %s (%dx%d)", monitor.name.c_str(), monitor.size.x, monitor.size.y);
record_area_box->add_item(name, monitor.name);
}
- if(gsr_info.supported_capture_options.portal)
+ if(capture_options.portal)
record_area_box->add_item("Desktop portal", "portal");
record_area_box_ptr = record_area_box.get();
return record_area_box;
}
- std::unique_ptr<Widget> SettingsPage::create_record_area(const GsrInfo &gsr_info) {
+ std::unique_ptr<Widget> SettingsPage::create_record_area() {
auto record_area_list = std::make_unique<List>(List::Orientation::VERTICAL);
record_area_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Capture target:", get_color_theme().text_color));
- record_area_list->add_widget(create_record_area_box(gsr_info));
+ record_area_list->add_widget(create_record_area_box());
return record_area_list;
}
@@ -172,11 +174,11 @@ namespace gsr {
return checkbox;
}
- std::unique_ptr<Widget> SettingsPage::create_capture_target(const GsrInfo &gsr_info) {
+ std::unique_ptr<Widget> SettingsPage::create_capture_target() {
auto ll = std::make_unique<List>(List::Orientation::VERTICAL);
auto capture_target_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
- capture_target_list->add_widget(create_record_area(gsr_info));
+ capture_target_list->add_widget(create_record_area());
capture_target_list->add_widget(create_select_window());
capture_target_list->add_widget(create_area_size_section());
capture_target_list->add_widget(create_video_resolution_section());
@@ -378,40 +380,40 @@ namespace gsr {
return quality_list;
}
- std::unique_ptr<ComboBox> SettingsPage::create_video_codec_box(const GsrInfo &gsr_info) {
+ std::unique_ptr<ComboBox> SettingsPage::create_video_codec_box() {
auto video_codec_box = std::make_unique<ComboBox>(&get_theme().body_font);
// TODO: Show options not supported but disable them.
// TODO: Show error if no encoders are supported.
// TODO: Show warning (once) if only software encoder is available.
video_codec_box->add_item("Auto (Recommended)", "auto");
- if(gsr_info.supported_video_codecs.h264)
+ if(gsr_info->supported_video_codecs.h264)
video_codec_box->add_item("H264", "h264");
- if(gsr_info.supported_video_codecs.hevc)
+ if(gsr_info->supported_video_codecs.hevc)
video_codec_box->add_item("HEVC", "hevc");
- if(gsr_info.supported_video_codecs.av1)
+ if(gsr_info->supported_video_codecs.av1)
video_codec_box->add_item("AV1", "av1");
- if(gsr_info.supported_video_codecs.vp8)
+ if(gsr_info->supported_video_codecs.vp8)
video_codec_box->add_item("VP8", "vp8");
- if(gsr_info.supported_video_codecs.vp9)
+ if(gsr_info->supported_video_codecs.vp9)
video_codec_box->add_item("VP9", "vp9");
- if(gsr_info.supported_video_codecs.hevc_hdr)
+ if(gsr_info->supported_video_codecs.hevc_hdr)
video_codec_box->add_item("HEVC (HDR)", "hevc_hdr");
- if(gsr_info.supported_video_codecs.hevc_10bit)
+ if(gsr_info->supported_video_codecs.hevc_10bit)
video_codec_box->add_item("HEVC (10 bit, reduces banding)", "hevc_10bit");
- if(gsr_info.supported_video_codecs.av1_hdr)
+ if(gsr_info->supported_video_codecs.av1_hdr)
video_codec_box->add_item("AV1 (HDR)", "av1_hdr");
- if(gsr_info.supported_video_codecs.av1_10bit)
+ if(gsr_info->supported_video_codecs.av1_10bit)
video_codec_box->add_item("AV1 (10 bit, reduces banding)", "av1_10bit");
- if(gsr_info.supported_video_codecs.h264_software)
+ if(gsr_info->supported_video_codecs.h264_software)
video_codec_box->add_item("H264 Software Encoder (Slow, not recommended)", "h264_software");
video_codec_box_ptr = video_codec_box.get();
return video_codec_box;
}
- std::unique_ptr<List> SettingsPage::create_video_codec(const GsrInfo &gsr_info) {
+ std::unique_ptr<List> SettingsPage::create_video_codec() {
auto video_codec_list = std::make_unique<List>(List::Orientation::VERTICAL);
video_codec_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video codec:", get_color_theme().text_color));
- video_codec_list->add_widget(create_video_codec_box(gsr_info));
+ video_codec_list->add_widget(create_video_codec_box());
video_codec_ptr = video_codec_list.get();
return video_codec_list;
}
@@ -477,16 +479,16 @@ namespace gsr {
return record_cursor_checkbox;
}
- std::unique_ptr<Widget> SettingsPage::create_video_section(const GsrInfo &gsr_info) {
+ std::unique_ptr<Widget> SettingsPage::create_video_section() {
auto video_section_list = std::make_unique<List>(List::Orientation::VERTICAL);
video_section_list->add_widget(create_video_quality_section());
- video_section_list->add_widget(create_video_codec(gsr_info));
+ video_section_list->add_widget(create_video_codec());
video_section_list->add_widget(create_framerate_section());
video_section_list->add_widget(create_record_cursor_section());
return std::make_unique<Subsection>("Video", std::move(video_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
}
- std::unique_ptr<Widget> SettingsPage::create_settings(const GsrInfo &gsr_info) {
+ std::unique_ptr<Widget> SettingsPage::create_settings() {
auto page_list = std::make_unique<List>(List::Orientation::VERTICAL);
page_list->set_spacing(0.018f);
page_list->add_widget(create_view_radio_button());
@@ -496,16 +498,16 @@ namespace gsr {
auto settings_list = std::make_unique<List>(List::Orientation::VERTICAL);
settings_list->set_spacing(0.018f);
- settings_list->add_widget(create_capture_target(gsr_info));
+ settings_list->add_widget(create_capture_target());
settings_list->add_widget(create_audio_section());
- settings_list->add_widget(create_video_section(gsr_info));
+ settings_list->add_widget(create_video_section());
settings_list_ptr = settings_list.get();
settings_scrollable_page_ptr->add_widget(std::move(settings_list));
return page_list;
}
- void SettingsPage::add_widgets(const GsrInfo &gsr_info) {
- content_page_ptr->add_widget(create_settings(gsr_info));
+ void SettingsPage::add_widgets() {
+ content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) {
(void)text;
@@ -534,32 +536,32 @@ namespace gsr {
};
video_quality_box_ptr->on_selection_changed("", video_quality_box_ptr->get_selected_id());
- if(!gsr_info.supported_capture_options.monitors.empty())
- record_area_box_ptr->set_selected_item(gsr_info.supported_capture_options.monitors.front().name);
- else if(gsr_info.supported_capture_options.portal)
+ if(!capture_options.monitors.empty())
+ record_area_box_ptr->set_selected_item(capture_options.monitors.front().name);
+ else if(capture_options.portal)
record_area_box_ptr->set_selected_item("portal");
- else if(gsr_info.supported_capture_options.window)
+ else if(capture_options.window)
record_area_box_ptr->set_selected_item("window");
else
record_area_box_ptr->on_selection_changed("", "");
- if(!gsr_info.system_info.supports_app_audio) {
+ if(!gsr_info->system_info.supports_app_audio) {
add_application_audio_button_ptr->set_visible(false);
add_custom_application_audio_button_ptr->set_visible(false);
application_audio_invert_checkbox_ptr->set_visible(false);
}
}
- void SettingsPage::add_page_specific_widgets(const GsrInfo &gsr_info) {
+ void SettingsPage::add_page_specific_widgets() {
switch(type) {
case Type::REPLAY:
- add_replay_widgets(gsr_info);
+ add_replay_widgets();
break;
case Type::RECORD:
- add_record_widgets(gsr_info);
+ add_record_widgets();
break;
case Type::STREAM:
- add_stream_widgets(gsr_info);
+ add_stream_widgets();
break;
}
}
@@ -622,9 +624,9 @@ namespace gsr {
return replay_time_list;
}
- std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically(const GsrInfo &gsr_info) {
+ std::unique_ptr<RadioButton> SettingsPage::create_start_replay_automatically() {
char fullscreen_text[256];
- snprintf(fullscreen_text, sizeof(fullscreen_text), "Turn on replay when starting a fullscreen application%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
+ snprintf(fullscreen_text, sizeof(fullscreen_text), "Turn on replay when starting a fullscreen application%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
auto radiobutton = std::make_unique<RadioButton>(&get_theme().body_font, RadioButton::Orientation::VERTICAL);
radiobutton->add_item("Don't turn on replay automatically", "dont_turn_on_automatically");
@@ -635,9 +637,9 @@ namespace gsr {
return radiobutton;
}
- std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder(const GsrInfo &gsr_info) {
+ std::unique_ptr<CheckBox> SettingsPage::create_save_replay_in_game_folder() {
char text[256];
- snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
+ snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
save_replay_in_game_folder_ptr = checkbox.get();
return checkbox;
@@ -659,7 +661,7 @@ namespace gsr {
estimated_file_size_ptr->set_text(buffer);
}
- void SettingsPage::add_replay_widgets(const GsrInfo &gsr_info) {
+ void SettingsPage::add_replay_widgets() {
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
file_info_data_list->add_widget(create_save_directory("Directory to save replays:"));
@@ -670,8 +672,8 @@ namespace gsr {
settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
- general_list->add_widget(create_start_replay_automatically(gsr_info));
- general_list->add_widget(create_save_replay_in_game_folder(gsr_info));
+ general_list->add_widget(create_start_replay_automatically());
+ general_list->add_widget(create_save_replay_in_game_folder());
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
@@ -716,22 +718,22 @@ namespace gsr {
};
}
- std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder(const GsrInfo &gsr_info) {
+ std::unique_ptr<CheckBox> SettingsPage::create_save_recording_in_game_folder() {
char text[256];
- snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info.system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
+ snprintf(text, sizeof(text), "Save video in a folder with the name of the game%s", gsr_info->system_info.display_server == DisplayServer::X11 ? "" : " (X11 only)");
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, text);
save_recording_in_game_folder_ptr = checkbox.get();
return checkbox;
}
- void SettingsPage::add_record_widgets(const GsrInfo &gsr_info) {
+ void SettingsPage::add_record_widgets() {
auto file_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
file_list->add_widget(create_save_directory("Directory to save the video:"));
file_list->add_widget(create_container_section());
settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
- general_list->add_widget(create_save_recording_in_game_folder(gsr_info));
+ general_list->add_widget(create_save_recording_in_game_folder());
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
@@ -825,7 +827,7 @@ namespace gsr {
return container_list;
}
- void SettingsPage::add_stream_widgets(const GsrInfo&) {
+ void SettingsPage::add_stream_widgets() {
auto streaming_info_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
streaming_info_list->add_widget(create_streaming_service_section());
streaming_info_list->add_widget(create_stream_key_section());
@@ -879,16 +881,16 @@ namespace gsr {
save();
}
- void SettingsPage::load(const GsrInfo &gsr_info) {
+ void SettingsPage::load() {
switch(type) {
case Type::REPLAY:
- load_replay(gsr_info);
+ load_replay();
break;
case Type::RECORD:
- load_record(gsr_info);
+ load_record();
break;
case Type::STREAM:
- load_stream(gsr_info);
+ load_stream();
break;
}
}
@@ -921,11 +923,11 @@ namespace gsr {
return str.size() >= len && memcmp(str.data(), substr, len) == 0;
}
- void SettingsPage::load_audio_tracks(const RecordOptions &record_options, const GsrInfo &gsr_info) {
+ void SettingsPage::load_audio_tracks(const RecordOptions &record_options) {
audio_track_list_ptr->clear();
for(const std::string &audio_track : record_options.audio_tracks) {
if(starts_with(audio_track, "app:")) {
- if(!gsr_info.system_info.supports_app_audio)
+ if(!gsr_info->system_info.supports_app_audio)
continue;
std::string audio_track_name = audio_track.substr(4);
@@ -955,12 +957,12 @@ namespace gsr {
}
}
- void SettingsPage::load_common(RecordOptions &record_options, const GsrInfo &gsr_info) {
+ void SettingsPage::load_common(RecordOptions &record_options) {
record_area_box_ptr->set_selected_item(record_options.record_area_option);
merge_audio_tracks_checkbox_ptr->set_checked(record_options.merge_audio_tracks);
application_audio_invert_checkbox_ptr->set_checked(record_options.application_audio_invert);
change_video_resolution_checkbox_ptr->set_checked(record_options.change_video_resolution);
- load_audio_tracks(record_options, gsr_info);
+ load_audio_tracks(record_options);
color_range_box_ptr->set_selected_item(record_options.color_range);
video_quality_box_ptr->set_selected_item(record_options.video_quality);
video_codec_box_ptr->set_selected_item(record_options.video_codec);
@@ -1009,8 +1011,8 @@ namespace gsr {
video_bitrate_entry_ptr->set_text(std::to_string(record_options.video_bitrate));
}
- void SettingsPage::load_replay(const GsrInfo &gsr_info) {
- load_common(config.replay_config.record_options, gsr_info);
+ void SettingsPage::load_replay() {
+ load_common(config.replay_config.record_options);
turn_on_replay_automatically_mode_ptr->set_selected_item(config.replay_config.turn_on_replay_automatically_mode);
save_replay_in_game_folder_ptr->set_checked(config.replay_config.save_video_in_game_folder);
show_replay_started_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_started_notifications);
@@ -1024,8 +1026,8 @@ namespace gsr {
replay_time_entry_ptr->set_text(std::to_string(config.replay_config.replay_time));
}
- void SettingsPage::load_record(const GsrInfo &gsr_info) {
- load_common(config.record_config.record_options, gsr_info);
+ void SettingsPage::load_record() {
+ load_common(config.record_config.record_options);
save_recording_in_game_folder_ptr->set_checked(config.record_config.save_video_in_game_folder);
show_recording_started_notification_checkbox_ptr->set_checked(config.record_config.show_recording_started_notifications);
show_video_saved_notification_checkbox_ptr->set_checked(config.record_config.show_video_saved_notifications);
@@ -1033,8 +1035,8 @@ namespace gsr {
container_box_ptr->set_selected_item(config.record_config.container);
}
- void SettingsPage::load_stream(const GsrInfo &gsr_info) {
- load_common(config.streaming_config.record_options, gsr_info);
+ void SettingsPage::load_stream() {
+ load_common(config.streaming_config.record_options);
show_streaming_started_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_started_notifications);
show_streaming_stopped_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_stopped_notifications);
streaming_service_box_ptr->set_selected_item(config.streaming_config.streaming_service);
diff --git a/src/gui/Utils.cpp b/src/gui/Utils.cpp
index e000b7a..d1643f2 100644
--- a/src/gui/Utils.cpp
+++ b/src/gui/Utils.cpp
@@ -50,4 +50,21 @@ namespace gsr {
void set_frame_delta_seconds(double frame_delta) {
frame_delta_seconds = frame_delta;
}
+
+ mgl::vec2f scale_keep_aspect_ratio(mgl::vec2f from, mgl::vec2f to) {
+ if(std::abs(from.x) <= 0.0001f || std::abs(from.y) <= 0.0001f)
+ return {0.0f, 0.0f};
+
+ const double height_to_width_ratio = (double)from.y / (double)from.x;
+ from.x = to.x;
+ from.y = from.x * height_to_width_ratio;
+
+ if(from.y > to.y) {
+ const double width_height_ratio = (double)from.x / (double)from.y;
+ from.y = to.y;
+ from.x = from.y * width_height_ratio;
+ }
+
+ return from;
+ }
} \ No newline at end of file
diff --git a/src/main.cpp b/src/main.cpp
index 7002288..98b1ce3 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -168,8 +168,11 @@ int main(void) {
exit(1);
}
- if(gsr_info.system_info.display_server == gsr::DisplayServer::WAYLAND)
- fprintf(stderr, "warning: Wayland support is experimental and requires XWayland. Things may not work as expected.\n");
+ const gsr::DisplayServer display_server = gsr_info.system_info.display_server;
+ if(display_server == gsr::DisplayServer::WAYLAND)
+ fprintf(stderr, "warning: Wayland support is experimental and requires XWayland. Things may not work as expected.\n");
+
+ gsr::SupportedCaptureOptions capture_options = gsr::get_supported_capture_options(gsr_info);
std::string resources_path;
if(access("sibs-build", F_OK) == 0) {
@@ -197,11 +200,11 @@ int main(void) {
fprintf(stderr, "info: gsr ui is now ready, waiting for inputs. Press alt+z to show/hide the overlay\n");
- auto overlay = std::make_unique<gsr::Overlay>(resources_path, gsr_info, egl_funcs);
+ auto overlay = std::make_unique<gsr::Overlay>(resources_path, std::move(gsr_info), std::move(capture_options), egl_funcs);
//overlay.show();
std::unique_ptr<gsr::GlobalHotkeys> global_hotkeys = nullptr;
- if(gsr_info.system_info.display_server == gsr::DisplayServer::X11) {
+ if(display_server == gsr::DisplayServer::X11) {
global_hotkeys = register_x11_hotkeys(overlay.get());
if(!global_hotkeys) {
fprintf(stderr, "info: failed to register some x11 hotkeys because they are registered by another program. Will use linux hotkeys instead that can clash with keys used by other applications\n");