From de019711d19f566aee16dd083aba297b28e0854b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 21 Jul 2023 02:49:02 +0200 Subject: [PATCH] Support wlroots screen capture, which doesn't require kms root access and also captures cursor --- TODO | 1 - build.sh | 14 +- include/egl.h | 43 +++- src/capture/kms_cuda.c | 140 ++++++++----- src/capture/kms_vaapi.c | 290 +++++++++++++++------------ src/egl.c | 426 +++++++++++++++++++++++----------------- src/main.cpp | 24 ++- src/utils.c | 22 ++- 8 files changed, 586 insertions(+), 374 deletions(-) diff --git a/TODO b/TODO index 3a73ce3..73af124 100644 --- a/TODO +++ b/TODO @@ -80,7 +80,6 @@ Support "screen" (all monitors) capture on wayland. This should be done by getti Support pipewire screen capture? Support screen rotation in amd/intel/nvidia wayland. -Support wlroots dmabuf screen recording, because it doesn't require root access unlike kms grab. Capture cursor on amd/intel wayland without xwayland. When nvidia supports hardware cursor then capture the cursor. Right now the cursor is captured because it's a software cursor so it's composed on the dma buf. CPU usage is pretty high on AMD/Intel/(Nvidia(wayland)), why? opening and closing fds, creating egl, cuda association, is slow when done every frame. Test if desktop portal screencast has better performance. diff --git a/build.sh b/build.sh index c93b428..28db825 100755 --- a/build.sh +++ b/build.sh @@ -9,10 +9,10 @@ CXX=${CXX:-g++} opts="-O2 -g0 -DNDEBUG -Wall -Wextra" [ -n "$DEBUG" ] && opts="-O0 -g3 -Wall -Wextra"; -#build_wayland_protocol() { -# wayland-scanner private-code external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-protocol.c -# wayland-scanner client-header external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-client-protocol.h -#} +build_wayland_protocol() { + wayland-scanner private-code external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-protocol.c + wayland-scanner client-header external/wlr-export-dmabuf-unstable-v1.xml external/wlr-export-dmabuf-unstable-v1-client-protocol.h +} build_gsr_kms_server() { # TODO: -fcf-protection=full, not supported on arm @@ -45,14 +45,14 @@ build_gsr() { $CC -c src/cursor.c $opts $includes $CC -c src/utils.c $opts $includes $CC -c src/library_loader.c $opts $includes - #$CC -c external/wlr-export-dmabuf-unstable-v1-protocol.c $opts $includes + $CC -c external/wlr-export-dmabuf-unstable-v1-protocol.c $opts $includes $CXX -c src/sound.cpp $opts $includes $CXX -c src/main.cpp $opts $includes $CXX -o gpu-screen-recorder capture.o nvfbc.o kms_client.o egl.o cuda.o xnvctrl.o overclock.o window_texture.o shader.o \ - color_conversion.o cursor.o utils.o library_loader.o xcomposite_cuda.o xcomposite_vaapi.o kms_vaapi.o kms_cuda.o sound.o main.o $libs $opts + color_conversion.o cursor.o utils.o library_loader.o xcomposite_cuda.o xcomposite_vaapi.o kms_vaapi.o kms_cuda.o wlr-export-dmabuf-unstable-v1-protocol.o sound.o main.o $libs $opts } -#build_wayland_protocol +build_wayland_protocol build_gsr_kms_server build_gsr echo "Successfully built gpu-screen-recorder" diff --git a/include/egl.h b/include/egl.h index 920e0f0..58c1861 100644 --- a/include/egl.h +++ b/include/egl.h @@ -7,6 +7,7 @@ #include #include #include +#include "vec2.h" #ifdef _WIN64 typedef signed long long int khronos_intptr_t; @@ -105,6 +106,35 @@ typedef unsigned int (*FUNC_eglExportDMABUFImageQueryMESA)(EGLDisplay dpy, EGLIm typedef unsigned int (*FUNC_eglExportDMABUFImageMESA)(EGLDisplay dpy, EGLImageKHR image, int *fds, int32_t *strides, int32_t *offsets); typedef void (*FUNC_glEGLImageTargetTexture2DOES)(unsigned int target, GLeglImageOES image); +#define GSR_MAX_OUTPUTS 32 + +typedef struct { + Display *dpy; + Window window; +} gsr_x11; + +typedef struct { + uint32_t wl_name; + void *output; + vec2i pos; + vec2i size; + char *name; +} gsr_wayland_output; + +typedef struct { + void *dpy; + void *window; + void *registry; + void *surface; + void *compositor; + void *export_manager; + void *current_frame; + void *frame_callback; + gsr_wayland_output outputs[GSR_MAX_OUTPUTS]; + int num_outputs; + gsr_wayland_output *output_to_capture; +} gsr_wayland; + typedef struct { void *egl_library; void *gl_library; @@ -113,14 +143,8 @@ typedef struct { EGLSurface egl_surface; EGLContext egl_context; - Display *x11_dpy; - Window x11_window; - - void *wayland_dpy; - void *wayland_window; - void *wayland_registry; - void *wayland_surface; - void *wayland_compositor; + gsr_x11 x11; + gsr_wayland wayland; int fd; uint32_t width; @@ -204,6 +228,9 @@ typedef struct { bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland); void gsr_egl_unload(gsr_egl *self); +/* wayland protocol capture, does not include kms capture */ +bool gsr_egl_supports_wayland_capture(gsr_egl *self); +bool gsr_egl_start_capture(gsr_egl *self, const char *monitor_to_capture); void gsr_egl_update(gsr_egl *self); void gsr_egl_cleanup_frame(gsr_egl *self); diff --git a/src/capture/kms_cuda.c b/src/capture/kms_cuda.c index fc7da5b..284ec77 100644 --- a/src/capture/kms_cuda.c +++ b/src/capture/kms_cuda.c @@ -34,6 +34,8 @@ typedef struct { gsr_kms_client kms_client; gsr_kms_response kms_response; + gsr_kms_response_fd wayland_kms_data; + bool using_wayland_capture; vec2i capture_pos; vec2i capture_size; @@ -131,36 +133,47 @@ static void monitor_callback(const gsr_monitor *monitor, void *userdata) { static int gsr_capture_kms_cuda_start(gsr_capture *cap, AVCodecContext *video_codec_context) { gsr_capture_kms_cuda *cap_kms = cap->priv; - if(gsr_kms_client_init(&cap_kms->kms_client, cap_kms->params.card_path) != 0) { - return -1; - } - cap_kms->monitor_id.num_connector_ids = 0; - MonitorCallbackUserdata monitor_callback_userdata = { - cap_kms, - cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture), - 0 - }; - for_each_active_monitor_output((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, monitor_callback, &monitor_callback_userdata); - - gsr_monitor monitor; - if(!get_monitor_by_name((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, cap_kms->params.display_to_capture, &monitor)) { - fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); + if(!gsr_egl_load(&cap_kms->egl, NULL, true)) { + fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to load opengl\n"); gsr_capture_kms_cuda_stop(cap, video_codec_context); return -1; } + gsr_monitor monitor; + cap_kms->monitor_id.num_connector_ids = 0; + if(gsr_egl_start_capture(&cap_kms->egl, cap_kms->params.display_to_capture)) { + if(!get_monitor_by_name(&cap_kms->egl, GSR_CONNECTION_WAYLAND, cap_kms->params.display_to_capture, &monitor)) { + fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); + gsr_capture_kms_cuda_stop(cap, video_codec_context); + return -1; + } + cap_kms->using_wayland_capture = true; + } else { + if(gsr_kms_client_init(&cap_kms->kms_client, cap_kms->params.card_path) != 0) { + gsr_capture_kms_cuda_stop(cap, video_codec_context); + return -1; + } + + MonitorCallbackUserdata monitor_callback_userdata = { + cap_kms, + cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture), + 0 + }; + for_each_active_monitor_output((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, monitor_callback, &monitor_callback_userdata); + + if(!get_monitor_by_name((void*)cap_kms->params.card_path, GSR_CONNECTION_DRM, cap_kms->params.display_to_capture, &monitor)) { + fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); + gsr_capture_kms_cuda_stop(cap, video_codec_context); + return -1; + } + } + cap_kms->capture_pos = monitor.pos; cap_kms->capture_size = monitor.size; video_codec_context->width = max_int(2, even_number_ceil(cap_kms->capture_size.x)); video_codec_context->height = max_int(2, even_number_ceil(cap_kms->capture_size.y)); - if(!gsr_egl_load(&cap_kms->egl, NULL, true)) { - fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to load opengl\n"); - gsr_capture_kms_cuda_stop(cap, video_codec_context); - return -1; - } - /* Disable vsync */ cap_kms->egl.eglSwapInterval(cap_kms->egl.egl_display, 0); @@ -183,8 +196,6 @@ static uint32_t fourcc(uint32_t a, uint32_t b, uint32_t c, uint32_t d) { return (d << 24) | (c << 16) | (b << 8) | a; } -#define FOURCC_NV12 842094158 - static void gsr_capture_kms_cuda_tick(gsr_capture *cap, AVCodecContext *video_codec_context, AVFrame **frame) { gsr_capture_kms_cuda *cap_kms = cap->priv; @@ -278,8 +289,18 @@ static bool gsr_capture_kms_register_egl_image_in_cuda(gsr_capture_kms_cuda *cap return false; } + if(res != CUDA_SUCCESS) { + fprintf(stderr, "cuda error 1\n"); + } + res = cap_kms->cuda.cuGraphicsResourceSetMapFlags(cap_kms->cuda_graphics_resource, CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY); + if(res != CUDA_SUCCESS) { + fprintf(stderr, "cuda error 2\n"); + } res = cap_kms->cuda.cuGraphicsSubResourceGetMappedArray(&cap_kms->mapped_array, cap_kms->cuda_graphics_resource, 0, 0); + if(res != CUDA_SUCCESS) { + fprintf(stderr, "cuda error 3\n"); + } res = cap_kms->cuda.cuCtxPopCurrent_v2(&old_ctx); return true; } @@ -310,31 +331,49 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { } cap_kms->kms_response.num_fds = 0; - if(gsr_kms_client_get_kms(&cap_kms->kms_client, &cap_kms->kms_response) != 0) { - fprintf(stderr, "gsr error: gsr_capture_kms_cuda_capture: failed to get kms, error: %d (%s)\n", cap_kms->kms_response.result, cap_kms->kms_response.err_msg); - return -1; - } - - if(cap_kms->kms_response.num_fds == 0) { - static bool error_shown = false; - if(!error_shown) { - error_shown = true; - fprintf(stderr, "gsr error: no drm found, capture will fail\n"); - } - return -1; - } - gsr_kms_response_fd *drm_fd = NULL; - for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) { - drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]); - if(drm_fd) - break; - } + if(cap_kms->using_wayland_capture) { + gsr_egl_update(&cap_kms->egl); + cap_kms->wayland_kms_data.fd = cap_kms->egl.fd; + cap_kms->wayland_kms_data.width = cap_kms->egl.width; + cap_kms->wayland_kms_data.height = cap_kms->egl.height; + cap_kms->wayland_kms_data.pitch = cap_kms->egl.pitch; + cap_kms->wayland_kms_data.offset = cap_kms->egl.offset; + cap_kms->wayland_kms_data.pixel_format = cap_kms->egl.pixel_format; + cap_kms->wayland_kms_data.modifier = cap_kms->egl.modifier; + cap_kms->wayland_kms_data.connector_id = 0; + cap_kms->wayland_kms_data.is_combined_plane = false; - if(!drm_fd) { - drm_fd = find_first_combined_drm(&cap_kms->kms_response); - if(!drm_fd) - drm_fd = find_largest_drm(&cap_kms->kms_response); + if(cap_kms->wayland_kms_data.fd <= 0) + return -1; + + drm_fd = &cap_kms->wayland_kms_data; + } else { + if(gsr_kms_client_get_kms(&cap_kms->kms_client, &cap_kms->kms_response) != 0) { + fprintf(stderr, "gsr error: gsr_capture_kms_cuda_capture: failed to get kms, error: %d (%s)\n", cap_kms->kms_response.result, cap_kms->kms_response.err_msg); + return -1; + } + + if(cap_kms->kms_response.num_fds == 0) { + static bool error_shown = false; + if(!error_shown) { + error_shown = true; + fprintf(stderr, "gsr error: no drm found, capture will fail\n"); + } + return -1; + } + + for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) { + drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]); + if(drm_fd) + break; + } + + if(!drm_fd) { + drm_fd = find_first_combined_drm(&cap_kms->kms_response); + if(!drm_fd) + drm_fd = find_largest_drm(&cap_kms->kms_response); + } } if(!drm_fd) @@ -342,9 +381,6 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { //bool capture_is_combined_plane = drm_fd->is_combined_plane || ((int)drm_fd->width == cap_kms->screen_size.x && (int)drm_fd->height == cap_kms->screen_size.y); - //gsr_egl_cleanup_frame(&cap_kms->egl); - //gsr_egl_update(&cap_kms->egl); - const intptr_t img_attr[] = { //EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_LINUX_DRM_FOURCC_EXT, fourcc('A', 'R', '2', '4'),//cap_kms->egl.pixel_format, ARGB8888 @@ -358,7 +394,13 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { EGL_NONE }; + while(cap_kms->egl.glGetError()) {} + while(cap_kms->egl.eglGetError() != EGL_SUCCESS){} EGLImage image = cap_kms->egl.eglCreateImage(cap_kms->egl.egl_display, 0, EGL_LINUX_DMA_BUF_EXT, NULL, img_attr); + if(cap_kms->egl.glGetError() != 0 || cap_kms->egl.eglGetError() != EGL_SUCCESS) { + fprintf(stderr, "egl error!\n"); + } + gsr_capture_kms_register_egl_image_in_cuda(cap_kms, image); cap_kms->egl.eglDestroyImage(cap_kms->egl.egl_display, image); @@ -385,6 +427,8 @@ static int gsr_capture_kms_cuda_capture(gsr_capture *cap, AVFrame *frame) { gsr_capture_kms_unload_cuda_graphics(cap_kms); + gsr_egl_cleanup_frame(&cap_kms->egl); + for(int i = 0; i < cap_kms->kms_response.num_fds; ++i) { if(cap_kms->kms_response.fds[i].fd > 0) close(cap_kms->kms_response.fds[i].fd); diff --git a/src/capture/kms_vaapi.c b/src/capture/kms_vaapi.c index 54b8816..912e260 100644 --- a/src/capture/kms_vaapi.c +++ b/src/capture/kms_vaapi.c @@ -44,6 +44,8 @@ typedef struct { gsr_kms_client kms_client; gsr_kms_response kms_response; + gsr_kms_response_fd wayland_kms_data; + bool using_wayland_capture; vec2i screen_size; vec2i capture_pos; @@ -190,63 +192,7 @@ static void monitor_callback(const gsr_monitor *monitor, void *userdata) { static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_codec_context) { gsr_capture_kms_vaapi *cap_kms = cap->priv; - if(gsr_kms_client_init(&cap_kms->kms_client, cap_kms->params.card_path) != 0) { - return -1; - } - - void *connection = cap_kms->params.wayland ? (void*)cap_kms->params.card_path : (void*)cap_kms->dpy; - const gsr_connection_type connection_type = cap_kms->params.wayland ? GSR_CONNECTION_DRM : GSR_CONNECTION_X11; - - MonitorCallbackUserdata monitor_callback_userdata = { - cap_kms, None, - cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture), - 0, - X11_ROT_0, - true - }; - - if(cap_kms->params.wayland) { - cap_kms->monitor_id.num_connector_ids = 0; - for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata); - - cap_kms->screen_size.x = 0; - cap_kms->screen_size.y = 0; - } else { - const Atom randr_connector_id_atom = XInternAtom(cap_kms->dpy, "CONNECTOR_ID", False); - cap_kms->monitor_id.num_connector_ids = 0; - monitor_callback_userdata.randr_connector_id_atom = randr_connector_id_atom; - monitor_callback_userdata.wayland = false; - for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata); - - cap_kms->screen_size.x = WidthOfScreen(DefaultScreenOfDisplay(cap_kms->dpy)); - cap_kms->screen_size.y = HeightOfScreen(DefaultScreenOfDisplay(cap_kms->dpy)); - } - - gsr_monitor monitor; - if(strcmp(cap_kms->params.display_to_capture, "screen") == 0) { - monitor.pos.x = 0; - monitor.pos.y = 0; - monitor.size = cap_kms->screen_size; - cap_kms->screen_capture = true; - } else if(!get_monitor_by_name(connection, connection_type, cap_kms->params.display_to_capture, &monitor)) { - fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); - gsr_capture_kms_vaapi_stop(cap, video_codec_context); - return -1; - } - - // TODO: Find a better way to do this. Is this info available somewhere in drm? it should be! - - // Note: workaround AMD/Intel issue. If there is one monitor enabled and it's rotated then - // the drm buf will also be rotated. This only happens when you only have one monitor enabled. - cap_kms->x11_rot = monitor_callback_userdata.rotation; - if(monitor_callback_userdata.num_monitors == 1 && cap_kms->x11_rot != X11_ROT_0) { - cap_kms->requires_rotation = true; - } else { - cap_kms->requires_rotation = false; - } - - cap_kms->capture_pos = monitor.pos; - cap_kms->capture_size = monitor.size; + cap_kms->x11_rot = X11_ROT_0; if(!gsr_egl_load(&cap_kms->egl, cap_kms->dpy, cap_kms->params.wayland)) { fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to load opengl\n"); @@ -254,6 +200,74 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c return -1; } + gsr_monitor monitor; + cap_kms->monitor_id.num_connector_ids = 0; + if(cap_kms->params.wayland && gsr_egl_start_capture(&cap_kms->egl, cap_kms->params.display_to_capture)) { + if(!get_monitor_by_name(&cap_kms->egl, GSR_CONNECTION_WAYLAND, cap_kms->params.display_to_capture, &monitor)) { + fprintf(stderr, "gsr error: gsr_capture_kms_cuda_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); + gsr_capture_kms_vaapi_stop(cap, video_codec_context); + return -1; + } + cap_kms->using_wayland_capture = true; + } else { + if(gsr_kms_client_init(&cap_kms->kms_client, cap_kms->params.card_path) != 0) { + gsr_capture_kms_vaapi_stop(cap, video_codec_context); + return -1; + } + + void *connection = cap_kms->params.wayland ? (void*)cap_kms->params.card_path : (void*)cap_kms->dpy; + const gsr_connection_type connection_type = cap_kms->params.wayland ? GSR_CONNECTION_DRM : GSR_CONNECTION_X11; + + MonitorCallbackUserdata monitor_callback_userdata = { + cap_kms, None, + cap_kms->params.display_to_capture, strlen(cap_kms->params.display_to_capture), + 0, + X11_ROT_0, + true + }; + + if(cap_kms->params.wayland) { + for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata); + + cap_kms->screen_size.x = 0; + cap_kms->screen_size.y = 0; + } else { + const Atom randr_connector_id_atom = XInternAtom(cap_kms->dpy, "CONNECTOR_ID", False); + monitor_callback_userdata.randr_connector_id_atom = randr_connector_id_atom; + monitor_callback_userdata.wayland = false; + for_each_active_monitor_output(connection, connection_type, monitor_callback, &monitor_callback_userdata); + + cap_kms->screen_size.x = WidthOfScreen(DefaultScreenOfDisplay(cap_kms->dpy)); + cap_kms->screen_size.y = HeightOfScreen(DefaultScreenOfDisplay(cap_kms->dpy)); + } + + gsr_monitor monitor; + if(strcmp(cap_kms->params.display_to_capture, "screen") == 0) { + monitor.pos.x = 0; + monitor.pos.y = 0; + monitor.size = cap_kms->screen_size; + cap_kms->screen_capture = true; + } else if(!get_monitor_by_name(connection, connection_type, cap_kms->params.display_to_capture, &monitor)) { + fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_start: failed to find monitor by name \"%s\"\n", cap_kms->params.display_to_capture); + gsr_capture_kms_vaapi_stop(cap, video_codec_context); + return -1; + } + + // TODO: Find a better way to do this. Is this info available somewhere in drm? it should be! + + // Note: workaround AMD/Intel issue. If there is one monitor enabled and it's rotated then + // the drm buf will also be rotated. This only happens when you only have one monitor enabled. + cap_kms->x11_rot = monitor_callback_userdata.rotation; + if(monitor_callback_userdata.num_monitors == 1 && cap_kms->x11_rot != X11_ROT_0) { + cap_kms->requires_rotation = true; + } else { + cap_kms->requires_rotation = false; + } + } + + cap_kms->capture_pos = monitor.pos; + cap_kms->capture_size = monitor.size; + /* Disable vsync */ cap_kms->egl.eglSwapInterval(cap_kms->egl.egl_display, 0); @@ -265,7 +279,7 @@ static int gsr_capture_kms_vaapi_start(gsr_capture *cap, AVCodecContext *video_c return -1; } - if(cap_kms->dpy) { + if(cap_kms->dpy && !cap_kms->params.wayland) { if(gsr_cursor_init(&cap_kms->cursor, &cap_kms->egl, cap_kms->dpy) != 0) { gsr_capture_kms_vaapi_stop(cap, video_codec_context); return -1; @@ -290,7 +304,7 @@ static void gsr_capture_kms_vaapi_tick(gsr_capture *cap, AVCodecContext *video_c // TODO: cap_kms->egl.glClear(GL_COLOR_BUFFER_BIT); - if(cap_kms->dpy) { + if(cap_kms->dpy && !cap_kms->params.wayland) { while(XPending(cap_kms->dpy)) { XNextEvent(cap_kms->dpy, &cap_kms->xev); gsr_cursor_update(&cap_kms->cursor, &cap_kms->xev); @@ -479,40 +493,57 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { } cap_kms->kms_response.num_fds = 0; - if(gsr_kms_client_get_kms(&cap_kms->kms_client, &cap_kms->kms_response) != 0) { - fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_capture: failed to get kms, error: %d (%s)\n", cap_kms->kms_response.result, cap_kms->kms_response.err_msg); - return -1; - } - - if(cap_kms->kms_response.num_fds == 0) { - static bool error_shown = false; - if(!error_shown) { - error_shown = true; - fprintf(stderr, "gsr error: no drm found, capture will fail\n"); - } - return -1; - } - - bool requires_rotation = cap_kms->requires_rotation; - gsr_kms_response_fd *drm_fd = NULL; - if(cap_kms->screen_capture) { - drm_fd = find_first_combined_drm(&cap_kms->kms_response); - if(!drm_fd) - drm_fd = find_largest_drm(&cap_kms->kms_response); + bool requires_rotation = cap_kms->requires_rotation; + if(cap_kms->using_wayland_capture) { + gsr_egl_update(&cap_kms->egl); + cap_kms->wayland_kms_data.fd = cap_kms->egl.fd; + cap_kms->wayland_kms_data.width = cap_kms->egl.width; + cap_kms->wayland_kms_data.height = cap_kms->egl.height; + cap_kms->wayland_kms_data.pitch = cap_kms->egl.pitch; + cap_kms->wayland_kms_data.offset = cap_kms->egl.offset; + cap_kms->wayland_kms_data.pixel_format = cap_kms->egl.pixel_format; + cap_kms->wayland_kms_data.modifier = cap_kms->egl.modifier; + cap_kms->wayland_kms_data.connector_id = 0; + cap_kms->wayland_kms_data.is_combined_plane = false; + + if(cap_kms->wayland_kms_data.fd <= 0) + return -1; + + drm_fd = &cap_kms->wayland_kms_data; } else { - for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) { - drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]); - if(drm_fd) { - requires_rotation = cap_kms->x11_rot != X11_ROT_0; - break; - } + if(gsr_kms_client_get_kms(&cap_kms->kms_client, &cap_kms->kms_response) != 0) { + fprintf(stderr, "gsr error: gsr_capture_kms_vaapi_capture: failed to get kms, error: %d (%s)\n", cap_kms->kms_response.result, cap_kms->kms_response.err_msg); + return -1; } - if(!drm_fd) { + if(cap_kms->kms_response.num_fds == 0) { + static bool error_shown = false; + if(!error_shown) { + error_shown = true; + fprintf(stderr, "gsr error: no drm found, capture will fail\n"); + } + return -1; + } + + if(cap_kms->screen_capture) { drm_fd = find_first_combined_drm(&cap_kms->kms_response); if(!drm_fd) drm_fd = find_largest_drm(&cap_kms->kms_response); + } else { + for(int i = 0; i < cap_kms->monitor_id.num_connector_ids; ++i) { + drm_fd = find_drm_by_connector_id(&cap_kms->kms_response, cap_kms->monitor_id.connector_ids[i]); + if(drm_fd) { + requires_rotation = cap_kms->x11_rot != X11_ROT_0; + break; + } + } + + if(!drm_fd) { + drm_fd = find_first_combined_drm(&cap_kms->kms_response); + if(!drm_fd) + drm_fd = find_largest_drm(&cap_kms->kms_response); + } } } @@ -558,50 +589,59 @@ static int gsr_capture_kms_vaapi_capture(gsr_capture *cap, AVFrame *frame) { cap_kms->egl.eglDestroyImage(cap_kms->egl.egl_display, image); cap_kms->egl.glBindTexture(GL_TEXTURE_2D, 0); - float texture_rotation = 0.0f; - if(requires_rotation) { - switch(cap_kms->x11_rot) { - case X11_ROT_90: - texture_rotation = M_PI*0.5f; - break; - case X11_ROT_180: - texture_rotation = M_PI; - break; - case X11_ROT_270: - texture_rotation = M_PI*1.5f; - break; - default: - texture_rotation = 0.0f; - break; + if(cap_kms->using_wayland_capture) { + gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, + (vec2i){0, 0}, cap_kms->capture_size, + cap_kms->capture_pos, cap_kms->capture_size, + 0.0f); + } else { + float texture_rotation = 0.0f; + if(requires_rotation) { + switch(cap_kms->x11_rot) { + case X11_ROT_90: + texture_rotation = M_PI*0.5f; + break; + case X11_ROT_180: + texture_rotation = M_PI; + break; + case X11_ROT_270: + texture_rotation = M_PI*1.5f; + break; + default: + texture_rotation = 0.0f; + break; + } + } + + if(cap_kms->dpy && !cap_kms->params.wayland) { + gsr_cursor_tick(&cap_kms->cursor); + } + + vec2i capture_pos = cap_kms->capture_pos; + vec2i capture_size = cap_kms->capture_size; + vec2i cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x - capture_pos.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y - capture_pos.y}; + if(!capture_is_combined_plane) { + capture_pos = (vec2i){0, 0}; + //cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y}; + } + + gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, + (vec2i){0, 0}, capture_size, + capture_pos, capture_size, + texture_rotation); + + if(cap_kms->dpy && !cap_kms->params.wayland) { + gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor.texture_id, + cursor_capture_pos, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, + (vec2i){0, 0}, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, + 0.0f); } } - if(cap_kms->dpy) { - gsr_cursor_tick(&cap_kms->cursor); - } - - vec2i capture_pos = cap_kms->capture_pos; - vec2i capture_size = cap_kms->capture_size; - vec2i cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x - capture_pos.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y - capture_pos.y}; - if(!capture_is_combined_plane) { - capture_pos = (vec2i){0, 0}; - //cursor_capture_pos = (vec2i){cap_kms->cursor.position.x - cap_kms->cursor.hotspot.x, cap_kms->cursor.position.y - cap_kms->cursor.hotspot.y}; - } - - gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->input_texture, - (vec2i){0, 0}, capture_size, - capture_pos, capture_size, - texture_rotation); - - if(cap_kms->dpy) { - gsr_color_conversion_draw(&cap_kms->color_conversion, cap_kms->cursor.texture_id, - cursor_capture_pos, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, - (vec2i){0, 0}, (vec2i){cap_kms->cursor.size.x, cap_kms->cursor.size.y}, - 0.0f); - } - cap_kms->egl.eglSwapBuffers(cap_kms->egl.egl_display, cap_kms->egl.egl_surface); + gsr_egl_cleanup_frame(&cap_kms->egl); + for(int i = 0; i < cap_kms->kms_response.num_fds; ++i) { if(cap_kms->kms_response.fds[i].fd > 0) close(cap_kms->kms_response.fds[i].fd); diff --git a/src/egl.c b/src/egl.c index bbdb031..482b3df 100644 --- a/src/egl.c +++ b/src/egl.c @@ -2,157 +2,213 @@ #include "../include/library_loader.h" #include #include +#include #include #include #include #include -//#include "../external/wlr-export-dmabuf-unstable-v1-client-protocol.h" +#include "../external/wlr-export-dmabuf-unstable-v1-client-protocol.h" #include -#if 0 -static struct wl_compositor *compositor = NULL; -static struct wl_output *output = NULL; -static struct zwlr_export_dmabuf_manager_v1 *export_manager = NULL; -static struct zwlr_export_dmabuf_frame_v1 *current_frame = NULL; -//static struct wl_shell *shell = NULL; - -struct window { - EGLContext egl_context; - struct wl_surface *surface; - //struct wl_shell_surface *shell_surface; - struct wl_egl_window *egl_window; - EGLSurface egl_surface; -}; - 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) { - fprintf(stderr, "output geometry, make: %s, model: %s\n", make, model); + 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; + (void)transform; + gsr_wayland_output *gsr_output = data; + gsr_output->pos.x = x; + gsr_output->pos.y = y; } -static void output_handle_mode(void *data, struct wl_output *wl_output, - uint32_t flags, int32_t width, int32_t height, int32_t refresh) { - +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; + gsr_wayland_output *gsr_output = data; + gsr_output->size.x = width; + gsr_output->size.y = height; } -static void output_handle_done(void* data, struct wl_output *wl_output) { - /* Nothing to do */ +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) { - /* Nothing to do */ +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; + gsr_wayland_output *gsr_output = data; + if(gsr_output->name) { + free(gsr_output->name); + gsr_output->name = NULL; + } + gsr_output->name = strdup(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 = { - .geometry = output_handle_geometry, - .mode = output_handle_mode, - .done = output_handle_done, - .scale = output_handle_scale, + .geometry = output_handle_geometry, + .mode = output_handle_mode, + .done = output_handle_done, + .scale = output_handle_scale, + .name = output_handle_name, + .description = output_handle_description, }; -#endif -static void registry_add_object (void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { +static void registry_add_object(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { (void)version; - struct wl_compositor **wayland_compositor = data; - if (strcmp(interface, "wl_compositor") == 0) { - if(*wayland_compositor) { - wl_compositor_destroy(*wayland_compositor); - *wayland_compositor = NULL; + gsr_egl *egl = data; + if (strcmp(interface, "wl_compositor") == 0) { + if(egl->wayland.compositor) { + wl_compositor_destroy(egl->wayland.compositor); + egl->wayland.compositor = NULL; } - *wayland_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); - }/* else if(strcmp(interface, wl_output_interface.name) == 0) { - fprintf(stderr, "wayland output, name: %u\n", name); - output = wl_registry_bind(registry, name, &wl_output_interface, 1); - wl_output_add_listener(output, &output_listener, NULL); - } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) { - export_manager = wl_registry_bind(registry, name, &zwlr_export_dmabuf_manager_v1_interface, 1); - }*/ - //fprintf(stderr, "interface: %s\n", interface); + egl->wayland.compositor = wl_registry_bind(registry, name, &wl_compositor_interface, 1); + } else if(strcmp(interface, wl_output_interface.name) == 0) { + if(version < 4) { + fprintf(stderr, "gsr warning: wl output interface version is < 4, expected >= 4 to capture a monitor. Using KMS capture instead\n"); + return; + } + + if(egl->wayland.num_outputs == GSR_MAX_OUTPUTS) { + fprintf(stderr, "gsr warning: reached maximum outputs (32), ignoring output %u\n", name); + return; + } + + gsr_wayland_output *gsr_output = &egl->wayland.outputs[egl->wayland.num_outputs]; + egl->wayland.num_outputs++; + *gsr_output = (gsr_wayland_output) { + .wl_name = name, + .output = wl_registry_bind(registry, name, &wl_output_interface, 4), + .pos = { .x = 0, .y = 0 }, + .size = { .x = 0, .y = 0 }, + .name = NULL, + }; + wl_output_add_listener(gsr_output->output, &output_listener, gsr_output); + } else if(strcmp(interface, zwlr_export_dmabuf_manager_v1_interface.name) == 0) { + if(egl->wayland.export_manager) { + zwlr_export_dmabuf_manager_v1_destroy(egl->wayland.export_manager); + egl->wayland.export_manager = NULL; + } + egl->wayland.export_manager = wl_registry_bind(registry, name, &zwlr_export_dmabuf_manager_v1_interface, 1); + } } -static void registry_remove_object (void *data, struct wl_registry *registry, uint32_t name) { - (void)data; +static void registry_remove_object(void *data, struct wl_registry *registry, uint32_t name) { + (void)data; (void)registry; (void)name; } -static struct wl_registry_listener registry_listener = {®istry_add_object, ®istry_remove_object}; +static struct wl_registry_listener registry_listener = { + .global = registry_add_object, + .global_remove = registry_remove_object, +}; -#if 0 -static void register_cb(gsr_egl *egl); +static void frame_capture_output(gsr_egl *egl); static void frame_start(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, - uint32_t buffer_flags, uint32_t flags, uint32_t format, - uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { + uint32_t width, uint32_t height, uint32_t offset_x, uint32_t offset_y, + uint32_t buffer_flags, uint32_t flags, uint32_t format, + uint32_t mod_high, uint32_t mod_low, uint32_t num_objects) { + (void)offset_x; + (void)offset_y; + (void)buffer_flags; + (void)flags; + (void)num_objects; gsr_egl *egl = data; - //fprintf(stderr, "frame start, width: %u, height: %u, offset x: %u, offset y: %u, format: %u, num objects: %u\n", width, height, offset_x, offset_y, format, num_objects); + //fprintf(stderr, "frame start %p, width: %u, height: %u, offset x: %u, offset y: %u, format: %u, num objects: %u\n", (void*)frame, width, height, offset_x, offset_y, format, num_objects); egl->width = width; egl->height = height; egl->pixel_format = format; - egl->modifier = ((uint64_t)mod_high << 32) | mod_low; - current_frame = frame; + egl->modifier = ((uint64_t)mod_high << 32) | (uint64_t)mod_low; + egl->wayland.current_frame = frame; } static void frame_object(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t index, int32_t fd, uint32_t size, uint32_t offset, - uint32_t stride, uint32_t plane_index) { + uint32_t index, int32_t fd, uint32_t size, uint32_t offset, + uint32_t stride, uint32_t plane_index) { // TODO: What if we get multiple objects? then we get multiple fd per frame + (void)frame; + (void)index; + (void)size; + (void)plane_index; gsr_egl *egl = data; - //egl->fd = fd; + if(egl->fd > 0) { + close(egl->fd); + egl->fd = 0; + } + egl->fd = fd; egl->pitch = stride; egl->offset = offset; - //fprintf(stderr, "new frame, fd: %d, index: %u, size: %u, offset: %u, stride: %u, plane_index: %u\n", fd, index, size, offset, stride, plane_index); - close(fd); + //fprintf(stderr, "new frame %p, fd: %d, index: %u, size: %u, offset: %u, stride: %u, plane_index: %u\n", (void*)frame, fd, index, size, offset, stride, plane_index); } - -static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { - register_cb(data); +static void frame_ready(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { + (void)frame; + (void)tv_sec_hi; + (void)tv_sec_lo; + (void)tv_nsec; + frame_capture_output(data); } -static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, - uint32_t reason) { - register_cb(data); +static void frame_cancel(void *data, struct zwlr_export_dmabuf_frame_v1 *frame, uint32_t reason) { + (void)frame; + (void)reason; + frame_capture_output(data); } - static const struct zwlr_export_dmabuf_frame_v1_listener frame_listener = { - .frame = frame_start, - .object = frame_object, - .ready = frame_ready, - .cancel = frame_cancel, + .frame = frame_start, + .object = frame_object, + .ready = frame_ready, + .cancel = frame_cancel, }; -static struct zwlr_export_dmabuf_frame_v1 *frame_callback = NULL; -static void register_cb(gsr_egl *egl) { - bool with_cursor = false; - frame_callback = zwlr_export_dmabuf_manager_v1_capture_output(export_manager, with_cursor, output); - zwlr_export_dmabuf_frame_v1_add_listener(frame_callback, &frame_listener, egl); +static void frame_capture_output(gsr_egl *egl) { + assert(egl->wayland.output_to_capture); + bool with_cursor = true; + + if(egl->wayland.frame_callback) { + zwlr_export_dmabuf_frame_v1_destroy(egl->wayland.frame_callback); + egl->wayland.frame_callback = NULL; + } + + egl->wayland.frame_callback = zwlr_export_dmabuf_manager_v1_capture_output(egl->wayland.export_manager, with_cursor, egl->wayland.output_to_capture->output); + zwlr_export_dmabuf_frame_v1_add_listener(egl->wayland.frame_callback, &frame_listener, egl); +} + +static gsr_wayland_output* get_wayland_output_by_name(gsr_egl *egl, const char *name) { + assert(name); + for(int i = 0; i < egl->wayland.num_outputs; ++i) { + if(egl->wayland.outputs[i].name && strcmp(egl->wayland.outputs[i].name, name) == 0) + return &egl->wayland.outputs[i]; + } + return NULL; } -#endif // TODO: Create egl context without surface (in other words, x11/wayland agnostic, doesn't require x11/wayland dependency) static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { EGLConfig ecfg; int32_t num_config = 0; - EGLDisplay egl_display = NULL; - EGLSurface egl_surface = NULL; - EGLContext egl_context = NULL; - - Window x11_window = None; - - struct wl_registry *wayland_registry = NULL; - struct wl_compositor *wayland_compositor = NULL; - struct wl_surface *wayland_surface = NULL; - void *wayland_dpy = NULL; - void *wayland_window = NULL; - const int32_t attr[] = { EGL_BUFFER_SIZE, 24, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, @@ -165,39 +221,29 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { }; if(wayland) { - wayland_dpy = wl_display_connect(NULL); - if(!wayland_dpy) { + self->wayland.dpy = wl_display_connect(NULL); + if(!self->wayland.dpy) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: wl_display_connect failed\n"); goto fail; } - wayland_registry = wl_display_get_registry(wayland_dpy); // TODO: Error checking - wl_registry_add_listener(wayland_registry, ®istry_listener, &wayland_compositor); // TODO: Error checking + self->wayland.registry = wl_display_get_registry(self->wayland.dpy); // TODO: Error checking + wl_registry_add_listener(self->wayland.registry, ®istry_listener, self); // TODO: Error checking // Fetch globals - wl_display_roundtrip(wayland_dpy); + wl_display_roundtrip(self->wayland.dpy); // fetch wl_output - wl_display_roundtrip(wayland_dpy); + wl_display_roundtrip(self->wayland.dpy); - if(!wayland_compositor) { + if(!self->wayland.compositor) { fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find compositor\n"); goto fail; } - - /*if(!output) { - fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find output\n"); - goto fail; - } - - if(!export_manager) { - fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to find export manager\n"); - goto fail; - }*/ } else { - x11_window = XCreateWindow(self->x11_dpy, DefaultRootWindow(self->x11_dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); + self->x11.window = XCreateWindow(self->x11.dpy, DefaultRootWindow(self->x11.dpy), 0, 0, 16, 16, 0, CopyFromParent, InputOutput, CopyFromParent, 0, NULL); - if(!x11_window) { + if(!self->x11.window) { fprintf(stderr, "gsr error: gsr_gl_create_window failed: failed to create gl window\n"); goto fail; } @@ -205,78 +251,50 @@ static bool gsr_egl_create_window(gsr_egl *self, bool wayland) { self->eglBindAPI(EGL_OPENGL_ES_API); - egl_display = self->eglGetDisplay(wayland_dpy ? (EGLNativeDisplayType)wayland_dpy : (EGLNativeDisplayType)self->x11_dpy); - if(!egl_display) { + self->egl_display = self->eglGetDisplay(self->wayland.dpy ? (EGLNativeDisplayType)self->wayland.dpy : (EGLNativeDisplayType)self->x11.dpy); + if(!self->egl_display) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglGetDisplay failed\n"); goto fail; } - if(!self->eglInitialize(egl_display, NULL, NULL)) { + if(!self->eglInitialize(self->egl_display, NULL, NULL)) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: eglInitialize failed\n"); goto fail; } - if(!self->eglChooseConfig(egl_display, attr, &ecfg, 1, &num_config) || num_config != 1) { + if(!self->eglChooseConfig(self->egl_display, attr, &ecfg, 1, &num_config) || num_config != 1) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to find a matching config\n"); goto fail; } - egl_context = self->eglCreateContext(egl_display, ecfg, NULL, ctxattr); - if(!egl_context) { + self->egl_context = self->eglCreateContext(self->egl_display, ecfg, NULL, ctxattr); + if(!self->egl_context) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create egl context\n"); goto fail; } if(wayland) { - wayland_surface = wl_compositor_create_surface(wayland_compositor); - wayland_window = wl_egl_window_create(wayland_surface, 16, 16); - egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)wayland_window, NULL); + self->wayland.surface = wl_compositor_create_surface(self->wayland.compositor); + self->wayland.window = wl_egl_window_create(self->wayland.surface, 16, 16); + self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->wayland.window, NULL); } else { - egl_surface = self->eglCreateWindowSurface(egl_display, ecfg, (EGLNativeWindowType)x11_window, NULL); + self->egl_surface = self->eglCreateWindowSurface(self->egl_display, ecfg, (EGLNativeWindowType)self->x11.window, NULL); } - if(!egl_surface) { + if(!self->egl_surface) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to create window surface\n"); goto fail; } - if(!self->eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context)) { + if(!self->eglMakeCurrent(self->egl_display, self->egl_surface, self->egl_surface, self->egl_context)) { fprintf(stderr, "gsr error: gsr_egl_create_window failed: failed to make context current\n"); goto fail; } - self->egl_display = egl_display; - self->egl_surface = egl_surface; - self->egl_context = egl_context; - - self->x11_window = x11_window; - - self->wayland_dpy = wayland_dpy; - self->wayland_window = wayland_window; - self->wayland_surface = wayland_surface; - self->wayland_compositor = wayland_compositor; - self->wayland_registry = wayland_registry; return true; fail: - if(egl_context) - self->eglDestroyContext(egl_display, egl_context); - if(egl_surface) - self->eglDestroySurface(egl_display, egl_surface); - if(egl_display) - self->eglTerminate(egl_display); - if(x11_window) - XDestroyWindow(self->x11_dpy, x11_window); - if(wayland_window) - wl_egl_window_destroy(wayland_window); - if(wayland_surface) - wl_surface_destroy(wayland_surface); - if(wayland_compositor) - wl_compositor_destroy(wayland_compositor); - if(wayland_registry) - wl_registry_destroy(wayland_registry); - if(wayland_dpy) - wl_display_disconnect(wayland_dpy); + gsr_egl_unload(self); return false; } @@ -387,7 +405,7 @@ static bool gsr_egl_load_gl(gsr_egl *self, void *library) { bool gsr_egl_load(gsr_egl *self, Display *dpy, bool wayland) { memset(self, 0, sizeof(gsr_egl)); - self->x11_dpy = dpy; + self->x11.dpy = dpy; void *egl_lib = NULL; void *gl_lib = NULL; @@ -449,34 +467,59 @@ void gsr_egl_unload(gsr_egl *self) { self->egl_display = NULL; } - if(self->x11_window) { - XDestroyWindow(self->x11_dpy, self->x11_window); - self->x11_window = None; + if(self->x11.window) { + XDestroyWindow(self->x11.dpy, self->x11.window); + self->x11.window = None; } - if(self->wayland_window) { - wl_egl_window_destroy(self->wayland_window); - self->wayland_window = NULL; + gsr_egl_cleanup_frame(self); + + if(self->wayland.frame_callback) { + zwlr_export_dmabuf_frame_v1_destroy(self->wayland.frame_callback); + self->wayland.frame_callback = NULL; } - if(self->wayland_surface) { - wl_surface_destroy(self->wayland_surface); - self->wayland_surface = NULL; + if(self->wayland.export_manager) { + zwlr_export_dmabuf_manager_v1_destroy(self->wayland.export_manager); + self->wayland.export_manager = NULL; } - if(self->wayland_compositor) { - wl_compositor_destroy(self->wayland_compositor); - self->wayland_compositor = NULL; + if(self->wayland.window) { + wl_egl_window_destroy(self->wayland.window); + self->wayland.window = NULL; } - if(self->wayland_registry) { - wl_registry_destroy(self->wayland_registry); - self->wayland_registry = NULL; + if(self->wayland.surface) { + wl_surface_destroy(self->wayland.surface); + self->wayland.surface = NULL; } - if(self->wayland_dpy) { - wl_display_disconnect(self->wayland_dpy); - self->wayland_dpy = NULL; + for(int i = 0; i < self->wayland.num_outputs; ++i) { + if(self->wayland.outputs[i].output) { + wl_output_destroy(self->wayland.outputs[i].output); + self->wayland.outputs[i].output = NULL; + } + + if(self->wayland.outputs[i].name) { + free(self->wayland.outputs[i].name); + self->wayland.outputs[i].name = NULL; + } + } + self->wayland.num_outputs = 0; + + if(self->wayland.compositor) { + wl_compositor_destroy(self->wayland.compositor); + self->wayland.compositor = NULL; + } + + if(self->wayland.registry) { + wl_registry_destroy(self->wayland.registry); + self->wayland.registry = NULL; + } + + if(self->wayland.dpy) { + wl_display_disconnect(self->wayland.dpy); + self->wayland.dpy = NULL; } if(self->egl_library) { @@ -492,15 +535,38 @@ void gsr_egl_unload(gsr_egl *self) { memset(self, 0, sizeof(gsr_egl)); } +bool gsr_egl_supports_wayland_capture(gsr_egl *self) { + return !!self->wayland.export_manager && self->wayland.num_outputs > 0; +} + +bool gsr_egl_start_capture(gsr_egl *self, const char *monitor_to_capture) { + assert(monitor_to_capture); + if(!monitor_to_capture) + return false; + + if(!self->wayland.dpy) + return false; + + if(!gsr_egl_supports_wayland_capture(self)) + return false; + + self->wayland.output_to_capture = get_wayland_output_by_name(self, monitor_to_capture); + if(!self->wayland.output_to_capture) + return false; + + frame_capture_output(self); + return true; +} + void gsr_egl_update(gsr_egl *self) { - if(!self->wayland_dpy) + if(!self->wayland.dpy) return; - wl_display_dispatch(self->wayland_dpy); + wl_display_dispatch(self->wayland.dpy); } void gsr_egl_cleanup_frame(gsr_egl *self) { - if(!self->wayland_dpy) + if(!self->wayland.dpy) return; if(self->fd > 0) { @@ -508,8 +574,8 @@ void gsr_egl_cleanup_frame(gsr_egl *self) { self->fd = 0; } - /*if(current_frame) { - zwlr_export_dmabuf_frame_v1_destroy(current_frame); - current_frame = NULL; - }*/ + if(self->wayland.current_frame) { + //zwlr_export_dmabuf_frame_v1_destroy(self->wayland.current_frame); + self->wayland.current_frame = NULL; + } } diff --git a/src/main.cpp b/src/main.cpp index 7723bc2..af7b224 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1378,12 +1378,28 @@ int main(int argc, char **argv) { } else if(contains_non_hex_number(window_str)) { // TODO: wayland, not only drm (if wlroots) if(wayland) { - gsr_monitor gmon; - if(!get_monitor_by_name(card_path, GSR_CONNECTION_DRM, window_str, &gmon)) { - fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str); - for_each_active_monitor_output(card_path, GSR_CONNECTION_DRM, monitor_output_callback_print, NULL); + gsr_egl egl; + if(!gsr_egl_load(&egl, NULL, true)) { + fprintf(stderr, "gsr error: failed to load opengl\n"); _exit(1); } + + if(gsr_egl_supports_wayland_capture(&egl)) { + gsr_monitor gmon; + if(!get_monitor_by_name(&egl, GSR_CONNECTION_WAYLAND, window_str, &gmon)) { + fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str); + for_each_active_monitor_output(&egl, GSR_CONNECTION_WAYLAND, monitor_output_callback_print, NULL); + _exit(1); + } + } else { + gsr_monitor gmon; + if(!get_monitor_by_name(card_path, GSR_CONNECTION_DRM, window_str, &gmon)) { + fprintf(stderr, "gsr error: display \"%s\" not found, expected one of:\n", window_str); + for_each_active_monitor_output(card_path, GSR_CONNECTION_DRM, monitor_output_callback_print, NULL); + _exit(1); + } + } + gsr_egl_unload(&egl); } else { if(strcmp(window_str, "screen") != 0 && strcmp(window_str, "screen-direct") != 0 && strcmp(window_str, "screen-direct-force") != 0) { gsr_monitor gmon; diff --git a/src/utils.c b/src/utils.c index 27cca42..be77923 100644 --- a/src/utils.c +++ b/src/utils.c @@ -99,6 +99,26 @@ static bool connector_get_property_by_name(int drmfd, drmModeConnectorPtr props, return false; } +static void for_each_active_monitor_output_wayland(gsr_egl *egl, active_monitor_callback callback, void *userdata) { + if(!gsr_egl_supports_wayland_capture(egl)) + return; + + for(int i = 0; i < egl->wayland.num_outputs; ++i) { + if(!egl->wayland.outputs[i].name) + continue; + + gsr_monitor monitor = { + .name = egl->wayland.outputs[i].name, + .name_len = strlen(egl->wayland.outputs[i].name), + .pos = { .x = egl->wayland.outputs[i].pos.x, .y = egl->wayland.outputs[i].pos.y }, + .size = { .x = egl->wayland.outputs[i].size.x, .y = egl->wayland.outputs[i].size.y }, + .crt_info = NULL, + .connector_id = 0 + }; + callback(&monitor, userdata); + } +} + static void for_each_active_monitor_output_drm(const char *drm_card_path, active_monitor_callback callback, void *userdata) { int fd = open(drm_card_path, O_RDONLY); if(fd == -1) @@ -162,7 +182,7 @@ void for_each_active_monitor_output(void *connection, gsr_connection_type connec for_each_active_monitor_output_x11(connection, callback, userdata); break; case GSR_CONNECTION_WAYLAND: - // TODO: use gsr_egl here (connection) + for_each_active_monitor_output_wayland(connection, callback, userdata); break; case GSR_CONNECTION_DRM: for_each_active_monitor_output_drm(connection, callback, userdata);