Add led indicator setting, use one setting for notifications, move it under general

This commit is contained in:
dec05eba
2025-11-07 22:05:14 +01:00
parent be07070789
commit 70df557c2b
13 changed files with 291 additions and 168 deletions

2
TODO
View File

@@ -216,8 +216,6 @@ The UI is unusable on a vertical monitor.
Steam overlay interfers with controller input in gsr ui. Maybe move controller handling the gsr-global-hotkeys to do a grab on the controller, to only give the key input to gsr ui.
Add option to show recording status with scroll lock led (use x11 xkb). Blink when starting/stopping recording and set led on when recording is running and set led off when not recording.
For joysticks (gamepads) create a virtual device for each one (/dev/uinput) that has the same vendor, product and name. This is to make sure that it behaves the same way in applications since applications
access joysticks directly through /dev/input/eventN or /dev/input/jsN. It needs the same number of buttons and pretend to be a controller of the same time, for example a ps4 controller
so that games automatically display ps4 buttons if supported.

View File

@@ -60,6 +60,9 @@ namespace gsr {
bool overclock = false;
bool record_cursor = true;
bool restore_portal_session = true;
bool show_notifications = true;
bool use_led_indicator = false;
};
struct MainConfig {
@@ -93,8 +96,6 @@ namespace gsr {
struct StreamingConfig {
RecordOptions record_options;
bool show_streaming_started_notifications = true;
bool show_streaming_stopped_notifications = true;
std::string streaming_service = "twitch";
YoutubeStreamConfig youtube;
TwitchStreamConfig twitch;
@@ -106,9 +107,6 @@ namespace gsr {
struct RecordConfig {
RecordOptions record_options;
bool save_video_in_game_folder = false;
bool show_recording_started_notifications = true;
bool show_video_saved_notifications = true;
bool show_video_paused_notifications = true;
std::string save_directory;
std::string container = "mp4";
ConfigHotkey start_stop_hotkey;
@@ -120,9 +118,6 @@ namespace gsr {
std::string turn_on_replay_automatically_mode = "dont_turn_on_automatically";
bool save_video_in_game_folder = false;
bool restart_replay_on_save = false;
bool show_replay_started_notifications = true;
bool show_replay_stopped_notifications = true;
bool show_replay_saved_notifications = true;
std::string save_directory;
std::string container = "mp4";
int32_t replay_time = 60;
@@ -145,7 +140,8 @@ namespace gsr {
bool save_screenshot_in_game_folder = false;
bool save_screenshot_to_clipboard = false;
bool show_screenshot_saved_notifications = true;
bool show_notifications = true;
bool use_led_indicator = false;
std::string save_directory;
ConfigHotkey take_screenshot_hotkey;
ConfigHotkey take_screenshot_region_hotkey;

27
include/LedIndicator.hpp Normal file
View File

@@ -0,0 +1,27 @@
#pragma once
#include <X11/Xlib.h>
#include <mglpp/system/Clock.hpp>
namespace gsr {
class LedIndicator {
public:
LedIndicator(Display *dpy);
LedIndicator(const LedIndicator&) = delete;
LedIndicator& operator=(const LedIndicator&) = delete;
~LedIndicator();
void set_led(bool enabled);
void blink();
void update();
private:
void update_led(bool new_state);
private:
Display *dpy = nullptr;
Atom scroll_lock_atom = None;
bool led_indicator_on = false;
bool led_enabled = false;
bool perform_blink = false;
mgl::Clock blink_timer;
};
}

View File

@@ -11,6 +11,7 @@
#include "RegionSelector.hpp"
#include "WindowSelector.hpp"
#include "ClipboardFile.hpp"
#include "LedIndicator.hpp"
#include "CursorTracker/CursorTracker.hpp"
#include <mglpp/window/Window.hpp>
@@ -235,7 +236,7 @@ namespace gsr {
std::unique_ptr<GlobalHotkeys> global_hotkeys = nullptr;
std::unique_ptr<GlobalHotkeysJoystick> global_hotkeys_js = nullptr;
Display *x11_mapping_display = nullptr;
Display *x11_dpy = nullptr;
XEvent x11_mapping_xev;
mgl::Clock replay_save_clock;
@@ -269,5 +270,7 @@ namespace gsr {
bool hide_ui = false;
double notification_duration_multiplier = 1.0;
ClipboardFile clipboard_file;
std::unique_ptr<LedIndicator> led_indicator = nullptr;
};
}

View File

@@ -43,8 +43,9 @@ namespace gsr {
std::unique_ptr<Widget> create_file_info_section();
std::unique_ptr<CheckBox> create_save_screenshot_in_game_folder();
std::unique_ptr<CheckBox> create_save_screenshot_to_clipboard();
std::unique_ptr<Widget> create_notifications();
std::unique_ptr<Widget> create_led_indicator();
std::unique_ptr<Widget> create_general_section();
std::unique_ptr<Widget> create_notifications_section();
std::unique_ptr<Widget> create_settings();
void add_widgets();
@@ -71,7 +72,8 @@ namespace gsr {
Button *save_directory_button_ptr = nullptr;
CheckBox *save_screenshot_in_game_folder_checkbox_ptr = nullptr;
CheckBox *save_screenshot_to_clipboard_checkbox_ptr = nullptr;
CheckBox *show_screenshot_saved_notification_checkbox_ptr = nullptr;
CheckBox *show_notification_checkbox_ptr = nullptr;
CheckBox *led_indicator_checkbox_ptr = nullptr;
PageStack *page_stack = nullptr;
};

View File

@@ -112,6 +112,8 @@ namespace gsr {
std::unique_ptr<CheckBox> create_save_recording_in_game_folder();
std::unique_ptr<Label> create_estimated_record_file_size();
void update_estimated_record_file_size();
std::unique_ptr<CheckBox> create_led_indicator(const char *type);
std::unique_ptr<CheckBox> create_notifications(const char *type);
void add_replay_widgets();
void add_record_widgets();
@@ -136,7 +138,7 @@ namespace gsr {
void save_record();
void save_stream();
void view_changed(bool advanced_view, Subsection *notifications_subsection_ptr);
void view_changed(bool advanced_view);
private:
Type type;
Config &config;
@@ -179,15 +181,7 @@ namespace gsr {
CheckBox *save_replay_in_game_folder_ptr = nullptr;
CheckBox *restart_replay_on_save = nullptr;
Label *estimated_file_size_ptr = nullptr;
CheckBox *show_replay_started_notification_checkbox_ptr = nullptr;
CheckBox *show_replay_stopped_notification_checkbox_ptr = nullptr;
CheckBox *show_replay_saved_notification_checkbox_ptr = nullptr;
CheckBox *save_recording_in_game_folder_ptr = nullptr;
CheckBox *show_recording_started_notification_checkbox_ptr = nullptr;
CheckBox *show_video_saved_notification_checkbox_ptr = nullptr;
CheckBox *show_video_paused_notification_checkbox_ptr = nullptr;
CheckBox *show_streaming_started_notification_checkbox_ptr = nullptr;
CheckBox *show_streaming_stopped_notification_checkbox_ptr = nullptr;
Button *save_directory_button_ptr = nullptr;
Entry *twitch_stream_key_entry_ptr = nullptr;
Entry *youtube_stream_key_entry_ptr = nullptr;
@@ -200,6 +194,8 @@ namespace gsr {
RadioButton *turn_on_replay_automatically_mode_ptr = nullptr;
Subsection *audio_section_ptr = nullptr;
List *audio_track_section_list_ptr = nullptr;
CheckBox *led_indicator_checkbox_ptr = nullptr;
CheckBox *show_notification_checkbox_ptr = nullptr;
PageStack *page_stack = nullptr;
};

View File

@@ -50,6 +50,7 @@ src = [
'src/AudioPlayer.cpp',
'src/Hotplug.cpp',
'src/ClipboardFile.cpp',
'src/LedIndicator.cpp',
'src/Rpc.cpp',
'src/main.cpp',
]

View File

@@ -199,8 +199,8 @@ namespace gsr {
{"streaming.record_options.overclock", &config.streaming_config.record_options.overclock},
{"streaming.record_options.record_cursor", &config.streaming_config.record_options.record_cursor},
{"streaming.record_options.restore_portal_session", &config.streaming_config.record_options.restore_portal_session},
{"streaming.show_streaming_started_notifications", &config.streaming_config.show_streaming_started_notifications},
{"streaming.show_streaming_stopped_notifications", &config.streaming_config.show_streaming_stopped_notifications},
{"streaming.record_options.show_notifications", &config.streaming_config.record_options.show_notifications},
{"streaming.record_options.use_led_indicator", &config.streaming_config.record_options.use_led_indicator},
{"streaming.service", &config.streaming_config.streaming_service},
{"streaming.youtube.key", &config.streaming_config.youtube.stream_key},
{"streaming.twitch.key", &config.streaming_config.twitch.stream_key},
@@ -231,10 +231,9 @@ namespace gsr {
{"record.record_options.overclock", &config.record_config.record_options.overclock},
{"record.record_options.record_cursor", &config.record_config.record_options.record_cursor},
{"record.record_options.restore_portal_session", &config.record_config.record_options.restore_portal_session},
{"record.record_options.show_notifications", &config.replay_config.record_options.show_notifications},
{"record.record_options.use_led_indicator", &config.replay_config.record_options.use_led_indicator},
{"record.save_video_in_game_folder", &config.record_config.save_video_in_game_folder},
{"record.show_recording_started_notifications", &config.record_config.show_recording_started_notifications},
{"record.show_video_saved_notifications", &config.record_config.show_video_saved_notifications},
{"record.show_video_paused_notifications", &config.record_config.show_video_paused_notifications},
{"record.save_directory", &config.record_config.save_directory},
{"record.container", &config.record_config.container},
{"record.start_stop_hotkey", &config.record_config.start_stop_hotkey},
@@ -261,12 +260,11 @@ namespace gsr {
{"replay.record_options.overclock", &config.replay_config.record_options.overclock},
{"replay.record_options.record_cursor", &config.replay_config.record_options.record_cursor},
{"replay.record_options.restore_portal_session", &config.replay_config.record_options.restore_portal_session},
{"replay.record_options.show_notifications", &config.replay_config.record_options.show_notifications},
{"replay.record_options.use_led_indicator", &config.replay_config.record_options.use_led_indicator},
{"replay.turn_on_replay_automatically_mode", &config.replay_config.turn_on_replay_automatically_mode},
{"replay.save_video_in_game_folder", &config.replay_config.save_video_in_game_folder},
{"replay.restart_replay_on_save", &config.replay_config.restart_replay_on_save},
{"replay.show_replay_started_notifications", &config.replay_config.show_replay_started_notifications},
{"replay.show_replay_stopped_notifications", &config.replay_config.show_replay_stopped_notifications},
{"replay.show_replay_saved_notifications", &config.replay_config.show_replay_saved_notifications},
{"replay.save_directory", &config.replay_config.save_directory},
{"replay.container", &config.replay_config.container},
{"replay.time", &config.replay_config.replay_time},
@@ -286,7 +284,8 @@ namespace gsr {
{"screenshot.restore_portal_session", &config.screenshot_config.restore_portal_session},
{"screenshot.save_screenshot_in_game_folder", &config.screenshot_config.save_screenshot_in_game_folder},
{"screenshot.save_screenshot_to_clipboard", &config.screenshot_config.save_screenshot_to_clipboard},
{"screenshot.show_screenshot_saved_notifications", &config.screenshot_config.show_screenshot_saved_notifications},
{"screenshot.show_notifications", &config.screenshot_config.show_notifications},
{"screenshot.use_led_indicator", &config.screenshot_config.use_led_indicator},
{"screenshot.save_directory", &config.screenshot_config.save_directory},
{"screenshot.take_screenshot_hotkey", &config.screenshot_config.take_screenshot_hotkey},
{"screenshot.take_screenshot_region_hotkey", &config.screenshot_config.take_screenshot_region_hotkey},

51
src/LedIndicator.cpp Normal file
View File

@@ -0,0 +1,51 @@
#include "../include/LedIndicator.hpp"
#include <X11/XKBlib.h>
namespace gsr {
LedIndicator::LedIndicator(Display *dpy) : dpy(dpy) {
scroll_lock_atom = XInternAtom(dpy, "Scroll Lock", False);
XkbSetNamedIndicator(dpy, scroll_lock_atom, True, False, False, NULL);
}
LedIndicator::~LedIndicator() {
XkbSetNamedIndicator(dpy, scroll_lock_atom, True, False, False, NULL);
}
void LedIndicator::set_led(bool enabled) {
led_enabled = enabled;
perform_blink = false;
}
void LedIndicator::blink() {
perform_blink = true;
blink_timer.restart();
}
void LedIndicator::update_led(bool new_state) {
if(new_state == led_indicator_on)
return;
led_indicator_on = new_state;
XkbSetNamedIndicator(dpy, scroll_lock_atom, True, new_state, False, NULL);
}
void LedIndicator::update() {
if(perform_blink) {
const double blink_elapsed_sec = blink_timer.get_elapsed_time_seconds();
if(blink_elapsed_sec < 0.2) {
update_led(false);
} else if(blink_elapsed_sec < 0.4) {
update_led(true);
} else if(blink_elapsed_sec < 0.6) {
update_led(false);
} else if(blink_elapsed_sec < 0.8) {
update_led(true);
} else {
perform_blink = false;
}
} else {
update_led(led_enabled);
}
}
}

View File

@@ -32,6 +32,7 @@
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <X11/XKBlib.h>
#include <X11/cursorfont.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput2.h>
@@ -502,9 +503,9 @@ namespace gsr {
if(config.main_config.joystick_hotkeys_enable_option == "enable_hotkeys")
global_hotkeys_js = register_joystick_hotkeys(this);
x11_mapping_display = XOpenDisplay(nullptr);
if(x11_mapping_display)
XKeysymToKeycode(x11_mapping_display, XK_F1); // If we dont call we will never get a MappingNotify
x11_dpy = XOpenDisplay(nullptr);
if(x11_dpy)
XKeysymToKeycode(x11_dpy, XK_F1); // If we dont call we will never get a MappingNotify
else
fprintf(stderr, "Warning: XOpenDisplay failed to mapping notify\n");
@@ -520,6 +521,9 @@ namespace gsr {
show_notification("Wayland doesn't support GPU Screen Recorder properly,\nthings may not work as expected. Use X11 if you experience issues.", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE, nullptr, NotificationLevel::ERROR);
}
}
if(x11_dpy)
led_indicator = std::make_unique<LedIndicator>(x11_dpy);
}
Overlay::~Overlay() {
@@ -555,11 +559,13 @@ namespace gsr {
gpu_screen_recorder_screenshot_process = -1;
}
led_indicator.reset();
close_gpu_screen_recorder_output();
deinit_color_theme();
if(x11_mapping_display)
XCloseDisplay(x11_mapping_display);
if(x11_dpy)
XCloseDisplay(x11_dpy);
}
void Overlay::xi_setup() {
@@ -703,12 +709,12 @@ namespace gsr {
}
void Overlay::handle_keyboard_mapping_event() {
if(!x11_mapping_display)
if(!x11_dpy)
return;
bool mapping_updated = false;
while(XPending(x11_mapping_display)) {
XNextEvent(x11_mapping_display, &x11_mapping_xev);
while(XPending(x11_dpy)) {
XNextEvent(x11_dpy, &x11_mapping_xev);
if(x11_mapping_xev.type == MappingNotify) {
XRefreshKeyboardMapping(&x11_mapping_xev.xmapping);
mapping_updated = true;
@@ -720,6 +726,9 @@ namespace gsr {
}
void Overlay::handle_events() {
if(led_indicator)
led_indicator->update();
if(global_hotkeys_ungrab_keyboard) {
global_hotkeys_ungrab_keyboard = false;
show_notification(
@@ -1512,14 +1521,17 @@ namespace gsr {
if(paused) {
paused_clock.restart();
update_ui_recording_paused();
if(config.record_config.show_video_paused_notifications)
if(config.record_config.record_options.show_notifications)
show_notification("Recording has been paused", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
} else {
paused_total_time_seconds += paused_clock.get_elapsed_time_seconds();
update_ui_recording_unpaused();
if(config.record_config.show_video_paused_notifications)
if(config.record_config.record_options.show_notifications)
show_notification("Recording has been unpaused", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::RECORD);
}
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->blink();
}
void Overlay::update_upause_status() {
@@ -1923,7 +1935,10 @@ namespace gsr {
switch(notification_type) {
case NotificationType::RECORD: {
if(!config.record_config.show_video_saved_notifications)
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->blink();
if(!config.record_config.record_options.show_notifications)
return;
const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds() - paused_total_time_seconds - (paused ? paused_clock.get_elapsed_time_seconds() : 0.0));
@@ -1934,7 +1949,10 @@ namespace gsr {
break;
}
case NotificationType::REPLAY: {
if(!config.replay_config.show_replay_saved_notifications)
if(led_indicator && config.replay_config.record_options.use_led_indicator)
led_indicator->blink();
if(!config.replay_config.record_options.show_notifications)
return;
const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds());
@@ -1945,7 +1963,10 @@ namespace gsr {
break;
}
case NotificationType::SCREENSHOT: {
if(!config.screenshot_config.show_screenshot_saved_notifications)
if(led_indicator && config.screenshot_config.use_led_indicator)
led_indicator->blink();
if(!config.screenshot_config.show_notifications)
return;
snprintf(msg, sizeof(msg), "Saved a screenshot of %s\nto \"%s\"",
@@ -1977,7 +1998,9 @@ namespace gsr {
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 if(config.replay_config.show_replay_saved_notifications) {
return;
} else {
if(config.replay_config.record_options.show_notifications) {
const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds());
char msg[512];
@@ -1986,6 +2009,10 @@ namespace gsr {
capture_target_get_notification_name(recording_capture_target.c_str(), true).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());
}
if(led_indicator && config.replay_config.record_options.use_led_indicator)
led_indicator->blink();
}
}
void Overlay::process_gsr_output() {
@@ -2094,26 +2121,36 @@ namespace gsr {
replay_save_duration_min = 0;
update_ui_replay_stopped();
if(exit_code == 0) {
if(config.replay_config.show_replay_stopped_notifications)
if(config.replay_config.record_options.show_notifications)
show_notification("Replay stopped", short_notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
} else {
on_gsr_process_error(exit_code, NotificationType::REPLAY);
}
if(led_indicator && current_recording_config.replay_config.record_options.use_led_indicator)
led_indicator->set_led(false);
break;
}
case RecordingStatus::RECORD: {
update_ui_recording_stopped();
on_stop_recording(exit_code, record_filepath);
if(led_indicator && current_recording_config.record_config.record_options.use_led_indicator)
led_indicator->set_led(false);
break;
}
case RecordingStatus::STREAM: {
update_ui_streaming_stopped();
if(exit_code == 0) {
if(config.streaming_config.show_streaming_stopped_notifications)
if(config.streaming_config.record_options.show_notifications)
show_notification("Streaming has stopped", short_notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
} else {
on_gsr_process_error(exit_code, NotificationType::STREAM);
}
if(led_indicator && current_recording_config.streaming_config.record_options.use_led_indicator)
led_indicator->set_led(false);
break;
}
}
@@ -2139,7 +2176,8 @@ namespace gsr {
if(exit_code == 0) {
if(config.screenshot_config.save_screenshot_in_game_folder) {
save_video_in_current_game_directory(screenshot_filepath.c_str(), NotificationType::SCREENSHOT);
} else if(config.screenshot_config.show_screenshot_saved_notifications) {
} else {
if(config.screenshot_config.show_notifications) {
char msg[512];
snprintf(msg, sizeof(msg), "Saved a screenshot of %s",
capture_target_get_notification_name(screenshot_capture_target.c_str(), true).c_str());
@@ -2148,6 +2186,10 @@ namespace gsr {
if(config.screenshot_config.save_screenshot_to_clipboard)
clipboard_file.set_current_file(screenshot_filepath, filename_to_clipboard_file_type(screenshot_filepath));
}
if(led_indicator && config.screenshot_config.use_led_indicator)
led_indicator->blink();
}
} else {
fprintf(stderr, "Warning: gpu-screen-recorder (%d) exited with exit status %d\n", (int)gpu_screen_recorder_screenshot_process, exit_code);
show_notification("Failed to take a screenshot. Verify if settings are correct", notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::SCREENSHOT, nullptr, NotificationLevel::ERROR);
@@ -2242,7 +2284,8 @@ namespace gsr {
if(exit_code == 0) {
if(config.record_config.save_video_in_game_folder) {
save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD);
} else if(config.record_config.show_video_saved_notifications) {
} else {
if(config.record_config.record_options.show_notifications) {
const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds() - paused_total_time_seconds - (paused ? paused_clock.get_elapsed_time_seconds() : 0.0));
char msg[512];
@@ -2251,9 +2294,14 @@ namespace gsr {
capture_target_get_notification_name(recording_capture_target.c_str(), true).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());
}
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->blink();
}
} else {
on_gsr_process_error(exit_code, NotificationType::RECORD);
}
update_ui_recording_stopped();
replay_recording = false;
}
@@ -2540,6 +2588,7 @@ namespace gsr {
replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds();
if(replay_restart_on_save)
replay_duration_clock.restart();
kill(gpu_screen_recorder_process, SIGUSR1);
}
@@ -2654,8 +2703,11 @@ namespace gsr {
replay_save_duration_min = 0;
update_ui_replay_stopped();
if(led_indicator && current_recording_config.replay_config.record_options.use_led_indicator)
led_indicator->set_led(false);
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
if(!disable_notification && config.replay_config.show_replay_stopped_notifications)
if(!disable_notification && config.replay_config.record_options.show_notifications)
show_notification("Replay stopped", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::REPLAY);
return true;
@@ -2753,6 +2805,9 @@ namespace gsr {
} else {
recording_status = RecordingStatus::REPLAY;
update_ui_replay_started();
if(led_indicator && config.replay_config.record_options.use_led_indicator)
led_indicator->set_led(true);
}
prepare_gsr_output_for_reading();
@@ -2766,7 +2821,7 @@ namespace gsr {
// TODO: Do not run this is a daemon. Instead get the pid and when launching another notification close the current notification
// program and start another one. This can also be used to check when the notification has finished by checking with waitpid NOWAIT
// to see when the program has exit.
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
if(!disable_notification && config.replay_config.record_options.show_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).c_str());
show_notification(msg, short_notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
@@ -2795,7 +2850,7 @@ namespace gsr {
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
if(!replay_recording) {
if(config.record_config.show_recording_started_notifications)
if(config.record_config.record_options.show_notifications)
show_notification("Started recording in the replay session", short_notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
update_ui_recording_started();
@@ -2803,7 +2858,11 @@ namespace gsr {
// selected what to capture and accepted it.
recording_duration_clock.restart();
update_upause_status();
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->blink();
}
replay_recording = true;
kill(gpu_screen_recorder_process, SIGRTMIN);
} else {
@@ -2817,7 +2876,7 @@ namespace gsr {
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 4, 0}) {
if(!replay_recording) {
if(config.record_config.show_recording_started_notifications)
if(config.record_config.record_options.show_notifications)
show_notification("Started recording in the streaming session", short_notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD);
update_ui_recording_started();
@@ -2825,7 +2884,11 @@ namespace gsr {
// selected what to capture and accepted it.
recording_duration_clock.restart();
update_upause_status();
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->blink();
}
replay_recording = true;
kill(gpu_screen_recorder_process, SIGRTMIN);
} else {
@@ -2855,6 +2918,9 @@ namespace gsr {
update_ui_recording_stopped();
update_upause_status();
record_filepath.clear();
if(led_indicator && current_recording_config.record_config.record_options.use_led_indicator)
led_indicator->set_led(false);
return;
}
@@ -2935,6 +3001,9 @@ namespace gsr {
} else {
recording_status = RecordingStatus::RECORD;
update_ui_recording_started();
if(led_indicator && config.record_config.record_options.use_led_indicator)
led_indicator->set_led(true);
}
prepare_gsr_output_for_reading();
@@ -2945,7 +3014,7 @@ namespace gsr {
// Starting recording in 3...
// 2...
// 1...
if(config.record_config.show_recording_started_notifications) {
if(config.record_config.record_options.show_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).c_str());
show_notification(msg, short_notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str());
@@ -3031,8 +3100,11 @@ namespace gsr {
recording_status = RecordingStatus::NONE;
update_ui_streaming_stopped();
if(led_indicator && current_recording_config.streaming_config.record_options.use_led_indicator)
led_indicator->set_led(false);
// TODO: Show this with a slight delay to make sure it doesn't show up in the video
if(config.streaming_config.show_streaming_stopped_notifications)
if(config.streaming_config.record_options.show_notifications)
show_notification("Streaming has stopped", notification_timeout_seconds, mgl::Color(255, 255, 255), get_color_theme().tint_color, NotificationType::STREAM);
return;
}
@@ -3120,6 +3192,9 @@ namespace gsr {
} else {
recording_status = RecordingStatus::STREAM;
update_ui_streaming_started();
if(led_indicator && config.streaming_config.record_options.use_led_indicator)
led_indicator->set_led(true);
}
prepare_gsr_output_for_reading();
@@ -3133,7 +3208,7 @@ namespace gsr {
// TODO: Do not run this is a daemon. Instead get the pid and when launching another notification close the current notification
// program and start another one. This can also be used to check when the notification has finished by checking with waitpid NOWAIT
// to see when the program has exit.
if(config.streaming_config.show_streaming_started_notifications) {
if(config.streaming_config.record_options.show_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).c_str());
show_notification(msg, short_notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM, recording_capture_target.c_str());

View File

@@ -221,20 +221,29 @@ namespace gsr {
return checkbox;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_notifications() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show screenshot notifications");
checkbox->set_checked(true);
show_notification_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_led_indicator() {
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Blink scroll lock led when taking a screenshot");
checkbox->set_checked(true);
led_indicator_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_general_section() {
auto list = std::make_unique<List>(List::Orientation::VERTICAL);
list->add_widget(create_save_screenshot_in_game_folder());
list->add_widget(create_save_screenshot_to_clipboard());
list->add_widget(create_notifications());
list->add_widget(create_led_indicator());
return std::make_unique<Subsection>("General", std::move(list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_notifications_section() {
auto show_screenshot_saved_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show screenshot saved notification");
show_screenshot_saved_notification_checkbox->set_checked(true);
show_screenshot_saved_notification_checkbox_ptr = show_screenshot_saved_notification_checkbox.get();
return std::make_unique<Subsection>("Notifications", std::move(show_screenshot_saved_notification_checkbox), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
}
std::unique_ptr<Widget> ScreenshotSettingsPage::create_settings() {
auto page_list = std::make_unique<List>(List::Orientation::VERTICAL);
page_list->set_spacing(0.018f);
@@ -248,7 +257,6 @@ namespace gsr {
settings_list->add_widget(create_image_section());
settings_list->add_widget(create_file_info_section());
settings_list->add_widget(create_general_section());
settings_list->add_widget(create_notifications_section());
settings_scrollable_page_ptr->add_widget(std::move(settings_list));
return page_list;
}
@@ -291,7 +299,8 @@ namespace gsr {
save_directory_button_ptr->set_text(config.screenshot_config.save_directory);
save_screenshot_in_game_folder_checkbox_ptr->set_checked(config.screenshot_config.save_screenshot_in_game_folder);
save_screenshot_to_clipboard_checkbox_ptr->set_checked(config.screenshot_config.save_screenshot_to_clipboard);
show_screenshot_saved_notification_checkbox_ptr->set_checked(config.screenshot_config.show_screenshot_saved_notifications);
show_notification_checkbox_ptr->set_checked(config.screenshot_config.show_notifications);
led_indicator_checkbox_ptr->set_checked(config.screenshot_config.use_led_indicator);
if(config.screenshot_config.image_width == 0)
config.screenshot_config.image_width = 1920;
@@ -320,7 +329,8 @@ namespace gsr {
config.screenshot_config.save_directory = save_directory_button_ptr->get_text();
config.screenshot_config.save_screenshot_in_game_folder = save_screenshot_in_game_folder_checkbox_ptr->is_checked();
config.screenshot_config.save_screenshot_to_clipboard = save_screenshot_to_clipboard_checkbox_ptr->is_checked();
config.screenshot_config.show_screenshot_saved_notifications = show_screenshot_saved_notification_checkbox_ptr->is_checked();
config.screenshot_config.show_notifications = show_notification_checkbox_ptr->is_checked();
config.screenshot_config.use_led_indicator = led_indicator_checkbox_ptr->is_checked();
if(config.screenshot_config.image_width == 0)
config.screenshot_config.image_width = 1920;

View File

@@ -821,16 +821,33 @@ namespace gsr {
replay_time_label_ptr->set_text(buffer);
}
void SettingsPage::view_changed(bool advanced_view, Subsection *notifications_subsection_ptr) {
void SettingsPage::view_changed(bool advanced_view) {
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();
}
std::unique_ptr<CheckBox> SettingsPage::create_led_indicator(const char *type) {
char label_str[256];
snprintf(label_str, sizeof(label_str), "Show %s status with scroll lock led", type);
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, label_str);
checkbox->set_checked(false);
led_indicator_checkbox_ptr = checkbox.get();
return checkbox;
}
std::unique_ptr<CheckBox> SettingsPage::create_notifications(const char *type) {
char label_str[256];
snprintf(label_str, sizeof(label_str), "Show %s notifications", type);
auto checkbox = std::make_unique<CheckBox>(&get_theme().body_font, label_str);
checkbox->set_checked(true);
show_notification_checkbox_ptr = checkbox.get();
return checkbox;
}
void SettingsPage::add_replay_widgets() {
auto file_info_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto file_info_data_list = std::make_unique<List>(List::Orientation::HORIZONTAL);
@@ -846,33 +863,14 @@ namespace gsr {
general_list->add_widget(create_save_replay_in_game_folder());
if(gsr_info->system_info.gsr_version >= GsrVersion{5, 0, 3})
general_list->add_widget(create_restart_replay_on_save());
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
general_list->add_widget(create_notifications("replay"));
general_list->add_widget(create_led_indicator("replay"));
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
settings_list_ptr->add_widget(std::make_unique<Subsection>("Autostart", create_start_replay_automatically(), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto show_replay_started_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show replay started notification");
show_replay_started_notification_checkbox->set_checked(true);
show_replay_started_notification_checkbox_ptr = show_replay_started_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_replay_started_notification_checkbox));
auto show_replay_stopped_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show replay stopped notification");
show_replay_stopped_notification_checkbox->set_checked(true);
show_replay_stopped_notification_checkbox_ptr = show_replay_stopped_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_replay_stopped_notification_checkbox));
auto show_replay_saved_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show replay saved notification");
show_replay_saved_notification_checkbox->set_checked(true);
show_replay_saved_notification_checkbox_ptr = show_replay_saved_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_replay_saved_notification_checkbox));
auto notifications_subsection = std::make_unique<Subsection>("Notifications", std::move(checkboxes_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection));
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
view_changed(id == "advanced", notifications_subsection_ptr);
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_changed(id == "advanced");
return true;
};
view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -917,33 +915,18 @@ namespace gsr {
file_info_data_list->add_widget(create_container_section());
file_info_list->add_widget(std::move(file_info_data_list));
file_info_list->add_widget(create_estimated_record_file_size());
settings_list_ptr->add_widget(std::make_unique<Subsection>("File info", std::move(file_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", create_save_recording_in_game_folder(), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
general_list->add_widget(create_save_recording_in_game_folder());
general_list->add_widget(create_notifications("recording"));
general_list->add_widget(create_led_indicator("recording"));
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto show_recording_started_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show recording started notification");
show_recording_started_notification_checkbox->set_checked(true);
show_recording_started_notification_checkbox_ptr = show_recording_started_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_recording_started_notification_checkbox));
auto show_video_saved_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show video saved notification");
show_video_saved_notification_checkbox->set_checked(true);
show_video_saved_notification_checkbox_ptr = show_video_saved_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_video_saved_notification_checkbox));
auto show_video_paused_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show video paused/unpaused notification");
show_video_paused_notification_checkbox->set_checked(true);
show_video_paused_notification_checkbox_ptr = show_video_paused_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_video_paused_notification_checkbox));
auto notifications_subsection = std::make_unique<Subsection>("Notifications", std::move(checkboxes_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection));
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
view_changed(id == "advanced", notifications_subsection_ptr);
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_changed(id == "advanced");
return true;
};
view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -1063,23 +1046,15 @@ namespace gsr {
streaming_info_list->add_widget(create_streaming_service_section());
streaming_info_list->add_widget(create_stream_key_section());
streaming_info_list->add_widget(create_stream_custom_section());
settings_list_ptr->add_widget(std::make_unique<Subsection>("Streaming info", std::move(streaming_info_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
auto checkboxes_list = std::make_unique<List>(List::Orientation::VERTICAL);
auto general_list = std::make_unique<List>(List::Orientation::VERTICAL);
general_list->add_widget(create_save_recording_in_game_folder());
general_list->add_widget(create_notifications("streaming"));
general_list->add_widget(create_led_indicator("streaming"));
auto show_streaming_started_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show streaming started notification");
show_streaming_started_notification_checkbox->set_checked(true);
show_streaming_started_notification_checkbox_ptr = show_streaming_started_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_streaming_started_notification_checkbox));
auto show_streaming_stopped_notification_checkbox = std::make_unique<CheckBox>(&get_theme().body_font, "Show streaming stopped notification");
show_streaming_stopped_notification_checkbox->set_checked(true);
show_streaming_stopped_notification_checkbox_ptr = show_streaming_stopped_notification_checkbox.get();
checkboxes_list->add_widget(std::move(show_streaming_stopped_notification_checkbox));
auto notifications_subsection = std::make_unique<Subsection>("Notifications", std::move(checkboxes_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f));
Subsection *notifications_subsection_ptr = notifications_subsection.get();
settings_list_ptr->add_widget(std::move(notifications_subsection));
settings_list_ptr->add_widget(std::make_unique<Subsection>("General", std::move(general_list), mgl::vec2f(settings_scrollable_page_ptr->get_inner_size().x, 0.0f)));
streaming_service_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
const bool twitch_option = id == "twitch";
@@ -1095,8 +1070,8 @@ namespace gsr {
};
streaming_service_box_ptr->on_selection_changed("Twitch", "twitch");
view_radio_button_ptr->on_selection_changed = [this, notifications_subsection_ptr](const std::string&, const std::string &id) {
view_changed(id == "advanced", notifications_subsection_ptr);
view_radio_button_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
view_changed(id == "advanced");
return true;
};
view_radio_button_ptr->on_selection_changed("Simple", "simple");
@@ -1217,6 +1192,8 @@ namespace gsr {
//record_options.overclock = false;
record_cursor_checkbox_ptr->set_checked(record_options.record_cursor);
restore_portal_session_checkbox_ptr->set_checked(record_options.restore_portal_session);
show_notification_checkbox_ptr->set_checked(record_options.show_notifications);
led_indicator_checkbox_ptr->set_checked(record_options.use_led_indicator);
if(record_options.record_area_width == 0)
record_options.record_area_width = 1920;
@@ -1262,9 +1239,7 @@ namespace gsr {
save_replay_in_game_folder_ptr->set_checked(config.replay_config.save_video_in_game_folder);
if(restart_replay_on_save)
restart_replay_on_save->set_checked(config.replay_config.restart_replay_on_save);
show_replay_started_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_started_notifications);
show_replay_stopped_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_stopped_notifications);
show_replay_saved_notification_checkbox_ptr->set_checked(config.replay_config.show_replay_saved_notifications);
save_directory_button_ptr->set_text(config.replay_config.save_directory);
container_box_ptr->set_selected_item(config.replay_config.container);
@@ -1278,17 +1253,12 @@ namespace gsr {
void SettingsPage::load_record() {
load_common(config.record_config.record_options);
save_recording_in_game_folder_ptr->set_checked(config.record_config.save_video_in_game_folder);
show_recording_started_notification_checkbox_ptr->set_checked(config.record_config.show_recording_started_notifications);
show_video_saved_notification_checkbox_ptr->set_checked(config.record_config.show_video_saved_notifications);
show_video_paused_notification_checkbox_ptr->set_checked(config.record_config.show_video_paused_notifications);
save_directory_button_ptr->set_text(config.record_config.save_directory);
container_box_ptr->set_selected_item(config.record_config.container);
}
void SettingsPage::load_stream() {
load_common(config.streaming_config.record_options);
show_streaming_started_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_started_notifications);
show_streaming_stopped_notification_checkbox_ptr->set_checked(config.streaming_config.show_streaming_stopped_notifications);
streaming_service_box_ptr->set_selected_item(config.streaming_config.streaming_service);
youtube_stream_key_entry_ptr->set_text(config.streaming_config.youtube.stream_key);
twitch_stream_key_entry_ptr->set_text(config.streaming_config.twitch.stream_key);
@@ -1354,6 +1324,8 @@ namespace gsr {
//record_options.overclock = false;
record_options.record_cursor = record_cursor_checkbox_ptr->is_checked();
record_options.restore_portal_session = restore_portal_session_checkbox_ptr->is_checked();
record_options.show_notifications = show_notification_checkbox_ptr->is_checked();
record_options.use_led_indicator = led_indicator_checkbox_ptr->is_checked();
if(record_options.record_area_width == 0)
record_options.record_area_width = 1920;
@@ -1404,9 +1376,6 @@ namespace gsr {
config.replay_config.save_video_in_game_folder = save_replay_in_game_folder_ptr->is_checked();
if(restart_replay_on_save)
config.replay_config.restart_replay_on_save = restart_replay_on_save->is_checked();
config.replay_config.show_replay_started_notifications = show_replay_started_notification_checkbox_ptr->is_checked();
config.replay_config.show_replay_stopped_notifications = show_replay_stopped_notification_checkbox_ptr->is_checked();
config.replay_config.show_replay_saved_notifications = show_replay_saved_notification_checkbox_ptr->is_checked();
config.replay_config.save_directory = save_directory_button_ptr->get_text();
config.replay_config.container = container_box_ptr->get_selected_id();
config.replay_config.replay_time = atoi(replay_time_entry_ptr->get_text().c_str());
@@ -1421,17 +1390,12 @@ namespace gsr {
void SettingsPage::save_record() {
save_common(config.record_config.record_options);
config.record_config.save_video_in_game_folder = save_recording_in_game_folder_ptr->is_checked();
config.record_config.show_recording_started_notifications = show_recording_started_notification_checkbox_ptr->is_checked();
config.record_config.show_video_saved_notifications = show_video_saved_notification_checkbox_ptr->is_checked();
config.record_config.show_video_paused_notifications = show_video_paused_notification_checkbox_ptr->is_checked();
config.record_config.save_directory = save_directory_button_ptr->get_text();
config.record_config.container = container_box_ptr->get_selected_id();
}
void SettingsPage::save_stream() {
save_common(config.streaming_config.record_options);
config.streaming_config.show_streaming_started_notifications = show_streaming_started_notification_checkbox_ptr->is_checked();
config.streaming_config.show_streaming_stopped_notifications = show_streaming_stopped_notification_checkbox_ptr->is_checked();
config.streaming_config.streaming_service = streaming_service_box_ptr->get_selected_id();
config.streaming_config.youtube.stream_key = youtube_stream_key_entry_ptr->get_text();
config.streaming_config.twitch.stream_key = twitch_stream_key_entry_ptr->get_text();

View File

@@ -194,7 +194,7 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
}
if(extra_data->gsr_ui_virtual_keyboard) {
if(event.type == EV_KEY)
if(event.type == EV_KEY || event.type == EV_MSC)
self->check_grab_lock = false;
return;
}
@@ -227,10 +227,11 @@ static void keyboard_event_process_input_event_data(keyboard_event *self, event_
}
if(extra_data->grabbed) {
if(!self->check_grab_lock) {
if(!self->check_grab_lock && (event.type == EV_KEY || event.type == EV_MSC)) {
self->uinput_written_time_seconds = clock_get_monotonic_seconds();
self->check_grab_lock = true;
}
/* TODO: What to do on error? */
if(write(self->uinput_fd, &event, sizeof(event)) != sizeof(event))
fprintf(stderr, "Error: failed to write event data to virtual keyboard for exclusively grabbed device\n");