Add alt+f2 to take a screenshot of a region

This commit is contained in:
dec05eba
2025-03-21 23:47:24 +01:00
parent a0d8af9d37
commit 2ec59c6812
10 changed files with 81 additions and 20 deletions

2
TODO
View File

@@ -141,7 +141,7 @@ Allow using a hotkey such as printscreen or any other non-alphanumeric key witho
Use x11 shm instead of XGetImage (https://stackoverflow.com/questions/43442675/how-to-use-xshmgetimage-and-xshmputimage). Use x11 shm instead of XGetImage (https://stackoverflow.com/questions/43442675/how-to-use-xshmgetimage-and-xshmputimage).
Add a hotkey to record/stream/replay/screenshot region. Add a hotkey to record/stream/replay region.
Do xi grab for keys as well. Otherwise the ui cant be used for keyboard input if a program has grabbed the keyboard, and there could possibly be a game that grabs the keyboard as well. Do xi grab for keys as well. Otherwise the ui cant be used for keyboard input if a program has grabbed the keyboard, and there could possibly be a game that grabs the keyboard as well.

View File

@@ -126,6 +126,7 @@ namespace gsr {
bool show_screenshot_saved_notifications = true; bool show_screenshot_saved_notifications = true;
std::string save_directory; std::string save_directory;
ConfigHotkey take_screenshot_hotkey; ConfigHotkey take_screenshot_hotkey;
ConfigHotkey take_screenshot_region_hotkey;
}; };
struct Config { struct Config {

View File

@@ -59,6 +59,7 @@ namespace gsr {
void toggle_replay(); void toggle_replay();
void save_replay(); void save_replay();
void take_screenshot(); void take_screenshot();
void take_screenshot_region();
void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type); void show_notification(const char *str, double timeout_seconds, mgl::Color icon_color, mgl::Color bg_color, NotificationType notification_type);
bool is_open() const; bool is_open() const;
bool should_exit(std::string &reason) const; bool should_exit(std::string &reason) const;
@@ -111,7 +112,7 @@ namespace gsr {
bool on_press_start_replay(bool disable_notification, bool finished_region_selection); bool on_press_start_replay(bool disable_notification, bool finished_region_selection);
void on_press_start_record(bool finished_region_selection); void on_press_start_record(bool finished_region_selection);
void on_press_start_stream(bool finished_region_selection); void on_press_start_stream(bool finished_region_selection);
void on_press_take_screenshot(bool finished_region_selection); void on_press_take_screenshot(bool finished_region_selection, bool force_region_capture);
bool update_compositor_texture(const Monitor &monitor); bool update_compositor_texture(const Monitor &monitor);
void force_window_on_top(); void force_window_on_top();

View File

@@ -26,6 +26,7 @@ namespace gsr {
RECORD_PAUSE_UNPAUSE, RECORD_PAUSE_UNPAUSE,
STREAM_START_STOP, STREAM_START_STOP,
TAKE_SCREENSHOT, TAKE_SCREENSHOT,
TAKE_SCREENSHOT_REGION,
SHOW_HIDE SHOW_HIDE
}; };
@@ -58,6 +59,7 @@ namespace gsr {
std::unique_ptr<List> create_record_hotkey_options(); std::unique_ptr<List> create_record_hotkey_options();
std::unique_ptr<List> create_stream_hotkey_options(); std::unique_ptr<List> create_stream_hotkey_options();
std::unique_ptr<List> create_screenshot_hotkey_options(); std::unique_ptr<List> create_screenshot_hotkey_options();
std::unique_ptr<List> create_screenshot_region_hotkey_options();
std::unique_ptr<List> create_hotkey_control_buttons(); std::unique_ptr<List> create_hotkey_control_buttons();
std::unique_ptr<Subsection> create_keyboard_hotkey_subsection(ScrollablePage *parent_page); std::unique_ptr<Subsection> create_keyboard_hotkey_subsection(ScrollablePage *parent_page);
std::unique_ptr<Subsection> create_controller_hotkey_subsection(ScrollablePage *parent_page); std::unique_ptr<Subsection> create_controller_hotkey_subsection(ScrollablePage *parent_page);
@@ -91,6 +93,7 @@ namespace gsr {
Button *pause_unpause_recording_button_ptr = nullptr; Button *pause_unpause_recording_button_ptr = nullptr;
Button *start_stop_streaming_button_ptr = nullptr; Button *start_stop_streaming_button_ptr = nullptr;
Button *take_screenshot_button_ptr = nullptr; Button *take_screenshot_button_ptr = nullptr;
Button *take_screenshot_region_button_ptr = nullptr;
Button *show_hide_button_ptr = nullptr; Button *show_hide_button_ptr = nullptr;
ConfigHotkey configure_config_hotkey; ConfigHotkey configure_config_hotkey;

View File

@@ -140,6 +140,7 @@ namespace gsr {
replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT}; replay_config.save_hotkey = {mgl::Keyboard::F10, HOTKEY_MOD_LALT};
screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::F1, HOTKEY_MOD_LALT}; screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::F1, HOTKEY_MOD_LALT};
screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::F2, HOTKEY_MOD_LALT};
main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT}; main_config.show_hide_hotkey = {mgl::Keyboard::Z, HOTKEY_MOD_LALT};
} }
@@ -262,7 +263,8 @@ namespace gsr {
{"screenshot.save_screenshot_in_game_folder", &config.screenshot_config.save_screenshot_in_game_folder}, {"screenshot.save_screenshot_in_game_folder", &config.screenshot_config.save_screenshot_in_game_folder},
{"screenshot.show_screenshot_saved_notifications", &config.screenshot_config.show_screenshot_saved_notifications}, {"screenshot.show_screenshot_saved_notifications", &config.screenshot_config.show_screenshot_saved_notifications},
{"screenshot.save_directory", &config.screenshot_config.save_directory}, {"screenshot.save_directory", &config.screenshot_config.save_directory},
{"screenshot.take_screenshot_hotkey", &config.screenshot_config.take_screenshot_hotkey} {"screenshot.take_screenshot_hotkey", &config.screenshot_config.take_screenshot_hotkey},
{"screenshot.take_screenshot_region_hotkey", &config.screenshot_config.take_screenshot_region_hotkey}
}; };
} }

View File

@@ -318,6 +318,13 @@ namespace gsr {
fprintf(stderr, "pressed %s\n", id.c_str()); fprintf(stderr, "pressed %s\n", id.c_str());
overlay->take_screenshot(); overlay->take_screenshot();
}); });
global_hotkeys->bind_key_press(
config_hotkey_to_hotkey(overlay->get_config().screenshot_config.take_screenshot_region_hotkey),
"take_screenshot_region", [overlay](const std::string &id) {
fprintf(stderr, "pressed %s\n", id.c_str());
overlay->take_screenshot_region();
});
} }
static std::unique_ptr<GlobalHotkeysLinux> register_linux_hotkeys(Overlay *overlay, GlobalHotkeysLinux::GrabType grab_type) { static std::unique_ptr<GlobalHotkeysLinux> register_linux_hotkeys(Overlay *overlay, GlobalHotkeysLinux::GrabType grab_type) {
@@ -1311,7 +1318,11 @@ namespace gsr {
} }
void Overlay::take_screenshot() { void Overlay::take_screenshot() {
on_press_take_screenshot(false); on_press_take_screenshot(false, false);
}
void Overlay::take_screenshot_region() {
on_press_take_screenshot(false, true);
} }
static const char* notification_type_to_string(NotificationType notification_type) { static const char* notification_type_to_string(NotificationType notification_type) {
@@ -2300,7 +2311,7 @@ namespace gsr {
show_notification("Streaming has started", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM); show_notification("Streaming has started", notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::STREAM);
} }
void Overlay::on_press_take_screenshot(bool finished_region_selection) { void Overlay::on_press_take_screenshot(bool finished_region_selection, bool force_region_capture) {
if(region_selector.is_started()) if(region_selector.is_started())
return; return;
@@ -2309,18 +2320,20 @@ namespace gsr {
return; return;
} }
if(!validate_capture_target(gsr_info, config.screenshot_config.record_area_option)) { 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();
if(!validate_capture_target(gsr_info, record_area_option)) {
char err_msg[256]; 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", config.screenshot_config.record_area_option.c_str()); snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", record_area_option);
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, 0), mgl::Color(255, 0, 0, 0), NotificationType::SCREENSHOT);
return; return;
} }
if(config.screenshot_config.record_area_option == "region" && !finished_region_selection) { if(region_capture && !finished_region_selection) {
start_region_capture = true; start_region_capture = true;
on_region_selected = [this]() { 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 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); on_press_take_screenshot(true, force_region_capture);
}; };
return; return;
} }
@@ -2329,7 +2342,7 @@ namespace gsr {
const std::string output_file = config.screenshot_config.save_directory + "/Screenshot_" + get_date_str() + "." + config.screenshot_config.image_format; // TODO: Validate image format const std::string output_file = config.screenshot_config.save_directory + "/Screenshot_" + get_date_str() + "." + config.screenshot_config.image_format; // TODO: Validate image format
std::vector<const char*> args = { std::vector<const char*> args = {
"gpu-screen-recorder", "-w", config.screenshot_config.record_area_option.c_str(), "gpu-screen-recorder", "-w", record_area_option,
"-cursor", config.screenshot_config.record_cursor ? "yes" : "no", "-cursor", config.screenshot_config.record_cursor ? "yes" : "no",
"-v", "no", "-v", "no",
"-q", config.screenshot_config.image_quality.c_str(), "-q", config.screenshot_config.image_quality.c_str(),
@@ -2350,7 +2363,7 @@ namespace gsr {
} }
char region_str[128]; char region_str[128];
if(config.screenshot_config.record_area_option == "region") if(region_capture)
add_region_command(args, region_str, sizeof(region_str), region_selector); add_region_command(args, region_str, sizeof(region_str), region_selector);
args.push_back(nullptr); args.push_back(nullptr);

View File

@@ -300,6 +300,21 @@ namespace gsr {
return list; return list;
} }
std::unique_ptr<List> GlobalSettingsPage::create_screenshot_region_hotkey_options() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Take a screenshot of a region:", get_color_theme().text_color));
auto take_screenshot_region_button = std::make_unique<Button>(&get_theme().body_font, "", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120));
take_screenshot_region_button_ptr = take_screenshot_region_button.get();
list->add_widget(std::move(take_screenshot_region_button));
take_screenshot_region_button_ptr->on_click = [this] {
configure_hotkey_start(ConfigureHotkeyType::TAKE_SCREENSHOT_REGION);
};
return list;
}
std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() { std::unique_ptr<List> GlobalSettingsPage::create_hotkey_control_buttons() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER); auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
@@ -311,6 +326,7 @@ namespace gsr {
config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0}; config.replay_config.start_stop_hotkey = {mgl::Keyboard::Unknown, 0};
config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0}; config.replay_config.save_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0}; config.screenshot_config.take_screenshot_hotkey = {mgl::Keyboard::Unknown, 0};
config.screenshot_config.take_screenshot_region_hotkey = {mgl::Keyboard::Unknown, 0};
config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0}; config.main_config.show_hide_hotkey = {mgl::Keyboard::Unknown, 0};
load_hotkeys(); load_hotkeys();
overlay->rebind_all_keyboard_hotkeys(); overlay->rebind_all_keyboard_hotkeys();
@@ -351,6 +367,7 @@ namespace gsr {
list_ptr->add_widget(create_record_hotkey_options()); list_ptr->add_widget(create_record_hotkey_options());
list_ptr->add_widget(create_stream_hotkey_options()); list_ptr->add_widget(create_stream_hotkey_options());
list_ptr->add_widget(create_screenshot_hotkey_options()); list_ptr->add_widget(create_screenshot_hotkey_options());
list_ptr->add_widget(create_screenshot_region_hotkey_options());
list_ptr->add_widget(create_hotkey_control_buttons()); list_ptr->add_widget(create_hotkey_control_buttons());
return subsection; return subsection;
} }
@@ -470,6 +487,7 @@ namespace gsr {
start_stop_streaming_button_ptr->set_text(config.streaming_config.start_stop_hotkey.to_string()); start_stop_streaming_button_ptr->set_text(config.streaming_config.start_stop_hotkey.to_string());
take_screenshot_button_ptr->set_text(config.screenshot_config.take_screenshot_hotkey.to_string()); 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());
show_hide_button_ptr->set_text(config.main_config.show_hide_hotkey.to_string()); show_hide_button_ptr->set_text(config.main_config.show_hide_hotkey.to_string());
} }
@@ -547,6 +565,8 @@ namespace gsr {
return start_stop_streaming_button_ptr; return start_stop_streaming_button_ptr;
case ConfigureHotkeyType::TAKE_SCREENSHOT: case ConfigureHotkeyType::TAKE_SCREENSHOT:
return take_screenshot_button_ptr; return take_screenshot_button_ptr;
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
return take_screenshot_region_button_ptr;
case ConfigureHotkeyType::SHOW_HIDE: case ConfigureHotkeyType::SHOW_HIDE:
return show_hide_button_ptr; return show_hide_button_ptr;
} }
@@ -569,6 +589,8 @@ namespace gsr {
return &config.streaming_config.start_stop_hotkey; return &config.streaming_config.start_stop_hotkey;
case ConfigureHotkeyType::TAKE_SCREENSHOT: case ConfigureHotkeyType::TAKE_SCREENSHOT:
return &config.screenshot_config.take_screenshot_hotkey; return &config.screenshot_config.take_screenshot_hotkey;
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
return &config.screenshot_config.take_screenshot_region_hotkey;
case ConfigureHotkeyType::SHOW_HIDE: case ConfigureHotkeyType::SHOW_HIDE:
return &config.main_config.show_hide_hotkey; return &config.main_config.show_hide_hotkey;
} }
@@ -583,6 +605,7 @@ namespace gsr {
&config.record_config.pause_unpause_hotkey, &config.record_config.pause_unpause_hotkey,
&config.streaming_config.start_stop_hotkey, &config.streaming_config.start_stop_hotkey,
&config.screenshot_config.take_screenshot_hotkey, &config.screenshot_config.take_screenshot_hotkey,
&config.screenshot_config.take_screenshot_region_hotkey,
&config.main_config.show_hide_hotkey &config.main_config.show_hide_hotkey
}; };
for(ConfigHotkey *config_hotkey : config_hotkeys) { for(ConfigHotkey *config_hotkey : config_hotkeys) {
@@ -622,6 +645,9 @@ namespace gsr {
case ConfigureHotkeyType::TAKE_SCREENSHOT: case ConfigureHotkeyType::TAKE_SCREENSHOT:
hotkey_configure_action_name = "Take a screenshot"; hotkey_configure_action_name = "Take a screenshot";
break; break;
case ConfigureHotkeyType::TAKE_SCREENSHOT_REGION:
hotkey_configure_action_name = "Take a screenshot of a region";
break;
case ConfigureHotkeyType::SHOW_HIDE: case ConfigureHotkeyType::SHOW_HIDE:
hotkey_configure_action_name = "Show/hide UI"; hotkey_configure_action_name = "Show/hide UI";
break; break;

View File

@@ -370,7 +370,7 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_video_bitrate() { std::unique_ptr<List> SettingsPage::create_video_bitrate() {
auto video_bitrate_list = std::make_unique<List>(List::Orientation::VERTICAL); auto video_bitrate_list = std::make_unique<List>(List::Orientation::VERTICAL);
video_bitrate_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video bitrate (kbps):", get_color_theme().text_color)); video_bitrate_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Video bitrate (Kbps):", get_color_theme().text_color));
video_bitrate_list->add_widget(create_video_bitrate_entry()); video_bitrate_list->add_widget(create_video_bitrate_entry());
video_bitrate_list_ptr = video_bitrate_list.get(); video_bitrate_list_ptr = video_bitrate_list.get();
return video_bitrate_list; return video_bitrate_list;

View File

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

View File

@@ -44,13 +44,22 @@ static void usage(void) {
printf("Run commands on the running gsr-ui instance.\n"); printf("Run commands on the running gsr-ui instance.\n");
printf("\n"); printf("\n");
printf("COMMANDS:\n"); printf("COMMANDS:\n");
printf(" toggle-show Show/hide the UI.\n"); printf(" toggle-show\n");
printf(" toggle-record Start/stop recording.\n"); printf(" Show/hide the UI.\n");
printf(" toggle-pause Pause/unpause recording. Only applies to regular recording.\n"); printf(" toggle-record\n");
printf(" toggle-stream Start/stop streaming.\n"); printf(" Start/stop recording.\n");
printf(" toggle-replay Start/stop replay.\n"); printf(" toggle-pause\n");
printf(" replay-save Save replay.\n"); printf(" Pause/unpause recording. Only applies to regular recording.\n");
printf(" take-screenshot Take a screenshot.\n"); printf(" toggle-stream\n");
printf(" Start/stop streaming.\n");
printf(" toggle-replay\n");
printf(" Start/stop replay.\n");
printf(" replay-save\n");
printf(" Save replay.\n");
printf(" take-screenshot\n");
printf(" Take a screenshot.\n");
printf(" take-screenshot-region\n");
printf(" Take a screenshot of a region.\n");
printf("\n"); printf("\n");
printf("EXAMPLES:\n"); printf("EXAMPLES:\n");
printf(" gsr-ui-cli toggle-show\n"); printf(" gsr-ui-cli toggle-show\n");
@@ -67,6 +76,7 @@ static bool is_valid_command(const char *command) {
"toggle-replay", "toggle-replay",
"replay-save", "replay-save",
"take-screenshot", "take-screenshot",
"take-screenshot-region",
NULL NULL
}; };