aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Config.cpp2
-rw-r--r--src/CursorTracker/CursorTrackerWayland.cpp34
-rw-r--r--src/GlobalHotkeys/GlobalHotkeysJoystick.cpp13
-rw-r--r--src/Overlay.cpp323
-rw-r--r--src/Process.cpp21
-rw-r--r--src/RegionSelector.cpp6
-rw-r--r--src/Utils.cpp22
-rw-r--r--src/WindowSelector.cpp229
-rw-r--r--src/WindowUtils.cpp25
-rw-r--r--src/gui/ScreenshotSettingsPage.cpp16
-rw-r--r--src/gui/SettingsPage.cpp24
-rw-r--r--src/main.cpp5
12 files changed, 528 insertions, 192 deletions
diff --git a/src/Config.cpp b/src/Config.cpp
index 9e10534..313cd38 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -119,7 +119,7 @@ namespace gsr {
streaming_config.record_options.video_quality = "custom";
streaming_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
- streaming_config.record_options.video_bitrate = 15000;
+ streaming_config.record_options.video_bitrate = 8000;
record_config.save_directory = default_videos_save_directory;
record_config.record_options.audio_tracks_list.push_back({std::vector<std::string>{"default_output"}, false});
diff --git a/src/CursorTracker/CursorTrackerWayland.cpp b/src/CursorTracker/CursorTrackerWayland.cpp
index b28b978..7af86b4 100644
--- a/src/CursorTracker/CursorTrackerWayland.cpp
+++ b/src/CursorTracker/CursorTrackerWayland.cpp
@@ -9,14 +9,8 @@
namespace gsr {
static const int MAX_CONNECTORS = 32;
- static const int CONNECTOR_TYPE_COUNTS = 32;
static const uint32_t plane_property_all = 0xF;
- typedef struct {
- int type;
- int count;
- } drm_connector_type_count;
-
typedef enum {
PLANE_PROPERTY_CRTC_X = 1 << 0,
PLANE_PROPERTY_CRTC_Y = 1 << 1,
@@ -105,22 +99,6 @@ namespace gsr {
return get_drm_property_by_name(drm_fd, &properties, name, result);
}
- static drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) {
- for(int i = 0; i < *num_type_counts; ++i) {
- if(type_counts[i].type == connector_type)
- return &type_counts[i];
- }
-
- if(*num_type_counts == CONNECTOR_TYPE_COUNTS)
- return NULL;
-
- const int index = *num_type_counts;
- type_counts[index].type = connector_type;
- type_counts[index].count = 0;
- ++*num_type_counts;
- return &type_counts[index];
- }
-
// Note: this monitor name logic is kept in sync with gpu screen recorder
static std::string get_monitor_name_from_crtc_id(int drm_fd, uint32_t crtc_id) {
std::string result;
@@ -128,27 +106,23 @@ namespace gsr {
if(!resources)
return result;
- drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS];
- int num_type_counts = 0;
-
for(int i = 0; i < resources->count_connectors; ++i) {
uint64_t connector_crtc_id = 0;
drmModeConnectorPtr connector = drmModeGetConnectorCurrent(drm_fd, resources->connectors[i]);
if(!connector)
continue;
- drm_connector_type_count *connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector->connector_type);
const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type);
- if(connector_type)
- ++connector_type->count;
+ if(!connection_name)
+ goto next;
if(connector->connection != DRM_MODE_CONNECTED)
goto next;
- if(connector_type && connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &connector_crtc_id) && connector_crtc_id == crtc_id) {
+ if(connector_get_property_by_name(drm_fd, connector, "CRTC_ID", &connector_crtc_id) && connector_crtc_id == crtc_id) {
result = connection_name;
result += "-";
- result += std::to_string(connector_type->count);
+ result += std::to_string(connector->connector_type_id);
drmModeFreeConnector(connector);
break;
}
diff --git a/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp
index 23f8a20..5969438 100644
--- a/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp
+++ b/src/GlobalHotkeys/GlobalHotkeysJoystick.cpp
@@ -11,6 +11,8 @@ namespace gsr {
static constexpr int triangle_button = 2;
static constexpr int options_button = 9;
static constexpr int playstation_button = 10;
+ static constexpr int l3_button = 11;
+ static constexpr int r3_button = 12;
static constexpr int axis_up_down = 7;
static constexpr int axis_left_right = 6;
@@ -266,7 +268,8 @@ namespace gsr {
if((event.type & JS_EVENT_BUTTON) == JS_EVENT_BUTTON) {
switch(event.number) {
case playstation_button: {
- playstation_button_pressed = event.value == button_pressed;
+ // Workaround weird steam input (in-game) behavior where steam triggers playstation button + options when pressing both l3 and r3 at the same time
+ playstation_button_pressed = (event.value == button_pressed) && !l3_button_pressed && !r3_button_pressed;
break;
}
case options_button: {
@@ -284,6 +287,14 @@ namespace gsr {
save_10_min_replay = true;
break;
}
+ case l3_button: {
+ l3_button_pressed = event.value == button_pressed;
+ break;
+ }
+ case r3_button: {
+ r3_button_pressed = event.value == button_pressed;
+ break;
+ }
}
} else if((event.type & JS_EVENT_AXIS) == JS_EVENT_AXIS && playstation_button_pressed) {
const int trigger_threshold = 16383;
diff --git a/src/Overlay.cpp b/src/Overlay.cpp
index 8a60b91..2d9b275 100644
--- a/src/Overlay.cpp
+++ b/src/Overlay.cpp
@@ -26,6 +26,7 @@
#include <malloc.h>
#include <stdexcept>
#include <algorithm>
+#include <inttypes.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -272,6 +273,33 @@ namespace gsr {
return true;
}
+ static bool is_hyprland_waybar_running_as_dock() {
+ const char *args[] = { "hyprctl", "layers", nullptr };
+ std::string stdout_str;
+ if(exec_program_on_host_get_stdout(args, stdout_str) != 0)
+ return false;
+
+ int waybar_layer_level = -1;
+ int current_layer_level = 0;
+ string_split_char(stdout_str, '\n', [&](const std::string_view line) {
+ if(line.find("Layer level 0") != std::string_view::npos)
+ current_layer_level = 0;
+ else if(line.find("Layer level 1") != std::string_view::npos)
+ current_layer_level = 1;
+ else if(line.find("Layer level 2") != std::string_view::npos)
+ current_layer_level = 2;
+ else if(line.find("Layer level 3") != std::string_view::npos)
+ current_layer_level = 3;
+ else if(line.find("namespace: waybar") != std::string_view::npos) {
+ waybar_layer_level = current_layer_level;
+ return false;
+ }
+ return true;
+ });
+
+ return waybar_layer_level >= 0 && waybar_layer_level <= 1;
+ }
+
static Hotkey config_hotkey_to_hotkey(ConfigHotkey config_hotkey) {
return {
(uint32_t)mgl::Keyboard::key_to_x11_keysym((mgl::Keyboard::Key)config_hotkey.key),
@@ -678,6 +706,22 @@ namespace gsr {
on_region_selected = nullptr;
}
+ window_selector.poll_events();
+ if(window_selector.take_canceled()) {
+ on_window_selected = nullptr;
+ } else if(window_selector.take_selection() && on_window_selected) {
+ mgl_context *context = mgl_get_context();
+ Display *display = (Display*)context->connection;
+
+ const Window selected_window = window_selector.get_selection();
+ if(selected_window && selected_window != DefaultRootWindow(display)) {
+ on_window_selected();
+ } else {
+ show_notification("No window selected", notification_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
+ }
+ on_window_selected = nullptr;
+ }
+
if(!visible || !window)
return;
@@ -723,7 +767,16 @@ namespace gsr {
}
}
- if(region_selector.is_started()) {
+ if(start_window_capture) {
+ start_window_capture = false;
+ hide();
+ if(!window_selector.start(get_color_theme().tint_color)) {
+ show_notification("Failed to start window capture", notification_error_timeout_seconds, mgl::Color(255, 0, 0), mgl::Color(255, 0, 0), NotificationType::NONE);
+ on_window_selected = nullptr;
+ }
+ }
+
+ if(region_selector.is_started() || window_selector.is_started()) {
usleep(5 * 1000); // 5 ms
return true;
}
@@ -857,7 +910,7 @@ namespace gsr {
if(visible)
return;
- if(region_selector.is_started())
+ if(region_selector.is_started() || window_selector.is_started())
return;
drawn_first_frame = false;
@@ -878,6 +931,8 @@ namespace gsr {
const std::string wm_name = get_window_manager_name(display);
const bool is_kwin = wm_name == "KWin";
const bool is_wlroots = wm_name.find("wlroots") != std::string::npos;
+ const bool is_hyprland = wm_name.find("Hyprland") != std::string::npos;
+ const bool hyprland_waybar_is_dock = is_hyprland && is_hyprland_waybar_running_as_dock();
std::optional<CursorInfo> cursor_info;
if(cursor_tracker) {
@@ -1009,7 +1064,7 @@ namespace gsr {
// Owlboy seems to use xi events and XGrabPointer doesn't prevent owlboy from receiving events.
xi_grab_all_mouse_devices(xi_display);
- if(!is_wlroots)
+ if(!is_wlroots && !hyprland_waybar_is_dock)
window->set_fullscreen(true);
visible = true;
@@ -1313,6 +1368,7 @@ namespace gsr {
visible = false;
drawn_first_frame = false;
start_region_capture = false;
+ start_window_capture = false;
if(xi_input_xev) {
free(xi_input_xev);
@@ -1435,6 +1491,24 @@ namespace gsr {
return nullptr;
}
+ static void truncate_string(std::string &str, int max_length) {
+ int index = 0;
+ size_t byte_index = 0;
+
+ while(index < max_length && byte_index < str.size()) {
+ uint32_t codepoint = 0;
+ size_t codepoint_length = 0;
+ mgl::utf8_decode((const unsigned char*)str.c_str() + byte_index, str.size() - byte_index, &codepoint, &codepoint_length);
+ if(codepoint_length == 0)
+ codepoint_length = 1;
+
+ index += 1;
+ byte_index += codepoint_length;
+ }
+
+ str.erase(byte_index);
+ }
+
static bool is_hex_num(char c) {
return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f') || (c >= '0' && c <= '9');
}
@@ -1462,8 +1536,44 @@ namespace gsr {
return is_hex && !hex_start;
}
+ static bool is_number(const char *str) {
+ const char *p = str;
+ while(*p) {
+ char c = *p;
+ if(c < '0' || c > '9')
+ return false;
+ ++p;
+ }
+ return true;
+ }
+
static bool is_capture_target_monitor(const char *capture_target) {
- return strcmp(capture_target, "focused") != 0 && strcmp(capture_target, "region") != 0 && strcmp(capture_target, "portal") != 0 && contains_non_hex_number(capture_target);
+ 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) {
+ std::string result;
+ if(is_capture_target_monitor(capture_target)) {
+ result = "this monitor";
+ } else if(is_number(capture_target)) {
+ mgl_context *context = mgl_get_context();
+ Display *display = (Display*)context->connection;
+
+ int64_t window_id = None;
+ sscanf(capture_target, "%" PRIi64, &window_id);
+
+ const std::optional<std::string> window_title = get_window_title(display, window_id);
+ if(window_title) {
+ result = strip(window_title.value());
+ truncate_string(result, 20);
+ result = "window \"" + result + "\"";
+ } else {
+ result = std::string("window ") + capture_target;
+ }
+ } else {
+ result = capture_target;
+ }
+ return result;
}
static std::string get_valid_monitor_x11(const std::string &target_monitor_name, const std::vector<Monitor> &monitors) {
@@ -1642,24 +1752,6 @@ namespace gsr {
return result;
}
- static void truncate_string(std::string &str, int max_length) {
- int index = 0;
- size_t byte_index = 0;
-
- while(index < max_length && byte_index < str.size()) {
- uint32_t codepoint = 0;
- size_t codepoint_length = 0;
- mgl::utf8_decode((const unsigned char*)str.c_str() + byte_index, str.size() - byte_index, &codepoint, &codepoint_length);
- if(codepoint_length == 0)
- codepoint_length = 1;
-
- index += 1;
- byte_index += codepoint_length;
- }
-
- str.erase(byte_index);
- }
-
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;
@@ -1689,11 +1781,7 @@ namespace gsr {
if(!config.record_config.show_video_saved_notifications)
return;
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a recording of this monitor to \"%s\"", focused_window_name.c_str());
- else
- snprintf(msg, sizeof(msg), "Saved a recording of %s to \"%s\"", recording_capture_target.c_str(), focused_window_name.c_str());
-
+ 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());
capture_target = recording_capture_target.c_str();
break;
}
@@ -1707,11 +1795,7 @@ namespace gsr {
else
snprintf(duration, sizeof(duration), " ");
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor to \"%s\"", duration, focused_window_name.c_str());
- else
- snprintf(msg, sizeof(msg), "Saved a%sreplay of %s to \"%s\"", duration, recording_capture_target.c_str(), focused_window_name.c_str());
-
+ 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());
capture_target = recording_capture_target.c_str();
break;
}
@@ -1719,11 +1803,7 @@ namespace gsr {
if(!config.screenshot_config.show_screenshot_saved_notifications)
return;
- if(is_capture_target_monitor(screenshot_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor to \"%s\"", focused_window_name.c_str());
- else
- snprintf(msg, sizeof(msg), "Saved a screenshot of %s to \"%s\"", screenshot_capture_target.c_str(), focused_window_name.c_str());
-
+ 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());
capture_target = screenshot_capture_target.c_str();
break;
}
@@ -1756,10 +1836,7 @@ namespace gsr {
snprintf(duration, sizeof(duration), " ");
char msg[512];
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a%sreplay of this monitor", duration);
- else
- snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, recording_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Saved a%sreplay of %s", duration, capture_target_get_notification_name(recording_capture_target.c_str()).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());
}
}
@@ -1880,10 +1957,7 @@ 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];
- if(is_capture_target_monitor(screenshot_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a screenshot of this monitor");
- else
- snprintf(msg, sizeof(msg), "Saved a screenshot of %s", screenshot_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Saved a screenshot of %s", capture_target_get_notification_name(screenshot_capture_target.c_str()).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 {
@@ -1982,10 +2056,7 @@ namespace gsr {
save_video_in_current_game_directory(video_filepath.c_str(), NotificationType::RECORD);
} else if(config.record_config.show_video_saved_notifications) {
char msg[512];
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Saved a recording of this monitor");
- else
- snprintf(msg, sizeof(msg), "Saved a recording of %s", recording_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Saved a recording of %s", capture_target_get_notification_name(recording_capture_target.c_str()).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 {
@@ -2178,11 +2249,12 @@ namespace gsr {
}
static bool validate_capture_target(const std::string &capture_target, const SupportedCaptureOptions &capture_options) {
- // TODO: Also check x11 window when enabled (check if capture_target is a decminal/hex number)
- if(capture_target == "region") {
- return capture_options.region;
+ if(capture_target == "window") {
+ return capture_options.window;
} else if(capture_target == "focused") {
return capture_options.focused;
+ } else if(capture_target == "region") {
+ return capture_options.region;
} else if(capture_target == "portal") {
return capture_options.portal;
} else if(capture_target == "focused_monitor") {
@@ -2214,7 +2286,9 @@ namespace gsr {
}
std::string Overlay::get_capture_target(const std::string &capture_target, const SupportedCaptureOptions &capture_options) {
- if(capture_target == "focused_monitor") {
+ if(capture_target == "window") {
+ return std::to_string(window_selector.get_selection());
+ } else if(capture_target == "focused_monitor") {
std::optional<CursorInfo> cursor_info;
if(cursor_tracker) {
cursor_tracker->update();
@@ -2283,8 +2357,50 @@ namespace gsr {
kill(gpu_screen_recorder_process, SIGRTMIN+5);
}
- bool Overlay::on_press_start_replay(bool disable_notification, bool finished_region_selection) {
- if(region_selector.is_started())
+ static const char* switch_video_codec_to_usable_hardware_encoder(const GsrInfo &gsr_info) {
+ if(gsr_info.supported_video_codecs.h264)
+ return "h264";
+ else if(gsr_info.supported_video_codecs.hevc)
+ return "hevc";
+ else if(gsr_info.supported_video_codecs.av1)
+ return "av1";
+ else if(gsr_info.supported_video_codecs.vp8)
+ return "vp8";
+ else if(gsr_info.supported_video_codecs.vp9)
+ return "vp9";
+ return nullptr;
+ }
+
+ static const char* change_container_if_codec_not_supported(const char *video_codec, const char *container) {
+ if(strcmp(video_codec, "vp8") == 0 || strcmp(video_codec, "vp9") == 0) {
+ if(strcmp(container, "webm") != 0 && strcmp(container, "matroska") != 0) {
+ fprintf(stderr, "Warning: container '%s' is not compatible with video codec '%s', using webm container instead\n", container, video_codec);
+ return "webm";
+ }
+ } else if(strcmp(container, "webm") == 0) {
+ fprintf(stderr, "Warning: container webm is not compatible with video codec '%s', using mp4 container instead\n", video_codec);
+ return "mp4";
+ }
+ return container;
+ }
+
+ static void choose_video_codec_and_container_with_fallback(const GsrInfo &gsr_info, const char **video_codec, const char **container, const char **encoder) {
+ *encoder = "gpu";
+ if(strcmp(*video_codec, "h264_software") == 0) {
+ *video_codec = "h264";
+ *encoder = "cpu";
+ } else if(strcmp(*video_codec, "auto") == 0) {
+ *video_codec = switch_video_codec_to_usable_hardware_encoder(gsr_info);
+ if(!*video_codec) {
+ *video_codec = "h264";
+ *encoder = "cpu";
+ }
+ }
+ *container = change_container_if_codec_not_supported(*video_codec, *container);
+ }
+
+ bool Overlay::on_press_start_replay(bool disable_notification, bool finished_selection) {
+ if(region_selector.is_started() || window_selector.is_started())
return false;
switch(recording_status) {
@@ -2334,7 +2450,7 @@ namespace gsr {
return false;
}
- if(config.replay_config.record_options.record_area_option == "region" && !finished_region_selection) {
+ if(config.replay_config.record_options.record_area_option == "region" && !finished_selection) {
start_region_capture = true;
on_region_selected = [disable_notification, this]() {
on_press_start_replay(disable_notification, true);
@@ -2342,6 +2458,14 @@ namespace gsr {
return false;
}
+ if(config.replay_config.record_options.record_area_option == "window" && !finished_selection) {
+ start_window_capture = true;
+ on_window_selected = [disable_notification, this]() {
+ on_press_start_replay(disable_notification, true);
+ };
+ return false;
+ }
+
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.replay_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.replay_config.record_options.video_bitrate);
@@ -2349,12 +2473,10 @@ namespace gsr {
const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.replay_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.replay_config.record_options.framerate_mode == "auto" ? "vfr" : config.replay_config.record_options.framerate_mode;
const std::string replay_time = std::to_string(config.replay_config.replay_time);
+ const char *container = config.replay_config.container.c_str();
const char *video_codec = config.replay_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
- if(strcmp(video_codec, "h264_software") == 0) {
- video_codec = "h264";
- encoder = "cpu";
- }
+ choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
char size[64];
size[0] = '\0';
@@ -2366,7 +2488,7 @@ namespace gsr {
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
- "-c", config.replay_config.container.c_str(),
+ "-c", container,
"-ac", config.replay_config.record_options.audio_codec.c_str(),
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
"-cr", config.replay_config.record_options.color_range.c_str(),
@@ -2421,18 +2543,15 @@ namespace gsr {
// to see when the program has exit.
if(!disable_notification && config.replay_config.show_replay_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Started replaying this monitor");
- else
- snprintf(msg, sizeof(msg), "Started replaying %s", recording_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started replaying %s", capture_target_get_notification_name(recording_capture_target.c_str()).c_str());
show_notification(msg, notification_timeout_seconds, get_color_theme().tint_color, get_color_theme().tint_color, NotificationType::REPLAY, recording_capture_target.c_str());
}
return true;
}
- void Overlay::on_press_start_record(bool finished_region_selection) {
- if(region_selector.is_started())
+ void Overlay::on_press_start_record(bool finished_selection) {
+ if(region_selector.is_started() || window_selector.is_started())
return;
switch(recording_status) {
@@ -2508,7 +2627,7 @@ namespace gsr {
return;
}
- if(config.record_config.record_options.record_area_option == "region" && !finished_region_selection) {
+ if(config.record_config.record_options.record_area_option == "region" && !finished_selection) {
start_region_capture = true;
on_region_selected = [this]() {
on_press_start_record(true);
@@ -2516,6 +2635,14 @@ namespace gsr {
return;
}
+ if(config.record_config.record_options.record_area_option == "window" && !finished_selection) {
+ start_window_capture = true;
+ on_window_selected = [this]() {
+ on_press_start_record(true);
+ };
+ return;
+ }
+
record_filepath.clear();
// TODO: Validate input, fallback to valid values
@@ -2524,12 +2651,10 @@ namespace gsr {
const std::string output_file = config.record_config.save_directory + "/Video_" + get_date_str() + "." + container_to_file_extension(config.record_config.container.c_str());
const std::vector<std::string> audio_tracks = create_audio_tracks_cli_args(config.record_config.record_options.audio_tracks_list, gsr_info);
const std::string framerate_mode = config.record_config.record_options.framerate_mode == "auto" ? "vfr" : config.record_config.record_options.framerate_mode;
+ const char *container = config.record_config.container.c_str();
const char *video_codec = config.record_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
- if(strcmp(video_codec, "h264_software") == 0) {
- video_codec = "h264";
- encoder = "cpu";
- }
+ choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
char size[64];
size[0] = '\0';
@@ -2541,7 +2666,7 @@ namespace gsr {
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
- "-c", config.record_config.container.c_str(),
+ "-c", container,
"-ac", config.record_config.record_options.audio_codec.c_str(),
"-cursor", config.record_config.record_options.record_cursor ? "yes" : "no",
"-cr", config.record_config.record_options.color_range.c_str(),
@@ -2578,10 +2703,7 @@ namespace gsr {
// 1...
if(config.record_config.show_recording_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Started recording this monitor");
- else
- snprintf(msg, sizeof(msg), "Started recording %s", recording_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started recording %s", capture_target_get_notification_name(recording_capture_target.c_str()).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());
}
}
@@ -2621,8 +2743,8 @@ namespace gsr {
return url;
}
- void Overlay::on_press_start_stream(bool finished_region_selection) {
- if(region_selector.is_started())
+ void Overlay::on_press_start_stream(bool finished_selection) {
+ if(region_selector.is_started() || window_selector.is_started())
return;
switch(recording_status) {
@@ -2668,7 +2790,7 @@ namespace gsr {
return;
}
- if(config.streaming_config.record_options.record_area_option == "region" && !finished_region_selection) {
+ if(config.streaming_config.record_options.record_area_option == "region" && !finished_selection) {
start_region_capture = true;
on_region_selected = [this]() {
on_press_start_stream(true);
@@ -2676,6 +2798,14 @@ namespace gsr {
return;
}
+ if(config.streaming_config.record_options.record_area_option == "window" && !finished_selection) {
+ start_window_capture = true;
+ on_window_selected = [this]() {
+ on_press_start_stream(true);
+ };
+ return;
+ }
+
// TODO: Validate input, fallback to valid values
const std::string fps = std::to_string(config.streaming_config.record_options.fps);
const std::string video_bitrate = std::to_string(config.streaming_config.record_options.video_bitrate);
@@ -2685,16 +2815,12 @@ namespace gsr {
if(audio_tracks.size() > 1)
audio_tracks.resize(1);
const std::string framerate_mode = config.streaming_config.record_options.framerate_mode == "auto" ? "vfr" : config.streaming_config.record_options.framerate_mode;
+ const char *container = "flv";
+ if(config.streaming_config.streaming_service == "custom")
+ container = config.streaming_config.custom.container.c_str();
const char *video_codec = config.streaming_config.record_options.video_codec.c_str();
const char *encoder = "gpu";
- if(strcmp(video_codec, "h264_software") == 0) {
- video_codec = "h264";
- encoder = "cpu";
- }
-
- std::string container = "flv";
- if(config.streaming_config.streaming_service == "custom")
- container = config.streaming_config.custom.container;
+ choose_video_codec_and_container_with_fallback(gsr_info, &video_codec, &container, &encoder);
const std::string url = streaming_get_url(config);
@@ -2708,7 +2834,7 @@ namespace gsr {
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", recording_capture_target.c_str(),
- "-c", container.c_str(),
+ "-c", container,
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
"-cr", config.streaming_config.record_options.color_range.c_str(),
@@ -2751,16 +2877,13 @@ namespace gsr {
// to see when the program has exit.
if(config.streaming_config.show_streaming_started_notifications) {
char msg[256];
- if(is_capture_target_monitor(recording_capture_target.c_str()))
- snprintf(msg, sizeof(msg), "Started streaming this monitor");
- else
- snprintf(msg, sizeof(msg), "Started streaming %s", recording_capture_target.c_str());
+ snprintf(msg, sizeof(msg), "Started streaming %s", capture_target_get_notification_name(recording_capture_target.c_str()).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());
}
}
- void Overlay::on_press_take_screenshot(bool finished_region_selection, bool force_region_capture) {
- if(region_selector.is_started())
+ void Overlay::on_press_take_screenshot(bool finished_selection, bool force_region_capture) {
+ if(region_selector.is_started() || window_selector.is_started())
return;
if(gpu_screen_recorder_screenshot_process > 0) {
@@ -2779,7 +2902,7 @@ namespace gsr {
return;
}
- if(region_capture && !finished_region_selection) {
+ if(region_capture && !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
@@ -2788,6 +2911,14 @@ namespace gsr {
return;
}
+ if(config.screenshot_config.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);
+ };
+ return;
+ }
+
// TODO: Validate input, fallback to valid values
const std::string output_file = config.screenshot_config.save_directory + "/Screenshot_" + get_date_str() + "." + config.screenshot_config.image_format; // TODO: Validate image format
diff --git a/src/Process.cpp b/src/Process.cpp
index 45be208..c02753a 100644
--- a/src/Process.cpp
+++ b/src/Process.cpp
@@ -176,11 +176,21 @@ namespace gsr {
}
}
+ static const char *get_basename(const char *path, int size) {
+ for(int i = size - 1; i >= 0; --i) {
+ if(path[i] == '/')
+ return path + i + 1;
+ }
+ return path;
+ }
+
// |output_buffer| should be at least PATH_MAX in size
bool read_cmdline_arg0(const char *filepath, char *output_buffer, int output_buffer_size) {
output_buffer[0] = '\0';
+ const char *arg0_start = NULL;
const char *arg0_end = NULL;
+ int arg0_size = 0;
int fd = open(filepath, O_RDONLY);
if(fd == -1)
return false;
@@ -190,13 +200,16 @@ namespace gsr {
if(bytes_read == -1)
goto err;
- arg0_end = (const char*)memchr(buffer, '\0', bytes_read);
+ arg0_start = buffer;
+ arg0_end = (const char*)memchr(arg0_start, '\0', bytes_read);
if(!arg0_end)
goto err;
- if((arg0_end - buffer) + 1 <= output_buffer_size) {
- memcpy(output_buffer, buffer, arg0_end - buffer);
- output_buffer[arg0_end - buffer] = '\0';
+ arg0_start = get_basename(arg0_start, arg0_end - arg0_start);
+ arg0_size = arg0_end - arg0_start;
+ if(arg0_size + 1 <= output_buffer_size) {
+ memcpy(output_buffer, arg0_start, arg0_size);
+ output_buffer[arg0_size] = '\0';
close(fd);
return true;
}
diff --git a/src/RegionSelector.cpp b/src/RegionSelector.cpp
index 5b7243b..89a0209 100644
--- a/src/RegionSelector.cpp
+++ b/src/RegionSelector.cpp
@@ -208,7 +208,7 @@ namespace gsr {
window_attr.background_pixel = is_wayland ? 0 : border_color_x11;
window_attr.border_pixel = 0;
window_attr.override_redirect = true;
- window_attr.event_mask = StructureNotifyMask | PointerMotionMask;
+ window_attr.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
window_attr.colormap = region_window_colormap;
Screen *screen = XDefaultScreenOfDisplay(dpy);
@@ -366,10 +366,6 @@ namespace gsr {
return true;
}
- bool RegionSelector::is_selected() const {
- return selected;
- }
-
bool RegionSelector::take_selection() {
const bool result = selected;
selected = false;
diff --git a/src/Utils.cpp b/src/Utils.cpp
index f23a330..c36a64a 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -32,6 +32,28 @@ namespace gsr {
return str.size() >= len && memcmp(str.data() + str.size() - len, substr, len) == 0;
}
+ std::string strip(const std::string &str) {
+ int start_index = 0;
+ int str_len = str.size();
+
+ for(int i = 0; i < str_len; ++i) {
+ if(str[i] != ' ') {
+ start_index += i;
+ str_len -= i;
+ break;
+ }
+ }
+
+ for(int i = str_len - 1; i >= 0; --i) {
+ if(str[i] != ' ') {
+ str_len = i + 1;
+ break;
+ }
+ }
+
+ return str.substr(start_index, str_len);
+ }
+
std::string get_home_dir() {
const char *home_dir = getenv("HOME");
if(!home_dir) {
diff --git a/src/WindowSelector.cpp b/src/WindowSelector.cpp
new file mode 100644
index 0000000..f04d600
--- /dev/null
+++ b/src/WindowSelector.cpp
@@ -0,0 +1,229 @@
+#include "../include/WindowSelector.hpp"
+#include "../include/WindowUtils.hpp"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <X11/extensions/shape.h>
+#include <X11/cursorfont.h>
+#include <X11/keysym.h>
+
+namespace gsr {
+ static const int rectangle_border_size = 2;
+
+ static int max_int(int a, int b) {
+ return a >= b ? a : b;
+ }
+
+ static void set_region_rectangle(Display *dpy, Window window, int x, int y, int width, int height, int border_size) {
+ if(width < 0) {
+ x += width;
+ width = abs(width);
+ }
+
+ if(height < 0) {
+ y += height;
+ height = abs(height);
+ }
+
+ XRectangle rectangles[] = {
+ {
+ (short)max_int(0, x), (short)max_int(0, y),
+ (unsigned short)max_int(0, border_size), (unsigned short)max_int(0, height)
+ }, // Left
+ {
+ (short)max_int(0, x + width - border_size), (short)max_int(0, y),
+ (unsigned short)max_int(0, border_size), (unsigned short)max_int(0, height)
+ }, // Right
+ {
+ (short)max_int(0, x + border_size), (short)max_int(0, y),
+ (unsigned short)max_int(0, width - border_size*2), (unsigned short)max_int(0, border_size)
+ }, // Top
+ {
+ (short)max_int(0, x + border_size), (short)max_int(0, y + height - border_size),
+ (unsigned short)max_int(0, width - border_size*2), (unsigned short)max_int(0, border_size)
+ }, // Bottom
+ };
+ XShapeCombineRectangles(dpy, window, ShapeBounding, 0, 0, rectangles, 4, ShapeSet, Unsorted);
+ XFlush(dpy);
+ }
+
+ static unsigned long mgl_color_to_x11_color(mgl::Color color) {
+ if(color.a == 0)
+ return 0;
+ return ((uint32_t)color.a << 24) | (((uint32_t)color.r * color.a / 0xFF) << 16) | (((uint32_t)color.g * color.a / 0xFF) << 8) | ((uint32_t)color.b * color.a / 0xFF);
+ }
+
+ static Window get_cursor_window(Display *dpy) {
+ Window root_window = None;
+ Window window = None;
+ int dummy_i;
+ unsigned int dummy_u;
+ mgl::vec2i root_pos;
+ XQueryPointer(dpy, DefaultRootWindow(dpy), &root_window, &window, &root_pos.x, &root_pos.y, &dummy_i, &dummy_i, &dummy_u);
+ return window;
+ }
+
+ static void get_window_geometry(Display *dpy, Window window, mgl::vec2i &pos, mgl::vec2i &size) {
+ Window root_window;
+ int x = 0;
+ int y = 0;
+ unsigned int w = 0;
+ unsigned int h = 0;
+ unsigned int dummy_border, dummy_depth;
+ XGetGeometry(dpy, window, &root_window, &x, &y, &w, &h, &dummy_border, &dummy_depth);
+ pos.x = x;
+ pos.y = y;
+ size.x = w;
+ size.y = h;
+ }
+
+ WindowSelector::WindowSelector() {
+
+ }
+
+ WindowSelector::~WindowSelector() {
+ stop();
+ }
+
+ bool WindowSelector::start(mgl::Color border_color) {
+ if(dpy)
+ return false;
+
+ const unsigned long border_color_x11 = mgl_color_to_x11_color(border_color);
+ dpy = XOpenDisplay(nullptr);
+ if(!dpy) {
+ fprintf(stderr, "Error: WindowSelector::start: failed to connect to the X11 server\n");
+ return false;
+ }
+
+ const Window cursor_window = get_cursor_window(dpy);
+ mgl::vec2i cursor_window_pos, cursor_window_size;
+ get_window_geometry(dpy, cursor_window, cursor_window_pos, cursor_window_size);
+
+ XVisualInfo vinfo;
+ memset(&vinfo, 0, sizeof(vinfo));
+ XMatchVisualInfo(dpy, DefaultScreen(dpy), 32, TrueColor, &vinfo);
+ border_window_colormap = XCreateColormap(dpy, DefaultRootWindow(dpy), vinfo.visual, AllocNone);
+
+ XSetWindowAttributes window_attr;
+ window_attr.background_pixel = border_color_x11;
+ window_attr.border_pixel = 0;
+ window_attr.override_redirect = true;
+ window_attr.event_mask = StructureNotifyMask | PointerMotionMask | ButtonPressMask | ButtonReleaseMask;
+ window_attr.colormap = border_window_colormap;
+
+ Screen *screen = XDefaultScreenOfDisplay(dpy);
+ border_window = XCreateWindow(dpy, DefaultRootWindow(dpy), 0, 0, XWidthOfScreen(screen), XHeightOfScreen(screen), 0,
+ vinfo.depth, InputOutput, vinfo.visual, CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWEventMask | CWColormap, &window_attr);
+ if(!border_window) {
+ fprintf(stderr, "Error: WindowSelector::start: failed to create region window\n");
+ stop();
+ return false;
+ }
+ set_window_size_not_resizable(dpy, border_window, XWidthOfScreen(screen), XHeightOfScreen(screen));
+ if(cursor_window && cursor_window != DefaultRootWindow(dpy))
+ set_region_rectangle(dpy, border_window, cursor_window_pos.x, cursor_window_pos.y, cursor_window_size.x, cursor_window_size.y, rectangle_border_size);
+ else
+ set_region_rectangle(dpy, border_window, 0, 0, 0, 0, 0);
+ make_window_click_through(dpy, border_window);
+ XMapWindow(dpy, border_window);
+
+ crosshair_cursor = XCreateFontCursor(dpy, XC_crosshair);
+ XGrabPointer(dpy, DefaultRootWindow(dpy), True, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, crosshair_cursor, CurrentTime);
+ XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, GrabModeAsync, CurrentTime);
+ XFlush(dpy);
+
+ selected = false;
+ canceled = false;
+ selected_window = None;
+ return true;
+ }
+
+ void WindowSelector::stop() {
+ if(!dpy)
+ return;
+
+ XUngrabPointer(dpy, CurrentTime);
+ XUngrabKeyboard(dpy, CurrentTime);
+
+ if(border_window_colormap) {
+ XFreeColormap(dpy, border_window_colormap);
+ border_window_colormap = 0;
+ }
+
+ if(border_window) {
+ XDestroyWindow(dpy, border_window);
+ border_window = 0;
+ }
+
+ if(crosshair_cursor) {
+ XFreeCursor(dpy, crosshair_cursor);
+ crosshair_cursor = None;
+ }
+
+ XFlush(dpy);
+ XCloseDisplay(dpy);
+ dpy = nullptr;
+ }
+
+ bool WindowSelector::is_started() const {
+ return dpy != nullptr;
+ }
+
+ bool WindowSelector::failed() const {
+ return !dpy;
+ }
+
+ bool WindowSelector::poll_events() {
+ if(!dpy || selected)
+ return false;
+
+ XEvent xev;
+ while(XPending(dpy)) {
+ XNextEvent(dpy, &xev);
+
+ if(xev.type == MotionNotify) {
+ const Window motion_window = xev.xmotion.subwindow;
+ mgl::vec2i motion_window_pos, motion_window_size;
+ get_window_geometry(dpy, motion_window, motion_window_pos, motion_window_size);
+ if(motion_window && motion_window != DefaultRootWindow(dpy))
+ set_region_rectangle(dpy, border_window, motion_window_pos.x, motion_window_pos.y, motion_window_size.x, motion_window_size.y, rectangle_border_size);
+ else
+ set_region_rectangle(dpy, border_window, 0, 0, 0, 0, 0);
+ XFlush(dpy);
+ } else if(xev.type == ButtonRelease && xev.xbutton.button == Button1) {
+ selected_window = xev.xbutton.subwindow;
+ const Window clicked_window_real = window_get_target_window_child(dpy, selected_window);
+ if(clicked_window_real)
+ selected_window = clicked_window_real;
+ selected = true;
+
+ stop();
+ break;
+ } else if(xev.type == KeyRelease && XKeycodeToKeysym(dpy, xev.xkey.keycode, 0) == XK_Escape) {
+ canceled = true;
+ selected = false;
+ stop();
+ break;
+ }
+ }
+ return true;
+ }
+
+ bool WindowSelector::take_selection() {
+ const bool result = selected;
+ selected = false;
+ return result;
+ }
+
+ bool WindowSelector::take_canceled() {
+ const bool result = canceled;
+ canceled = false;
+ return result;
+ }
+
+ Window WindowSelector::get_selection() const {
+ return selected_window;
+ }
+} \ No newline at end of file
diff --git a/src/WindowUtils.cpp b/src/WindowUtils.cpp
index eb6080f..c6b278b 100644
--- a/src/WindowUtils.cpp
+++ b/src/WindowUtils.cpp
@@ -1,4 +1,5 @@
#include "../include/WindowUtils.hpp"
+#include "../include/Utils.hpp"
#include <X11/Xatom.h>
#include <X11/Xutil.h>
@@ -62,7 +63,7 @@ namespace gsr {
return window_has_atom(dpy, window, net_wm_state_atom) || window_has_atom(dpy, window, wm_state_atom);
}
- static Window window_get_target_window_child(Display *display, Window window) {
+ Window window_get_target_window_child(Display *display, Window window) {
if(window == None)
return None;
@@ -212,28 +213,6 @@ namespace gsr {
return result;
}
- static std::string strip(const std::string &str) {
- int start_index = 0;
- int str_len = str.size();
-
- for(int i = 0; i < str_len; ++i) {
- if(str[i] != ' ') {
- start_index += i;
- str_len -= i;
- break;
- }
- }
-
- for(int i = str_len - 1; i >= 0; --i) {
- if(str[i] != ' ') {
- str_len = i + 1;
- break;
- }
- }
-
- return str.substr(start_index, str_len);
- }
-
std::string get_focused_window_name(Display *dpy, WindowCaptureType window_capture_type) {
std::string result;
const Window focused_window = get_focused_window(dpy, window_capture_type);
diff --git a/src/gui/ScreenshotSettingsPage.cpp b/src/gui/ScreenshotSettingsPage.cpp
index 5b8efbd..27a94b0 100644
--- a/src/gui/ScreenshotSettingsPage.cpp
+++ b/src/gui/ScreenshotSettingsPage.cpp
@@ -35,9 +35,8 @@ namespace gsr {
std::unique_ptr<ComboBox> ScreenshotSettingsPage::create_record_area_box() {
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
// TODO: Show options not supported but disable them
- // TODO: Enable this
- //if(capture_options.window)
- // record_area_box->add_item("Window", "window");
+ if(capture_options.window)
+ record_area_box->add_item("Window", "window");
if(capture_options.region)
record_area_box->add_item("Region", "region");
if(!capture_options.monitors.empty())
@@ -60,14 +59,6 @@ namespace gsr {
return record_area_list;
}
- std::unique_ptr<List> ScreenshotSettingsPage::create_select_window() {
- auto select_window_list = std::make_unique<List>(List::Orientation::VERTICAL);
- select_window_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Select window:", get_color_theme().text_color));
- select_window_list->add_widget(std::make_unique<Button>(&get_theme().body_font, "Click here to select a window...", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)));
- select_window_list_ptr = select_window_list.get();
- return select_window_list;
- }
-
std::unique_ptr<Entry> ScreenshotSettingsPage::create_image_width_entry() {
auto image_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
image_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
@@ -124,7 +115,6 @@ namespace gsr {
auto capture_target_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
capture_target_list->add_widget(create_record_area());
- capture_target_list->add_widget(create_select_window());
capture_target_list->add_widget(create_image_resolution_section());
capture_target_list->add_widget(create_restore_portal_session_section());
@@ -258,9 +248,7 @@ namespace gsr {
content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
- const bool window_selected = id == "window";
const bool portal_selected = id == "portal";
- select_window_list_ptr->set_visible(window_selected);
image_resolution_list_ptr->set_visible(change_image_resolution_checkbox_ptr->is_checked());
restore_portal_session_list_ptr->set_visible(portal_selected);
return true;
diff --git a/src/gui/SettingsPage.cpp b/src/gui/SettingsPage.cpp
index 1230290..26e7335 100644
--- a/src/gui/SettingsPage.cpp
+++ b/src/gui/SettingsPage.cpp
@@ -65,13 +65,12 @@ namespace gsr {
std::unique_ptr<ComboBox> SettingsPage::create_record_area_box() {
auto record_area_box = std::make_unique<ComboBox>(&get_theme().body_font);
// TODO: Show options not supported but disable them
- // TODO: Enable this
- //if(capture_options.window)
- // record_area_box->add_item("Window", "window");
- if(capture_options.region)
- record_area_box->add_item("Region", "region");
+ if(capture_options.window)
+ record_area_box->add_item("Window", "window");
if(capture_options.focused)
record_area_box->add_item("Follow focused window", "focused");
+ if(capture_options.region)
+ record_area_box->add_item("Region", "region");
if(!capture_options.monitors.empty())
record_area_box->add_item(gsr_info->system_info.display_server == DisplayServer::WAYLAND ? "Focused monitor (Experimental on Wayland)" : "Focused monitor", "focused_monitor");
for(const auto &monitor : capture_options.monitors) {
@@ -92,14 +91,6 @@ namespace gsr {
return record_area_list;
}
- std::unique_ptr<List> SettingsPage::create_select_window() {
- auto select_window_list = std::make_unique<List>(List::Orientation::VERTICAL);
- select_window_list->add_widget(std::make_unique<Label>(&get_theme().body_font, "Select window:", get_color_theme().text_color));
- select_window_list->add_widget(std::make_unique<Button>(&get_theme().body_font, "Click here to select a window...", mgl::vec2f(0.0f, 0.0f), mgl::Color(0, 0, 0, 120)));
- select_window_list_ptr = select_window_list.get();
- return select_window_list;
- }
-
std::unique_ptr<Entry> SettingsPage::create_area_width_entry() {
auto area_width_entry = std::make_unique<Entry>(&get_theme().body_font, "1920", get_theme().body_font.get_character_size() * 3);
area_width_entry->validate_handler = create_entry_validator_integer_in_range(1, 1 << 15);
@@ -186,7 +177,6 @@ namespace gsr {
auto capture_target_list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
capture_target_list->add_widget(create_record_area());
- capture_target_list->add_widget(create_select_window());
capture_target_list->add_widget(create_area_size_section());
capture_target_list->add_widget(create_video_resolution_section());
capture_target_list->add_widget(create_restore_portal_session_section());
@@ -451,13 +441,13 @@ namespace gsr {
std::unique_ptr<List> SettingsPage::create_video_bitrate_entry() {
auto list = std::make_unique<List>(List::Orientation::HORIZONTAL, List::Alignment::CENTER);
- auto video_bitrate_entry = std::make_unique<Entry>(&get_theme().body_font, "15000", (int)(get_theme().body_font.get_character_size() * 4.0f));
+ auto video_bitrate_entry = std::make_unique<Entry>(&get_theme().body_font, "8000", (int)(get_theme().body_font.get_character_size() * 4.0f));
video_bitrate_entry->validate_handler = create_entry_validator_integer_in_range(1, 500000);
video_bitrate_entry_ptr = video_bitrate_entry.get();
list->add_widget(std::move(video_bitrate_entry));
if(type == Type::STREAM) {
- auto size_mb_label = std::make_unique<Label>(&get_theme().body_font, "1.64MB", get_color_theme().text_color);
+ auto size_mb_label = std::make_unique<Label>(&get_theme().body_font, "", get_color_theme().text_color);
Label *size_mb_label_ptr = size_mb_label.get();
list->add_widget(std::move(size_mb_label));
@@ -634,10 +624,8 @@ namespace gsr {
content_page_ptr->add_widget(create_settings());
record_area_box_ptr->on_selection_changed = [this](const std::string&, const std::string &id) {
- const bool window_selected = id == "window";
const bool focused_selected = id == "focused";
const bool portal_selected = id == "portal";
- select_window_list_ptr->set_visible(window_selected);
area_size_list_ptr->set_visible(focused_selected);
video_resolution_list_ptr->set_visible(!focused_selected && change_video_resolution_checkbox_ptr->is_checked());
change_video_resolution_checkbox_ptr->set_visible(!focused_selected);
diff --git a/src/main.cpp b/src/main.cpp
index 31ec8ff..a68ff7d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -240,6 +240,11 @@ int main(int argc, char **argv) {
return 1;
}
+ if(gsr::pidof("gpu-screen-recorder", getpid()) != -1) {
+ const char *args[] = { "gsr-notify", "--text", "GPU Screen Recorder is already running in another process.\nPlease close it before using GPU Screen Recorder UI.", "--timeout", "5.0", "--icon-color", "ff0000", "--bg-color", "ff0000", nullptr };
+ gsr::exec_program_daemonized(args);
+ }
+
if(is_flatpak())
install_flatpak_systemd_service();
else