Query capture options when opening settings and validate capture options when starting recording

This commit is contained in:
dec05eba
2024-12-08 14:32:11 +01:00
parent 6b66eebb68
commit f3565fdd77
12 changed files with 243 additions and 153 deletions

View File

@@ -27,6 +27,8 @@ These are the dependencies needed to build GPU Screen Recorder UI:
* libxkbcommon
## Runtime dependencies
There are also additional dependencies needed at runtime:
* [GPU Screen Recorder](https://git.dec05eba.com/gpu-screen-recorder/)
* [GPU Screen Recorder Notification](https://git.dec05eba.com/gpu-screen-recorder-notification/)
@@ -47,4 +49,4 @@ If you want to donate you can donate via bitcoin or monero.
# Known issues
* Some games receive mouse input while the UI is open
* Global hotkeys on Wayland can clash with keys used by other applications. This is primarly because Wayland compositors are missing support for global hotkey so this software uses a global hotkey system that works on all Wayland compositors.
* Global hotkeys on Wayland can clash with keys used by other applications. This is primarly because Wayland compositors are missing support for global hotkey so this software uses a global hotkey system that works on all Wayland compositors.

4
TODO
View File

@@ -66,7 +66,7 @@ Make save-video-in-game-folder.sh and notify-saved-name.sh run ~/.config/gpu-scr
if the profile is called 4chan.
Create a directory of such example scripts, including 4chan webm one.
On nvidia check if suspend fix is applied. If not, show a popup asking the user to apply it (and apply it automatically).
On nvidia check if suspend fix is applied. If not, show a popup asking the user to apply it (and apply it automatically). This is a requirement before this package is made to a flatpak.
Show warning when using steam deck or when trying to capture hevc/av1 on amd (the same warnings as gpu screen recorder gtk).
@@ -103,4 +103,4 @@ Dont allow autostart of replay if capture option is window recording (when windo
Use global shortcuts desktop portal protocol on wayland when available.
Use `gpu-screen-recorder --list-capture-options` instead of gsr_info.supported_capture_options.monitors and update it everytime when opening setting page and when starting recording.
When support for window capture is enabled on x11 then make sure to not save the window except temporary while the program is open.

View File

@@ -7,7 +7,7 @@
#include <optional>
namespace gsr {
struct GsrInfo;
struct SupportedCaptureOptions;
struct ConfigHotkey {
int64_t keysym = 0;
@@ -92,7 +92,7 @@ namespace gsr {
};
struct Config {
Config(const GsrInfo &gsr_info);
Config(const SupportedCaptureOptions &capture_options);
MainConfig main_config;
StreamingConfig streaming_config;
@@ -100,6 +100,6 @@ namespace gsr {
ReplayConfig replay_config;
};
std::optional<Config> read_config(const GsrInfo &gsr_info);
std::optional<Config> read_config(const SupportedCaptureOptions &capture_options);
void save_config(Config &config);
}

View File

@@ -52,13 +52,13 @@ namespace gsr {
struct GpuInfo {
GpuVendor vendor = GpuVendor::UNKNOWN;
std::string card_path;
};
struct GsrInfo {
SystemInfo system_info;
GpuInfo gpu_info;
SupportedVideoCodecs supported_video_codecs;
SupportedCaptureOptions supported_capture_options;
};
enum class GsrInfoExitStatus {
@@ -78,4 +78,5 @@ namespace gsr {
std::vector<AudioDevice> get_audio_devices();
std::vector<std::string> get_application_audio();
SupportedCaptureOptions get_supported_capture_options(const GsrInfo &gsr_info);
}

View File

@@ -36,7 +36,7 @@ namespace gsr {
class Overlay {
public:
Overlay(std::string resources_path, GsrInfo gsr_info, egl_functions egl_funcs);
Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs);
Overlay(const Overlay&) = delete;
Overlay& operator=(const Overlay&) = delete;
~Overlay();

View File

@@ -25,17 +25,17 @@ namespace gsr {
STREAM
};
SettingsPage(Type type, const GsrInfo &gsr_info, Config &config, PageStack *page_stack);
SettingsPage(Type type, const GsrInfo *gsr_info, Config &config, PageStack *page_stack);
SettingsPage(const SettingsPage&) = delete;
SettingsPage& operator=(const SettingsPage&) = delete;
void load(const GsrInfo &gsr_info);
void load();
void save();
void on_navigate_away_from_page() override;
private:
std::unique_ptr<RadioButton> create_view_radio_button();
std::unique_ptr<ComboBox> create_record_area_box(const GsrInfo &gsr_info);
std::unique_ptr<Widget> create_record_area(const GsrInfo &gsr_info);
std::unique_ptr<ComboBox> create_record_area_box();
std::unique_ptr<Widget> create_record_area();
std::unique_ptr<List> create_select_window();
std::unique_ptr<Entry> create_area_width_entry();
std::unique_ptr<Entry> create_area_height_entry();
@@ -48,7 +48,7 @@ namespace gsr {
std::unique_ptr<CheckBox> create_restore_portal_session_checkbox();
std::unique_ptr<List> create_restore_portal_session_section();
std::unique_ptr<Widget> create_change_video_resolution_section();
std::unique_ptr<Widget> create_capture_target(const GsrInfo &gsr_info);
std::unique_ptr<Widget> create_capture_target();
std::unique_ptr<ComboBox> create_audio_device_selection_combobox();
std::unique_ptr<Button> create_remove_audio_device_button(List *audio_device_list_ptr);
std::unique_ptr<List> create_audio_device();
@@ -70,8 +70,8 @@ namespace gsr {
std::unique_ptr<ComboBox> create_color_range_box();
std::unique_ptr<List> create_color_range();
std::unique_ptr<List> create_video_quality_section();
std::unique_ptr<ComboBox> create_video_codec_box(const GsrInfo &gsr_info);
std::unique_ptr<List> create_video_codec(const GsrInfo &gsr_info);
std::unique_ptr<ComboBox> create_video_codec_box();
std::unique_ptr<List> create_video_codec();
std::unique_ptr<ComboBox> create_audio_codec_box();
std::unique_ptr<List> create_audio_codec();
std::unique_ptr<Entry> create_framerate_entry();
@@ -80,24 +80,24 @@ namespace gsr {
std::unique_ptr<List> create_framerate_mode();
std::unique_ptr<List> create_framerate_section();
std::unique_ptr<Widget> create_record_cursor_section();
std::unique_ptr<Widget> create_video_section(const GsrInfo &gsr_info);
std::unique_ptr<Widget> create_settings(const GsrInfo &gsr_info);
void add_widgets(const GsrInfo &gsr_info);
std::unique_ptr<Widget> create_video_section();
std::unique_ptr<Widget> create_settings();
void add_widgets();
void add_page_specific_widgets(const GsrInfo &gsr_info);
void add_page_specific_widgets();
std::unique_ptr<List> create_save_directory(const char *label);
std::unique_ptr<ComboBox> create_container_box();
std::unique_ptr<List> create_container_section();
std::unique_ptr<Entry> create_replay_time_entry();
std::unique_ptr<List> create_replay_time();
std::unique_ptr<RadioButton> create_start_replay_automatically(const GsrInfo &gsr_info);
std::unique_ptr<CheckBox> create_save_replay_in_game_folder(const GsrInfo &gsr_info);
std::unique_ptr<RadioButton> create_start_replay_automatically();
std::unique_ptr<CheckBox> create_save_replay_in_game_folder();
std::unique_ptr<Label> create_estimated_file_size();
void update_estimated_file_size();
std::unique_ptr<CheckBox> create_save_recording_in_game_folder(const GsrInfo &gsr_info);
void add_replay_widgets(const GsrInfo &gsr_info);
void add_record_widgets(const GsrInfo &gsr_info);
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
void add_replay_widgets();
void add_record_widgets();
std::unique_ptr<ComboBox> create_streaming_service_box();
std::unique_ptr<List> create_streaming_service_section();
@@ -105,13 +105,13 @@ namespace gsr {
std::unique_ptr<List> create_stream_url_section();
std::unique_ptr<ComboBox> create_stream_container_box();
std::unique_ptr<List> create_stream_container_section();
void add_stream_widgets(const GsrInfo &gsr_info);
void add_stream_widgets();
void load_audio_tracks(const RecordOptions &record_options, const GsrInfo &gsr_info);
void load_common(RecordOptions &record_options, const GsrInfo &gsr_info);
void load_replay(const GsrInfo &gsr_info);
void load_record(const GsrInfo &gsr_info);
void load_stream(const GsrInfo &gsr_info);
void load_audio_tracks(const RecordOptions &record_options);
void load_common(RecordOptions &record_options);
void load_replay();
void load_record();
void load_stream();
void save_common(RecordOptions &record_options);
void save_replay();
@@ -120,8 +120,10 @@ namespace gsr {
private:
Type type;
Config &config;
const GsrInfo *gsr_info = nullptr;
std::vector<AudioDevice> audio_devices;
std::vector<std::string> application_audio;
SupportedCaptureOptions capture_options;
GsrPage *content_page_ptr = nullptr;
ScrollablePage *settings_scrollable_page_ptr = nullptr;

View File

@@ -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;
}
}
@@ -140,7 +140,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 +150,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();

View File

@@ -38,6 +38,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 +66,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,
@@ -161,7 +131,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;
}
}
@@ -230,7 +200,7 @@ namespace gsr {
return application_audio;
}
char output[16384];
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 --list-application-audio' output\n");
@@ -246,4 +216,75 @@ namespace gsr {
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;
char command[512];
snprintf(command, sizeof(command), "gpu-screen-recorder --list-capture-options %s %s", gsr_info.gpu_info.card_path.c_str(), gpu_vendor_to_string(gsr_info.gpu_info.vendor));
FILE *f = popen(command, "r");
if(!f) {
fprintf(stderr, "error: 'gpu-screen-recorder --list-capture-options' failed\n");
return capture_options;
}
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 --list-capture-options' output\n");
pclose(f);
return capture_options;
}
output[bytes_read] = '\0';
string_split_char({output, (size_t)bytes_read}, '\n', [&](std::string_view line) {
parse_capture_options_line(capture_options, line);
return true;
});
return capture_options;
}
}

View File

@@ -393,11 +393,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})
@@ -416,11 +416,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(this->gsr_info);
power_supply_online_filepath = get_power_supply_online_filepath();
@@ -875,7 +875,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();
@@ -896,7 +896,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();
@@ -915,7 +915,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();
@@ -1525,6 +1525,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;
@@ -1570,6 +1588,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);
@@ -1677,6 +1702,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
@@ -1806,6 +1838,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);

View File

@@ -22,15 +22,17 @@ 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),
gsr_info(gsr_info),
page_stack(page_stack),
settings_title_text("Settings", get_theme().title_font)
{
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 +43,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 +57,31 @@ 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)
if(capture_options.screen)
record_area_box->add_item("All monitors", "screen");
for(const auto &monitor : gsr_info.supported_capture_options.monitors) {
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);

View File

@@ -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");