mirror of
https://repo.dec05eba.com/gpu-screen-recorder-ui
synced 2026-03-31 09:17:04 +09:00
Add option to capture the focused monitor
This commit is contained in:
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
428
src/CursorTrackerWayland.cpp
Normal file
428
src/CursorTrackerWayland.cpp
Normal 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, ®istry_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
29
src/CursorTrackerX11.cpp
Normal 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) };
|
||||
}
|
||||
}
|
||||
113
src/Overlay.cpp
113
src/Overlay.cpp
@@ -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(),
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user