Compare commits

..

11 Commits
1.3.4 ... 1.5.0

45 changed files with 1072 additions and 486 deletions

View File

@@ -22,14 +22,12 @@ GPU Screen Recorder UI uses meson build system so you need to install `meson` to
## Build dependencies ## Build dependencies
These are the dependencies needed to build GPU Screen Recorder UI: These are the dependencies needed to build GPU Screen Recorder UI:
* x11 (libx11, libxrandr, libxrender, libxcomposite, libxfixes, libxext, libxi) * x11 (libx11, libxrandr, libxrender, libxcomposite, libxfixes, libxext, libxi, libxcursor)
* libxcursor
* libglvnd (which provides libgl, libglx and libegl) * libglvnd (which provides libgl, libglx and libegl)
* linux-api-headers * linux-api-headers
* libpulse (libpulse-simple) * libpulse (libpulse-simple)
* libdrm * libdrm
* wayland-client * wayland (wayland-client, wayland-egl, wayland-scanner)
* wayland-scanner
## Runtime dependencies ## Runtime dependencies
There are also additional dependencies needed at runtime: There are also additional dependencies needed at runtime:
@@ -48,7 +46,7 @@ If you are stuck in such a lock where you cant press and keyboard keys you can p
This software is licensed under GPL3.0-only. Files under `fonts/` directory belong to the Noto Sans Google fonts project and they are licensed under `SIL Open Font License`.\ This software is licensed under GPL3.0-only. Files under `fonts/` directory belong to the Noto Sans Google fonts project and they are licensed under `SIL Open Font License`.\
`images/default.cur` it part of the [Adwaita icon theme](https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/tree/master) which is licensed under `CC BY-SA 3.0`.\ `images/default.cur` it part of the [Adwaita icon theme](https://gitlab.gnome.org/GNOME/adwaita-icon-theme/-/tree/master) which is licensed under `CC BY-SA 3.0`.\
The controller buttons under `images/` were created by [Julio Cacko](https://juliocacko.itch.io/free-input-prompts) and they are licensed under `CC0 1.0 Universal`.\ The controller buttons under `images/` were created by [Julio Cacko](https://juliocacko.itch.io/free-input-prompts) and they are licensed under `CC0 1.0 Universal`.\
The PlayStation logo under `images/` was created by [ArksDigital](https://arks.itch.io/ps4-buttons) and it's are licensed under `CC BY 4.0`. The PlayStation logo under `images/` was created by [ArksDigital](https://arks.itch.io/ps4-buttons) and it's licensed under `CC BY 4.0`.
# Reporting bugs, contributing patches, questions or donation # Reporting bugs, contributing patches, questions or donation
See [https://git.dec05eba.com/?p=about](https://git.dec05eba.com/?p=about).\ See [https://git.dec05eba.com/?p=about](https://git.dec05eba.com/?p=about).\

8
TODO
View File

@@ -14,8 +14,6 @@ Add nvidia overclock option.
Add support for window selection in capture. Add support for window selection in capture.
Add option to record the focused monitor. This works on wayland too when using kms capture since we can get cursor position without root and see which monitor (crtc) the cursor is on. Or use create_window_get_center_position.
Filechooser should have the option to select list view, search bar and common folders/mounted drives on the left side for quick navigation. Also a button to create a new directory. Filechooser should have the option to select list view, search bar and common folders/mounted drives on the left side for quick navigation. Also a button to create a new directory.
Restart replay on system start if monitor resolution changes. Restart replay on system start if monitor resolution changes.
@@ -123,8 +121,6 @@ Maybe change gsr-ui startup retry time in the systemd service, from 5 seconds to
Add support for window capture. This should not prompt for window selection directly but instead prompt for window selection when recording starts and hide the ui first. Add support for window capture. This should not prompt for window selection directly but instead prompt for window selection when recording starts and hide the ui first.
For screenshots window capture should exist but "follow focused" option should not exist. For screenshots window capture should exist but "follow focused" option should not exist.
Improve audio design. It should have a button to add/remove audio tracks and button to add audio into each audio track separately and "record audio from all applications except the selected ones" for each audio track. Then also remove the "merge audio tracks" option.
Make it possible to take a screenshot through a button in the ui instead of having to use hotkey. Make it possible to take a screenshot through a button in the ui instead of having to use hotkey.
Handle failing to save a replay. gsr should output "failed to save replay, or something like that" to make it possible to detect that. Handle failing to save a replay. gsr should output "failed to save replay, or something like that" to make it possible to detect that.
@@ -162,3 +158,7 @@ If CursorTrackerWayland fails then fallback to getting focused monitor by window
In that case also add all monitors available to capture in the capture list and automatically choose the gpu that controls the monitor. In that case also add all monitors available to capture in the capture list and automatically choose the gpu that controls the monitor.
Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts. Support cjk font. Use fc-match to find the location of the font. This also works in flatpak, in which case the fonts are in /run/host/..., where it lists system fonts.
Keyboard layout is incorrect on wayland when using kde plasma keyboard settings to setup multiple keyboards, for example when changing to french.
Text input is correct, but hotkey is incorrect.
Need to use "setxkbmap fr" as well.

BIN
images/delete.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 511 B

BIN
images/ps4_cross.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
images/ps4_triangle.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/trash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 410 B

View File

@@ -6,7 +6,7 @@
#include <vector> #include <vector>
#include <optional> #include <optional>
#define GSR_CONFIG_FILE_VERSION 1 #define GSR_CONFIG_FILE_VERSION 2
namespace gsr { namespace gsr {
struct SupportedCaptureOptions; struct SupportedCaptureOptions;
@@ -30,6 +30,14 @@ namespace gsr {
std::string to_string(bool spaces = true, bool modifier_side = true) const; std::string to_string(bool spaces = true, bool modifier_side = true) const;
}; };
struct AudioTrack {
std::vector<std::string> audio_inputs; // ids
bool application_audio_invert = false;
bool operator==(const AudioTrack &other) const;
bool operator!=(const AudioTrack &other) const;
};
struct RecordOptions { struct RecordOptions {
std::string record_area_option = "screen"; std::string record_area_option = "screen";
int32_t record_area_width = 0; int32_t record_area_width = 0;
@@ -38,10 +46,11 @@ namespace gsr {
int32_t video_height = 0; int32_t video_height = 0;
int32_t fps = 60; int32_t fps = 60;
int32_t video_bitrate = 15000; int32_t video_bitrate = 15000;
bool merge_audio_tracks = true; // Currently unused for streaming because all known streaming sites only support 1 audio track bool merge_audio_tracks = true; // TODO: Remove in the future
bool application_audio_invert = false; bool application_audio_invert = false; // TODO: Remove in the future
bool change_video_resolution = false; bool change_video_resolution = false;
std::vector<std::string> audio_tracks; std::vector<std::string> audio_tracks; // ids, TODO: Remove in the future
std::vector<AudioTrack> audio_tracks_list;
std::string color_range = "limited"; std::string color_range = "limited";
std::string video_quality = "very_high"; std::string video_quality = "very_high";
std::string video_codec = "auto"; std::string video_codec = "auto";
@@ -110,6 +119,8 @@ namespace gsr {
int32_t replay_time = 60; int32_t replay_time = 60;
ConfigHotkey start_stop_hotkey; ConfigHotkey start_stop_hotkey;
ConfigHotkey save_hotkey; ConfigHotkey save_hotkey;
ConfigHotkey save_1_min_hotkey;
ConfigHotkey save_10_min_hotkey;
}; };
struct ScreenshotConfig { struct ScreenshotConfig {

View File

@@ -21,6 +21,8 @@ namespace gsr {
bool start(); bool start();
// Currently valid ids: // Currently valid ids:
// save_replay // save_replay
// save_1_min_replay
// save_10_min_replay
// take_screenshot // take_screenshot
// toggle_record // toggle_record
// toggle_replay // toggle_replay
@@ -56,6 +58,8 @@ namespace gsr {
bool right_pressed = false; bool right_pressed = false;
bool save_replay = false; bool save_replay = false;
bool save_1_min_replay = false;
bool save_10_min_replay = false;
bool take_screenshot = false; bool take_screenshot = false;
bool toggle_record = false; bool toggle_record = false;
bool toggle_replay = false; bool toggle_replay = false;

View File

@@ -59,6 +59,8 @@ namespace gsr {
void toggle_stream(); void toggle_stream();
void toggle_replay(); void toggle_replay();
void save_replay(); void save_replay();
void save_replay_1_min();
void save_replay_10_min();
void take_screenshot(); void take_screenshot();
void take_screenshot_region(); void take_screenshot_region();
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type, const char *capture_target = nullptr); void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type, const char *capture_target = nullptr);
@@ -87,7 +89,7 @@ namespace gsr {
void update_notification_process_status(); void update_notification_process_status();
void save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type); void save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type);
void on_replay_saved(const char *replay_saved_filepath); void on_replay_saved(const char *replay_saved_filepath);
void update_gsr_replay_save(); void process_gsr_output();
void update_gsr_process_status(); void update_gsr_process_status();
void update_gsr_screenshot_process_status(); void update_gsr_screenshot_process_status();
@@ -96,7 +98,7 @@ namespace gsr {
void update_power_supply_status(); void update_power_supply_status();
void update_system_startup_status(); void update_system_startup_status();
void on_stop_recording(int exit_code); void on_stop_recording(int exit_code, const std::string &video_filepath);
void update_ui_recording_paused(); void update_ui_recording_paused();
void update_ui_recording_unpaused(); void update_ui_recording_unpaused();
@@ -110,7 +112,10 @@ namespace gsr {
void update_ui_replay_started(); void update_ui_replay_started();
void update_ui_replay_stopped(); void update_ui_replay_stopped();
void prepare_gsr_output_for_reading();
void on_press_save_replay(); void on_press_save_replay();
void on_press_save_replay_1_min_replay();
void on_press_save_replay_10_min_replay();
bool on_press_start_replay(bool disable_notification, bool finished_region_selection); bool on_press_start_replay(bool disable_notification, bool finished_region_selection);
void on_press_start_record(bool finished_region_selection); void on_press_start_record(bool finished_region_selection);
void on_press_start_stream(bool finished_region_selection); void on_press_start_stream(bool finished_region_selection);
@@ -204,6 +209,8 @@ namespace gsr {
bool replay_save_show_notification = false; bool replay_save_show_notification = false;
ReplayStartupMode replay_startup_mode = ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP; ReplayStartupMode replay_startup_mode = ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP;
bool try_replay_startup = true; bool try_replay_startup = true;
bool replay_recording = false;
int replay_save_duration_min = 0;
AudioPlayer audio_player; AudioPlayer audio_player;
RegionSelector region_selector; RegionSelector region_selector;
@@ -211,7 +218,6 @@ namespace gsr {
std::function<void()> on_region_selected; std::function<void()> on_region_selected;
std::string recording_capture_target; std::string recording_capture_target;
std::string replay_capture_target;
std::string screenshot_capture_target; std::string screenshot_capture_target;
std::unique_ptr<CursorTracker> cursor_tracker; std::unique_ptr<CursorTracker> cursor_tracker;

View File

@@ -1,8 +1,15 @@
#pragma once #pragma once
#include <vector> #include <vector>
#include <memory>
#include <functional> #include <functional>
#include <assert.h>
#include "gui/Widget.hpp"
template <typename T>
struct SafeVectorItem {
T item;
bool alive = false;
};
// A vector that can be modified while iterating // A vector that can be modified while iterating
template <typename T> template <typename T>
@@ -10,64 +17,84 @@ class SafeVector {
public: public:
using PointerType = typename std::pointer_traits<T>::element_type*; using PointerType = typename std::pointer_traits<T>::element_type*;
SafeVector() = default;
SafeVector(const SafeVector&) = delete;
SafeVector& operator=(const SafeVector&) = delete;
~SafeVector() {
clear();
}
void push_back(T item) { void push_back(T item) {
data.push_back(std::move(item)); data.push_back({std::move(item), true});
++num_items_alive;
} }
// Safe to call when vector is empty // Safe to call when vector is empty
// TODO: Make this iterator safe // TODO: Make this iterator safe
void pop_back() { void pop_back() {
if(!data.empty()) if(!data.empty()) {
gsr::add_widget_to_remove(std::move(data.back().item));
data.pop_back(); data.pop_back();
--num_items_alive;
}
} }
// Might not remove the data immediately if inside for_each loop. // Might not remove the data immediately if inside for_each loop.
// In that case the item is removed at the end of the loop. // In that case the item is removed at the end of the loop.
void remove(PointerType item_to_remove) { void remove(PointerType item_to_remove) {
if(for_each_depth == 0) if(for_each_depth == 0) {
remove_item(item_to_remove); remove_item(item_to_remove);
else return;
remove_queue.push_back(item_to_remove); }
SafeVectorItem<T> *item = get_item(item_to_remove);
if(item && item->alive) {
item->alive = false;
--num_items_alive;
has_items_to_remove = true;
}
} }
// Safe to call when vector is empty, in which case it returns nullptr // Safe to call when vector is empty, in which case it returns nullptr
T* back() { T* back() {
if(data.empty()) for(auto it = data.rbegin(), end = data.rend(); it != end; ++it) {
return nullptr; if(it->alive)
else return &it->item;
return &data.back(); }
return nullptr;
} }
// TODO: Make this iterator safe // TODO: Make this iterator safe
void clear() { void clear() {
for(auto &item : data) {
gsr::add_widget_to_remove(std::move(item.item));
}
data.clear(); data.clear();
remove_queue.clear(); num_items_alive = 0;
} }
// Return true from |callback| to continue. This function returns false if |callback| returned false // Return true from |callback| to continue. This function returns false if |callback| returned false
bool for_each(std::function<bool(T &t)> callback) { bool for_each(std::function<bool(T &t)> callback, bool include_dead = false) {
bool result = true; bool result = true;
++for_each_depth; ++for_each_depth;
for(size_t i = 0; i < data.size(); ++i) { for(size_t i = 0; i < data.size(); ++i) {
result = callback(data[i]); if(data[i].alive || include_dead) {
if(!result) result = callback(data[i].item);
break; if(!result)
break;
}
} }
--for_each_depth; --for_each_depth;
if(for_each_depth == 0) { if(for_each_depth == 0)
for(PointerType item_to_remove : remove_queue) { remove_dead_items();
remove_item(item_to_remove);
}
remove_queue.clear();
}
return result; return result;
} }
// Return true from |callback| to continue. This function returns false if |callback| returned false // Return true from |callback| to continue. This function returns false if |callback| returned false
bool for_each_reverse(std::function<bool(T &t)> callback) { bool for_each_reverse(std::function<bool(T &t)> callback, bool include_dead = false) {
bool result = true; bool result = true;
++for_each_depth; ++for_each_depth;
@@ -80,50 +107,84 @@ public:
if(i < 0) if(i < 0)
break; break;
result = callback(data[i]); if(data[i].alive || include_dead) {
if(!result) result = callback(data[i].item);
break; if(!result)
break;
}
--i; --i;
} }
--for_each_depth; --for_each_depth;
if(for_each_depth == 0) { if(for_each_depth == 0)
for(PointerType item_to_remove : remove_queue) { remove_dead_items();
remove_item(item_to_remove);
}
remove_queue.clear();
}
return result; return result;
} }
T& operator[](size_t index) { T& operator[](size_t index) {
return data[index]; assert(index < data.size());
return data[index].item;
} }
const T& operator[](size_t index) const { const T& operator[](size_t index) const {
return data[index]; assert(index < data.size());
return data[index].item;
} }
size_t size() const { size_t size() const {
return data.size(); return (size_t)num_items_alive;
} }
bool empty() const { bool empty() const {
return data.empty(); return num_items_alive == 0;
}
void replace_item(PointerType item_to_replace, T new_item) {
SafeVectorItem<T> *item = get_item(item_to_replace);
if(item->alive) {
gsr::add_widget_to_remove(std::move(item->item));
item->item = std::move(new_item);
}
} }
private: private:
void remove_item(PointerType item_to_remove) { void remove_item(PointerType item_to_remove) {
for(auto it = data.begin(), end = data.end(); it != end; ++it) { for(auto it = data.begin(), end = data.end(); it != end; ++it) {
if(&*(*it) == item_to_remove) { if(&*(it->item) == item_to_remove) {
gsr::add_widget_to_remove(std::move(it->item));
data.erase(it); data.erase(it);
--num_items_alive;
return; return;
} }
} }
} }
SafeVectorItem<T>* get_item(PointerType item_to_remove) {
for(auto &item : data) {
if(&*(item.item) == item_to_remove)
return &item;
}
return nullptr;
}
void remove_dead_items() {
if(!has_items_to_remove)
return;
for(auto it = data.begin(); it != data.end();) {
if(it->alive) {
++it;
} else {
gsr::add_widget_to_remove(std::move(it->item));
it = data.erase(it);
}
}
has_items_to_remove = false;
}
private: private:
std::vector<T> data; std::vector<SafeVectorItem<T>> data;
std::vector<PointerType> remove_queue;
int for_each_depth = 0; int for_each_depth = 0;
int num_items_alive = 0;
bool has_items_to_remove = false;
}; };

View File

@@ -42,6 +42,7 @@ namespace gsr {
mgl::Texture pause_texture; mgl::Texture pause_texture;
mgl::Texture save_texture; mgl::Texture save_texture;
mgl::Texture screenshot_texture; mgl::Texture screenshot_texture;
mgl::Texture trash_texture;
mgl::Texture ps4_home_texture; mgl::Texture ps4_home_texture;
mgl::Texture ps4_options_texture; mgl::Texture ps4_options_texture;
@@ -49,6 +50,8 @@ namespace gsr {
mgl::Texture ps4_dpad_down_texture; mgl::Texture ps4_dpad_down_texture;
mgl::Texture ps4_dpad_left_texture; mgl::Texture ps4_dpad_left_texture;
mgl::Texture ps4_dpad_right_texture; mgl::Texture ps4_dpad_right_texture;
mgl::Texture ps4_cross_texture;
mgl::Texture ps4_triangle_texture;
double double_click_timeout_seconds = 0.4; double double_click_timeout_seconds = 0.4;

View File

@@ -14,6 +14,7 @@ namespace gsr {
using StringSplitCallback = std::function<bool(std::string_view line)>; using StringSplitCallback = std::function<bool(std::string_view line)>;
void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func); void string_split_char(std::string_view str, char delimiter, StringSplitCallback callback_func);
bool starts_with(std::string_view str, const char *substr);
std::string get_home_dir(); std::string get_home_dir();
std::string get_config_dir(); std::string get_config_dir();

View File

@@ -21,6 +21,7 @@ namespace gsr {
void set_item_label(const std::string &id, const std::string &new_label); void set_item_label(const std::string &id, const std::string &new_label);
void set_item_icon(const std::string &id, mgl::Texture *texture); void set_item_icon(const std::string &id, mgl::Texture *texture);
void set_item_description(const std::string &id, const std::string &new_description); void set_item_description(const std::string &id, const std::string &new_description);
void set_item_enabled(const std::string &id, bool enabled);
void set_description(std::string description_text); void set_description(std::string description_text);
void set_activated(bool activated); void set_activated(bool activated);
@@ -36,6 +37,7 @@ namespace gsr {
mgl::Text description_text; mgl::Text description_text;
mgl::Texture *icon_texture = nullptr; mgl::Texture *icon_texture = nullptr;
std::string id; std::string id;
bool enabled = true;
}; };
std::vector<Item> items; std::vector<Item> items;

View File

@@ -22,6 +22,8 @@ namespace gsr {
NONE, NONE,
REPLAY_START_STOP, REPLAY_START_STOP,
REPLAY_SAVE, REPLAY_SAVE,
REPLAY_SAVE_1_MIN,
REPLAY_SAVE_10_MIN,
RECORD_START_STOP, RECORD_START_STOP,
RECORD_PAUSE_UNPAUSE, RECORD_PAUSE_UNPAUSE,
STREAM_START_STOP, STREAM_START_STOP,
@@ -56,6 +58,7 @@ namespace gsr {
std::unique_ptr<RadioButton> create_enable_joystick_hotkeys_button(); std::unique_ptr<RadioButton> create_enable_joystick_hotkeys_button();
std::unique_ptr<List> create_show_hide_hotkey_options(); std::unique_ptr<List> create_show_hide_hotkey_options();
std::unique_ptr<List> create_replay_hotkey_options(); std::unique_ptr<List> create_replay_hotkey_options();
std::unique_ptr<List> create_replay_partial_save_hotkey_options();
std::unique_ptr<List> create_record_hotkey_options(); std::unique_ptr<List> create_record_hotkey_options();
std::unique_ptr<List> create_stream_hotkey_options(); std::unique_ptr<List> create_stream_hotkey_options();
std::unique_ptr<List> create_screenshot_hotkey_options(); std::unique_ptr<List> create_screenshot_hotkey_options();
@@ -89,6 +92,8 @@ namespace gsr {
Button *turn_replay_on_off_button_ptr = nullptr; Button *turn_replay_on_off_button_ptr = nullptr;
Button *save_replay_button_ptr = nullptr; Button *save_replay_button_ptr = nullptr;
Button *save_replay_1_min_button_ptr = nullptr;
Button *save_replay_10_min_button_ptr = nullptr;
Button *start_stop_recording_button_ptr = nullptr; Button *start_stop_recording_button_ptr = nullptr;
Button *pause_unpause_recording_button_ptr = nullptr; Button *pause_unpause_recording_button_ptr = nullptr;
Button *start_stop_streaming_button_ptr = nullptr; Button *start_stop_streaming_button_ptr = nullptr;

View File

@@ -21,19 +21,20 @@ namespace gsr {
List(Orientation orientation, Alignment content_alignment = Alignment::START); List(Orientation orientation, Alignment content_alignment = Alignment::START);
List(const List&) = delete; List(const List&) = delete;
List& operator=(const List&) = delete; List& operator=(const List&) = delete;
virtual ~List() override;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override;
//void remove_child_widget(Widget *widget) override;
void add_widget(std::unique_ptr<Widget> widget); void add_widget(std::unique_ptr<Widget> widget);
void remove_widget(Widget *widget); void remove_widget(Widget *widget);
void replace_widget(Widget *widget, std::unique_ptr<Widget> new_widget);
void clear(); void clear();
// Return true from |callback| to continue // Return true from |callback| to continue
void for_each_child_widget(std::function<bool(std::unique_ptr<Widget> &widget)> callback); void for_each_child_widget(std::function<bool(std::unique_ptr<Widget> &widget)> callback);
// Returns nullptr if index is invalid // Returns nullptr if index is invalid
Widget* get_child_widget_by_index(size_t index) const; Widget* get_child_widget_by_index(size_t index) const;
size_t get_num_children() const;
void set_spacing(float spacing); void set_spacing(float spacing);

View File

@@ -10,13 +10,11 @@ namespace gsr {
Page() = default; Page() = default;
Page(const Page&) = delete; Page(const Page&) = delete;
Page& operator=(const Page&) = delete; Page& operator=(const Page&) = delete;
virtual ~Page() = default; virtual ~Page() override;
virtual void on_navigate_to_page() {} virtual void on_navigate_to_page() {}
virtual void on_navigate_away_from_page() {} virtual void on_navigate_away_from_page() {}
//void remove_child_widget(Widget *widget) override;
virtual void add_widget(std::unique_ptr<Widget> widget); virtual void add_widget(std::unique_ptr<Widget> widget);
protected: protected:
SafeVector<std::unique_ptr<Widget>> widgets; SafeVector<std::unique_ptr<Widget>> widgets;

View File

@@ -12,6 +12,7 @@ namespace gsr {
ScrollablePage(mgl::vec2f size); ScrollablePage(mgl::vec2f size);
ScrollablePage(const ScrollablePage&) = delete; ScrollablePage(const ScrollablePage&) = delete;
ScrollablePage& operator=(const ScrollablePage&) = delete; ScrollablePage& operator=(const ScrollablePage&) = delete;
virtual ~ScrollablePage() override;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override;

View File

@@ -18,6 +18,7 @@ namespace gsr {
class ScrollablePage; class ScrollablePage;
class Label; class Label;
class LineSeparator; class LineSeparator;
class Subsection;
class SettingsPage : public StaticPage { class SettingsPage : public StaticPage {
public: public:
@@ -54,19 +55,20 @@ namespace gsr {
std::unique_ptr<Widget> create_change_video_resolution_section(); std::unique_ptr<Widget> create_change_video_resolution_section();
std::unique_ptr<Widget> create_capture_target_section(); std::unique_ptr<Widget> create_capture_target_section();
std::unique_ptr<ComboBox> create_audio_device_selection_combobox(); 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<Button> create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr);
std::unique_ptr<List> create_audio_device(); std::unique_ptr<List> create_audio_device(List *audio_input_list_ptr);
std::unique_ptr<Button> create_add_audio_device_button(); std::unique_ptr<Button> create_add_audio_track_button();
std::unique_ptr<ComboBox> create_application_audio_selection_combobox(); std::unique_ptr<Button> create_add_audio_device_button(List *audio_input_list_ptr);
std::unique_ptr<List> create_application_audio(); std::unique_ptr<ComboBox> create_application_audio_selection_combobox(List *application_audio_row);
std::unique_ptr<List> create_custom_application_audio(); std::unique_ptr<List> create_application_audio(List *audio_input_list_ptr);
std::unique_ptr<Button> create_add_application_audio_button(); std::unique_ptr<List> create_custom_application_audio(List *audio_input_list_ptr);
std::unique_ptr<Button> create_add_custom_application_audio_button(); std::unique_ptr<Button> create_add_application_audio_button(List *audio_input_list_ptr);
std::unique_ptr<List> create_add_audio_buttons(); std::unique_ptr<List> create_add_audio_buttons(List *audio_input_list_ptr);
std::unique_ptr<List> create_audio_track_track_section(); std::unique_ptr<List> create_audio_input_section();
std::unique_ptr<CheckBox> create_split_audio_checkbox();
std::unique_ptr<CheckBox> create_application_audio_invert_checkbox(); std::unique_ptr<CheckBox> create_application_audio_invert_checkbox();
std::unique_ptr<Widget> create_audio_track_section(); std::unique_ptr<List> create_audio_track_title_and_remove(Subsection *audio_track_subsection, const char *title);
std::unique_ptr<Subsection> create_audio_track_section(Widget *parent_widget);
std::unique_ptr<List> create_audio_track_section_list();
std::unique_ptr<Widget> create_audio_section(); std::unique_ptr<Widget> create_audio_section();
std::unique_ptr<List> create_video_quality_box(); std::unique_ptr<List> create_video_quality_box();
std::unique_ptr<List> create_video_bitrate_entry(); std::unique_ptr<List> create_video_bitrate_entry();
@@ -125,6 +127,8 @@ namespace gsr {
void save_replay(); void save_replay();
void save_record(); void save_record();
void save_stream(); void save_stream();
void view_changed(bool advanced_view, Subsection *notifications_subsection_ptr);
private: private:
Type type; Type type;
Config &config; Config &config;
@@ -152,11 +156,6 @@ namespace gsr {
Entry *framerate_entry_ptr = nullptr; Entry *framerate_entry_ptr = nullptr;
Entry *video_bitrate_entry_ptr = nullptr; Entry *video_bitrate_entry_ptr = nullptr;
List *video_bitrate_list_ptr = nullptr; List *video_bitrate_list_ptr = nullptr;
List *audio_track_list_ptr = nullptr;
Button *add_application_audio_button_ptr = nullptr;
Button *add_custom_application_audio_button_ptr = nullptr;
CheckBox *split_audio_checkbox_ptr = nullptr;
CheckBox *application_audio_invert_checkbox_ptr = nullptr;
CheckBox *change_video_resolution_checkbox_ptr = nullptr; CheckBox *change_video_resolution_checkbox_ptr = nullptr;
ComboBox *color_range_box_ptr = nullptr; ComboBox *color_range_box_ptr = nullptr;
ComboBox *video_quality_box_ptr = nullptr; ComboBox *video_quality_box_ptr = nullptr;
@@ -189,6 +188,8 @@ namespace gsr {
Entry *replay_time_entry_ptr = nullptr; Entry *replay_time_entry_ptr = nullptr;
Label *replay_time_label_ptr = nullptr; Label *replay_time_label_ptr = nullptr;
RadioButton *turn_on_replay_automatically_mode_ptr = nullptr; RadioButton *turn_on_replay_automatically_mode_ptr = nullptr;
Subsection *audio_section_ptr = nullptr;
List *audio_track_section_list_ptr = nullptr;
PageStack *page_stack = nullptr; PageStack *page_stack = nullptr;
}; };

View File

@@ -11,15 +11,20 @@ namespace gsr {
Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size); Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size);
Subsection(const Subsection&) = delete; Subsection(const Subsection&) = delete;
Subsection& operator=(const Subsection&) = delete; Subsection& operator=(const Subsection&) = delete;
virtual ~Subsection() override;
bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override; bool on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) override;
void draw(mgl::Window &window, mgl::vec2f offset) override; void draw(mgl::Window &window, mgl::vec2f offset) override;
mgl::vec2f get_size() override; mgl::vec2f get_size() override;
mgl::vec2f get_inner_size() override; mgl::vec2f get_inner_size() override;
Widget* get_inner_widget();
void set_bg_color(mgl::Color color);
private: private:
Label label; Label label;
std::unique_ptr<Widget> inner_widget; std::unique_ptr<Widget> inner_widget;
mgl::vec2f size; mgl::vec2f size;
mgl::Color bg_color{25, 30, 34};
}; };
} }

View File

@@ -1,6 +1,7 @@
#pragma once #pragma once
#include <mglpp/system/vec.hpp> #include <mglpp/system/vec.hpp>
#include <memory>
namespace mgl { namespace mgl {
class Event; class Event;
@@ -31,8 +32,6 @@ namespace gsr {
virtual void draw(mgl::Window &window, mgl::vec2f offset) = 0; virtual void draw(mgl::Window &window, mgl::vec2f offset) = 0;
virtual void set_position(mgl::vec2f position); virtual void set_position(mgl::vec2f position);
//virtual void remove_child_widget(Widget *widget) { (void)widget; }
virtual mgl::vec2f get_position() const; virtual mgl::vec2f get_position() const;
virtual mgl::vec2f get_size() = 0; virtual mgl::vec2f get_size() = 0;
// This can be different from get_size, for example with ScrollablePage this excludes the margins // This can be different from get_size, for example with ScrollablePage this excludes the margins
@@ -61,4 +60,7 @@ namespace gsr {
bool visible = true; bool visible = true;
}; };
void add_widget_to_remove(std::unique_ptr<Widget> widget);
void remove_widgets_to_be_removed();
} }

View File

@@ -1,4 +1,4 @@
project('gsr-ui', ['c', 'cpp'], version : '1.3.4', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends') project('gsr-ui', ['c', 'cpp'], version : '1.5.0', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
if get_option('buildtype') == 'debug' if get_option('buildtype') == 'debug'
add_project_arguments('-g3', language : ['c', 'cpp']) add_project_arguments('-g3', language : ['c', 'cpp'])
@@ -61,7 +61,7 @@ datadir = get_option('datadir')
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui') gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp']) add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
add_project_arguments('-DGSR_FLATPAK_VERSION="5.3.0"', language: ['c', 'cpp']) add_project_arguments('-DGSR_FLATPAK_VERSION="5.4.0"', language: ['c', 'cpp'])
executable( executable(
meson.project_name(), meson.project_name(),
@@ -75,6 +75,7 @@ executable(
dependency('xext'), dependency('xext'),
dependency('xi'), dependency('xi'),
dependency('xcursor'), dependency('xcursor'),
dependency('xrandr'),
dependency('libpulse-simple'), dependency('libpulse-simple'),
dependency('libdrm'), dependency('libdrm'),
dependency('wayland-client'), dependency('wayland-client'),

View File

@@ -1,7 +1,7 @@
[package] [package]
name = "gsr-ui" name = "gsr-ui"
type = "executable" type = "executable"
version = "1.3.4" version = "1.5.0"
platforms = ["posix"] platforms = ["posix"]
[lang.cpp] [lang.cpp]
@@ -16,6 +16,7 @@ xfixes = ">=0"
xext = ">=0" xext = ">=0"
xi = ">=0" xi = ">=0"
xcursor = ">=1" xcursor = ">=1"
xrandr = ">=0.5"
libpulse-simple = ">=0" libpulse-simple = ">=0"
libdrm = ">=2" libdrm = ">=2"
wayland-client = ">=1" wayland-client = ">=1"

View File

@@ -15,6 +15,8 @@
#define FORMAT_U32 "%" PRIu32 #define FORMAT_U32 "%" PRIu32
namespace gsr { namespace gsr {
static const std::string_view add_audio_track_tag = "[add_audio_track]";
static std::vector<mgl::Keyboard::Key> hotkey_modifiers_to_mgl_keys(uint32_t modifiers) { static std::vector<mgl::Keyboard::Key> hotkey_modifiers_to_mgl_keys(uint32_t modifiers) {
std::vector<mgl::Keyboard::Key> result; std::vector<mgl::Keyboard::Key> result;
if(modifiers & HOTKEY_MOD_LCTRL) if(modifiers & HOTKEY_MOD_LCTRL)
@@ -82,8 +84,8 @@ namespace gsr {
modifier_str = mgl::Keyboard::key_to_string(modifier_key); modifier_str = mgl::Keyboard::key_to_string(modifier_key);
if(!modifier_side) { if(!modifier_side) {
string_remove_all(modifier_str, "Left"); string_remove_all(modifier_str, "Left ");
string_remove_all(modifier_str, "Right"); string_remove_all(modifier_str, "Right ");
} }
result += modifier_str; result += modifier_str;
} }
@@ -101,6 +103,14 @@ namespace gsr {
return result; return result;
} }
bool AudioTrack::operator==(const AudioTrack &other) const {
return audio_inputs == other.audio_inputs && application_audio_invert == other.application_audio_invert;
}
bool AudioTrack::operator!=(const AudioTrack &other) const {
return !operator==(other);
}
Config::Config(const SupportedCaptureOptions &capture_options) { Config::Config(const SupportedCaptureOptions &capture_options) {
const std::string default_videos_save_directory = get_videos_dir(); const std::string default_videos_save_directory = get_videos_dir();
const std::string default_pictures_save_directory = get_pictures_dir(); const std::string default_pictures_save_directory = get_pictures_dir();
@@ -108,16 +118,16 @@ namespace gsr {
set_hotkeys_to_default(); set_hotkeys_to_default();
streaming_config.record_options.video_quality = "custom"; streaming_config.record_options.video_quality = "custom";
streaming_config.record_options.audio_tracks.push_back("default_output"); streaming_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
streaming_config.record_options.video_bitrate = 15000; streaming_config.record_options.video_bitrate = 15000;
record_config.save_directory = default_videos_save_directory; record_config.save_directory = default_videos_save_directory;
record_config.record_options.audio_tracks.push_back("default_output"); record_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
record_config.record_options.video_bitrate = 45000; record_config.record_options.video_bitrate = 45000;
replay_config.record_options.video_quality = "custom"; replay_config.record_options.video_quality = "custom";
replay_config.save_directory = default_videos_save_directory; replay_config.save_directory = default_videos_save_directory;
replay_config.record_options.audio_tracks.push_back("default_output"); replay_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
replay_config.record_options.video_bitrate = 45000; replay_config.record_options.video_bitrate = 45000;
screenshot_config.save_directory = default_pictures_save_directory; screenshot_config.save_directory = default_pictures_save_directory;
@@ -138,6 +148,8 @@ namespace gsr {
replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT}; replay_config.start_stop_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT | HOTKEY_MOD_LSHIFT};
replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT}; replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
replay_config.save_1_min_hotkey = {mgl::Keyboard::F11, HOTKEY_MOD_LALT};
replay_config.save_10_min_hotkey = {mgl::Keyboard::F12, HOTKEY_MOD_LALT};
screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Printscreen, 0}; screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Printscreen, 0};
screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LCTRL}; screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LCTRL};
@@ -152,7 +164,7 @@ namespace gsr {
return KeyValue{line.substr(0, space_index), line.substr(space_index + 1)}; return KeyValue{line.substr(0, space_index), line.substr(space_index + 1)};
} }
using ConfigValue = std::variant<bool*, std::string*, int32_t*, ConfigHotkey*, std::vector<std::string>*>; using ConfigValue = std::variant<bool*, std::string*, int32_t*, ConfigHotkey*, std::vector<std::string>*, std::vector<AudioTrack>*>;
static std::map<std::string_view, ConfigValue> get_config_options(Config &config) { static std::map<std::string_view, ConfigValue> get_config_options(Config &config) {
return { return {
@@ -174,6 +186,7 @@ namespace gsr {
{"streaming.record_options.application_audio_invert", &config.streaming_config.record_options.application_audio_invert}, {"streaming.record_options.application_audio_invert", &config.streaming_config.record_options.application_audio_invert},
{"streaming.record_options.change_video_resolution", &config.streaming_config.record_options.change_video_resolution}, {"streaming.record_options.change_video_resolution", &config.streaming_config.record_options.change_video_resolution},
{"streaming.record_options.audio_track", &config.streaming_config.record_options.audio_tracks}, {"streaming.record_options.audio_track", &config.streaming_config.record_options.audio_tracks},
{"streaming.record_options.audio_track_item", &config.streaming_config.record_options.audio_tracks_list},
{"streaming.record_options.color_range", &config.streaming_config.record_options.color_range}, {"streaming.record_options.color_range", &config.streaming_config.record_options.color_range},
{"streaming.record_options.video_quality", &config.streaming_config.record_options.video_quality}, {"streaming.record_options.video_quality", &config.streaming_config.record_options.video_quality},
{"streaming.record_options.codec", &config.streaming_config.record_options.video_codec}, {"streaming.record_options.codec", &config.streaming_config.record_options.video_codec},
@@ -203,6 +216,7 @@ namespace gsr {
{"record.record_options.application_audio_invert", &config.record_config.record_options.application_audio_invert}, {"record.record_options.application_audio_invert", &config.record_config.record_options.application_audio_invert},
{"record.record_options.change_video_resolution", &config.record_config.record_options.change_video_resolution}, {"record.record_options.change_video_resolution", &config.record_config.record_options.change_video_resolution},
{"record.record_options.audio_track", &config.record_config.record_options.audio_tracks}, {"record.record_options.audio_track", &config.record_config.record_options.audio_tracks},
{"record.record_options.audio_track_item", &config.record_config.record_options.audio_tracks_list},
{"record.record_options.color_range", &config.record_config.record_options.color_range}, {"record.record_options.color_range", &config.record_config.record_options.color_range},
{"record.record_options.video_quality", &config.record_config.record_options.video_quality}, {"record.record_options.video_quality", &config.record_config.record_options.video_quality},
{"record.record_options.codec", &config.record_config.record_options.video_codec}, {"record.record_options.codec", &config.record_config.record_options.video_codec},
@@ -231,6 +245,7 @@ namespace gsr {
{"replay.record_options.application_audio_invert", &config.replay_config.record_options.application_audio_invert}, {"replay.record_options.application_audio_invert", &config.replay_config.record_options.application_audio_invert},
{"replay.record_options.change_video_resolution", &config.replay_config.record_options.change_video_resolution}, {"replay.record_options.change_video_resolution", &config.replay_config.record_options.change_video_resolution},
{"replay.record_options.audio_track", &config.replay_config.record_options.audio_tracks}, {"replay.record_options.audio_track", &config.replay_config.record_options.audio_tracks},
{"replay.record_options.audio_track_item", &config.replay_config.record_options.audio_tracks_list},
{"replay.record_options.color_range", &config.replay_config.record_options.color_range}, {"replay.record_options.color_range", &config.replay_config.record_options.color_range},
{"replay.record_options.video_quality", &config.replay_config.record_options.video_quality}, {"replay.record_options.video_quality", &config.replay_config.record_options.video_quality},
{"replay.record_options.codec", &config.replay_config.record_options.video_codec}, {"replay.record_options.codec", &config.replay_config.record_options.video_codec},
@@ -251,6 +266,8 @@ namespace gsr {
{"replay.time", &config.replay_config.replay_time}, {"replay.time", &config.replay_config.replay_time},
{"replay.start_stop_hotkey", &config.replay_config.start_stop_hotkey}, {"replay.start_stop_hotkey", &config.replay_config.start_stop_hotkey},
{"replay.save_hotkey", &config.replay_config.save_hotkey}, {"replay.save_hotkey", &config.replay_config.save_hotkey},
{"replay.save_1_min_hotkey", &config.replay_config.save_1_min_hotkey},
{"replay.save_10_min_hotkey", &config.replay_config.save_10_min_hotkey},
{"screenshot.record_area_option", &config.screenshot_config.record_area_option}, {"screenshot.record_area_option", &config.screenshot_config.record_area_option},
{"screenshot.image_width", &config.screenshot_config.image_width}, {"screenshot.image_width", &config.screenshot_config.image_width},
@@ -291,6 +308,9 @@ namespace gsr {
} else if(std::holds_alternative<std::vector<std::string>*>(it.second)) { } else if(std::holds_alternative<std::vector<std::string>*>(it.second)) {
if(*std::get<std::vector<std::string>*>(it.second) != *std::get<std::vector<std::string>*>(it_other->second)) if(*std::get<std::vector<std::string>*>(it.second) != *std::get<std::vector<std::string>*>(it_other->second))
return false; return false;
} else if(std::holds_alternative<std::vector<AudioTrack>*>(it.second)) {
if(*std::get<std::vector<AudioTrack>*>(it.second) != *std::get<std::vector<AudioTrack>*>(it_other->second))
return false;
} else { } else {
assert(false); assert(false);
} }
@@ -302,6 +322,17 @@ namespace gsr {
return !operator==(other); return !operator==(other);
} }
static void populate_new_audio_track_from_old(RecordOptions &record_options) {
if(record_options.merge_audio_tracks) {
record_options.audio_tracks_list.push_back({std::move(record_options.audio_tracks), record_options.application_audio_invert});
} else {
for(const std::string &audio_input : record_options.audio_tracks) {
record_options.audio_tracks_list.push_back({std::vector<std::string>{audio_input}, record_options.application_audio_invert});
}
}
record_options.audio_tracks.clear();
}
std::optional<Config> read_config(const SupportedCaptureOptions &capture_options) { std::optional<Config> read_config(const SupportedCaptureOptions &capture_options) {
std::optional<Config> config; std::optional<Config> config;
@@ -313,10 +344,15 @@ namespace gsr {
} }
config = Config(capture_options); config = Config(capture_options);
config->streaming_config.record_options.audio_tracks.clear(); config->streaming_config.record_options.audio_tracks.clear();
config->record_config.record_options.audio_tracks.clear(); config->record_config.record_options.audio_tracks.clear();
config->replay_config.record_options.audio_tracks.clear(); config->replay_config.record_options.audio_tracks.clear();
config->streaming_config.record_options.audio_tracks_list.clear();
config->record_config.record_options.audio_tracks_list.clear();
config->replay_config.record_options.audio_tracks_list.clear();
auto config_options = get_config_options(config.value()); auto config_options = get_config_options(config.value());
string_split_char(file_content, '\n', [&](std::string_view line) { string_split_char(file_content, '\n', [&](std::string_view line) {
@@ -355,6 +391,23 @@ namespace gsr {
} else if(std::holds_alternative<std::vector<std::string>*>(it->second)) { } else if(std::holds_alternative<std::vector<std::string>*>(it->second)) {
std::string array_value(key_value->value); std::string array_value(key_value->value);
std::get<std::vector<std::string>*>(it->second)->push_back(std::move(array_value)); std::get<std::vector<std::string>*>(it->second)->push_back(std::move(array_value));
} else if(std::holds_alternative<std::vector<AudioTrack>*>(it->second)) {
const size_t space_index = key_value->value.find(' ');
if(space_index == std::string_view::npos) {
fprintf(stderr, "Warning: Invalid config option value for %.*s\n", (int)key_value->key.size(), key_value->key.data());
return true;
}
const bool application_audio_invert = key_value->value.substr(0, space_index) == "true";
const std::string_view audio_input = key_value->value.substr(space_index + 1);
std::vector<AudioTrack> &audio_tracks = *std::get<std::vector<AudioTrack>*>(it->second);
if(audio_input == add_audio_track_tag) {
audio_tracks.push_back({std::vector<std::string>{}, application_audio_invert});
} else if(!audio_tracks.empty()) {
audio_tracks.back().application_audio_invert = application_audio_invert;
audio_tracks.back().audio_inputs.emplace_back(audio_input);
}
} else { } else {
assert(false); assert(false);
} }
@@ -362,11 +415,16 @@ namespace gsr {
return true; return true;
}); });
if(config->main_config.config_file_version != GSR_CONFIG_FILE_VERSION) { if(config->main_config.config_file_version == 1) {
fprintf(stderr, "Info: the config file is outdated, resetting it\n"); populate_new_audio_track_from_old(config->streaming_config.record_options);
config = std::nullopt; populate_new_audio_track_from_old(config->record_config.record_options);
populate_new_audio_track_from_old(config->replay_config.record_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();
return config; return config;
} }
@@ -402,9 +460,17 @@ namespace gsr {
const ConfigHotkey *config_hotkey = std::get<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->key, config_hotkey->modifiers); fprintf(file, "%.*s " FORMAT_I64 " " FORMAT_U32 "\n", (int)it.first.size(), it.first.data(), config_hotkey->key, config_hotkey->modifiers);
} else if(std::holds_alternative<std::vector<std::string>*>(it.second)) { } else if(std::holds_alternative<std::vector<std::string>*>(it.second)) {
std::vector<std::string> *array = std::get<std::vector<std::string>*>(it.second); std::vector<std::string> *audio_inputs = std::get<std::vector<std::string>*>(it.second);
for(const std::string &value : *array) { for(const std::string &audio_input : *audio_inputs) {
fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), value.c_str()); fprintf(file, "%.*s %s\n", (int)it.first.size(), it.first.data(), audio_input.c_str());
}
} else if(std::holds_alternative<std::vector<AudioTrack>*>(it.second)) {
std::vector<AudioTrack> *audio_tracks = std::get<std::vector<AudioTrack>*>(it.second);
for(const AudioTrack &audio_track : *audio_tracks) {
fprintf(file, "%.*s %s %.*s\n", (int)it.first.size(), it.first.data(), audio_track.application_audio_invert ? "true" : "false", (int)add_audio_track_tag.size(), add_audio_track_tag.data());
for(const std::string &audio_input : audio_track.audio_inputs) {
fprintf(file, "%.*s %s %s\n", (int)it.first.size(), it.first.data(), audio_track.application_audio_invert ? "true" : "false", audio_input.c_str());
}
} }
} else { } else {
assert(false); assert(false);

View File

@@ -7,6 +7,8 @@
namespace gsr { namespace gsr {
static constexpr int button_pressed = 1; static constexpr int button_pressed = 1;
static constexpr int cross_button = 0;
static constexpr int triangle_button = 2;
static constexpr int options_button = 9; static constexpr int options_button = 9;
static constexpr int playstation_button = 10; static constexpr int playstation_button = 10;
static constexpr int axis_up_down = 7; static constexpr int axis_up_down = 7;
@@ -104,6 +106,20 @@ namespace gsr {
it->second("save_replay"); it->second("save_replay");
} }
if(save_1_min_replay) {
save_1_min_replay = false;
auto it = bound_actions_by_id.find("save_1_min_replay");
if(it != bound_actions_by_id.end())
it->second("save_1_min_replay");
}
if(save_10_min_replay) {
save_10_min_replay = false;
auto it = bound_actions_by_id.find("save_10_min_replay");
if(it != bound_actions_by_id.end())
it->second("save_10_min_replay");
}
if(take_screenshot) { if(take_screenshot) {
take_screenshot = false; take_screenshot = false;
auto it = bound_actions_by_id.find("take_screenshot"); auto it = bound_actions_by_id.find("take_screenshot");
@@ -186,10 +202,27 @@ namespace gsr {
return; return;
if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) { if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) {
if(event.number == playstation_button) switch(event.number) {
playstation_button_pressed = event.value == button_pressed; case playstation_button: {
else if(playstation_button_pressed && event.number == options_button && event.value == button_pressed) playstation_button_pressed = event.value == button_pressed;
toggle_show = true; break;
}
case options_button: {
if(playstation_button_pressed && event.value == button_pressed)
toggle_show = true;
break;
}
case cross_button: {
if(playstation_button_pressed && event.value == button_pressed)
save_1_min_replay = true;
break;
}
case triangle_button: {
if(playstation_button_pressed && event.value == button_pressed)
save_10_min_replay = true;
break;
}
}
} else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) { } else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) {
const int trigger_threshold = 16383; const int trigger_threshold = 16383;
const bool prev_up_pressed = up_pressed; const bool prev_up_pressed = up_pressed;

View File

@@ -77,6 +77,8 @@ namespace gsr {
fprintf(stderr, "Error: GlobalHotkeysLinux::~GlobalHotkeysLinux: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno)); fprintf(stderr, "Error: GlobalHotkeysLinux::~GlobalHotkeysLinux: failed to write command to gsr-global-hotkeys, error: %s\n", strerror(errno));
close_fds(); close_fds();
} }
} else {
close_fds();
} }
if(process_id > 0) { if(process_id > 0) {

View File

@@ -175,11 +175,6 @@ namespace gsr {
CAPTURE_OPTIONS CAPTURE_OPTIONS
}; };
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;
}
GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info) { GsrInfoExitStatus get_gpu_screen_recorder_info(GsrInfo *gsr_info) {
*gsr_info = GsrInfo{}; *gsr_info = GsrInfo{};

File diff suppressed because it is too large Load Diff

View File

@@ -75,7 +75,7 @@ namespace gsr {
if(!theme->folder_texture.load_from_file((resources_path + "images/folder.png").c_str())) if(!theme->folder_texture.load_from_file((resources_path + "images/folder.png").c_str()))
goto error; goto error;
if(!theme->up_arrow_texture.load_from_file((resources_path + "images/up_arrow.png").c_str())) if(!theme->up_arrow_texture.load_from_file((resources_path + "images/up_arrow.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->replay_button_texture.load_from_file((resources_path + "images/replay.png").c_str())) if(!theme->replay_button_texture.load_from_file((resources_path + "images/replay.png").c_str()))
@@ -93,10 +93,10 @@ namespace gsr {
if(!theme->logo_texture.load_from_file((resources_path + "images/gpu_screen_recorder_logo.png").c_str())) if(!theme->logo_texture.load_from_file((resources_path + "images/gpu_screen_recorder_logo.png").c_str()))
goto error; goto error;
if(!theme->checkbox_circle_texture.load_from_file((resources_path + "images/checkbox_circle.png").c_str())) if(!theme->checkbox_circle_texture.load_from_file((resources_path + "images/checkbox_circle.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->checkbox_background_texture.load_from_file((resources_path + "images/checkbox_background.png").c_str())) if(!theme->checkbox_background_texture.load_from_file((resources_path + "images/checkbox_background.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->play_texture.load_from_file((resources_path + "images/play.png").c_str())) if(!theme->play_texture.load_from_file((resources_path + "images/play.png").c_str()))
@@ -114,22 +114,31 @@ namespace gsr {
if(!theme->screenshot_texture.load_from_file((resources_path + "images/screenshot.png").c_str())) if(!theme->screenshot_texture.load_from_file((resources_path + "images/screenshot.png").c_str()))
goto error; goto error;
if(!theme->ps4_home_texture.load_from_file((resources_path + "images/ps4_home.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->trash_texture.load_from_file((resources_path + "images/trash.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->ps4_options_texture.load_from_file((resources_path + "images/ps4_options.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->ps4_home_texture.load_from_file((resources_path + "images/ps4_home.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->ps4_dpad_up_texture.load_from_file((resources_path + "images/ps4_dpad_up.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->ps4_options_texture.load_from_file((resources_path + "images/ps4_options.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->ps4_dpad_down_texture.load_from_file((resources_path + "images/ps4_dpad_down.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->ps4_dpad_up_texture.load_from_file((resources_path + "images/ps4_dpad_up.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error;
if(!theme->ps4_dpad_down_texture.load_from_file((resources_path + "images/ps4_dpad_down.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->ps4_dpad_left_texture.load_from_file((resources_path + "images/ps4_dpad_left.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->ps4_dpad_left_texture.load_from_file((resources_path + "images/ps4_dpad_left.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
if(!theme->ps4_dpad_right_texture.load_from_file((resources_path + "images/ps4_dpad_right.png").c_str(), mgl::Texture::LoadOptions{false, false, true})) if(!theme->ps4_dpad_right_texture.load_from_file((resources_path + "images/ps4_dpad_right.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error;
if(!theme->ps4_cross_texture.load_from_file((resources_path + "images/ps4_cross.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error;
if(!theme->ps4_triangle_texture.load_from_file((resources_path + "images/ps4_triangle.png").c_str(), mgl::Texture::LoadOptions{false, false, MGL_TEXTURE_SCALE_LINEAR_MIPMAP}))
goto error; goto error;
return true; return true;

View File

@@ -22,6 +22,11 @@ namespace gsr {
} }
} }
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;
}
std::string get_home_dir() { std::string get_home_dir() {
const char *home_dir = getenv("HOME"); const char *home_dir = getenv("HOME");
if(!home_dir) { if(!home_dir) {

View File

@@ -5,6 +5,7 @@
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
#include <X11/extensions/Xfixes.h> #include <X11/extensions/Xfixes.h>
#include <X11/extensions/shapeconst.h> #include <X11/extensions/shapeconst.h>
#include <X11/extensions/Xrandr.h>
#include <mglpp/system/Utf8.hpp> #include <mglpp/system/Utf8.hpp>
@@ -518,14 +519,21 @@ namespace gsr {
return XGetSelectionOwner(dpy, prop_atom) != None; return XGetSelectionOwner(dpy, prop_atom) != None;
} }
static void get_monitors_callback(const mgl_monitor *monitor, void *userdata) {
std::vector<Monitor> *monitors = (std::vector<Monitor>*)userdata;
monitors->push_back({mgl::vec2i(monitor->pos.x, monitor->pos.y), mgl::vec2i(monitor->size.x, monitor->size.y), std::string(monitor->name)});
}
std::vector<Monitor> get_monitors(Display *dpy) { std::vector<Monitor> get_monitors(Display *dpy) {
std::vector<Monitor> monitors; std::vector<Monitor> monitors;
mgl_for_each_active_monitor_output(dpy, get_monitors_callback, &monitors); int nmonitors = 0;
XRRMonitorInfo *monitor_info = XRRGetMonitors(dpy, DefaultRootWindow(dpy), True, &nmonitors);
if(monitor_info) {
for(int i = 0; i < nmonitors; ++i) {
char *monitor_name = XGetAtomName(dpy, monitor_info[i].name);
if(!monitor_name)
continue;
monitors.push_back({mgl::vec2i(monitor_info[i].x, monitor_info[i].y), mgl::vec2i(monitor_info[i].width, monitor_info[i].height), std::string(monitor_name)});
XFree(monitor_name);
}
XRRFreeMonitors(monitor_info);
}
return monitors; return monitors;
} }

View File

@@ -63,7 +63,7 @@ namespace gsr {
window.draw(sprite); window.draw(sprite);
const int padding_icon_right = padding_right_icon_scale * get_button_height(); const int padding_icon_right = padding_right_icon_scale * get_button_height();
text.set_position((sprite.get_position() + mgl::vec2f(sprite.get_size().x + padding_icon_right, sprite.get_size().y * 0.5f - text.get_bounds().size.y * 0.5f)).floor()); text.set_position((sprite.get_position() + mgl::vec2f(sprite.get_size().x + padding_icon_right, sprite.get_size().y * 0.5f - text.get_bounds().size.y * 0.52f)).floor());
window.draw(text); window.draw(text);
} else { } else {
text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor()); text.set_position((draw_pos + item_size * 0.5f - text.get_bounds().size * 0.5f).floor());

View File

@@ -110,6 +110,14 @@ namespace gsr {
window.draw(rect); window.draw(rect);
} }
if(activated) {
description.set_color(get_color_theme().tint_color);
icon_sprite.set_color(get_color_theme().tint_color);
} else {
description.set_color(mgl::Color(150, 150, 150));
icon_sprite.set_color(mgl::Color(255, 255, 255));
}
const int text_margin = size.y * 0.085; const int text_margin = size.y * 0.085;
const auto title_bounds = title.get_bounds(); const auto title_bounds = title.get_bounds();
@@ -148,7 +156,7 @@ namespace gsr {
window.draw(separator); window.draw(separator);
} }
if(mouse_inside_item == -1) { if(mouse_inside_item == -1 && item.enabled) {
const bool inside = mgl::FloatRect(item_position, item_size).contains({ (float)mouse_pos.x, (float)mouse_pos.y }); const bool inside = mgl::FloatRect(item_position, item_size).contains({ (float)mouse_pos.x, (float)mouse_pos.y });
if(inside) { if(inside) {
draw_rectangle_outline(window, item_position, item_size, get_color_theme().tint_color, border_size); draw_rectangle_outline(window, item_position, item_size, get_color_theme().tint_color, border_size);
@@ -161,16 +169,18 @@ namespace gsr {
mgl::Sprite icon(item.icon_texture); mgl::Sprite icon(item.icon_texture);
icon.set_height((int)(item_size.y * 0.4f)); icon.set_height((int)(item_size.y * 0.4f));
icon.set_position((item_position + mgl::vec2f(padding_left, item_size.y * 0.5f - icon.get_size().y * 0.5f)).floor()); icon.set_position((item_position + mgl::vec2f(padding_left, item_size.y * 0.5f - icon.get_size().y * 0.5f)).floor());
icon.set_color(item.enabled ? mgl::Color(255, 255, 255, 255) : mgl::Color(255, 255, 255, 80));
window.draw(icon); window.draw(icon);
icon_offset = icon.get_size().x + icon_spacing; icon_offset = icon.get_size().x + icon_spacing;
} }
item.text.set_position((item_position + mgl::vec2f(padding_left + icon_offset, item_size.y * 0.5f - text_bounds.size.y * 0.5f)).floor()); item.text.set_position((item_position + mgl::vec2f(padding_left + icon_offset, item_size.y * 0.5f - text_bounds.size.y * 0.5f)).floor());
item.text.set_color(item.enabled ? mgl::Color(255, 255, 255, 255) : mgl::Color(255, 255, 255, 80));
window.draw(item.text); window.draw(item.text);
const auto description_bounds = item.description_text.get_bounds(); const auto description_bounds = item.description_text.get_bounds();
item.description_text.set_position((item_position + mgl::vec2f(item_size.x - description_bounds.size.x - padding_right, item_size.y * 0.5f - description_bounds.size.y * 0.5f)).floor()); item.description_text.set_position((item_position + mgl::vec2f(item_size.x - description_bounds.size.x - padding_right, item_size.y * 0.5f - description_bounds.size.y * 0.5f)).floor());
item.description_text.set_color(mgl::Color(255, 255, 255, 120)); item.description_text.set_color(item.enabled ? mgl::Color(255, 255, 255, 120) : mgl::Color(255, 255, 255, 40));
window.draw(item.description_text); window.draw(item.description_text);
item_position.y += item_size.y; item_position.y += item_size.y;
@@ -179,6 +189,10 @@ namespace gsr {
} }
void DropdownButton::add_item(const std::string &text, const std::string &id, const std::string &description) { void DropdownButton::add_item(const std::string &text, const std::string &id, const std::string &description) {
for(auto &item : items) {
if(item.id == id)
return;
}
items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id}); items.push_back({mgl::Text(text, *title_font), mgl::Text(description, *description_font), nullptr, id});
dirty = true; dirty = true;
} }
@@ -210,6 +224,15 @@ namespace gsr {
} }
} }
void DropdownButton::set_item_enabled(const std::string &id, bool enabled) {
for(auto &item : items) {
if(item.id == id) {
item.enabled = enabled;
return;
}
}
}
void DropdownButton::set_description(std::string description_text) { void DropdownButton::set_description(std::string description_text) {
description.set_string(std::move(description_text)); description.set_string(std::move(description_text));
} }
@@ -219,14 +242,6 @@ namespace gsr {
return; return;
this->activated = activated; this->activated = activated;
if(activated) {
description.set_color(get_color_theme().tint_color);
icon_sprite.set_color(get_color_theme().tint_color);
} else {
description.set_color(mgl::Color(150, 150, 150));
icon_sprite.set_color(mgl::Color(255, 255, 255));
}
} }
void DropdownButton::update_if_dirty() { void DropdownButton::update_if_dirty() {

View File

@@ -149,7 +149,7 @@ namespace gsr {
tint_color_radio_button_ptr = tint_color_radio_button.get(); tint_color_radio_button_ptr = tint_color_radio_button.get();
tint_color_radio_button->add_item("Red", "amd"); tint_color_radio_button->add_item("Red", "amd");
tint_color_radio_button->add_item("Green", "nvidia"); tint_color_radio_button->add_item("Green", "nvidia");
tint_color_radio_button->add_item("blue", "intel"); tint_color_radio_button->add_item("Blue", "intel");
tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) { tint_color_radio_button->on_selection_changed = [](const std::string&, const std::string &id) {
if(id == "amd") if(id == "amd")
get_color_theme().tint_color = mgl::Color(221, 0, 49); get_color_theme().tint_color = mgl::Color(221, 0, 49);
@@ -256,6 +256,30 @@ namespace gsr {
return list; return list;
} }
std::unique_ptr<List> GlobalSettingsPage::create_replay_partial_save_hotkey_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 1 minute replay:", get_color_theme().text_color));
auto save_replay_1_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_replay_1_min_button_ptr = save_replay_1_min_button.get();
list->add_widget(std::move(save_replay_1_min_button));
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Save 10 minute replay:", get_color_theme().text_color));
auto save_replay_10_min_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
save_replay_10_min_button_ptr = save_replay_10_min_button.get();
list->add_widget(std::move(save_replay_10_min_button));
save_replay_1_min_button_ptr->on_click = [this] {
configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_1_MIN);
};
save_replay_10_min_button_ptr->on_click = [this] {
configure_hotkey_start(ConfigureHotkeyType::REPLAY_SAVE_10_MIN);
};
return list;
}
std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() { std::unique_ptr<List> GlobalSettingsPage::create_record_hotkey_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
@@ -335,6 +359,8 @@ namespace gsr {
config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0}; config.record_config.pause_unpause_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0}; config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0}; config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.save_1_min_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.save_10_min_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0}; config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0}; config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0};
config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0}; config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
@@ -374,6 +400,7 @@ namespace gsr {
list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x)); list_ptr->add_widget(std::make_unique<LineSeparator>(LineSeparator::Orientation::HORIZONTAL, subsection->get_inner_size().x));
list_ptr->add_widget(create_show_hide_hotkey_options()); list_ptr->add_widget(create_show_hide_hotkey_options());
list_ptr->add_widget(create_replay_hotkey_options()); list_ptr->add_widget(create_replay_hotkey_options());
list_ptr->add_widget(create_replay_partial_save_hotkey_options());
list_ptr->add_widget(create_record_hotkey_options()); list_ptr->add_widget(create_record_hotkey_options());
list_ptr->add_widget(create_stream_hotkey_options()); list_ptr->add_widget(create_stream_hotkey_options());
list_ptr->add_widget(create_screenshot_hotkey_options()); list_ptr->add_widget(create_screenshot_hotkey_options());
@@ -393,6 +420,8 @@ namespace gsr {
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, get_theme().body_font.get_character_size(), "to show/hide the UI")); list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_options_texture, get_theme().body_font.get_character_size(), "to show/hide the UI"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, get_theme().body_font.get_character_size(), "to take a screenshot")); list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_up_texture, get_theme().body_font.get_character_size(), "to take a screenshot"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, get_theme().body_font.get_character_size(), "to save a replay")); list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_down_texture, get_theme().body_font.get_character_size(), "to save a replay"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_cross_texture, get_theme().body_font.get_character_size(), "to save a 1 minute replay"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_triangle_texture, get_theme().body_font.get_character_size(), "to save a 10 minute replay"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, get_theme().body_font.get_character_size(), "to start/stop recording")); list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_left_texture, get_theme().body_font.get_character_size(), "to start/stop recording"));
list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, get_theme().body_font.get_character_size(), "to turn replay on/off")); list_ptr->add_widget(create_joystick_hotkey_text(&get_theme().ps4_home_texture, &get_theme().ps4_dpad_right_texture, get_theme().body_font.get_character_size(), "to turn replay on/off"));
return subsection; return subsection;
@@ -490,6 +519,8 @@ namespace gsr {
void GlobalSettingsPage::load_hotkeys() { void GlobalSettingsPage::load_hotkeys() {
turn_replay_on_off_button_ptr->set_text(config.replay_config.start_stop_hotkey.to_string()); turn_replay_on_off_button_ptr->set_text(config.replay_config.start_stop_hotkey.to_string());
save_replay_button_ptr->set_text(config.replay_config.save_hotkey.to_string()); save_replay_button_ptr->set_text(config.replay_config.save_hotkey.to_string());
save_replay_1_min_button_ptr->set_text(config.replay_config.save_1_min_hotkey.to_string());
save_replay_10_min_button_ptr->set_text(config.replay_config.save_10_min_hotkey.to_string());
start_stop_recording_button_ptr->set_text(config.record_config.start_stop_hotkey.to_string()); start_stop_recording_button_ptr->set_text(config.record_config.start_stop_hotkey.to_string());
pause_unpause_recording_button_ptr->set_text(config.record_config.pause_unpause_hotkey.to_string()); pause_unpause_recording_button_ptr->set_text(config.record_config.pause_unpause_hotkey.to_string());
@@ -567,6 +598,10 @@ namespace gsr {
return turn_replay_on_off_button_ptr; return turn_replay_on_off_button_ptr;
case ConfigureHotkeyType::REPLAY_SAVE: case ConfigureHotkeyType::REPLAY_SAVE:
return save_replay_button_ptr; return save_replay_button_ptr;
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
return save_replay_1_min_button_ptr;
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
return save_replay_10_min_button_ptr;
case ConfigureHotkeyType::RECORD_START_STOP: case ConfigureHotkeyType::RECORD_START_STOP:
return start_stop_recording_button_ptr; return start_stop_recording_button_ptr;
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE: case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
@@ -591,6 +626,10 @@ namespace gsr {
return &config.replay_config.start_stop_hotkey; return &config.replay_config.start_stop_hotkey;
case ConfigureHotkeyType::REPLAY_SAVE: case ConfigureHotkeyType::REPLAY_SAVE:
return &config.replay_config.save_hotkey; return &config.replay_config.save_hotkey;
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
return &config.replay_config.save_1_min_hotkey;
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
return &config.replay_config.save_10_min_hotkey;
case ConfigureHotkeyType::RECORD_START_STOP: case ConfigureHotkeyType::RECORD_START_STOP:
return &config.record_config.start_stop_hotkey; return &config.record_config.start_stop_hotkey;
case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE: case ConfigureHotkeyType::RECORD_PAUSE_UNPAUSE:
@@ -643,6 +682,12 @@ namespace gsr {
case ConfigureHotkeyType::REPLAY_SAVE: case ConfigureHotkeyType::REPLAY_SAVE:
hotkey_configure_action_name = "Save replay"; hotkey_configure_action_name = "Save replay";
break; break;
case ConfigureHotkeyType::REPLAY_SAVE_1_MIN:
hotkey_configure_action_name = "Save 1 minute replay";
break;
case ConfigureHotkeyType::REPLAY_SAVE_10_MIN:
hotkey_configure_action_name = "Save 10 minute replay";
break;
case ConfigureHotkeyType::RECORD_START_STOP: case ConfigureHotkeyType::RECORD_START_STOP:
hotkey_configure_action_name = "Start/stop recording"; hotkey_configure_action_name = "Start/stop recording";
break; break;

View File

@@ -39,8 +39,9 @@ namespace gsr {
// Process widgets by visibility (backwards) // Process widgets by visibility (backwards)
return widgets.for_each_reverse([selected_widget, &window, &event, content_page_position](std::unique_ptr<Widget> &widget) { return widgets.for_each_reverse([selected_widget, &window, &event, content_page_position](std::unique_ptr<Widget> &widget) {
if(widget.get() != selected_widget) { Widget *p = widget.get();
if(!widget->on_event(event, window, content_page_position)) if(p != selected_widget) {
if(!p->on_event(event, window, content_page_position))
return false; return false;
} }
return true; return true;

View File

@@ -24,14 +24,23 @@ namespace gsr {
// Process widgets by visibility (backwards) // Process widgets by visibility (backwards)
return widgets.for_each_reverse([selected_widget, &event, &window](std::unique_ptr<Widget> &widget) { return widgets.for_each_reverse([selected_widget, &event, &window](std::unique_ptr<Widget> &widget) {
// Ignore offset because widgets are positioned with offset in ::draw, this solution is simpler // Ignore offset because widgets are positioned with offset in ::draw, this solution is simpler
if(widget.get() != selected_widget) { Widget *p = widget.get();
if(!widget->on_event(event, window, mgl::vec2f(0.0f, 0.0f))) if(p != selected_widget) {
if(!p->on_event(event, window, mgl::vec2f(0.0f, 0.0f)))
return false; return false;
} }
return true; return true;
}); });
} }
List::~List() {
widgets.for_each([this](std::unique_ptr<Widget> &widget) {
if(widget->parent_widget == this)
widget->parent_widget = nullptr;
return true;
}, true);
}
void List::draw(mgl::Window &window, mgl::vec2f offset) { void List::draw(mgl::Window &window, mgl::vec2f offset) {
if(!visible) if(!visible)
return; return;
@@ -104,15 +113,6 @@ namespace gsr {
selected_widget->draw(window, mgl::vec2f(0.0f, 0.0f)); selected_widget->draw(window, mgl::vec2f(0.0f, 0.0f));
} }
// void List::remove_child_widget(Widget *widget) {
// for(auto it = widgets.begin(), end = widgets.end(); it != end; ++it) {
// if(it->get() == widget) {
// widgets.erase(it);
// return;
// }
// }
// }
void List::add_widget(std::unique_ptr<Widget> widget) { void List::add_widget(std::unique_ptr<Widget> widget) {
widget->parent_widget = this; widget->parent_widget = this;
widgets.push_back(std::move(widget)); widgets.push_back(std::move(widget));
@@ -122,6 +122,10 @@ namespace gsr {
widgets.remove(widget); widgets.remove(widget);
} }
void List::replace_widget(Widget *widget, std::unique_ptr<Widget> new_widget) {
widgets.replace_item(widget, std::move(new_widget));
}
void List::clear() { void List::clear() {
widgets.clear(); widgets.clear();
} }
@@ -137,6 +141,10 @@ namespace gsr {
return nullptr; return nullptr;
} }
size_t List::get_num_children() const {
return widgets.size();
}
void List::set_spacing(float spacing) { void List::set_spacing(float spacing) {
spacing_scale = spacing; spacing_scale = spacing;
} }

View File

@@ -1,14 +1,13 @@
#include "../../include/gui/Page.hpp" #include "../../include/gui/Page.hpp"
namespace gsr { namespace gsr {
// void Page::remove_child_widget(Widget *widget) { Page::~Page() {
// for(auto it = widgets.begin(), end = widgets.end(); it != end; ++it) { widgets.for_each([this](std::unique_ptr<Widget> &widget) {
// if(it->get() == widget) { if(widget->parent_widget == this)
// widgets.erase(it); widget->parent_widget = nullptr;
// return; return true;
// } }, true);
// } }
// }
void Page::add_widget(std::unique_ptr<Widget> widget) { void Page::add_widget(std::unique_ptr<Widget> widget) {
widget->parent_widget = this; widget->parent_widget = this;

View File

@@ -257,8 +257,7 @@ namespace gsr {
void ScreenshotSettingsPage::add_widgets() { void ScreenshotSettingsPage::add_widgets() {
content_page_ptr->add_widget(create_settings()); content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) { record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
(void)text;
const bool window_selected = id == "window"; const bool window_selected = id == "window";
const bool portal_selected = id == "portal"; const bool portal_selected = id == "portal";
select_window_list_ptr->set_visible(window_selected); select_window_list_ptr->set_visible(window_selected);

View File

@@ -15,6 +15,14 @@ namespace gsr {
ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {} ScrollablePage::ScrollablePage(mgl::vec2f size) : size(size) {}
ScrollablePage::~ScrollablePage() {
widgets.for_each([this](std::unique_ptr<Widget> &widget) {
if(widget->parent_widget == this)
widget->parent_widget = nullptr;
return true;
}, true);
}
bool ScrollablePage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) { bool ScrollablePage::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f offset) {
if(!visible) if(!visible)
return true; return true;
@@ -57,8 +65,9 @@ namespace gsr {
// Process widgets by visibility (backwards) // Process widgets by visibility (backwards)
const bool continue_events = widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) { const bool continue_events = widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) {
if(widget.get() != selected_widget) { Widget *p = widget.get();
if(!widget->on_event(event, window, offset)) if(p != selected_widget) {
if(!p->on_event(event, window, offset))
return false; return false;
} }
return true; return true;

View File

@@ -11,6 +11,8 @@
#include <string.h> #include <string.h>
namespace gsr { namespace gsr {
static const char *custom_app_audio_tag = "[custom]";
enum class AudioTrackType { enum class AudioTrackType {
DEVICE, DEVICE,
APPLICATION, APPLICATION,
@@ -202,121 +204,201 @@ namespace gsr {
return audio_device_box; return audio_device_box;
} }
std::unique_ptr<Button> SettingsPage::create_remove_audio_device_button(List *audio_device_list_ptr) { static void set_application_audio_options_visible(Subsection *audio_track_subsection, bool visible, const GsrInfo &gsr_info) {
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Remove", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); if(!gsr_info.system_info.supports_app_audio)
remove_audio_track_button->on_click = [this, audio_device_list_ptr]() { visible = false;
audio_track_list_ptr->remove_widget(audio_device_list_ptr);
List *audio_track_items_list = dynamic_cast<List*>(audio_track_subsection->get_inner_widget());
List *buttons_list = dynamic_cast<List*>(audio_track_items_list->get_child_widget_by_index(1));
Button *add_application_audio_button = dynamic_cast<Button*>(buttons_list->get_child_widget_by_index(1));
add_application_audio_button->set_visible(visible);
CheckBox *invert_app_audio_checkbox = dynamic_cast<CheckBox*>(audio_track_items_list->get_child_widget_by_index(3));
invert_app_audio_checkbox->set_visible(visible);
}
static void set_application_audio_options_visible(List *audio_track_section_list_ptr, bool visible, const GsrInfo &gsr_info) {
audio_track_section_list_ptr->for_each_child_widget([visible, &gsr_info](std::unique_ptr<Widget> &widget) {
Subsection *audio_track_subsection = dynamic_cast<Subsection*>(widget.get());
set_application_audio_options_visible(audio_track_subsection, visible, gsr_info);
return true;
});
}
std::unique_ptr<Button> SettingsPage::create_remove_audio_device_button(List *audio_input_list_ptr, List *audio_device_list_ptr) {
auto remove_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
remove_audio_track_button->set_icon(&get_theme().trash_texture);
remove_audio_track_button->set_icon_padding_scale(0.75f);
remove_audio_track_button->on_click = [audio_input_list_ptr, audio_device_list_ptr]() {
audio_input_list_ptr->remove_widget(audio_device_list_ptr);
}; };
return remove_audio_track_button; return remove_audio_track_button;
} }
std::unique_ptr<List> SettingsPage::create_audio_device() { std::unique_ptr<List> SettingsPage::create_audio_device(List *audio_input_list_ptr) {
auto audio_device_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto audio_device_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
audio_device_list->userdata = (void*)(uintptr_t)AudioTrackType::DEVICE; audio_device_list->userdata = (void*)(uintptr_t)AudioTrackType::DEVICE;
audio_device_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Device:", get_color_theme().text_color)); audio_device_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Device:", get_color_theme().text_color));
audio_device_list->add_widget(create_audio_device_selection_combobox()); audio_device_list->add_widget(create_audio_device_selection_combobox());
audio_device_list->add_widget(create_remove_audio_device_button(audio_device_list.get())); audio_device_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, audio_device_list.get()));
return audio_device_list; return audio_device_list;
} }
std::unique_ptr<Button> SettingsPage::create_add_audio_device_button() { std::unique_ptr<Button> SettingsPage::create_add_audio_track_button() {
auto button = std::make_unique<Button>(&get_theme().body_font, "Add audio track", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
button->on_click = [this]() {
audio_track_section_list_ptr->add_widget(create_audio_track_section(audio_section_ptr));
};
button->set_visible(type != Type::STREAM);
return button;
}
std::unique_ptr<Button> SettingsPage::create_add_audio_device_button(List *audio_input_list_ptr) {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add audio device", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add audio device", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_audio_track_button->on_click = [this]() { add_audio_track_button->on_click = [this, audio_input_list_ptr]() {
audio_devices = get_audio_devices(); audio_devices = get_audio_devices();
audio_track_list_ptr->add_widget(create_audio_device()); audio_input_list_ptr->add_widget(create_audio_device(audio_input_list_ptr));
}; };
return add_audio_track_button; return add_audio_track_button;
} }
std::unique_ptr<ComboBox> SettingsPage::create_application_audio_selection_combobox() { std::unique_ptr<ComboBox> SettingsPage::create_application_audio_selection_combobox(List *application_audio_row) {
auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font); auto audio_device_box = std::make_unique<ComboBox>(&get_theme().body_font);
ComboBox *audio_device_box_ptr = audio_device_box.get();
for(const auto &app_audio : application_audio) { for(const auto &app_audio : application_audio) {
audio_device_box->add_item(app_audio, app_audio); audio_device_box->add_item(app_audio, app_audio);
} }
audio_device_box->add_item("Custom...", custom_app_audio_tag);
audio_device_box->on_selection_changed = [application_audio_row, audio_device_box_ptr](const std::string&, const std::string &id) {
if(id == custom_app_audio_tag) {
application_audio_row->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM;
auto custom_app_audio_entry = std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f));
application_audio_row->replace_widget(audio_device_box_ptr, std::move(custom_app_audio_entry));
}
};
return audio_device_box; return audio_device_box;
} }
std::unique_ptr<List> SettingsPage::create_application_audio() { std::unique_ptr<List> SettingsPage::create_application_audio(List *audio_input_list_ptr) {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION; application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION;
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color)); application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color));
application_audio_list->add_widget(create_application_audio_selection_combobox()); application_audio_list->add_widget(create_application_audio_selection_combobox(application_audio_list.get()));
application_audio_list->add_widget(create_remove_audio_device_button(application_audio_list.get())); application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
return application_audio_list; return application_audio_list;
} }
std::unique_ptr<List> SettingsPage::create_custom_application_audio() { std::unique_ptr<List> SettingsPage::create_custom_application_audio(List *audio_input_list_ptr) {
auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto application_audio_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM; application_audio_list->userdata = (void*)(uintptr_t)AudioTrackType::APPLICATION_CUSTOM;
application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color)); application_audio_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "App: ", get_color_theme().text_color));
application_audio_list->add_widget(std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f))); application_audio_list->add_widget(std::make_unique<Entry>(&get_theme().body_font, "", (int)(get_theme().body_font.get_character_size() * 10.0f)));
application_audio_list->add_widget(create_remove_audio_device_button(application_audio_list.get())); application_audio_list->add_widget(create_remove_audio_device_button(audio_input_list_ptr, application_audio_list.get()));
return application_audio_list; return application_audio_list;
} }
std::unique_ptr<Button> SettingsPage::create_add_application_audio_button() { std::unique_ptr<Button> SettingsPage::create_add_application_audio_button(List *audio_input_list_ptr) {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add application audio", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)); auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add application audio", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_application_audio_button_ptr = add_audio_track_button.get(); add_audio_track_button->on_click = [this, audio_input_list_ptr]() {
add_audio_track_button->on_click = [this]() {
application_audio = get_application_audio(); application_audio = get_application_audio();
audio_track_list_ptr->add_widget(create_application_audio()); if(application_audio.empty())
audio_input_list_ptr->add_widget(create_custom_application_audio(audio_input_list_ptr));
else
audio_input_list_ptr->add_widget(create_application_audio(audio_input_list_ptr));
}; };
return add_audio_track_button; return add_audio_track_button;
} }
std::unique_ptr<Button> SettingsPage::create_add_custom_application_audio_button() { std::unique_ptr<List> SettingsPage::create_add_audio_buttons(List *audio_input_list_ptr) {
auto add_audio_track_button = std::make_unique<Button>(&get_theme().body_font, "Add custom application audio", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
add_custom_application_audio_button_ptr = add_audio_track_button.get();
add_audio_track_button->on_click = [this]() {
audio_track_list_ptr->add_widget(create_custom_application_audio());
};
return add_audio_track_button;
}
std::unique_ptr<List> SettingsPage::create_add_audio_buttons() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(create_add_audio_device_button()); list->add_widget(create_add_audio_device_button(audio_input_list_ptr));
list->add_widget(create_add_application_audio_button()); list->add_widget(create_add_application_audio_button(audio_input_list_ptr));
list->add_widget(create_add_custom_application_audio_button());
return list; return list;
} }
std::unique_ptr<List> SettingsPage::create_audio_track_track_section() { std::unique_ptr<List> SettingsPage::create_audio_input_section() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL); auto list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_track_list_ptr = list.get(); //list->add_widget(create_audio_device(list.get())); // Add default_output by default
audio_track_list_ptr->add_widget(create_audio_device()); // Add default_output by default
return list; return list;
} }
std::unique_ptr<CheckBox> SettingsPage::create_split_audio_checkbox() {
auto split_audio_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Split each device/app audio into separate audio tracks");
split_audio_checkbox->set_checked(false);
split_audio_checkbox_ptr = split_audio_checkbox.get();
return split_audio_checkbox;
}
std::unique_ptr<CheckBox> SettingsPage::create_application_audio_invert_checkbox() { std::unique_ptr<CheckBox> SettingsPage::create_application_audio_invert_checkbox() {
auto application_audio_invert_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Record audio from all applications except the selected ones"); auto application_audio_invert_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Record audio from all applications except the selected ones");
application_audio_invert_checkbox->set_checked(false); application_audio_invert_checkbox->set_checked(false);
application_audio_invert_checkbox_ptr = application_audio_invert_checkbox.get();
return application_audio_invert_checkbox; return application_audio_invert_checkbox;
} }
std::unique_ptr<Widget> SettingsPage::create_audio_track_section() { static void update_audio_track_titles(List *audio_track_section_list_ptr) {
int index = 0;
audio_track_section_list_ptr->for_each_child_widget([&index](std::unique_ptr<Widget> &widget) {
char audio_track_name[32];
snprintf(audio_track_name, sizeof(audio_track_name), "Audio track #%d", 1 + index);
++index;
Subsection *subsection = dynamic_cast<Subsection*>(widget.get());
List *subesection_items = dynamic_cast<List*>(subsection->get_inner_widget());
Label *audio_track_title = dynamic_cast<Label*>(dynamic_cast<List*>(subesection_items->get_child_widget_by_index(0))->get_child_widget_by_index(0));
audio_track_title->set_text(audio_track_name);
return true;
});
}
std::unique_ptr<List> SettingsPage::create_audio_track_title_and_remove(Subsection *audio_track_subsection, const char *title) {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().title_font, title, get_color_theme().text_color));
auto remove_track_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 0));
remove_track_button->set_icon(&get_theme().trash_texture);
remove_track_button->set_icon_padding_scale(0.75f);
remove_track_button->on_click = [this, audio_track_subsection]() {
audio_track_section_list_ptr->remove_widget(audio_track_subsection);
update_audio_track_titles(audio_track_section_list_ptr);
};
list->add_widget(std::move(remove_track_button));
list->set_visible(type != Type::STREAM);
return list;
}
std::unique_ptr<Subsection> SettingsPage::create_audio_track_section(Widget *parent_widget) {
char audio_track_name[32];
snprintf(audio_track_name, sizeof(audio_track_name), "Audio track #%d", 1 + (int)audio_track_section_list_ptr->get_num_children());
auto audio_input_section = create_audio_input_section();
List *audio_input_section_ptr = audio_input_section.get();
auto list = std::make_unique<List>(List::Orientation::VERTICAL); auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(create_add_audio_buttons()); List *list_ptr = list.get();
list->add_widget(create_audio_track_track_section()); auto subsection = std::make_unique<Subsection>("", std::move(std::move(list)), mgl::vec2f(parent_widget->get_inner_size().x, 0.0f));
subsection->set_bg_color(mgl::Color(35, 40, 44));
list_ptr->add_widget(create_audio_track_title_and_remove(subsection.get(), audio_track_name));
list_ptr->add_widget(create_add_audio_buttons(audio_input_section_ptr));
list_ptr->add_widget(std::move(audio_input_section));
list_ptr->add_widget(create_application_audio_invert_checkbox());
set_application_audio_options_visible(subsection.get(), view_radio_button_ptr->get_selected_id() == "advanced", *gsr_info);
return subsection;
}
std::unique_ptr<List> SettingsPage::create_audio_track_section_list() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_track_section_list_ptr = list.get();
return list; return list;
} }
std::unique_ptr<Widget> SettingsPage::create_audio_section() { std::unique_ptr<Widget> SettingsPage::create_audio_section() {
auto audio_device_section_list = std::make_unique<List>(List::Orientation::VERTICAL); auto audio_device_section_list = std::make_unique<List>(List::Orientation::VERTICAL);
audio_device_section_list->add_widget(create_audio_track_section()); List *audio_device_section_list_ptr = audio_device_section_list.get();
if(type != Type::STREAM)
audio_device_section_list->add_widget(create_split_audio_checkbox()); auto subsection = std::make_unique<Subsection>("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
audio_device_section_list->add_widget(create_application_audio_invert_checkbox()); audio_section_ptr = subsection.get();
audio_device_section_list->add_widget(create_audio_codec()); audio_device_section_list_ptr->add_widget(create_add_audio_track_button());
return std::make_unique<Subsection>("Audio", std::move(audio_device_section_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)); audio_device_section_list_ptr->add_widget(create_audio_track_section_list());
audio_device_section_list_ptr->add_widget(create_audio_codec());
return subsection;
} }
std::unique_ptr<List> SettingsPage::create_video_quality_box() { std::unique_ptr<List> SettingsPage::create_video_quality_box() {
@@ -531,8 +613,7 @@ namespace gsr {
void SettingsPage::add_widgets() { void SettingsPage::add_widgets() {
content_page_ptr->add_widget(create_settings()); content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) { record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
(void)text;
const bool window_selected = id == "window"; const bool window_selected = id == "window";
const bool focused_selected = id == "focused"; const bool focused_selected = id == "focused";
const bool portal_selected = id == "portal"; const bool portal_selected = id == "portal";
@@ -549,8 +630,7 @@ namespace gsr {
video_resolution_list_ptr->set_visible(!focused_selected && checked); video_resolution_list_ptr->set_visible(!focused_selected && checked);
}; };
video_quality_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) { video_quality_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
(void)text;
const bool custom_selected = id == "custom"; const bool custom_selected = id == "custom";
video_bitrate_list_ptr->set_visible(custom_selected); video_bitrate_list_ptr->set_visible(custom_selected);
@@ -569,12 +649,6 @@ namespace gsr {
record_area_box_ptr->set_selected_item("window"); record_area_box_ptr->set_selected_item("window");
else else
record_area_box_ptr->on_selection_changed("", ""); record_area_box_ptr->on_selection_changed("", "");
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() { void SettingsPage::add_page_specific_widgets() {
@@ -716,6 +790,16 @@ namespace gsr {
replay_time_label_ptr->set_text(buffer); replay_time_label_ptr->set_text(buffer);
} }
void SettingsPage::view_changed(bool advanced_view, Subsection *notifications_subsection_ptr) {
color_range_list_ptr->set_visible(advanced_view);
audio_codec_ptr->set_visible(advanced_view);
video_codec_ptr->set_visible(advanced_view);
framerate_mode_list_ptr->set_visible(advanced_view);
notifications_subsection_ptr->set_visible(advanced_view);
set_application_audio_options_visible(audio_track_section_list_ptr, advanced_view, *gsr_info);
settings_scrollable_page_ptr->reset_scroll();
}
void SettingsPage::add_replay_widgets() { void SettingsPage::add_replay_widgets() {
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL); auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL); auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
@@ -754,16 +838,8 @@ namespace gsr {
Subsection *notifications_subsection_ptr = notifications_subsection.get(); Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection)); settings_list_ptr->add_widget(std::move(notifications_subsection));
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string &text, const std::string &id) { view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
(void)text; view_changed(id == "advanced", notifications_subsection_ptr);
const bool advanced_view = id == "advanced";
color_range_list_ptr->set_visible(advanced_view);
audio_codec_ptr->set_visible(advanced_view);
video_codec_ptr->set_visible(advanced_view);
framerate_mode_list_ptr->set_visible(advanced_view);
notifications_subsection_ptr->set_visible(advanced_view);
split_audio_checkbox_ptr->set_visible(advanced_view);
settings_scrollable_page_ptr->reset_scroll();
return true; return true;
}; };
view_radio_button_ptr->on_selection_changed("Simple", "simple"); view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -828,16 +904,8 @@ namespace gsr {
Subsection *notifications_subsection_ptr = notifications_subsection.get(); Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection)); settings_list_ptr->add_widget(std::move(notifications_subsection));
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string &text, const std::string &id) { view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
(void)text; view_changed(id == "advanced", notifications_subsection_ptr);
const bool advanced_view = id == "advanced";
color_range_list_ptr->set_visible(advanced_view);
audio_codec_ptr->set_visible(advanced_view);
video_codec_ptr->set_visible(advanced_view);
framerate_mode_list_ptr->set_visible(advanced_view);
notifications_subsection_ptr->set_visible(advanced_view);
split_audio_checkbox_ptr->set_visible(advanced_view);
settings_scrollable_page_ptr->reset_scroll();
return true; return true;
}; };
view_radio_button_ptr->on_selection_changed("Simple", "simple"); view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -933,8 +1001,7 @@ namespace gsr {
Subsection *notifications_subsection_ptr = notifications_subsection.get(); Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection)); settings_list_ptr->add_widget(std::move(notifications_subsection));
streaming_service_box_ptr->on_selection_changed = [this](const std::string &text, const std::string &id) { streaming_service_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
(void)text;
const bool twitch_option = id == "twitch"; const bool twitch_option = id == "twitch";
const bool youtube_option = id == "youtube"; const bool youtube_option = id == "youtube";
const bool custom_option = id == "custom"; const bool custom_option = id == "custom";
@@ -947,15 +1014,8 @@ namespace gsr {
}; };
streaming_service_box_ptr->on_selection_changed("Twitch", "twitch"); streaming_service_box_ptr->on_selection_changed("Twitch", "twitch");
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string &text, const std::string &id) { view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
(void)text; view_changed(id == "advanced", notifications_subsection_ptr);
const bool advanced_view = id == "advanced";
color_range_list_ptr->set_visible(advanced_view);
audio_codec_ptr->set_visible(advanced_view);
video_codec_ptr->set_visible(advanced_view);
framerate_mode_list_ptr->set_visible(advanced_view);
notifications_subsection_ptr->set_visible(advanced_view);
settings_scrollable_page_ptr->reset_scroll();
return true; return true;
}; };
view_radio_button_ptr->on_selection_changed("Simple", "simple"); view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -1006,50 +1066,61 @@ namespace gsr {
return nullptr; return nullptr;
} }
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;
}
void SettingsPage::load_audio_tracks(const RecordOptions &record_options) { void SettingsPage::load_audio_tracks(const RecordOptions &record_options) {
audio_track_list_ptr->clear(); audio_track_section_list_ptr->clear();
for(const std::string &audio_track : record_options.audio_tracks) { for(const AudioTrack &audio_track : record_options.audio_tracks_list) {
if(starts_with(audio_track, "app:")) { auto audio_track_section = create_audio_track_section(audio_section_ptr);
if(!gsr_info->system_info.supports_app_audio) List *audio_track_section_items_list_ptr = dynamic_cast<List*>(audio_track_section->get_inner_widget());
continue; List *audio_input_list_ptr = dynamic_cast<List*>(audio_track_section_items_list_ptr->get_child_widget_by_index(2));
CheckBox *application_audio_invert_checkbox_ptr = dynamic_cast<CheckBox*>(audio_track_section_items_list_ptr->get_child_widget_by_index(3));
application_audio_invert_checkbox_ptr->set_checked(audio_track.application_audio_invert);
std::string audio_track_name = audio_track.substr(4); audio_input_list_ptr->clear();
const std::string *app_audio = get_application_audio_by_name_case_insensitive(application_audio, audio_track_name); for(const std::string &audio_input : audio_track.audio_inputs) {
if(app_audio) { if(starts_with(audio_input, "app:")) {
std::unique_ptr<List> application_audio_widget = create_application_audio(); if(!gsr_info->system_info.supports_app_audio)
ComboBox *application_audio_box = static_cast<ComboBox*>(application_audio_widget->get_child_widget_by_index(1)); continue;
application_audio_box->set_selected_item(*app_audio);
audio_track_list_ptr->add_widget(std::move(application_audio_widget)); std::string audio_track_name = audio_input.substr(4);
const std::string *app_audio = get_application_audio_by_name_case_insensitive(application_audio, audio_track_name);
if(app_audio) {
std::unique_ptr<List> application_audio_widget = create_application_audio(audio_input_list_ptr);
ComboBox *application_audio_box = dynamic_cast<ComboBox*>(application_audio_widget->get_child_widget_by_index(1));
application_audio_box->set_selected_item(*app_audio);
audio_input_list_ptr->add_widget(std::move(application_audio_widget));
} else {
std::unique_ptr<List> application_audio_widget = create_custom_application_audio(audio_input_list_ptr);
Entry *application_audio_entry = dynamic_cast<Entry*>(application_audio_widget->get_child_widget_by_index(1));
application_audio_entry->set_text(std::move(audio_track_name));
audio_input_list_ptr->add_widget(std::move(application_audio_widget));
}
} else if(starts_with(audio_input, "device:")) {
std::unique_ptr<List> audio_track_widget = create_audio_device(audio_input_list_ptr);
ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
audio_device_box->set_selected_item(audio_input.substr(7));
audio_input_list_ptr->add_widget(std::move(audio_track_widget));
} else { } else {
std::unique_ptr<List> application_audio_widget = create_custom_application_audio(); std::unique_ptr<List> audio_track_widget = create_audio_device(audio_input_list_ptr);
Entry *application_audio_entry = static_cast<Entry*>(application_audio_widget->get_child_widget_by_index(1)); ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
application_audio_entry->set_text(std::move(audio_track_name)); audio_device_box->set_selected_item(audio_input);
audio_track_list_ptr->add_widget(std::move(application_audio_widget)); audio_input_list_ptr->add_widget(std::move(audio_track_widget));
} }
} else if(starts_with(audio_track, "device:")) {
std::unique_ptr<List> audio_track_widget = create_audio_device();
ComboBox *audio_device_box = static_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
audio_device_box->set_selected_item(audio_track.substr(7));
audio_track_list_ptr->add_widget(std::move(audio_track_widget));
} else {
std::unique_ptr<List> audio_track_widget = create_audio_device();
ComboBox *audio_device_box = static_cast<ComboBox*>(audio_track_widget->get_child_widget_by_index(1));
audio_device_box->set_selected_item(audio_track);
audio_track_list_ptr->add_widget(std::move(audio_track_widget));
} }
audio_track_section_list_ptr->add_widget(std::move(audio_track_section));
if(type == Type::STREAM)
break;
}
if(type == Type::STREAM && audio_track_section_list_ptr->get_num_children() == 0) {
auto audio_track_section = create_audio_track_section(audio_section_ptr);
audio_track_section_list_ptr->add_widget(std::move(audio_track_section));
} }
} }
void SettingsPage::load_common(RecordOptions &record_options) { void SettingsPage::load_common(RecordOptions &record_options) {
record_area_box_ptr->set_selected_item(record_options.record_area_option); record_area_box_ptr->set_selected_item(record_options.record_area_option);
if(split_audio_checkbox_ptr)
split_audio_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); change_video_resolution_checkbox_ptr->set_checked(record_options.change_video_resolution);
load_audio_tracks(record_options); load_audio_tracks(record_options);
color_range_box_ptr->set_selected_item(record_options.color_range); color_range_box_ptr->set_selected_item(record_options.color_range);
@@ -1139,28 +1210,38 @@ namespace gsr {
container_box_ptr->set_selected_item(config.streaming_config.custom.container); container_box_ptr->set_selected_item(config.streaming_config.custom.container);
} }
static void save_audio_tracks(std::vector<std::string> &audio_devices, List *audio_devices_list_ptr) { static void save_audio_tracks(std::vector<AudioTrack> &audio_tracks, List *audio_track_section_list_ptr) {
audio_devices.clear(); audio_tracks.clear();
audio_devices_list_ptr->for_each_child_widget([&audio_devices](std::unique_ptr<Widget> &child_widget) { audio_track_section_list_ptr->for_each_child_widget([&audio_tracks](std::unique_ptr<Widget> &child_widget) {
List *audio_track_line = static_cast<List*>(child_widget.get()); Subsection *audio_subsection = dynamic_cast<Subsection*>(child_widget.get());
const AudioTrackType audio_track_type = (AudioTrackType)(uintptr_t)audio_track_line->userdata; List *audio_track_section_items_list_ptr = dynamic_cast<List*>(audio_subsection->get_inner_widget());
switch(audio_track_type) { List *audio_input_list_ptr = dynamic_cast<List*>(audio_track_section_items_list_ptr->get_child_widget_by_index(2));
case AudioTrackType::DEVICE: { CheckBox *application_audio_invert_checkbox_ptr = dynamic_cast<CheckBox*>(audio_track_section_items_list_ptr->get_child_widget_by_index(3));
ComboBox *audio_device_box = static_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1));
audio_devices.push_back("device:" + audio_device_box->get_selected_id()); audio_tracks.push_back({std::vector<std::string>{}, application_audio_invert_checkbox_ptr->is_checked()});
break; audio_input_list_ptr->for_each_child_widget([&audio_tracks](std::unique_ptr<Widget> &child_widget){
List *audio_track_line = dynamic_cast<List*>(child_widget.get());
const AudioTrackType audio_track_type = (AudioTrackType)(uintptr_t)audio_track_line->userdata;
switch(audio_track_type) {
case AudioTrackType::DEVICE: {
ComboBox *audio_device_box = dynamic_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("device:" + audio_device_box->get_selected_id());
break;
}
case AudioTrackType::APPLICATION: {
ComboBox *application_audio_box = dynamic_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("app:" + application_audio_box->get_selected_id());
break;
}
case AudioTrackType::APPLICATION_CUSTOM: {
Entry *application_audio_entry = dynamic_cast<Entry*>(audio_track_line->get_child_widget_by_index(1));
audio_tracks.back().audio_inputs.push_back("app:" + application_audio_entry->get_text());
break;
}
} }
case AudioTrackType::APPLICATION: { return true;
ComboBox *application_audio_box = static_cast<ComboBox*>(audio_track_line->get_child_widget_by_index(1)); });
audio_devices.push_back("app:" + application_audio_box->get_selected_id());
break;
}
case AudioTrackType::APPLICATION_CUSTOM: {
Entry *application_audio_entry = static_cast<Entry*>(audio_track_line->get_child_widget_by_index(1));
audio_devices.push_back("app:" + application_audio_entry->get_text());
break;
}
}
return true; return true;
}); });
} }
@@ -1173,11 +1254,8 @@ namespace gsr {
record_options.video_height = atoi(video_height_entry_ptr->get_text().c_str()); record_options.video_height = atoi(video_height_entry_ptr->get_text().c_str());
record_options.fps = atoi(framerate_entry_ptr->get_text().c_str()); record_options.fps = atoi(framerate_entry_ptr->get_text().c_str());
record_options.video_bitrate = atoi(video_bitrate_entry_ptr->get_text().c_str()); record_options.video_bitrate = atoi(video_bitrate_entry_ptr->get_text().c_str());
if(split_audio_checkbox_ptr)
record_options.merge_audio_tracks = !split_audio_checkbox_ptr->is_checked();
record_options.application_audio_invert = application_audio_invert_checkbox_ptr->is_checked();
record_options.change_video_resolution = change_video_resolution_checkbox_ptr->is_checked(); record_options.change_video_resolution = change_video_resolution_checkbox_ptr->is_checked();
save_audio_tracks(record_options.audio_tracks, audio_track_list_ptr); save_audio_tracks(record_options.audio_tracks_list, audio_track_section_list_ptr);
record_options.color_range = color_range_box_ptr->get_selected_id(); record_options.color_range = color_range_box_ptr->get_selected_id();
record_options.video_quality = video_quality_box_ptr->get_selected_id(); record_options.video_quality = video_quality_box_ptr->get_selected_id();
record_options.video_codec = video_codec_box_ptr->get_selected_id(); record_options.video_codec = video_codec_box_ptr->get_selected_id();

View File

@@ -20,8 +20,9 @@ namespace gsr {
// Process widgets by visibility (backwards) // Process widgets by visibility (backwards)
return widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) { return widgets.for_each_reverse([selected_widget, &window, &event, offset](std::unique_ptr<Widget> &widget) {
if(widget.get() != selected_widget) { Widget *p = widget.get();
if(!widget->on_event(event, window, offset)) if(p != selected_widget) {
if(!p->on_event(event, window, offset))
return false; return false;
} }
return true; return true;

View File

@@ -12,12 +12,17 @@ namespace gsr {
static const float title_spacing_scale = 0.010f; static const float title_spacing_scale = 0.010f;
Subsection::Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size) : Subsection::Subsection(const char *title, std::unique_ptr<Widget> inner_widget, mgl::vec2f size) :
label(&get_theme().title_font, title, get_color_theme().text_color), label(&get_theme().title_font, title ? title : "", get_color_theme().text_color),
inner_widget(std::move(inner_widget)), inner_widget(std::move(inner_widget)),
size(size) size(size)
{ {
this->inner_widget->parent_widget = this; this->inner_widget->parent_widget = this;
} }
Subsection::~Subsection() {
if(inner_widget->parent_widget == this)
inner_widget->parent_widget = nullptr;
}
bool Subsection::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f) { bool Subsection::on_event(mgl::Event &event, mgl::Window &window, mgl::vec2f) {
if(!visible) if(!visible)
@@ -32,7 +37,7 @@ namespace gsr {
mgl::vec2f draw_pos = position + offset; mgl::vec2f draw_pos = position + offset;
mgl::Rectangle background(draw_pos.floor(), get_size().floor()); mgl::Rectangle background(draw_pos.floor(), get_size().floor());
background.set_color(mgl::Color(25, 30, 34)); background.set_color(bg_color);
window.draw(background); window.draw(background);
draw_pos += mgl::vec2f(margin_left_scale, margin_top_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height); draw_pos += mgl::vec2f(margin_left_scale, margin_top_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height);
@@ -69,4 +74,12 @@ namespace gsr {
const mgl::vec2f margin_size = mgl::vec2f(margin_left_scale + margin_right_scale, margin_top_scale + margin_bottom_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height); const mgl::vec2f margin_size = mgl::vec2f(margin_left_scale + margin_right_scale, margin_top_scale + margin_bottom_scale) * mgl::vec2f(get_theme().window_height, get_theme().window_height);
return get_size() - margin_size; return get_size() - margin_size;
} }
Widget* Subsection::get_inner_widget() {
return inner_widget.get();
}
void Subsection::set_bg_color(mgl::Color color) {
bg_color = color;
}
} }

View File

@@ -1,14 +1,15 @@
#include "../../include/gui/Widget.hpp" #include "../../include/gui/Widget.hpp"
#include <vector>
namespace gsr { namespace gsr {
static std::vector<std::unique_ptr<Widget>> widgets_to_remove;
Widget::Widget() { Widget::Widget() {
} }
Widget::~Widget() { Widget::~Widget() {
remove_widget_as_selected_in_parent(); remove_widget_as_selected_in_parent();
// if(parent_widget)
// parent_widget->remove_child_widget(this);
} }
void Widget::set_position(mgl::vec2f position) { void Widget::set_position(mgl::vec2f position) {
@@ -62,4 +63,15 @@ namespace gsr {
void Widget::set_visible(bool visible) { void Widget::set_visible(bool visible) {
this->visible = visible; this->visible = visible;
} }
void add_widget_to_remove(std::unique_ptr<Widget> widget) {
widgets_to_remove.push_back(std::move(widget));
}
void remove_widgets_to_be_removed() {
for(size_t i = 0; i < widgets_to_remove.size(); ++i) {
widgets_to_remove[i].reset();
}
widgets_to_remove.clear();
}
} }

View File

@@ -30,6 +30,10 @@ static void sigint_handler(int signal) {
running = 0; running = 0;
} }
static void signal_ignore(int) {
}
static void disable_prime_run() { static void disable_prime_run() {
unsetenv("__NV_PRIME_RENDER_OFFLOAD"); unsetenv("__NV_PRIME_RENDER_OFFLOAD");
unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER"); unsetenv("__NV_PRIME_RENDER_OFFLOAD_PROVIDER");
@@ -74,6 +78,16 @@ static void rpc_add_commands(gsr::Rpc *rpc, gsr::Overlay *overlay) {
overlay->save_replay(); overlay->save_replay();
}); });
rpc->add_handler("replay-save-1-min", [overlay](const std::string &name) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
overlay->save_replay_1_min();
});
rpc->add_handler("replay-save-10-min", [overlay](const std::string &name) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
overlay->save_replay_10_min();
});
rpc->add_handler("take-screenshot", [overlay](const std::string &name) { rpc->add_handler("take-screenshot", [overlay](const std::string &name) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str()); fprintf(stderr, "rpc command executed: %s\n", name.c_str());
overlay->take_screenshot(); overlay->take_screenshot();
@@ -218,6 +232,16 @@ int main(int argc, char **argv) {
unsetenv("vblank_mode"); unsetenv("vblank_mode");
signal(SIGINT, sigint_handler); signal(SIGINT, sigint_handler);
signal(SIGTERM, sigint_handler);
signal(SIGUSR1, signal_ignore);
signal(SIGUSR2, signal_ignore);
signal(SIGRTMIN, signal_ignore);
signal(SIGRTMIN+1, signal_ignore);
signal(SIGRTMIN+2, signal_ignore);
signal(SIGRTMIN+3, signal_ignore);
signal(SIGRTMIN+4, signal_ignore);
signal(SIGRTMIN+5, signal_ignore);
signal(SIGRTMIN+6, signal_ignore);
gsr::GsrInfo gsr_info; gsr::GsrInfo gsr_info;
// TODO: Show the error in ui // TODO: Show the error in ui
@@ -235,7 +259,7 @@ int main(int argc, char **argv) {
disable_prime_run(); disable_prime_run();
} }
if(mgl_init() != 0) { if(mgl_init(MGL_WINDOW_SYSTEM_X11) != 0) {
fprintf(stderr, "Error: failed to initialize mgl. Failed to either connect to the X11 server or setup opengl\n"); fprintf(stderr, "Error: failed to initialize mgl. Failed to either connect to the X11 server or setup opengl\n");
exit(1); exit(1);
} }
@@ -303,7 +327,10 @@ int main(int argc, char **argv) {
if(exit_reason == "back-to-old-ui") { if(exit_reason == "back-to-old-ui") {
const char *args[] = { "gpu-screen-recorder-gtk", "use-old-ui", nullptr }; const char *args[] = { "gpu-screen-recorder-gtk", "use-old-ui", nullptr };
execvp(args[0], (char* const*)args); execvp(args[0], (char* const*)args);
return 0;
} else if(exit_reason == "exit") {
return 0;
} }
return 0; return mgl_is_connected_to_display_server() ? 0 : 1;
} }

View File

@@ -56,6 +56,10 @@ static void usage(void) {
printf(" Start/stop replay.\n"); printf(" Start/stop replay.\n");
printf(" replay-save\n"); printf(" replay-save\n");
printf(" Save replay.\n"); printf(" Save replay.\n");
printf(" replay-save-1-min\n");
printf(" Save 1 minute replay.\n");
printf(" replay-save-10-min\n");
printf(" Save 10 minute replay.\n");
printf(" take-screenshot\n"); printf(" take-screenshot\n");
printf(" Take a screenshot.\n"); printf(" Take a screenshot.\n");
printf(" take-screenshot-region\n"); printf(" take-screenshot-region\n");
@@ -75,6 +79,8 @@ static bool is_valid_command(const char *command) {
"toggle-stream", "toggle-stream",
"toggle-replay", "toggle-replay",
"replay-save", "replay-save",
"replay-save-1-min",
"replay-save-10-min",
"take-screenshot", "take-screenshot",
"take-screenshot-region", "take-screenshot-region",
NULL NULL