Add option to capture the focused monitor

This commit is contained in:
dec05eba
2025-04-04 20:51:28 +02:00
parent 44f35f8f3b
commit 2e3adfc510
19 changed files with 913 additions and 37 deletions

View File

@@ -123,10 +123,10 @@ namespace gsr {
screenshot_config.save_directory = default_pictures_save_directory;
if(!capture_options.monitors.empty()) {
streaming_config.record_options.record_area_option = capture_options.monitors.front().name;
record_config.record_options.record_area_option = capture_options.monitors.front().name;
replay_config.record_options.record_area_option = capture_options.monitors.front().name;
screenshot_config.record_area_option = capture_options.monitors.front().name;
streaming_config.record_options.record_area_option = "focused_monitor";
record_config.record_options.record_area_option = "focused_monitor";
replay_config.record_options.record_area_option = "focused_monitor";
screenshot_config.record_area_option = "focused_monitor";
}
}

View File

@@ -0,0 +1,428 @@
#include "../include/CursorTrackerWayland.hpp"
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <wayland-client.h>
#include "xdg-output-unstable-v1-client-protocol.h"
namespace gsr {
typedef struct {
int type;
int count;
} drm_connector_type_count;
static const int CONNECTOR_TYPE_COUNTS = 32;
typedef enum {
PLANE_PROPERTY_CRTC_X = 1 << 0,
PLANE_PROPERTY_CRTC_Y = 1 << 1,
PLANE_PROPERTY_CRTC_ID = 1 << 2,
PLANE_PROPERTY_TYPE_CURSOR = 1 << 3,
} plane_property_mask;
static const uint32_t plane_property_all = 0xF;
/* Returns plane_property_mask */
static uint32_t plane_get_properties(int drm_fd, uint32_t plane_id, int *crtc_x, int *crtc_y, int *crtc_id, bool *is_cursor) {
*crtc_x = 0;
*crtc_y = 0;
*crtc_id = 0;
*is_cursor = false;
uint32_t property_mask = 0;
drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(drm_fd, plane_id, DRM_MODE_OBJECT_PLANE);
if(!props)
return property_mask;
for(uint32_t i = 0; i < props->count_props; ++i) {
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
if(!prop)
continue;
// SRC_* values are fixed 16.16 points
const uint32_t type = prop->flags & (DRM_MODE_PROP_LEGACY_TYPE | DRM_MODE_PROP_EXTENDED_TYPE);
if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_X") == 0) {
*crtc_x = (int)props->prop_values[i];
property_mask |= PLANE_PROPERTY_CRTC_X;
} else if((type & DRM_MODE_PROP_SIGNED_RANGE) && strcmp(prop->name, "CRTC_Y") == 0) {
*crtc_y = (int)props->prop_values[i];
property_mask |= PLANE_PROPERTY_CRTC_Y;
} else if((type & DRM_MODE_PROP_OBJECT) && strcmp(prop->name, "CRTC_ID") == 0) {
*crtc_id = (int)props->prop_values[i];
property_mask |= PLANE_PROPERTY_CRTC_ID;
} else if((type & DRM_MODE_PROP_ENUM) && strcmp(prop->name, "type") == 0) {
const uint64_t current_enum_value = props->prop_values[i];
for(int j = 0; j < prop->count_enums; ++j) {
if(prop->enums[j].value == current_enum_value && strcmp(prop->enums[j].name, "Cursor") == 0) {
property_mask |= PLANE_PROPERTY_TYPE_CURSOR;
break;
}
}
}
drmModeFreeProperty(prop);
}
drmModeFreeObjectProperties(props);
return property_mask;
}
static bool connector_get_property_by_name(int drm_fd, drmModeConnectorPtr props, const char *name, uint64_t *result) {
for(int i = 0; i < props->count_props; ++i) {
drmModePropertyPtr prop = drmModeGetProperty(drm_fd, props->props[i]);
if(!prop)
continue;
if(strcmp(name, prop->name) == 0) {
*result = props->prop_values[i];
drmModeFreeProperty(prop);
return true;
}
drmModeFreeProperty(prop);
}
return false;
}
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;
drmModeResPtr resources = drmModeGetResources(drm_fd);
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(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) {
result = connection_name;
result += "-";
result += std::to_string(connector_type->count);
drmModeFreeConnector(connector);
break;
}
next:
drmModeFreeConnector(connector);
}
drmModeFreeResources(resources);
return result;
}
// Name is the crtc name. TODO: verify if this works on all wayland compositors
static const WaylandOutput* get_wayland_monitor_by_name(const std::vector<WaylandOutput> &monitors, const std::string &name) {
for(const WaylandOutput &monitor : monitors) {
if(monitor.name == name)
return &monitor;
}
return nullptr;
}
static WaylandOutput* get_wayland_monitor_by_output(CursorTrackerWayland &cursor_tracker_wayland, struct wl_output *output) {
for(WaylandOutput &monitor : cursor_tracker_wayland.monitors) {
if(monitor.output == output)
return &monitor;
}
return nullptr;
}
static void output_handle_geometry(void *data, struct wl_output *wl_output,
int32_t x, int32_t y, int32_t phys_width, int32_t phys_height,
int32_t subpixel, const char *make, const char *model,
int32_t transform) {
(void)wl_output;
(void)phys_width;
(void)phys_height;
(void)subpixel;
(void)make;
(void)model;
CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data;
WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output);
if(!monitor)
return;
monitor->pos.x = x;
monitor->pos.y = y;
monitor->transform = transform;
}
static void output_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) {
(void)wl_output;
(void)flags;
(void)refresh;
CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data;
WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output);
if(!monitor)
return;
monitor->size.x = width;
monitor->size.y = height;
}
static void output_handle_done(void *data, struct wl_output *wl_output) {
(void)data;
(void)wl_output;
}
static void output_handle_scale(void* data, struct wl_output *wl_output, int32_t factor) {
(void)data;
(void)wl_output;
(void)factor;
}
static void output_handle_name(void *data, struct wl_output *wl_output, const char *name) {
(void)wl_output;
CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data;
WaylandOutput *monitor = get_wayland_monitor_by_output(*cursor_tracker_wayland, wl_output);
if(!monitor)
return;
monitor->name = name;
}
static void output_handle_description(void *data, struct wl_output *wl_output, const char *description) {
(void)data;
(void)wl_output;
(void)description;
}
static const struct wl_output_listener output_listener = {
output_handle_geometry,
output_handle_mode,
output_handle_done,
output_handle_scale,
output_handle_name,
output_handle_description,
};
static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) {
(void)version;
CursorTrackerWayland *cursor_tracker_wayland = (CursorTrackerWayland*)data;
if(strcmp(interface, wl_output_interface.name) == 0) {
if(version < 4) {
fprintf(stderr, "Warning: wl output interface version is < 4, expected >= 4\n");
return;
}
struct wl_output *output = (struct wl_output*)wl_registry_bind(registry, name, &wl_output_interface, 4);
cursor_tracker_wayland->monitors.push_back(
WaylandOutput{
name,
output,
nullptr,
mgl::vec2i{0, 0},
mgl::vec2i{0, 0},
0,
""
});
wl_output_add_listener(output, &output_listener, cursor_tracker_wayland);
} else if(strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
if(version < 1) {
fprintf(stderr, "Warning: xdg output interface version is < 1, expected >= 1\n");
return;
}
if(cursor_tracker_wayland->xdg_output_manager) {
zxdg_output_manager_v1_destroy(cursor_tracker_wayland->xdg_output_manager);
cursor_tracker_wayland->xdg_output_manager = NULL;
}
cursor_tracker_wayland->xdg_output_manager = (struct zxdg_output_manager_v1*)wl_registry_bind(registry, name, &zxdg_output_manager_v1_interface, 1);
}
}
static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) {
(void)data;
(void)registry;
(void)name;
// TODO: Remove output
}
static struct wl_registry_listener registry_listener = {
registry_add_object,
registry_remove_object,
};
static void xdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) {
(void)zxdg_output_v1;
WaylandOutput *monitor = (WaylandOutput*)data;
monitor->pos.x = x;
monitor->pos.y = y;
}
static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) {
(void)data;
(void)xdg_output;
(void)width;
(void)height;
}
static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) {
(void)data;
(void)xdg_output;
}
static void xdg_output_handle_name(void *data, struct zxdg_output_v1 *xdg_output, const char *name) {
(void)data;
(void)xdg_output;
(void)name;
}
static void xdg_output_handle_description(void *data, struct zxdg_output_v1 *xdg_output, const char *description) {
(void)data;
(void)xdg_output;
(void)description;
}
static const struct zxdg_output_v1_listener xdg_output_listener = {
xdg_output_logical_position,
xdg_output_handle_logical_size,
xdg_output_handle_done,
xdg_output_handle_name,
xdg_output_handle_description,
};
CursorTrackerWayland::CursorTrackerWayland(const char *card_path) {
drm_fd = open(card_path, O_RDONLY);
if(drm_fd <= 0) {
fprintf(stderr, "Error: CursorTrackerWayland: failed to open %s\n", card_path);
return;
}
drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
}
CursorTrackerWayland::~CursorTrackerWayland() {
if(drm_fd > 0)
close(drm_fd);
}
void CursorTrackerWayland::update() {
if(drm_fd <= 0)
return;
drmModePlaneResPtr planes = drmModeGetPlaneResources(drm_fd);
if(!planes)
return;
for(uint32_t i = 0; i < planes->count_planes; ++i) {
int crtc_x = 0;
int crtc_y = 0;
int crtc_id = 0;
bool is_cursor = false;
const uint32_t property_mask = plane_get_properties(drm_fd, planes->planes[i], &crtc_x, &crtc_y, &crtc_id, &is_cursor);
if(property_mask == plane_property_all && crtc_id > 0) {
latest_cursor_position.x = crtc_x;
latest_cursor_position.y = crtc_y;
latest_crtc_id = crtc_id;
break;
}
}
drmModeFreePlaneResources(planes);
}
void CursorTrackerWayland::set_monitor_outputs_from_xdg_output(struct wl_display *dpy) {
if(!xdg_output_manager) {
fprintf(stderr, "Warning: CursorTrackerWayland::set_monitor_outputs_from_xdg_output: zxdg_output_manager not found. Registered monitor positions might be incorrect\n");
return;
}
for(WaylandOutput &monitor : monitors) {
monitor.xdg_output = zxdg_output_manager_v1_get_xdg_output(xdg_output_manager, monitor.output);
zxdg_output_v1_add_listener(monitor.xdg_output, &xdg_output_listener, &monitor);
}
// Fetch xdg_output
wl_display_roundtrip(dpy);
}
std::optional<CursorInfo> CursorTrackerWayland::get_latest_cursor_info() {
if(drm_fd <= 0 || latest_crtc_id == -1)
return std::nullopt;
std::string monitor_name = get_monitor_name_from_crtc_id(drm_fd, latest_crtc_id);
if(monitor_name.empty())
return std::nullopt;
struct wl_display *dpy = wl_display_connect(nullptr);
if(!dpy) {
fprintf(stderr, "Error: CursorTrackerWayland::get_latest_cursor_info: failed to connect to the wayland server\n");
return std::nullopt;
}
monitors.clear();
struct wl_registry *registry = wl_display_get_registry(dpy);
wl_registry_add_listener(registry, &registry_listener, this);
// Fetch globals
wl_display_roundtrip(dpy);
// Fetch wl_output
wl_display_roundtrip(dpy);
set_monitor_outputs_from_xdg_output(dpy);
mgl::vec2i cursor_position = latest_cursor_position;
const WaylandOutput *wayland_monitor = get_wayland_monitor_by_name(monitors, monitor_name);
if(!wayland_monitor)
return std::nullopt;
cursor_position = wayland_monitor->pos + latest_cursor_position;
for(WaylandOutput &monitor : monitors) {
if(monitor.output) {
wl_output_destroy(monitor.output);
monitor.output = nullptr;
}
if(monitor.xdg_output) {
zxdg_output_v1_destroy(monitor.xdg_output);
monitor.xdg_output = nullptr;
}
}
monitors.clear();
if(xdg_output_manager) {
zxdg_output_manager_v1_destroy(xdg_output_manager);
xdg_output_manager = nullptr;
}
wl_registry_destroy(registry);
wl_display_disconnect(dpy);
return CursorInfo{ cursor_position, std::move(monitor_name) };
}
}

29
src/CursorTrackerX11.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "../include/CursorTrackerX11.hpp"
#include "../include/WindowUtils.hpp"
namespace gsr {
CursorTrackerX11::CursorTrackerX11(Display *dpy) : dpy(dpy) {
}
std::optional<CursorInfo> CursorTrackerX11::get_latest_cursor_info() {
Window window = None;
const auto cursor_pos = get_cursor_position(dpy, &window);
const auto monitors = get_monitors(dpy);
std::string monitor_name;
for(const auto &monitor : monitors) {
if(cursor_pos.x >= monitor.position.x && cursor_pos.x <= monitor.position.x + monitor.size.x
&& cursor_pos.y >= monitor.position.y && cursor_pos.y <= monitor.position.y + monitor.size.y)
{
monitor_name = monitor.name;
break;
}
}
if(monitor_name.empty())
return std::nullopt;
return CursorInfo{ cursor_pos, std::move(monitor_name) };
}
}

View File

@@ -14,6 +14,8 @@
#include "../include/WindowUtils.hpp"
#include "../include/GlobalHotkeys.hpp"
#include "../include/GlobalHotkeysLinux.hpp"
#include "../include/CursorTrackerX11.hpp"
#include "../include/CursorTrackerWayland.hpp"
#include <string.h>
#include <assert.h>
@@ -411,6 +413,11 @@ namespace gsr {
XKeysymToKeycode(x11_mapping_display, XK_F1); // If we dont call we will never get a MappingNotify
else
fprintf(stderr, "Warning: XOpenDisplay failed to mapping notify\n");
if(this->gsr_info.system_info.display_server == DisplayServer::X11)
cursor_tracker = std::make_unique<CursorTrackerX11>((Display*)mgl_get_context()->connection);
else if(this->gsr_info.system_info.display_server == DisplayServer::WAYLAND && !this->gsr_info.gpu_info.card_path.empty())
cursor_tracker = std::make_unique<CursorTrackerWayland>(this->gsr_info.gpu_info.card_path.c_str());
}
Overlay::~Overlay() {
@@ -617,6 +624,9 @@ namespace gsr {
if(global_hotkeys_js)
global_hotkeys_js->poll_events();
if(cursor_tracker)
cursor_tracker->update();
handle_keyboard_mapping_event();
region_selector.poll_events();
if(region_selector.take_canceled()) {
@@ -825,12 +835,23 @@ namespace gsr {
const bool is_kwin = wm_name == "KWin";
const bool is_wlroots = wm_name.find("wlroots") != std::string::npos;
std::optional<CursorInfo> cursor_info;
if(cursor_tracker) {
cursor_tracker->update();
cursor_info = cursor_tracker->get_latest_cursor_info();
}
// The cursor position is wrong on wayland if an x11 window is not focused. On wayland we instead create a window and get the position where the wayland compositor puts it
Window x11_cursor_window = None;
const mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
const Monitor *focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
mgl::vec2i cursor_position = get_cursor_position(display, &x11_cursor_window);
const Monitor *focused_monitor = nullptr;
if(cursor_info) {
focused_monitor = find_monitor_at_position(monitors, cursor_info->position);
cursor_position = cursor_info->position;
} else {
const mgl::vec2i monitor_position_query_value = (x11_cursor_window || gsr_info.system_info.display_server != DisplayServer::WAYLAND) ? cursor_position : create_window_get_center_position(display);
focused_monitor = find_monitor_at_position(monitors, monitor_position_query_value);
}
// Wayland doesn't allow XGrabPointer/XGrabKeyboard when a wayland application is focused.
// If the focused window is a wayland application then don't use override redirect and instead create
@@ -925,7 +946,12 @@ namespace gsr {
grab_mouse_and_keyboard();
// The real cursor doesn't move when all devices are grabbed, so we create our own cursor and diplay that while grabbed
cursor_hotspot = {0, 0};
xi_setup_fake_cursor();
if(cursor_info) {
win->cursor_position.x += cursor_hotspot.x;
win->cursor_position.y += cursor_hotspot.y;
}
// We want to grab all devices to prevent any other application below the UI from receiving events.
// Owlboy seems to use xi events and XGrabPointer doesn't prevent owlboy from receiving events.
@@ -1343,20 +1369,31 @@ namespace gsr {
const std::string icon_color_str = color_to_hex_str(icon_color);
const std::string bg_color_str = color_to_hex_str(bg_color);
const char *notification_args[12] = {
const char *notification_args[14] = {
"gsr-notify", "--text", str, "--timeout", timeout_seconds_str,
"--icon-color", icon_color_str.c_str(), "--bg-color", bg_color_str.c_str(),
};
int arg_index = 9;
const char *notification_type_str = notification_type_to_string(notification_type);
if(notification_type_str) {
notification_args[9] = "--icon";
notification_args[10] = notification_type_str;
notification_args[11] = nullptr;
} else {
notification_args[9] = nullptr;
notification_args[arg_index++] = "--icon";
notification_args[arg_index++] = notification_type_str;
}
std::optional<CursorInfo> cursor_info;
if(cursor_tracker) {
cursor_tracker->update();
cursor_info = cursor_tracker->get_latest_cursor_info();
}
if(cursor_info) {
notification_args[arg_index++] = "--monitor";
notification_args[arg_index++] = cursor_info->monitor_name.c_str();
}
notification_args[arg_index++] = nullptr;
if(notification_process > 0) {
kill(notification_process, SIGKILL);
int status = 0;
@@ -1878,8 +1915,7 @@ namespace gsr {
add_region_command(args, region_str, region_str_size, region_selector);
}
static bool validate_capture_target(const GsrInfo &gsr_info, const std::string &capture_target) {
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
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;
@@ -1887,6 +1923,8 @@ namespace gsr {
return capture_options.focused;
} else if(capture_target == "portal") {
return capture_options.portal;
} else if(capture_target == "focused_monitor") {
return !capture_options.monitors.empty();
} else {
for(const GsrMonitor &monitor : capture_options.monitors) {
if(capture_target == monitor.name)
@@ -1896,6 +1934,25 @@ namespace gsr {
}
}
std::string Overlay::get_capture_target(const std::string &capture_target, const SupportedCaptureOptions &capture_options) {
if(capture_target == "focused_monitor") {
std::optional<CursorInfo> cursor_info;
if(cursor_tracker) {
cursor_tracker->update();
cursor_info = cursor_tracker->get_latest_cursor_info();
}
if(cursor_info)
return cursor_info->monitor_name;
else if(!capture_options.monitors.empty())
return capture_options.monitors.front().name;
else
return "";
} else {
return capture_target;
}
}
void Overlay::on_press_save_replay() {
if(recording_status != RecordingStatus::REPLAY || gpu_screen_recorder_process <= 0)
return;
@@ -1949,9 +2006,11 @@ namespace gsr {
return true;
}
if(!validate_capture_target(gsr_info, config.replay_config.record_options.record_area_option)) {
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
const std::string capture_target = get_capture_target(config.replay_config.record_options.record_area_option, capture_options);
if(!validate_capture_target(capture_target, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", config.replay_config.record_options.record_area_option.c_str());
snprintf(err_msg, sizeof(err_msg), "Failed to start replay, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::REPLAY);
return false;
}
@@ -1988,7 +2047,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.replay_config.record_options.video_width, (int)config.replay_config.record_options.video_height);
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", config.replay_config.record_options.record_area_option.c_str(),
"gpu-screen-recorder", "-w", capture_target.c_str(),
"-c", config.replay_config.container.c_str(),
"-ac", config.replay_config.record_options.audio_codec.c_str(),
"-cursor", config.replay_config.record_options.record_cursor ? "yes" : "no",
@@ -2082,9 +2141,11 @@ namespace gsr {
return;
}
if(!validate_capture_target(gsr_info, config.record_config.record_options.record_area_option)) {
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
const std::string capture_target = get_capture_target(config.record_config.record_options.record_area_option, capture_options);
if(!validate_capture_target(config.record_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings", config.record_config.record_options.record_area_option.c_str());
snprintf(err_msg, sizeof(err_msg), "Failed to start recording, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::RECORD);
return;
}
@@ -2122,7 +2183,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.record_config.record_options.video_width, (int)config.record_config.record_options.video_height);
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", config.record_config.record_options.record_area_option.c_str(),
"gpu-screen-recorder", "-w", capture_target.c_str(),
"-c", config.record_config.container.c_str(),
"-ac", config.record_config.record_options.audio_codec.c_str(),
"-cursor", config.record_config.record_options.record_cursor ? "yes" : "no",
@@ -2230,9 +2291,11 @@ namespace gsr {
return;
}
if(!validate_capture_target(gsr_info, config.streaming_config.record_options.record_area_option)) {
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
const std::string capture_target = get_capture_target(config.streaming_config.record_options.record_area_option, capture_options);
if(!validate_capture_target(config.streaming_config.record_options.record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", config.streaming_config.record_options.record_area_option.c_str());
snprintf(err_msg, sizeof(err_msg), "Failed to start streaming, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::STREAM);
return;
}
@@ -2273,7 +2336,7 @@ namespace gsr {
snprintf(size, sizeof(size), "%dx%d", (int)config.streaming_config.record_options.video_width, (int)config.streaming_config.record_options.video_height);
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", config.streaming_config.record_options.record_area_option.c_str(),
"gpu-screen-recorder", "-w", capture_target.c_str(),
"-c", container.c_str(),
"-ac", config.streaming_config.record_options.audio_codec.c_str(),
"-cursor", config.streaming_config.record_options.record_cursor ? "yes" : "no",
@@ -2323,9 +2386,11 @@ namespace gsr {
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)) {
const SupportedCaptureOptions capture_options = get_supported_capture_options(gsr_info);
const std::string capture_target = get_capture_target(record_area_option, capture_options);
if(!validate_capture_target(record_area_option, capture_options)) {
char err_msg[256];
snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", record_area_option);
snprintf(err_msg, sizeof(err_msg), "Failed to take a screenshot, capture target \"%s\" is invalid. Please change capture target in settings", capture_target.c_str());
show_notification(err_msg, notification_error_timeout_seconds, mgl::Color(255, 0, 0, 0), mgl::Color(255, 0, 0, 0), NotificationType::SCREENSHOT);
return;
}
@@ -2343,7 +2408,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
std::vector<const char*> args = {
"gpu-screen-recorder", "-w", record_area_option,
"gpu-screen-recorder", "-w", capture_target.c_str(),
"-cursor", config.screenshot_config.record_cursor ? "yes" : "no",
"-v", "no",
"-q", config.screenshot_config.image_quality.c_str(),

View File

@@ -520,7 +520,7 @@ namespace gsr {
static void get_monitors_callback(const mgl_monitor *monitor, void *userdata) {
std::vector<Monitor> *monitors = (std::vector<Monitor>*)userdata;
monitors->push_back({mgl::vec2i(monitor->pos.x, monitor->pos.y), mgl::vec2i(monitor->size.x, monitor->size.y)});
monitors->push_back({mgl::vec2i(monitor->pos.x, monitor->pos.y), mgl::vec2i(monitor->size.x, monitor->size.y), std::string(monitor->name)});
}
std::vector<Monitor> get_monitors(Display *dpy) {

View File

@@ -40,6 +40,8 @@ namespace gsr {
// record_area_box->add_item("Window", "window");
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) {
char name[256];
snprintf(name, sizeof(name), "Monitor %s (%dx%d)", monitor.name.c_str(), monitor.size.x, monitor.size.y);
@@ -270,7 +272,7 @@ namespace gsr {
};
if(!capture_options.monitors.empty())
record_area_box_ptr->set_selected_item(capture_options.monitors.front().name);
record_area_box_ptr->set_selected_item("focused_monitor");
else if(capture_options.portal)
record_area_box_ptr->set_selected_item("portal");
else if(capture_options.window)

View File

@@ -70,6 +70,8 @@ namespace gsr {
record_area_box->add_item("Region", "region");
if(capture_options.focused)
record_area_box->add_item("Follow focused window", "focused");
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) {
char name[256];
snprintf(name, sizeof(name), "Monitor %s (%dx%d)", monitor.name.c_str(), monitor.size.x, monitor.size.y);
@@ -560,7 +562,7 @@ namespace gsr {
video_quality_box_ptr->on_selection_changed("", video_quality_box_ptr->get_selected_id());
if(!capture_options.monitors.empty())
record_area_box_ptr->set_selected_item(capture_options.monitors.front().name);
record_area_box_ptr->set_selected_item("focused_monitor");
else if(capture_options.portal)
record_area_box_ptr->set_selected_item("portal");
else if(capture_options.window)