Allow capture of external monitors on a laptop with dedicated gpu (prime) on x11, fix cursor not visible on some wayland compositors (hyprland) with multiple monitors

This commit is contained in:
dec05eba
2024-08-16 19:37:00 +02:00
parent f297a92e05
commit da4925b23e
9 changed files with 183 additions and 67 deletions

View File

@@ -1,8 +1,10 @@
#include "../../include/capture/kms.h"
#include "../../include/utils.h"
#include "../../include/color_conversion.h"
#include "../../include/cursor.h"
#include "../../kms/client/kms_client.h"
#include <X11/Xlib.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
@@ -45,6 +47,9 @@ typedef struct {
struct hdr_output_metadata hdr_metadata;
bool hdr_metadata_set;
gsr_cursor x11_cursor;
XEvent xev;
} gsr_capture_kms;
static void gsr_capture_kms_cleanup_kms_fds(gsr_capture_kms *self) {
@@ -79,6 +84,7 @@ static void gsr_capture_kms_stop(gsr_capture_kms *self) {
gsr_capture_kms_cleanup_kms_fds(self);
gsr_kms_client_deinit(&self->kms_client);
gsr_cursor_deinit(&self->x11_cursor);
}
static int max_int(int a, int b) {
@@ -151,14 +157,19 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c
if(kms_init_res != 0)
return kms_init_res;
const bool is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11;
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
if(is_x11)
gsr_cursor_init(&self->x11_cursor, self->params.egl, self->params.egl->x11.dpy);
MonitorCallbackUserdata monitor_callback_userdata = {
&self->monitor_id,
self->params.display_to_capture, strlen(self->params.display_to_capture),
0,
};
for_each_active_monitor_output(self->params.egl, GSR_CONNECTION_DRM, monitor_callback, &monitor_callback_userdata);
for_each_active_monitor_output(self->params.egl, connection_type, monitor_callback, &monitor_callback_userdata);
if(!get_monitor_by_name(self->params.egl, GSR_CONNECTION_DRM, self->params.display_to_capture, &monitor)) {
if(!get_monitor_by_name(self->params.egl, connection_type, self->params.display_to_capture, &monitor)) {
fprintf(stderr, "gsr error: gsr_capture_kms_start: failed to find monitor by name \"%s\"\n", self->params.display_to_capture);
gsr_capture_kms_stop(self);
return -1;
@@ -168,7 +179,8 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c
self->monitor_rotation = drm_monitor_get_display_server_rotation(self->params.egl, &monitor);
self->capture_pos = monitor.pos;
if(self->monitor_rotation == GSR_MONITOR_ROT_90 || self->monitor_rotation == GSR_MONITOR_ROT_270) {
/* Monitor size is already rotated on x11 when the monitor is rotated, no need to apply it ourselves */
if(!is_x11 && (self->monitor_rotation == GSR_MONITOR_ROT_90 || self->monitor_rotation == GSR_MONITOR_ROT_270)) {
self->capture_size.x = monitor.size.y;
self->capture_size.y = monitor.size.x;
} else {
@@ -186,6 +198,16 @@ static int gsr_capture_kms_start(gsr_capture *cap, AVCodecContext *video_codec_c
return 0;
}
static void gsr_capture_kms_tick(gsr_capture *cap, AVCodecContext *video_codec_context) {
(void)video_codec_context;
gsr_capture_kms *self = cap->priv;
while(XPending(self->params.egl->x11.dpy)) {
XNextEvent(self->params.egl->x11.dpy, &self->xev);
gsr_cursor_update(&self->x11_cursor, &self->xev);
}
}
static float monitor_rotation_to_radians(gsr_monitor_rotation rot) {
switch(rot) {
case GSR_MONITOR_ROT_0: return 0.0f;
@@ -238,12 +260,16 @@ static gsr_kms_response_item* find_largest_drm(gsr_kms_response *kms_response) {
return largest_drm;
}
static gsr_kms_response_item* find_cursor_drm(gsr_kms_response *kms_response) {
static gsr_kms_response_item* find_cursor_drm(gsr_kms_response *kms_response, uint32_t connector_id) {
gsr_kms_response_item *cursor_drm = NULL;
for(int i = 0; i < kms_response->num_items; ++i) {
if(kms_response->items[i].is_cursor)
return &kms_response->items[i];
if(kms_response->items[i].is_cursor) {
cursor_drm = &kms_response->items[i];
if(kms_response->items[i].connector_id == connector_id)
break;
}
}
return NULL;
return cursor_drm;
}
static bool hdr_metadata_is_supported_format(const struct hdr_output_metadata *hdr_metadata) {
@@ -329,7 +355,7 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
capture_is_combined_plane = true;
}
cursor_drm_fd = find_cursor_drm(&self->kms_response);
cursor_drm_fd = find_cursor_drm(&self->kms_response, drm_fd->connector_id);
if(!drm_fd)
return -1;
@@ -337,6 +363,10 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
if(!capture_is_combined_plane && cursor_drm_fd && cursor_drm_fd->connector_id != drm_fd->connector_id)
cursor_drm_fd = NULL;
const bool is_x11 = gsr_egl_get_display_server(self->params.egl) == GSR_DISPLAY_SERVER_X11;
if(is_x11)
cursor_drm_fd = NULL;
if(drm_fd->has_hdr_metadata && self->params.hdr && hdr_metadata_is_supported_format(&drm_fd->hdr_metadata))
gsr_kms_set_hdr_metadata(self, drm_fd);
@@ -471,6 +501,25 @@ static int gsr_capture_kms_capture(gsr_capture *cap, AVFrame *frame, gsr_color_c
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
if(self->params.record_cursor && !cursor_drm_fd && is_x11) {
gsr_cursor_tick(&self->x11_cursor, DefaultRootWindow(self->params.egl->x11.dpy));
const vec2i cursor_pos = {
target_x + self->x11_cursor.position.x - self->x11_cursor.hotspot.x - capture_pos.x,
target_y + self->x11_cursor.position.y - self->x11_cursor.hotspot.y - capture_pos.y
};
self->params.egl->glEnable(GL_SCISSOR_TEST);
self->params.egl->glScissor(target_x, target_y, self->capture_size.x, self->capture_size.y);
gsr_color_conversion_draw(color_conversion, self->x11_cursor.texture_id,
cursor_pos, self->x11_cursor.size,
(vec2i){0, 0}, self->x11_cursor.size,
0.0f, false);
self->params.egl->glDisable(GL_SCISSOR_TEST);
}
//self->params.egl->glFlush();
//self->params.egl->glFinish();
@@ -566,7 +615,7 @@ gsr_capture* gsr_capture_kms_create(const gsr_capture_kms_params *params) {
*cap = (gsr_capture) {
.start = gsr_capture_kms_start,
.tick = NULL,
.tick = gsr_capture_kms_tick,
.should_stop = gsr_capture_kms_should_stop,
.capture = gsr_capture_kms_capture,
.capture_end = gsr_capture_kms_capture_end,