Compare commits

..

5 Commits

Author SHA1 Message Date
dec05eba
2e0dc48f3e Fix controller hotplug not always working 2025-07-22 03:08:06 +02:00
dec05eba
d64e698eb1 Show recording/replay duration in notification 2025-07-22 01:13:42 +02:00
dec05eba
315fab99a8 1.7.0 2025-07-21 02:46:07 +02:00
dec05eba
8ffd8de74a Update TODO 2025-07-20 01:56:17 +02:00
dec05eba
ad94cff59e Add lshift + printscreen hotkey to take a screenshot of a window (or desktop portal on wayland) 2025-07-20 01:55:02 +02:00
13 changed files with 229 additions and 62 deletions

8
TODO
View File

@@ -196,8 +196,6 @@ Add a window that shows a warning if gpu video encoding isn't supported.
Disable system notifications when recording. Does the notification dbus interface support pausing notifications?
Automatically mark window region in window capture for screenshot on x11.
Disable hotkeys if virtual keyboard is found (either at startup or after running), if grab type if not virtual. Show a notification if that happens that hotkeys have been disabled.
Detect if keyboard is locked by listening to gsr-ui virtual keyboard events and if no event is received after pressing a key (when writing to it after receiving input from another keyboard)
then remove the keyboard grab and show a message or something.
@@ -205,3 +203,9 @@ Disable hotkeys if virtual keyboard is found (either at startup or after running
Maybe this can be fixed automatically by grabbing gsr-ui virtual keyboard and releasing it just before we write to it and then release it again.
But wont keyboard remapping software grab the keyboard first if they detect it quickly?
If we fail to grab it because some other software did then dont grab any keyboards nor gsr-ui virtual keyboards, just listen to them.
Support localization.
Add option to not capture cursor in screenshot when doing region/window capture.
Window selection doesn't work when a window is fullscreen on x11.

View File

@@ -145,6 +145,7 @@ namespace gsr {
std::string save_directory;
ConfigHotkey take_screenshot_hotkey;
ConfigHotkey take_screenshot_region_hotkey;
ConfigHotkey take_screenshot_window_hotkey; // Or desktop portal, on wayland
};
struct Config {

View File

@@ -41,6 +41,12 @@ namespace gsr {
SCREENSHOT
};
enum class ScreenshotForceType {
NONE,
REGION,
WINDOW
};
class Overlay {
public:
Overlay(std::string resources_path, GsrInfo gsr_info, SupportedCaptureOptions capture_options, egl_functions egl_funcs);
@@ -64,6 +70,7 @@ namespace gsr {
void save_replay_10_min();
void take_screenshot();
void take_screenshot_region();
void take_screenshot_window();
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type, const char *capture_target = nullptr);
bool is_open() const;
bool should_exit(std::string &reason) const;
@@ -87,6 +94,7 @@ namespace gsr {
void close_gpu_screen_recorder_output();
double get_time_passed_in_replay_buffer_seconds();
void update_notification_process_status();
void save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type);
void on_replay_saved(const char *replay_saved_filepath);
@@ -121,7 +129,7 @@ namespace gsr {
bool on_press_start_replay(bool disable_notification, bool finished_selection);
void on_press_start_record(bool finished_selection);
void on_press_start_stream(bool finished_selection);
void on_press_take_screenshot(bool finished_selection, bool force_region_capture);
void on_press_take_screenshot(bool finished_selection, ScreenshotForceType force_type);
bool update_compositor_texture(const Monitor &monitor);
std::string get_capture_target(const std::string &capture_target, const SupportedCaptureOptions &capture_options);
@@ -213,6 +221,12 @@ namespace gsr {
bool try_replay_startup = true;
bool replay_recording = false;
int replay_save_duration_min = 0;
double replay_buffer_save_duration_sec = 0.0;
mgl::Clock replay_duration_clock;
double replay_saved_duration_sec = 0.0;
bool replay_restart_on_save = false;
mgl::Clock recording_duration_clock;
AudioPlayer audio_player;

View File

@@ -29,6 +29,7 @@ namespace gsr {
STREAM_START_STOP,
TAKE_SCREENSHOT,
TAKE_SCREENSHOT_REGION,
TAKE_SCREENSHOT_WINDOW,
SHOW_HIDE
};
@@ -63,6 +64,7 @@ namespace gsr {
std::unique_ptr<List> create_stream_hotkey_options();
std::unique_ptr<List> create_screenshot_hotkey_options();
std::unique_ptr<List> create_screenshot_region_hotkey_options();
std::unique_ptr<List> create_screenshot_window_hotkey_options();
std::unique_ptr<List> create_hotkey_control_buttons();
std::unique_ptr<Subsection> create_keyboard_hotkey_subsection(ScrollablePage *parent_page);
std::unique_ptr<Subsection> create_controller_hotkey_subsection(ScrollablePage *parent_page);
@@ -99,6 +101,7 @@ namespace gsr {
Button *start_stop_streaming_button_ptr = nullptr;
Button *take_screenshot_button_ptr = nullptr;
Button *take_screenshot_region_button_ptr = nullptr;
Button *take_screenshot_window_button_ptr = nullptr;
Button *show_hide_button_ptr = nullptr;
ConfigHotkey configure_config_hotkey;

View File

@@ -1,4 +1,4 @@
project('gsr-ui', ['c', 'cpp'], version : '1.6.10', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
project('gsr-ui', ['c', 'cpp'], version : '1.7.1', default_options : ['warning_level=2', 'cpp_std=c++17'], subproject_dir : 'depends')
if get_option('buildtype') == 'debug'
add_project_arguments('-g3', language : ['c', 'cpp'])
@@ -62,7 +62,7 @@ datadir = get_option('datadir')
gsr_ui_resources_path = join_paths(prefix, datadir, 'gsr-ui')
add_project_arguments('-DGSR_UI_VERSION="' + meson.project_version() + '"', language: ['c', 'cpp'])
add_project_arguments('-DGSR_FLATPAK_VERSION="5.7.3"', language: ['c', 'cpp'])
add_project_arguments('-DGSR_FLATPAK_VERSION="5.7.4"', language: ['c', 'cpp'])
executable(
meson.project_name(),

View File

@@ -1,7 +1,7 @@
[package]
name = "gsr-ui"
type = "executable"
version = "1.6.10"
version = "1.7.1"
platforms = ["posix"]
[lang.cpp]

View File

@@ -153,6 +153,7 @@ namespace gsr {
screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Printscreen, 0};
screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LCTRL};
screenshot_config.take_screenshot_window_hotkey = {mgl::Keyboard::Printscreen, HOTKEY_MOD_LSHIFT};
main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT};
}
@@ -284,7 +285,8 @@ namespace gsr {
{"screenshot.show_screenshot_saved_notifications", &config.screenshot_config.show_screenshot_saved_notifications},
{"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}
{"screenshot.take_screenshot_region_hotkey", &config.screenshot_config.take_screenshot_region_hotkey},
{"screenshot.take_screenshot_window_hotkey", &config.screenshot_config.take_screenshot_window_hotkey}
};
}

View File

@@ -102,7 +102,8 @@ namespace gsr {
close(event_fd);
for(int i = 0; i < num_poll_fd; ++i) {
close(poll_fd[i].fd);
if(poll_fd[i].fd > 0)
close(poll_fd[i].fd);
}
}
@@ -221,6 +222,9 @@ namespace gsr {
if(i == event_index)
goto done;
char dev_input_filepath[256];
snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/input/js%d", extra_data[i].dev_input_id);
fprintf(stderr, "Info: removed joystick: %s\n", dev_input_filepath);
if(remove_poll_fd(i))
--i; // This item was removed so we want to repeat the same index to continue to the next item
@@ -234,18 +238,13 @@ namespace gsr {
goto done;
} else if(i == hotplug_poll_index) {
hotplug.process_event_data(poll_fd[i].fd, [&](HotplugAction hotplug_action, const char *devname) {
char dev_input_filepath[1024];
snprintf(dev_input_filepath, sizeof(dev_input_filepath), "/dev/%s", devname);
switch(hotplug_action) {
case HotplugAction::ADD: {
// Cant open the /dev/input device immediately or it fails.
// TODO: Remove this hack when a better solution is found.
usleep(50 * 1000);
add_device(dev_input_filepath);
add_device(devname);
break;
}
case HotplugAction::REMOVE: {
if(remove_device(dev_input_filepath))
if(remove_device(devname))
--i; // This item was removed so we want to repeat the same index to continue to the next item
break;
}
@@ -373,7 +372,9 @@ namespace gsr {
if(index < 0 || index >= num_poll_fd)
return false;
close(poll_fd[index].fd);
if(poll_fd[index].fd > 0)
close(poll_fd[index].fd);
for(int i = index + 1; i < num_poll_fd; ++i) {
poll_fd[i - 1] = poll_fd[i];
extra_data[i - 1] = extra_data[i];

View File

@@ -59,10 +59,9 @@ namespace gsr {
/* TODO: This assumes SUBSYSTEM= is output before DEVNAME=, is that always true? */
void Hotplug::parse_netlink_data(const char *line, const HotplugEventCallback &callback) {
const char *at_symbol = strchr(line, '@');
if(at_symbol) {
event_is_add = strncmp(line, "add@", 4) == 0;
event_is_remove = strncmp(line, "remove@", 7) == 0;
if(strncmp(line, "ACTION=", 7) == 0) {
event_is_add = strncmp(line+7, "add", 3) == 0;
event_is_remove = strncmp(line+7, "remove", 6) == 0;
subsystem_is_input = false;
} else if(event_is_add || event_is_remove) {
if(strcmp(line, "SUBSYSTEM=input") == 0)

View File

@@ -27,6 +27,7 @@
#include <stdexcept>
#include <algorithm>
#include <inttypes.h>
#include <math.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -49,7 +50,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.5;
static const double notification_timeout_seconds = 3.0;
static const double notification_error_timeout_seconds = 5.0;
static const double cursor_tracker_update_timeout_sec = 0.1;
@@ -378,6 +379,13 @@ namespace gsr {
overlay->take_screenshot_region();
});
global_hotkeys->bind_key_press(
config_hotkey_to_hotkey(overlay->get_config().screenshot_config.take_screenshot_window_hotkey),
"take_screenshot_window", [overlay](const std::string &id) {
fprintf(stderr, "pressed %s\n", id.c_str());
overlay->take_screenshot_window();
});
global_hotkeys->bind_key_press(
config_hotkey_to_hotkey(ConfigHotkey{ mgl::Keyboard::Key::Escape, HOTKEY_MOD_LCTRL | HOTKEY_MOD_LSHIFT | HOTKEY_MOD_LALT }),
"exit", [overlay](const std::string &id) {
@@ -1481,11 +1489,15 @@ namespace gsr {
}
void Overlay::take_screenshot() {
on_press_take_screenshot(false, false);
on_press_take_screenshot(false, ScreenshotForceType::NONE);
}
void Overlay::take_screenshot_region() {
on_press_take_screenshot(false, true);
on_press_take_screenshot(false, ScreenshotForceType::REGION);
}
void Overlay::take_screenshot_window() {
on_press_take_screenshot(false, ScreenshotForceType::WINDOW);
}
static const char* notification_type_to_string(NotificationType notification_type) {
@@ -1562,7 +1574,7 @@ namespace gsr {
return strcmp(capture_target, "window") != 0 && strcmp(capture_target, "focused") != 0 && strcmp(capture_target, "region") != 0 && strcmp(capture_target, "portal") != 0 && contains_non_hex_number(capture_target);
}
static std::string capture_target_get_notification_name(const char *capture_target) {
static std::string capture_target_get_notification_name(const char *capture_target, bool save) {
std::string result;
if(is_capture_target_monitor(capture_target)) {
result = "this monitor";
@@ -1574,9 +1586,11 @@ namespace gsr {
sscanf(capture_target, "%" PRIi64, &window_id);
const std::optional<std::string> window_title = get_window_title(display, window_id);
if(window_title) {
if(save) {
result = "window";
} else if(window_title) {
result = strip(window_title.value());
truncate_string(result, 20);
truncate_string(result, 30);
result = "window \"" + result + "\"";
} else {
result = std::string("window ") + capture_target;
@@ -1763,6 +1777,45 @@ namespace gsr {
return result;
}
static std::string to_duration_string(double duration_sec) {
int seconds = ceil(duration_sec);
const int hours = seconds / 60 / 60;
seconds -= (hours * 60 * 60);
const int minutes = seconds / 60;
seconds -= (minutes * 60);
std::string result;
if(hours > 0)
result += std::to_string(hours) + " hour" + (hours == 1 ? "" : "s");
if(minutes > 0) {
if(!result.empty())
result += " ";
result += std::to_string(minutes) + " minute" + (minutes == 1 ? "" : "s");
}
if(seconds > 0 || (hours == 0 && minutes == 0)) {
if(!result.empty())
result += " ";
result += std::to_string(seconds) + " second" + (seconds == 1 ? "" : "s");
}
fprintf(stderr, "to duration string: %f, %d, %d, %d\n", duration_sec, seconds, minutes, hours);
return result;
}
double Overlay::get_time_passed_in_replay_buffer_seconds() {
double replay_duration_sec = replay_saved_duration_sec;
if(replay_duration_sec > replay_buffer_save_duration_sec)
replay_duration_sec = replay_buffer_save_duration_sec;
if(replay_save_duration_min > 0 && replay_duration_sec > replay_save_duration_min * 60)
replay_duration_sec = replay_save_duration_min * 60;
return replay_duration_sec;
}
void Overlay::save_video_in_current_game_directory(const char *video_filepath, NotificationType notification_type) {
mgl_context *context = mgl_get_context();
Display *display = (Display*)context->connection;
@@ -1783,7 +1836,7 @@ namespace gsr {
const std::string new_video_filepath = video_directory + "/" + video_filename;
rename(video_filepath, new_video_filepath.c_str());
truncate_string(focused_window_name, 20);
truncate_string(focused_window_name, 40);
const char *capture_target = nullptr;
char msg[512];
@@ -1792,7 +1845,10 @@ namespace gsr {
if(!config.record_config.show_video_saved_notifications)
return;
snprintf(msg, sizeof(msg), "Saved a recording of %s to \"%s\"", capture_target_get_notification_name(recording_capture_target.c_str()).c_str(), focused_window_name.c_str());
const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds());
snprintf(msg, sizeof(msg), "Saved a %s recording of %s\nto \"%s\"",
duration_str.c_str(),
capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
capture_target = recording_capture_target.c_str();
break;
}
@@ -1800,13 +1856,10 @@ namespace gsr {
if(!config.replay_config.show_replay_saved_notifications)
return;
char duration[32];
if(replay_save_duration_min > 0)
snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
else
snprintf(duration, sizeof(duration), " ");
snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to \"%s\"", duration, capture_target_get_notification_name(recording_capture_target.c_str()).c_str(), focused_window_name.c_str());
const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds());
snprintf(msg, sizeof(msg), "Saved a %s replay of %s\nto \"%s\"",
duration_str.c_str(),
capture_target_get_notification_name(recording_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
capture_target = recording_capture_target.c_str();
break;
}
@@ -1814,7 +1867,8 @@ namespace gsr {
if(!config.screenshot_config.show_screenshot_saved_notifications)
return;
snprintf(msg, sizeof(msg), "Saved a screenshot of %s to \"%s\"", capture_target_get_notification_name(screenshot_capture_target.c_str()).c_str(), focused_window_name.c_str());
snprintf(msg, sizeof(msg), "Saved a screenshot of %s\nto \"%s\"",
capture_target_get_notification_name(screenshot_capture_target.c_str(), true).c_str(), focused_window_name.c_str());
capture_target = screenshot_capture_target.c_str();
break;
}
@@ -1840,14 +1894,12 @@ namespace gsr {
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) {
char duration[32];
if(replay_save_duration_min > 0)
snprintf(duration, sizeof(duration), " %d minute ", replay_save_duration_min);
else
snprintf(duration, sizeof(duration), " ");
const std::string duration_str = to_duration_string(get_time_passed_in_replay_buffer_seconds());
char msg[512];
snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Saved a %s replay of %s",
duration_str.c_str(),
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());
}
}
@@ -1995,7 +2047,8 @@ namespace gsr {
save_video_in_current_game_directory(screenshot_filepath.c_str(), NotificationType::SCREENSHOT);
} else if(config.screenshot_config.show_screenshot_saved_notifications) {
char msg[512];
snprintf(msg, sizeof(msg), "Saved a screenshot of %s", capture_target_get_notification_name(screenshot_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Saved a screenshot of %s",
capture_target_get_notification_name(screenshot_capture_target.c_str(), true).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 {
@@ -2093,8 +2146,12 @@ namespace gsr {
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) {
const std::string duration_str = to_duration_string(recording_duration_clock.get_elapsed_time_seconds());
char msg[512];
snprintf(msg, sizeof(msg), "Saved a recording of %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Saved a %s recording of %s",
duration_str.c_str(),
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());
}
} else {
@@ -2383,6 +2440,9 @@ namespace gsr {
replay_save_duration_min = 0;
replay_save_show_notification = true;
replay_save_clock.restart();
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);
}
@@ -2393,6 +2453,7 @@ namespace gsr {
replay_save_duration_min = 1;
replay_save_show_notification = true;
replay_save_clock.restart();
replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds();
kill(gpu_screen_recorder_process, SIGRTMIN+3);
}
@@ -2403,6 +2464,7 @@ namespace gsr {
replay_save_duration_min = 10;
replay_save_show_notification = true;
replay_save_clock.restart();
replay_saved_duration_sec = replay_duration_clock.get_elapsed_time_seconds();
kill(gpu_screen_recorder_process, SIGRTMIN+5);
}
@@ -2553,6 +2615,9 @@ namespace gsr {
if(config.replay_config.restart_replay_on_save && gsr_info.system_info.gsr_version >= GsrVersion{5, 0, 3}) {
args.push_back("-restart-replay-on-save");
args.push_back("yes");
replay_restart_on_save = true;
} else {
replay_restart_on_save = false;
}
if(gsr_info.system_info.gsr_version >= GsrVersion{5, 5, 0}) {
@@ -2592,13 +2657,17 @@ namespace gsr {
// to see when the program has exit.
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).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());
}
if(config.replay_config.record_options.record_area_option == "portal")
hide_ui = true;
// TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has
// selected what to capture and accepted it.
replay_duration_clock.restart();
replay_buffer_save_duration_sec = config.replay_config.replay_time;
return true;
}
@@ -2619,6 +2688,10 @@ namespace gsr {
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();
// TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has
// selected what to capture and accepted it.
recording_duration_clock.restart();
}
replay_recording = true;
kill(gpu_screen_recorder_process, SIGRTMIN);
@@ -2636,6 +2709,10 @@ namespace gsr {
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();
// TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has
// selected what to capture and accepted it.
recording_duration_clock.restart();
}
replay_recording = true;
kill(gpu_screen_recorder_process, SIGRTMIN);
@@ -2755,12 +2832,16 @@ namespace gsr {
// 1...
if(config.record_config.show_recording_started_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).c_str());
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::RECORD, recording_capture_target.c_str());
}
if(config.record_config.record_options.record_area_option == "portal")
hide_ui = true;
// TODO: This will be incorrect if the user uses portal capture, as capture wont start until the user has
// selected what to capture and accepted it.
recording_duration_clock.restart();
}
static std::string streaming_get_url(const Config &config) {
@@ -2932,7 +3013,7 @@ namespace gsr {
// to see when the program has exit.
if(config.streaming_config.show_streaming_started_notifications) {
char msg[256];
snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str(), false).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());
}
@@ -2940,7 +3021,7 @@ namespace gsr {
hide_ui = true;
}
void Overlay::on_press_take_screenshot(bool finished_selection, bool force_region_capture) {
void Overlay::on_press_take_screenshot(bool finished_selection, ScreenshotForceType force_type) {
if(region_selector.is_started() || window_selector.is_started())
return;
@@ -2949,8 +3030,21 @@ namespace gsr {
return;
}
const bool region_capture = config.screenshot_config.record_area_option == "region" || force_region_capture;
const char *record_area_option = region_capture ? "region" : config.screenshot_config.record_area_option.c_str();
bool hotkey_window_capture = false;
std::string record_area_option;
switch(force_type) {
case ScreenshotForceType::NONE:
record_area_option = config.screenshot_config.record_area_option;
break;
case ScreenshotForceType::REGION:
record_area_option = "region";
break;
case ScreenshotForceType::WINDOW:
record_area_option = gsr_info.system_info.display_server == DisplayServer::X11 ? "window" : "portal";
hotkey_window_capture = true;
break;
}
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
screenshot_capture_target = get_capture_target(record_area_option, capture_options);
if(!validate_capture_target(record_area_option, capture_options)) {
@@ -2960,19 +3054,18 @@ namespace gsr {
return;
}
if(region_capture && !finished_selection) {
if(record_area_option == "region" && !finished_selection) {
start_region_capture = true;
on_region_selected = [this, force_region_capture]() {
usleep(200 * 1000); // Hack: wait 0.2 seconds before taking a screenshot to allow user to move cursor away. TODO: Remove this
on_press_take_screenshot(true, force_region_capture);
on_region_selected = [this, force_type]() {
on_press_take_screenshot(true, force_type);
};
return;
}
if(config.screenshot_config.record_area_option == "window" && !finished_selection) {
if(record_area_option == "window" && !finished_selection) {
start_window_capture = true;
on_window_selected = [this, force_region_capture]() {
on_press_take_screenshot(true, force_region_capture);
on_window_selected = [this, force_type]() {
on_press_take_screenshot(true, force_type);
};
return;
}
@@ -2996,13 +3089,22 @@ namespace gsr {
args.push_back(size);
}
if(config.screenshot_config.restore_portal_session) {
if(config.screenshot_config.restore_portal_session && !hotkey_window_capture) {
args.push_back("-restore-portal-session");
args.push_back("yes");
}
const std::string hotkey_window_capture_portal_session_token_filepath = get_config_dir() + "/gpu-screen-recorder/gsr-ui-window-capture-token";
if(record_area_option == "portal") {
hide_ui = true;
if(hotkey_window_capture) {
args.push_back("-portal-session-token-filepath");
args.push_back(hotkey_window_capture_portal_session_token_filepath.c_str());
}
}
char region_str[128];
if(region_capture)
if(record_area_option == "region")
add_region_command(args, region_str, sizeof(region_str), region_selector);
args.push_back(nullptr);
@@ -3012,9 +3114,6 @@ namespace gsr {
if(gpu_screen_recorder_screenshot_process == -1) {
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);
}
if(config.screenshot_config.record_area_option == "portal")
hide_ui = true;
}
bool Overlay::update_compositor_texture(const Monitor &monitor) {

View File

@@ -348,6 +348,27 @@ namespace gsr {
return list;
}
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_window_hotkey_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
char str[128];
if(gsr_info->system_info.display_server == DisplayServer::X11)
snprintf(str, sizeof(str), "Take a screenshot of a window:");
else
snprintf(str, sizeof(str), "Take a screenshot with desktop portal:");
list->add_widget(std::make_unique<Label>(&get_theme().body_font, str, get_color_theme().text_color));
auto take_screenshot_window_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
take_screenshot_window_button_ptr = take_screenshot_window_button.get();
list->add_widget(std::move(take_screenshot_window_button));
take_screenshot_window_button_ptr->on_click = [this] {
configure_hotkey_start(ConfigureHotkeyType::TAKE_SCREENSHOT_WINDOW);
};
return list;
}
std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
@@ -362,6 +383,7 @@ namespace gsr {
config.replay_config.save_10_min_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_window_hotkey = {mgl::Keyboard::Unknown, 0};
config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
load_hotkeys();
overlay->rebind_all_keyboard_hotkeys();
@@ -404,6 +426,7 @@ namespace gsr {
list_ptr->add_widget(create_stream_hotkey_options());
list_ptr->add_widget(create_screenshot_hotkey_options());
list_ptr->add_widget(create_screenshot_region_hotkey_options());
list_ptr->add_widget(create_screenshot_window_hotkey_options());
list_ptr->add_widget(create_hotkey_control_buttons());
return subsection;
}
@@ -528,6 +551,7 @@ namespace gsr {
take_screenshot_button_ptr->set_text(config.screenshot_config.take_screenshot_hotkey.to_string());
take_screenshot_region_button_ptr->set_text(config.screenshot_config.take_screenshot_region_hotkey.to_string());
take_screenshot_window_button_ptr->set_text(config.screenshot_config.take_screenshot_window_hotkey.to_string());
show_hide_button_ptr->set_text(config.main_config.show_hide_hotkey.to_string());
}
@@ -611,6 +635,8 @@ namespace gsr {
return take_screenshot_button_ptr;
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
return take_screenshot_region_button_ptr;
case ConfigureHotkeyType::TAKE_SCREENSHOT_WINDOW:
return take_screenshot_window_button_ptr;
case ConfigureHotkeyType::SHOW_HIDE:
return show_hide_button_ptr;
}
@@ -639,6 +665,8 @@ namespace gsr {
return &config.screenshot_config.take_screenshot_hotkey;
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
return &config.screenshot_config.take_screenshot_region_hotkey;
case ConfigureHotkeyType::TAKE_SCREENSHOT_WINDOW:
return &config.screenshot_config.take_screenshot_window_hotkey;
case ConfigureHotkeyType::SHOW_HIDE:
return &config.main_config.show_hide_hotkey;
}
@@ -654,6 +682,7 @@ namespace gsr {
&config.streaming_config.start_stop_hotkey,
&config.screenshot_config.take_screenshot_hotkey,
&config.screenshot_config.take_screenshot_region_hotkey,
&config.screenshot_config.take_screenshot_window_hotkey,
&config.main_config.show_hide_hotkey
};
for(ConfigHotkey *config_hotkey : config_hotkeys) {
@@ -702,6 +731,13 @@ namespace gsr {
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
hotkey_configure_action_name = "Take a screenshot of a region";
break;
case ConfigureHotkeyType::TAKE_SCREENSHOT_WINDOW: {
if(gsr_info->system_info.display_server == DisplayServer::X11)
hotkey_configure_action_name = "Take a screenshot of a window";
else
hotkey_configure_action_name = "Take a screenshot with desktop portal";
break;
}
case ConfigureHotkeyType::SHOW_HIDE:
hotkey_configure_action_name = "Show/hide UI";
break;

View File

@@ -95,6 +95,11 @@ static void rpc_add_commands(gsr::Rpc *rpc, gsr::Overlay *overlay) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
overlay->take_screenshot_region();
});
rpc->add_handler("take-screenshot-window", [overlay](const std::string &name) {
fprintf(stderr, "rpc command executed: %s\n", name.c_str());
overlay->take_screenshot_window();
});
}
static bool is_gsr_ui_virtual_keyboard_running() {

View File

@@ -64,6 +64,8 @@ static void usage(void) {
printf(" Take a screenshot.\n");
printf(" take-screenshot-region\n");
printf(" Take a screenshot of a region.\n");
printf(" take-screenshot-window\n");
printf(" Take a screenshot of a window (or desktop portal on Wayland).\n");
printf("\n");
printf("EXAMPLES:\n");
printf(" gsr-ui-cli toggle-show\n");
@@ -83,6 +85,7 @@ static bool is_valid_command(const char *command) {
"replay-save-10-min",
"take-screenshot",
"take-screenshot-region",
"take-screenshot-window",
NULL
};