aboutsummaryrefslogtreecommitdiff
path: root/src/Overlay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Overlay.cpp')
-rw-r--r--src/Overlay.cpp548
1 files changed, 366 insertions, 182 deletions
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 2a4ca81..70278ee 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -12,10 +12,10 @@
#include "../include/gui/Utils.hpp"
#include "../include/gui/PageStack.hpp"
#include "../include/WindowUtils.hpp"
-#include "../include/GlobalHotkeys.hpp"
-#include "../include/GlobalHotkeysLinux.hpp"
-#include "../include/CursorTrackerX11.hpp"
-#include "../include/CursorTrackerWayland.hpp"
+#include "../include/GlobalHotkeys/GlobalHotkeys.hpp"
+#include "../include/GlobalHotkeys/GlobalHotkeysLinux.hpp"
+#include "../include/CursorTracker/CursorTrackerX11.hpp"
+#include "../include/CursorTracker/CursorTrackerWayland.hpp"
#include <string.h>
#include <assert.h>
@@ -47,7 +47,7 @@ namespace gsr {
static const double force_window_on_top_timeout_seconds = 1.0;
static const double replay_status_update_check_timeout_seconds = 1.5;
static const double replay_saving_notification_timeout_seconds = 0.5;
- static const double notification_timeout_seconds = 2.0;
+ static const double notification_timeout_seconds = 2.5;
static const double notification_error_timeout_seconds = 5.0;
static const double cursor_tracker_update_timeout_sec = 0.1;
@@ -207,24 +207,21 @@ namespace gsr {
return false;
}*/
- // Returns the first monitor if not found. Assumes there is at least one monitor connected.
static const Monitor* find_monitor_at_position(const std::vector<Monitor> &monitors, mgl::vec2i pos) {
assert(!monitors.empty());
for(const Monitor &monitor : monitors) {
if(mgl::IntRect(monitor.position, monitor.size).contains(pos))
return &monitor;
}
- return &monitors.front();
+ return nullptr;
}
- // Returns the first monitor if not found. Assumes there is at least one monitor connected.
static const Monitor* find_monitor_by_name(const std::vector<Monitor> &monitors, const std::string &name) {
- assert(!monitors.empty());
for(const Monitor &monitor : monitors) {
if(monitor.name == name)
return &monitor;
}
- return &monitors.front();
+ return nullptr;
}
static std::string get_power_supply_online_filepath() {
@@ -326,6 +323,20 @@ namespace gsr {
});
global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.save_1_min_hotkey),
+ "replay_save_1_min", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_1_min();
+ });
+
+ global_hotkeys->bind_key_press(
+ config_hotkey_to_hotkey(overlay->get_config().replay_config.save_10_min_hotkey),
+ "replay_save_10_min", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_10_min();
+ });
+
+ global_hotkeys->bind_key_press(
config_hotkey_to_hotkey(overlay->get_config().screenshot_config.take_screenshot_hotkey),
"take_screenshot", [overlay](const std::string &id) {
fprintf(stderr, "pressed %s\n", id.c_str());
@@ -371,6 +382,16 @@ namespace gsr {
overlay->save_replay();
});
+ global_hotkeys_js->bind_action("save_1_min_replay", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_1_min();
+ });
+
+ global_hotkeys_js->bind_action("save_10_min_replay", [overlay](const std::string &id) {
+ fprintf(stderr, "pressed %s\n", id.c_str());
+ overlay->save_replay_10_min();
+ });
+
global_hotkeys_js->bind_action("take_screenshot", [overlay](const std::string &id) {
fprintf(stderr, "pressed %s\n", id.c_str());
overlay->take_screenshot();
@@ -556,8 +577,8 @@ namespace gsr {
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
xi_output_xev->type = MotionNotify;
xi_output_xev->xmotion.display = display;
- xi_output_xev->xmotion.window = window->get_system_handle();
- xi_output_xev->xmotion.subwindow = window->get_system_handle();
+ xi_output_xev->xmotion.window = (Window)window->get_system_handle();
+ xi_output_xev->xmotion.subwindow = (Window)window->get_system_handle();
xi_output_xev->xmotion.x = de->root_x - window_pos.x;
xi_output_xev->xmotion.y = de->root_y - window_pos.y;
xi_output_xev->xmotion.x_root = de->root_x;
@@ -569,8 +590,8 @@ namespace gsr {
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
xi_output_xev->type = cookie->evtype == XI_ButtonPress ? ButtonPress : ButtonRelease;
xi_output_xev->xbutton.display = display;
- xi_output_xev->xbutton.window = window->get_system_handle();
- xi_output_xev->xbutton.subwindow = window->get_system_handle();
+ xi_output_xev->xbutton.window = (Window)window->get_system_handle();
+ xi_output_xev->xbutton.subwindow = (Window)window->get_system_handle();
xi_output_xev->xbutton.x = de->root_x - window_pos.x;
xi_output_xev->xbutton.y = de->root_y - window_pos.y;
xi_output_xev->xbutton.x_root = de->root_x;
@@ -583,8 +604,8 @@ namespace gsr {
memset(xi_output_xev, 0, sizeof(*xi_output_xev));
xi_output_xev->type = cookie->evtype == XI_KeyPress ? KeyPress : KeyRelease;
xi_output_xev->xkey.display = display;
- xi_output_xev->xkey.window = window->get_system_handle();
- xi_output_xev->xkey.subwindow = window->get_system_handle();
+ xi_output_xev->xkey.window = (Window)window->get_system_handle();
+ xi_output_xev->xkey.subwindow = (Window)window->get_system_handle();
xi_output_xev->xkey.x = de->root_x - window_pos.x;
xi_output_xev->xkey.y = de->root_y - window_pos.y;
xi_output_xev->xkey.x_root = de->root_x;
@@ -685,8 +706,10 @@ namespace gsr {
}
bool Overlay::draw() {
+ remove_widgets_to_be_removed();
+
update_notification_process_status();
- update_gsr_replay_save();
+ process_gsr_output();
update_gsr_process_status();
update_gsr_screenshot_process_status();
replay_status_update_status();
@@ -695,7 +718,7 @@ namespace gsr {
start_region_capture = false;
hide();
if(!region_selector.start(get_color_theme().tint_color)) {
- show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::NONE);
+ show_notification("Failed to start region capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
on_region_selected = nullptr;
}
}
@@ -765,13 +788,13 @@ namespace gsr {
// There should be a debug mode to not use these
mgl_context *context = mgl_get_context();
Display *display = (Display*)context->connection;
- XGrabPointer(display, window->get_system_handle(), True,
+ XGrabPointer(display, (Window)window->get_system_handle(), True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
Button1MotionMask | Button2MotionMask | Button3MotionMask | Button4MotionMask | Button5MotionMask |
ButtonMotionMask,
GrabModeAsync, GrabModeAsync, None, default_cursor, CurrentTime);
// TODO: This breaks global hotkeys (when using x11 global hotkeys)
- XGrabKeyboard(display, window->get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ XGrabKeyboard(display, (Window)window->get_system_handle(), True, GrabModeAsync, GrabModeAsync, CurrentTime);
XFlush(display);
}
@@ -868,10 +891,14 @@ namespace gsr {
const Monitor *focused_monitor = nullptr;
if(cursor_info) {
focused_monitor = find_monitor_by_name(monitors, cursor_info->monitor_name);
+ if(!focused_monitor)
+ focused_monitor = &monitors.front();
cursor_position = cursor_info->position;
} else {
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
+ if(!focused_monitor)
+ focused_monitor = &monitors.front();
}
// Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
@@ -905,18 +932,21 @@ namespace gsr {
// Nvidia + Wayland + Egl doesn't work on some systems properly and it instead falls back to software rendering.
// Use Glx on Wayland to workaround this issue. This is fine since Egl is only needed for x11 to reliably get the texture of the fullscreen window on Nvidia
// when a compositor isn't running.
- window_create_params.render_api = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? MGL_RENDER_API_GLX : MGL_RENDER_API_EGL;
+ window_create_params.graphics_api = gsr_info.system_info.display_server == DisplayServer::WAYLAND ? MGL_GRAPHICS_API_GLX : MGL_GRAPHICS_API_EGL;
- if(!window->create("gsr ui", window_create_params))
+ if(!window->create("gsr ui", window_create_params)) {
fprintf(stderr, "error: failed to create window\n");
+ window.reset();
+ return;
+ }
//window->set_low_latency(true);
unsigned char data = 2; // Prefer being composed to allow transparency
- XChangeProperty(display, window->get_system_handle(), XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
+ XChangeProperty(display, (Window)window->get_system_handle(), XInternAtom(display, "_NET_WM_BYPASS_COMPOSITOR", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
data = 1;
- XChangeProperty(display, window->get_system_handle(), XInternAtom(display, "GAMESCOPE_EXTERNAL_OVERLAY", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
+ XChangeProperty(display, (Window)window->get_system_handle(), XInternAtom(display, "GAMESCOPE_EXTERNAL_OVERLAY", False), XA_CARDINAL, 32, PropModeReplace, &data, 1);
const auto original_window_size = window_size;
window_pos = focused_monitor->position;
@@ -950,12 +980,12 @@ namespace gsr {
//window->set_fullscreen(true);
if(gsr_info.system_info.display_server == DisplayServer::X11)
- make_window_click_through(display, window->get_system_handle());
+ make_window_click_through(display, (Window)window->get_system_handle());
window->set_visible(true);
- make_window_sticky(display, window->get_system_handle());
- hide_window_from_taskbar(display, window->get_system_handle());
+ make_window_sticky(display, (Window)window->get_system_handle());
+ hide_window_from_taskbar(display, (Window)window->get_system_handle());
if(default_cursor) {
XFreeCursor(display, default_cursor);
@@ -1002,6 +1032,9 @@ namespace gsr {
if(paused)
update_ui_recording_paused();
+ if(replay_recording)
+ update_ui_recording_started();
+
// Wayland compositors have retarded fullscreen animations that we cant disable in a proper way
// without messing up window position.
show_overlay_timeout_seconds = prevent_game_minimizing ? 0.0 : 0.15;
@@ -1049,9 +1082,15 @@ namespace gsr {
replay_dropdown_button_ptr = button.get();
button->add_item("Turn on", "start", config.replay_config.start_stop_hotkey.to_string(false, false));
button->add_item("Save", "save", config.replay_config.save_hotkey.to_string(false, false));
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ button->add_item("Save 1 min", "save_1_min", config.replay_config.save_1_min_hotkey.to_string(false, false));
+ button->add_item("Save 10 min", "save_10_min", config.replay_config.save_10_min_hotkey.to_string(false, false));
+ }
button->add_item("Settings", "settings");
button->set_item_icon("start", &get_theme().play_texture);
button->set_item_icon("save", &get_theme().save_texture);
+ button->set_item_icon("save_1_min", &get_theme().save_texture);
+ button->set_item_icon("save_10_min", &get_theme().save_texture);
button->set_item_icon("settings", &get_theme().settings_small_texture);
button->on_click = [this](const std::string &id) {
if(id == "settings") {
@@ -1064,10 +1103,17 @@ namespace gsr {
page_stack.push(std::move(replay_settings_page));
} else if(id == "save") {
on_press_save_replay();
+ } else if(id == "save_1_min") {
+ on_press_save_replay_1_min_replay();
+ } else if(id == "save_10_min") {
+ on_press_save_replay_10_min_replay();
} else if(id == "start") {
on_press_start_replay(false, false);
}
};
+ button->set_item_enabled("save", false);
+ button->set_item_enabled("save_1_min", false);
+ button->set_item_enabled("save_10_min", false);
main_buttons_list->add_widget(std::move(button));
}
{
@@ -1094,6 +1140,7 @@ namespace gsr {
on_press_start_record(false);
}
};
+ button->set_item_enabled("pause", false);
main_buttons_list->add_widget(std::move(button));
}
{
@@ -1172,23 +1219,15 @@ namespace gsr {
};
settings_page->on_page_closed = [this]() {
- if(global_hotkeys) {
- replay_dropdown_button_ptr->set_item_description("start", config.replay_config.start_stop_hotkey.to_string(false, false));
- replay_dropdown_button_ptr->set_item_description("save", config.replay_config.save_hotkey.to_string(false, false));
+ replay_dropdown_button_ptr->set_item_description("start", config.replay_config.start_stop_hotkey.to_string(false, false));
+ replay_dropdown_button_ptr->set_item_description("save", config.replay_config.save_hotkey.to_string(false, false));
+ replay_dropdown_button_ptr->set_item_description("save_1_min", config.replay_config.save_1_min_hotkey.to_string(false, false));
+ replay_dropdown_button_ptr->set_item_description("save_10_min", config.replay_config.save_10_min_hotkey.to_string(false, false));
- record_dropdown_button_ptr->set_item_description("start", config.record_config.start_stop_hotkey.to_string(false, false));
- record_dropdown_button_ptr->set_item_description("pause", config.record_config.pause_unpause_hotkey.to_string(false, false));
+ record_dropdown_button_ptr->set_item_description("start", config.record_config.start_stop_hotkey.to_string(false, false));
+ record_dropdown_button_ptr->set_item_description("pause", config.record_config.pause_unpause_hotkey.to_string(false, false));
- stream_dropdown_button_ptr->set_item_description("start", config.streaming_config.start_stop_hotkey.to_string(false, false));
- } else {
- replay_dropdown_button_ptr->set_item_description("start", "");
- replay_dropdown_button_ptr->set_item_description("save", "");
-
- record_dropdown_button_ptr->set_item_description("start", "");
- record_dropdown_button_ptr->set_item_description("pause", "");
-
- stream_dropdown_button_ptr->set_item_description("start", "");
- }
+ stream_dropdown_button_ptr->set_item_description("start", config.streaming_config.start_stop_hotkey.to_string(false, false));
};
page_stack.push(std::move(settings_page));
@@ -1249,6 +1288,7 @@ namespace gsr {
while(!page_stack.empty()) {
page_stack.pop();
}
+ remove_widgets_to_be_removed();
if(default_cursor) {
XFreeCursor(display, default_cursor);
@@ -1365,6 +1405,14 @@ namespace gsr {
on_press_save_replay();
}
+ void Overlay::save_replay_1_min() {
+ on_press_save_replay_1_min_replay();
+ }
+
+ void Overlay::save_replay_10_min() {
+ on_press_save_replay_10_min_replay();
+ }
+
void Overlay::take_screenshot() {
on_press_take_screenshot(false, false);
}
@@ -1554,7 +1602,7 @@ namespace gsr {
Display *display = (Display*)context->connection;
const std::string video_filename = filepath_get_filename(video_filepath);
- const Window gsr_ui_window = window ? window->get_system_handle() : None;
+ const Window gsr_ui_window = window ? (Window)window->get_system_handle() : None;
std::string focused_window_name = get_window_name_at_cursor_position(display, gsr_ui_window);
if(focused_window_name.empty())
focused_window_name = get_focused_window_name(display, WindowCaptureType::FOCUSED);
@@ -1572,7 +1620,6 @@ namespace gsr {
truncate_string(focused_window_name, 20);
const char *capture_target = nullptr;
char msg[512];
- const std::string filename = focused_window_name + "/" + video_filename;
switch(notification_type) {
case NotificationType::RECORD: {
@@ -1580,9 +1627,9 @@ namespace gsr {
return;
if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved recording of this monitor to '%s'", filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a recording of this monitor to \"%s\"", focused_window_name.c_str());
else
- snprintf(msg, sizeof(msg), "Saved recording of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a recording of %s to \"%s\"", recording_capture_target.c_str(), focused_window_name.c_str());
capture_target = recording_capture_target.c_str();
break;
@@ -1591,12 +1638,18 @@ namespace gsr {
if(!config.replay_config.show_replay_saved_notifications)
return;
- if(is_capture_target_monitor(replay_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved replay of this monitor to '%s'", filename.c_str());
+ char duration[32];
+ if(replay_save_duration_min > 0)
+ snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
else
- snprintf(msg, sizeof(msg), "Saved replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
+ snprintf(duration, sizeof(duration), " ");
- capture_target = replay_capture_target.c_str();
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
+ snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor to \"%s\"", duration, focused_window_name.c_str());
+ else
+ snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to \"%s\"", duration, recording_capture_target.c_str(), focused_window_name.c_str());
+
+ capture_target = recording_capture_target.c_str();
break;
}
case NotificationType::SCREENSHOT: {
@@ -1604,9 +1657,9 @@ namespace gsr {
return;
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved screenshot of this monitor to '%s'", filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to \"%s\"", focused_window_name.c_str());
else
- snprintf(msg, sizeof(msg), "Saved screenshot of %s to '%s'", screenshot_capture_target.c_str(), filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a screenshot of %s to \"%s\"", screenshot_capture_target.c_str(), focused_window_name.c_str());
capture_target = screenshot_capture_target.c_str();
break;
@@ -1618,22 +1671,37 @@ namespace gsr {
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, notification_type, capture_target);
}
+ static NotificationType recording_status_to_notification_type(RecordingStatus recording_status) {
+ switch(recording_status) {
+ case RecordingStatus::NONE: return NotificationType::NONE;
+ case RecordingStatus::REPLAY: return NotificationType::REPLAY;
+ case RecordingStatus::RECORD: return NotificationType::RECORD;
+ case RecordingStatus::STREAM: return NotificationType::STREAM;
+ }
+ return NotificationType::NONE;
+ }
+
void Overlay::on_replay_saved(const char *replay_saved_filepath) {
replay_save_show_notification = false;
if(config.replay_config.save_video_in_game_folder) {
save_video_in_current_game_directory(replay_saved_filepath, NotificationType::REPLAY);
} else {
- const std::string filename = filepath_get_filename(replay_saved_filepath);
+ char duration[32];
+ if(replay_save_duration_min > 0)
+ snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
+ else
+ snprintf(duration, sizeof(duration), " ");
+
char msg[512];
- if(is_capture_target_monitor(replay_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved replay of this monitor to '%s'", filename.c_str());
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
+ snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor", duration);
else
- snprintf(msg, sizeof(msg), "Saved replay of %s to '%s'", replay_capture_target.c_str(), filename.c_str());
- show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, recording_capture_target.c_str());
+ show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
}
}
- void Overlay::update_gsr_replay_save() {
+ void Overlay::process_gsr_output() {
if(replay_save_show_notification && replay_save_clock.get_elapsed_time_seconds() >= replay_saving_notification_timeout_seconds) {
replay_save_show_notification = false;
show_notification("Saving replay, this might take some time", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
@@ -1641,15 +1709,36 @@ namespace gsr {
if(gpu_screen_recorder_process_output_file) {
char buffer[1024];
- char *replay_saved_filepath = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
- if(!replay_saved_filepath || replay_saved_filepath[0] == '\0')
+ char *line = fgets(buffer, sizeof(buffer), gpu_screen_recorder_process_output_file);
+ if(!line || line[0] == '\0')
return;
- const int line_len = strlen(replay_saved_filepath);
- if(replay_saved_filepath[line_len - 1] == '\n')
- replay_saved_filepath[line_len - 1] = '\0';
+ const int line_len = strlen(line);
+ if(line[line_len - 1] == '\n')
+ line[line_len - 1] = '\0';
- on_replay_saved(replay_saved_filepath);
+ if(starts_with({line, (size_t)line_len}, "Error: ")) {
+ show_notification(line + 7, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), recording_status_to_notification_type(recording_status));
+ return;
+ }
+
+ const std::string video_filepath = filepath_get_filename(line);
+ if(starts_with(video_filepath, "Video_")) {
+ on_stop_recording(0, line);
+ return;
+ }
+
+ switch(recording_status) {
+ case RecordingStatus::NONE:
+ break;
+ case RecordingStatus::REPLAY:
+ on_replay_saved(line);
+ break;
+ case RecordingStatus::RECORD:
+ break;
+ case RecordingStatus::STREAM:
+ break;
+ }
} else if(gpu_screen_recorder_process_output_fd > 0) {
char buffer[1024];
read(gpu_screen_recorder_process_output_fd, buffer, sizeof(buffer));
@@ -1676,6 +1765,7 @@ namespace gsr {
case RecordingStatus::NONE:
break;
case RecordingStatus::REPLAY: {
+ replay_save_duration_min = 0;
update_ui_replay_stopped();
if(exit_code == 0) {
if(config.replay_config.show_replay_stopped_notifications)
@@ -1688,7 +1778,7 @@ namespace gsr {
}
case RecordingStatus::RECORD: {
update_ui_recording_stopped();
- on_stop_recording(exit_code);
+ on_stop_recording(exit_code, record_filepath);
break;
}
case RecordingStatus::STREAM: {
@@ -1726,12 +1816,11 @@ namespace gsr {
if(config.screenshot_config.save_screenshot_in_game_folder) {
save_video_in_current_game_directory(screenshot_filepath.c_str(), NotificationType::SCREENSHOT);
} else {
- const std::string filename = filepath_get_filename(screenshot_filepath.c_str());
char msg[512];
if(is_capture_target_monitor(screenshot_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved screenshot of this monitor to '%s'", filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor");
else
- snprintf(msg, sizeof(msg), "Saved screenshot of %s to '%s'", screenshot_capture_target.c_str(), filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a screenshot of %s", screenshot_capture_target.c_str());
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::SCREENSHOT, screenshot_capture_target.c_str());
}
} else {
@@ -1742,28 +1831,25 @@ namespace gsr {
gpu_screen_recorder_screenshot_process = -1;
}
- 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;
- }
-
- static bool are_all_audio_tracks_available_to_capture(const std::vector<std::string> &audio_tracks) {
+ static bool are_all_audio_tracks_available_to_capture(const std::vector<AudioTrack> &audio_tracks) {
const auto audio_devices = get_audio_devices();
- for(const std::string &audio_track : audio_tracks) {
- std::string_view audio_track_name(audio_track.c_str());
- const bool is_app_audio = starts_with(audio_track_name, "app:");
- if(is_app_audio)
- continue;
+ for(const AudioTrack &audio_track : audio_tracks) {
+ for(const std::string &audio_input : audio_track.audio_inputs) {
+ std::string_view audio_track_name(audio_input.c_str());
+ const bool is_app_audio = starts_with(audio_track_name, "app:");
+ if(is_app_audio)
+ continue;
- if(starts_with(audio_track_name, "device:"))
- audio_track_name.remove_prefix(7);
+ if(starts_with(audio_track_name, "device:"))
+ audio_track_name.remove_prefix(7);
- auto it = std::find_if(audio_devices.begin(), audio_devices.end(), [&](const auto &audio_device) {
- return audio_device.name == audio_track_name;
- });
- if(it == audio_devices.end()) {
- //fprintf(stderr, "Audio not ready\n");
- return false;
+ auto it = std::find_if(audio_devices.begin(), audio_devices.end(), [&](const auto &audio_device) {
+ return audio_device.name == audio_track_name;
+ });
+ if(it == audio_devices.end()) {
+ //fprintf(stderr, "Audio not ready\n");
+ return false;
+ }
}
}
return true;
@@ -1787,14 +1873,14 @@ namespace gsr {
Display *display = (Display*)context->connection;
const Window focused_window = get_focused_window(display, WindowCaptureType::FOCUSED);
- if(window && focused_window == window->get_system_handle())
+ if(window && focused_window == (Window)window->get_system_handle())
return;
const bool prev_focused_window_is_fullscreen = focused_window_is_fullscreen;
focused_window_is_fullscreen = focused_window != 0 && window_is_fullscreen(display, focused_window);
if(focused_window_is_fullscreen != prev_focused_window_is_fullscreen) {
if(recording_status == RecordingStatus::NONE && focused_window_is_fullscreen) {
- if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks))
+ if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
on_press_start_replay(false, false);
} else if(recording_status == RecordingStatus::REPLAY && !focused_window_is_fullscreen) {
on_press_start_replay(true, false);
@@ -1811,7 +1897,7 @@ namespace gsr {
power_supply_connected = power_supply_online_filepath.empty() || power_supply_is_connected(power_supply_online_filepath.c_str());
if(power_supply_connected != prev_power_supply_status) {
if(recording_status == RecordingStatus::NONE && power_supply_connected) {
- if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks))
+ if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
on_press_start_replay(false, false);
} else if(recording_status == RecordingStatus::REPLAY && !power_supply_connected) {
on_press_start_replay(false, false);
@@ -1823,27 +1909,28 @@ namespace gsr {
if(replay_startup_mode != ReplayStartupMode::TURN_ON_AT_SYSTEM_STARTUP || recording_status != RecordingStatus::NONE || !try_replay_startup)
return;
- if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks))
+ if(are_all_audio_tracks_available_to_capture(config.replay_config.record_options.audio_tracks_list))
on_press_start_replay(true, false);
}
- void Overlay::on_stop_recording(int exit_code) {
+ void Overlay::on_stop_recording(int exit_code, const std::string &video_filepath) {
if(exit_code == 0) {
if(config.record_config.save_video_in_game_folder) {
- save_video_in_current_game_directory(record_filepath.c_str(), NotificationType::RECORD);
+ save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD);
} else {
- const std::string filename = filepath_get_filename(record_filepath.c_str());
char msg[512];
if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved recording of this monitor to '%s'", filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a recording of this monitor");
else
- snprintf(msg, sizeof(msg), "Saved recording of %s to '%s'", recording_capture_target.c_str(), filename.c_str());
+ snprintf(msg, sizeof(msg), "Saved a recording of %s", recording_capture_target.c_str());
show_notification(msg, notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str());
}
} else {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_process, exit_code);
show_notification("Failed to start/save recording. Verify if settings are correct", notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
}
+ update_ui_recording_stopped();
+ replay_recording = false;
}
void Overlay::update_ui_recording_paused() {
@@ -1872,6 +1959,7 @@ namespace gsr {
record_dropdown_button_ptr->set_activated(true);
record_dropdown_button_ptr->set_description("Recording");
record_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
+ record_dropdown_button_ptr->set_item_enabled("pause", recording_status == RecordingStatus::RECORD);
}
void Overlay::update_ui_recording_stopped() {
@@ -1885,7 +1973,9 @@ namespace gsr {
record_dropdown_button_ptr->set_item_label("pause", "Pause");
record_dropdown_button_ptr->set_item_icon("pause", &get_theme().pause_texture);
+ record_dropdown_button_ptr->set_item_enabled("pause", false);
paused = false;
+ replay_recording = false;
}
void Overlay::update_ui_streaming_started() {
@@ -1906,6 +1996,7 @@ namespace gsr {
stream_dropdown_button_ptr->set_activated(false);
stream_dropdown_button_ptr->set_description("Not streaming");
stream_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
+ update_ui_recording_stopped();
}
void Overlay::update_ui_replay_started() {
@@ -1916,6 +2007,9 @@ namespace gsr {
replay_dropdown_button_ptr->set_activated(true);
replay_dropdown_button_ptr->set_description("On");
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().stop_texture);
+ replay_dropdown_button_ptr->set_item_enabled("save", true);
+ replay_dropdown_button_ptr->set_item_enabled("save_1_min", true);
+ replay_dropdown_button_ptr->set_item_enabled("save_10_min", true);
}
void Overlay::update_ui_replay_stopped() {
@@ -1926,6 +2020,10 @@ namespace gsr {
replay_dropdown_button_ptr->set_activated(false);
replay_dropdown_button_ptr->set_description("Off");
replay_dropdown_button_ptr->set_item_icon("start", &get_theme().play_texture);
+ replay_dropdown_button_ptr->set_item_enabled("save", false);
+ replay_dropdown_button_ptr->set_item_enabled("save_1_min", false);
+ replay_dropdown_button_ptr->set_item_enabled("save_10_min", false);
+ update_ui_recording_stopped();
}
static std::string get_date_str() {
@@ -1947,29 +2045,31 @@ namespace gsr {
return container;
}
- static std::vector<std::string> create_audio_tracks_real_names(const std::vector<std::string> &audio_tracks, bool application_audio_invert, const GsrInfo &gsr_info) {
+ static std::vector<std::string> create_audio_tracks_cli_args(const std::vector<AudioTrack> &audio_tracks, const GsrInfo &gsr_info) {
std::vector<std::string> result;
- for(const std::string &audio_track : audio_tracks) {
- std::string audio_track_name = audio_track;
- const bool is_app_audio = starts_with(audio_track_name, "app:");
- if(is_app_audio && !gsr_info.system_info.supports_app_audio)
- continue;
+ result.reserve(audio_tracks.size());
+
+ for(const AudioTrack &audio_track : audio_tracks) {
+ std::string audio_track_merged;
+ for(const std::string &audio_input_name : audio_track.audio_inputs) {
+ std::string new_audio_input_name = audio_input_name;
+ const bool is_app_audio = starts_with(new_audio_input_name, "app:");
+ if(is_app_audio && !gsr_info.system_info.supports_app_audio)
+ continue;
- if(is_app_audio && application_audio_invert)
- audio_track_name.replace(0, 4, "app-inverse:");
+ if(is_app_audio && audio_track.application_audio_invert)
+ new_audio_input_name.replace(0, 4, "app-inverse:");
- result.push_back(std::move(audio_track_name));
- }
- return result;
- }
+ if(!audio_track_merged.empty())
+ audio_track_merged += "|";
- static std::string merge_audio_tracks(const std::vector<std::string> &audio_tracks) {
- std::string result;
- for(size_t i = 0; i < audio_tracks.size(); ++i) {
- if(i > 0)
- result += "|";
- result += audio_tracks[i];
+ audio_track_merged += new_audio_input_name;
+ }
+
+ if(!audio_track_merged.empty())
+ result.push_back(std::move(audio_track_merged));
}
+
return result;
}
@@ -1984,7 +2084,7 @@ namespace gsr {
args.push_back(region_str);
}
- static void add_common_gpu_screen_recorder_args(std::vector<const char*> &args, const RecordOptions &record_options, const std::vector<std::string> &audio_tracks, const std::string &video_bitrate, const char *region, const std::string &audio_devices_merged, char *region_str, int region_str_size, const RegionSelector &region_selector) {
+ static void add_common_gpu_screen_recorder_args(std::vector<const char*> &args, const RecordOptions &record_options, const std::vector<std::string> &audio_tracks, const std::string &video_bitrate, const char *region, char *region_str, int region_str_size, const RegionSelector &region_selector) {
if(record_options.video_quality == "custom") {
args.push_back("-bm");
args.push_back("cbr");
@@ -2000,16 +2100,9 @@ namespace gsr {
args.push_back(region);
}
- if(record_options.merge_audio_tracks) {
- if(!audio_devices_merged.empty()) {
- args.push_back("-a");
- args.push_back(audio_devices_merged.c_str());
- }
- } else {
- for(const std::string &audio_track : audio_tracks) {
- args.push_back("-a");
- args.push_back(audio_track.c_str());
- }
+ for(const std::string &audio_track : audio_tracks) {
+ args.push_back("-a");
+ args.push_back(audio_track.c_str());
}
if(record_options.restore_portal_session) {
@@ -2048,8 +2141,25 @@ namespace gsr {
cursor_info = cursor_tracker->get_latest_cursor_info();
}
- if(cursor_info)
- return cursor_info->monitor_name;
+ std::string focused_monitor_name;
+ if(cursor_info) {
+ focused_monitor_name = std::move(cursor_info->monitor_name);
+ } else {
+ mgl_context *context = mgl_get_context();
+ Display *display = (Display*)context->connection;
+
+ Window x11_cursor_window = None;
+ mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
+
+ const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
+ auto monitors = get_monitors(display);
+ const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
+ if(focused_monitor)
+ focused_monitor_name = focused_monitor->name;
+ }
+
+ if(!focused_monitor_name.empty())
+ return focused_monitor_name;
else if(!capture_options.monitors.empty())
return capture_options.monitors.front().name;
else
@@ -2059,15 +2169,47 @@ namespace gsr {
}
}
+ void Overlay::prepare_gsr_output_for_reading() {
+ if(gpu_screen_recorder_process_output_fd <= 0)
+ return;
+
+ const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
+ fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
+ gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
+ if(gpu_screen_recorder_process_output_file)
+ gpu_screen_recorder_process_output_fd = -1;
+ }
+
void Overlay::on_press_save_replay() {
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
return;
+ replay_save_duration_min = 0;
replay_save_show_notification = true;
replay_save_clock.restart();
kill(gpu_screen_recorder_process, SIGUSR1);
}
+ void Overlay::on_press_save_replay_1_min_replay() {
+ if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
+ return;
+
+ replay_save_duration_min = 1;
+ replay_save_show_notification = true;
+ replay_save_clock.restart();
+ kill(gpu_screen_recorder_process, SIGRTMIN+3);
+ }
+
+ void Overlay::on_press_save_replay_10_min_replay() {
+ if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
+ return;
+
+ replay_save_duration_min = 10;
+ replay_save_show_notification = true;
+ replay_save_clock.restart();
+ kill(gpu_screen_recorder_process, SIGRTMIN+5);
+ }
+
bool Overlay::on_press_start_replay(bool disable_notification, bool finished_region_selection) {
if(region_selector.is_started())
return false;
@@ -2077,10 +2219,10 @@ namespace gsr {
case RecordingStatus::REPLAY:
break;
case RecordingStatus::RECORD:
- show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
+ show_notification("Unable to start replay when recording.\nStop recording before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return false;
case RecordingStatus::STREAM:
- show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
+ show_notification("Unable to start replay when streaming.\nStop streaming before starting replay.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
return false;
}
@@ -2088,9 +2230,6 @@ namespace gsr {
replay_save_show_notification = false;
try_replay_startup = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
-
close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
@@ -2103,6 +2242,7 @@ namespace gsr {
gpu_screen_recorder_process = -1;
recording_status = RecordingStatus::NONE;
+ replay_save_duration_min = 0;
update_ui_replay_stopped();
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
@@ -2113,11 +2253,11 @@ namespace gsr {
}
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
- replay_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
- if(!validate_capture_target(replay_capture_target, capture_options)) {
+ recording_capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
+ if(!validate_capture_target(recording_capture_target, capture_options)) {
char err_msg[256];
- snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", replay_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::REPLAY);
+ snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
return false;
}
@@ -2133,8 +2273,7 @@ namespace gsr {
const std::string fps = std::to_string(config.replay_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
const std::string output_directory = config.replay_config.save_directory;
- const std::vector<std::string> audio_tracks = create_audio_tracks_real_names(config.replay_config.record_options.audio_tracks, config.replay_config.record_options.application_audio_invert, gsr_info);
- const std::string audio_tracks_merged = merge_audio_tracks(audio_tracks);
+ const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.replay_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.replay_config.record_options.framerate_mode == "auto" ? "vfr" : config.replay_config.record_options.framerate_mode;
const std::string replay_time = std::to_string(config.replay_config.replay_time);
const char *video_codec = config.replay_config.record_options.video_codec.c_str();
@@ -2153,7 +2292,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
std::vector<const char*> args = {
- "gpu-screen-recorder", "-w", replay_capture_target.c_str(),
+ "gpu-screen-recorder", "-w", recording_capture_target.c_str(),
"-c", config.replay_config.container.c_str(),
"-ac", config.replay_config.record_options.audio_codec.c_str(),
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
@@ -2172,24 +2311,31 @@ namespace gsr {
args.push_back("yes");
}
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 5, 0}) {
+ args.push_back("-replay-storage");
+ args.push_back(config.replay_config.replay_storage.c_str());
+ }
+
char region_str[128];
- add_common_gpu_screen_recorder_args(args, config.replay_config.record_options, audio_tracks, video_bitrate, size, audio_tracks_merged, region_str, sizeof(region_str), region_selector);
+ add_common_gpu_screen_recorder_args(args, config.replay_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
+
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ args.push_back("-ro");
+ args.push_back(config.record_config.save_directory.c_str());
+ }
args.push_back(nullptr);
gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start replay", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
+ return false;
} else {
recording_status = RecordingStatus::REPLAY;
update_ui_replay_started();
}
- const int fdl = fcntl(gpu_screen_recorder_process_output_fd, F_GETFL);
- fcntl(gpu_screen_recorder_process_output_fd, F_SETFL, fdl | O_NONBLOCK);
- gpu_screen_recorder_process_output_file = fdopen(gpu_screen_recorder_process_output_fd, "r");
- if(gpu_screen_recorder_process_output_file)
- gpu_screen_recorder_process_output_fd = -1;
+ prepare_gsr_output_for_reading();
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
@@ -2202,11 +2348,11 @@ namespace gsr {
// to see when the program has exit.
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(replay_capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Started replaying this monitor");
else
- snprintf(msg, sizeof(msg), "Started replaying %s", replay_capture_target.c_str());
- show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, replay_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started replaying %s", recording_capture_target.c_str());
+ show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
}
return true;
@@ -2220,18 +2366,45 @@ namespace gsr {
case RecordingStatus::NONE:
case RecordingStatus::RECORD:
break;
- case RecordingStatus::REPLAY:
- show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
+ case RecordingStatus::REPLAY: {
+ if(gpu_screen_recorder_process <= 0)
+ return;
+
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ if(!replay_recording) {
+ if(config.record_config.show_recording_started_notifications)
+ show_notification("Started recording in the replay session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
+ update_ui_recording_started();
+ }
+ replay_recording = true;
+ kill(gpu_screen_recorder_process, SIGRTMIN);
+ } else {
+ show_notification("Unable to start recording when replay is turned on.\nTurn off replay before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::REPLAY);
+ }
return;
- case RecordingStatus::STREAM:
- show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
+ }
+ case RecordingStatus::STREAM: {
+ if(gpu_screen_recorder_process <= 0)
+ return;
+
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ if(!replay_recording) {
+ if(config.record_config.show_recording_started_notifications)
+ show_notification("Started recording in the streaming session", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
+ update_ui_recording_started();
+ }
+ replay_recording = true;
+ kill(gpu_screen_recorder_process, SIGRTMIN);
+ } else {
+ show_notification("Unable to start recording when streaming.\nStop streaming before starting recording.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), get_color_theme().tint_color, NotificationType::STREAM);
+ }
return;
+ }
}
paused = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
+ close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
kill(gpu_screen_recorder_process, SIGINT);
@@ -2243,7 +2416,7 @@ namespace gsr {
int exit_code = -1;
if(WIFEXITED(status))
exit_code = WEXITSTATUS(status);
- on_stop_recording(exit_code);
+ on_stop_recording(exit_code, record_filepath);
}
gpu_screen_recorder_process = -1;
@@ -2258,7 +2431,7 @@ namespace gsr {
if(!validate_capture_target(config.record_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::RECORD);
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return;
}
@@ -2276,8 +2449,7 @@ namespace gsr {
const std::string fps = std::to_string(config.record_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.record_config.record_options.video_bitrate);
const std::string output_file = config.record_config.save_directory + "/Video_" + get_date_str() + "." + container_to_file_extension(config.record_config.container.c_str());
- const std::vector<std::string> audio_tracks = create_audio_tracks_real_names(config.record_config.record_options.audio_tracks, config.record_config.record_options.application_audio_invert, gsr_info);
- const std::string audio_tracks_merged = merge_audio_tracks(audio_tracks);
+ const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.record_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.record_config.record_options.framerate_mode == "auto" ? "vfr" : config.record_config.record_options.framerate_mode;
const char *video_codec = config.record_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
@@ -2309,19 +2481,22 @@ namespace gsr {
};
char region_str[128];
- add_common_gpu_screen_recorder_args(args, config.record_config.record_options, audio_tracks, video_bitrate, size, audio_tracks_merged, region_str, sizeof(region_str), region_selector);
+ add_common_gpu_screen_recorder_args(args, config.record_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
args.push_back(nullptr);
record_filepath = output_file;
- gpu_screen_recorder_process = exec_program(args.data(), nullptr);
+ gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start recording", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
+ return;
} else {
recording_status = RecordingStatus::RECORD;
update_ui_recording_started();
}
+ prepare_gsr_output_for_reading();
+
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
@@ -2379,17 +2554,16 @@ namespace gsr {
case RecordingStatus::STREAM:
break;
case RecordingStatus::REPLAY:
- show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
+ show_notification("Unable to start streaming when replay is turned on.\nTurn off replay before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::REPLAY);
return;
case RecordingStatus::RECORD:
- show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
+ show_notification("Unable to start streaming when recording.\nStop recording before starting streaming.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::RECORD);
return;
}
paused = false;
- // window->close();
- // usleep(1000 * 50); // 50 milliseconds
+ close_gpu_screen_recorder_output();
if(gpu_screen_recorder_process > 0) {
kill(gpu_screen_recorder_process, SIGINT);
@@ -2410,11 +2584,11 @@ namespace gsr {
}
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
- const std::string capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
+ recording_capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
if(!validate_capture_target(config.streaming_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
- snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::STREAM);
+ snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", recording_capture_target.c_str());
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
return;
}
@@ -2429,8 +2603,11 @@ namespace gsr {
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.streaming_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.streaming_config.record_options.video_bitrate);
- const std::vector<std::string> audio_tracks = create_audio_tracks_real_names(config.streaming_config.record_options.audio_tracks, config.streaming_config.record_options.application_audio_invert, gsr_info);
- const std::string audio_tracks_merged = merge_audio_tracks(audio_tracks);
+ std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.streaming_config.record_options.audio_tracks_list, gsr_info);
+ // This isn't possible unless the user modified the config file manually,
+ // But we check it anyways as streaming on some sites can fail if there is more than one audio track
+ if(audio_tracks.size() > 1)
+ audio_tracks.resize(1);
const std::string framerate_mode = config.streaming_config.record_options.framerate_mode == "auto" ? "vfr" : config.streaming_config.record_options.framerate_mode;
const char *video_codec = config.streaming_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
@@ -2454,7 +2631,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
std::vector<const char*> args = {
- "gpu-screen-recorder", "-w", capture_target.c_str(),
+ "gpu-screen-recorder", "-w", recording_capture_target.c_str(),
"-c", container.c_str(),
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
@@ -2466,20 +2643,27 @@ namespace gsr {
"-o", url.c_str()
};
- config.streaming_config.record_options.merge_audio_tracks = true;
char region_str[128];
- add_common_gpu_screen_recorder_args(args, config.streaming_config.record_options, audio_tracks, video_bitrate, size, audio_tracks_merged, region_str, sizeof(region_str), region_selector);
+ add_common_gpu_screen_recorder_args(args, config.streaming_config.record_options, audio_tracks, video_bitrate, size, region_str, sizeof(region_str), region_selector);
+
+ if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
+ args.push_back("-ro");
+ args.push_back(config.record_config.save_directory.c_str());
+ }
args.push_back(nullptr);
- gpu_screen_recorder_process = exec_program(args.data(), nullptr);
+ gpu_screen_recorder_process = exec_program(args.data(), &gpu_screen_recorder_process_output_fd);
if(gpu_screen_recorder_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to start streaming", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::STREAM);
+ return;
} else {
recording_status = RecordingStatus::STREAM;
update_ui_streaming_started();
}
+ prepare_gsr_output_for_reading();
+
// TODO: Start recording after this notification has disappeared to make sure it doesn't show up in the video.
// Make clear to the user that the recording starts after the notification is gone.
// Maybe have the option in notification to show timer until its getting hidden, then the notification can say:
@@ -2491,11 +2675,11 @@ namespace gsr {
// to see when the program has exit.
if(config.streaming_config.show_streaming_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(capture_target.c_str()))
+ if(is_capture_target_monitor(recording_capture_target.c_str()))
snprintf(msg, sizeof(msg), "Started streaming this monitor");
else
- snprintf(msg, sizeof(msg), "Started streaming %s", capture_target.c_str());
- show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started streaming %s", recording_capture_target.c_str());
+ show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, recording_capture_target.c_str());
}
}
@@ -2515,7 +2699,7 @@ namespace gsr {
if(!validate_capture_target(record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", screenshot_capture_target.c_str());
- show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::SCREENSHOT);
+ show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
return;
}
@@ -2561,7 +2745,7 @@ namespace gsr {
screenshot_filepath = output_file;
gpu_screen_recorder_screenshot_process = exec_program(args.data(), nullptr);
if(gpu_screen_recorder_screenshot_process == -1) {
- // TODO: Show notification failed to start
+ show_notification("Failed to launch gpu-screen-recorder to take a screenshot", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT);
}
}
@@ -2610,7 +2794,7 @@ namespace gsr {
mgl_context *context = mgl_get_context();
Display *display = (Display*)context->connection;
- XRaiseWindow(display, window->get_system_handle());
+ XRaiseWindow(display, (Window)window->get_system_handle());
XFlush(display);
}
}