From 50fb3d1a34be186c2ab1432c513bdb5a5b6b4360 Mon Sep 17 00:00:00 2001 From: itycodes Date: Thu, 3 Oct 2024 05:53:55 +0200 Subject: [PATCH] Preliminary support for toplevel capture. Note: Capture is currently untested, as no compositor has support. Based purely on the protocol specification. Toplevel listing was tested on Sway & wlroots (latest dev + wlroots MR #4545) --- main.c | 202 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 171 insertions(+), 31 deletions(-) diff --git a/main.c b/main.c index 8a67935..39d6ed9 100644 --- a/main.c +++ b/main.c @@ -9,6 +9,7 @@ #include "ext-image-capture-source-v1.prot.h" #include "ext-image-copy-capture-v1.prot.h" +#include "ext-foreign-toplevel-list-v1.prot.h" #include "rif.h" @@ -34,6 +35,8 @@ struct wl_shm* wl_shm = NULL; struct wl_buffer* frame_buffer = NULL; struct ext_output_image_capture_source_manager_v1* output_source_manager = NULL; struct ext_image_copy_capture_manager_v1* capture_manager = NULL; +struct ext_foreign_toplevel_list_v1* toplevel_list = NULL; +struct ext_foreign_toplevel_image_capture_source_manager_v1* toplevel_source_manager = NULL; void* shm_data = NULL; @@ -41,6 +44,7 @@ struct image_data img_data; uint8_t is_frame_ready = 0; uint8_t is_capture_ready = 0; +uint8_t full_screenshot = 0; uint32_t buf_size; @@ -62,6 +66,12 @@ void handle_global( if(strcmp(interface, ext_image_copy_capture_manager_v1_interface.name) == 0) { capture_manager = wl_registry_bind(registry, name, &ext_image_copy_capture_manager_v1_interface, version); } + if(strcmp(interface, ext_foreign_toplevel_list_v1_interface.name) == 0) { + toplevel_list = wl_registry_bind(registry, name, &ext_foreign_toplevel_list_v1_interface, version); + } + if(strcmp(interface, ext_foreign_toplevel_image_capture_source_manager_v1_interface.name) == 0) { + toplevel_source_manager = wl_registry_bind(registry, name, &ext_foreign_toplevel_image_capture_source_manager_v1_interface, version); + } } void handle_global_remove( @@ -76,8 +86,8 @@ const struct wl_registry_listener reg_callbacks = { .global_remove = handle_global_remove, }; -void buffer_size(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1, +void buffer_size(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1, uint32_t width, uint32_t height) { printf("x: %d, y: %d\n", width, height); @@ -85,36 +95,36 @@ void buffer_size(void *data, img_data.height = height; } -void shm_format(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1, +void shm_format(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1, uint32_t format) { img_data.shm_format = format; ASSERT(format == WL_SHM_FORMAT_XBGR8888 || format == WL_SHM_FORMAT_ABGR8888, "Unsupported buffer format."); } -void dmabuf_device(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1, - struct wl_array *device) { +void dmabuf_device(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1, + struct wl_array* device) { } -void dmabuf_format(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1, +void dmabuf_format(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1, uint32_t format, - struct wl_array *modifiers) { + struct wl_array* modifiers) { } -void done(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1) { +void capture_done(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1) { const int shm_fd = memfd_create("wl_shm data", 0); ASSERT(shm_fd != -1, "Failed to create memfd."); // Format is hardcoded to chan width 4 const uint32_t chan_width = 4; - const uint32_t stride = img_data.width * chan_width; - buf_size = img_data.height * stride; + const uint32_t stride = img_data.width* chan_width; + buf_size = img_data.height* stride; ASSERT(ftruncate(shm_fd, buf_size) != -1, "Failed to ftruncate the memfd."); @@ -131,8 +141,8 @@ void done(void *data, } -void stopped(void *data, - struct ext_image_copy_capture_session_v1 *ext_image_copy_capture_session_v1) { +void stopped(void* data, + struct ext_image_copy_capture_session_v1* ext_image_copy_capture_session_v1) { } @@ -141,37 +151,37 @@ const struct ext_image_copy_capture_session_v1_listener capture_listener = { .shm_format = shm_format, .dmabuf_device = dmabuf_device, .dmabuf_format = dmabuf_format, - .done = done, + .done = capture_done, .stopped = stopped }; -void transform(void *data, - struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1, +void transform(void* data, + struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1, uint32_t transform) { } -void damage(void *data, - struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1, +void damage(void* data, + struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1, int32_t x, int32_t y, int32_t width, int32_t height) { } -void presentation_time(void *data, - struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1, +void presentation_time(void* data, + struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { } -void ready(void *data, - struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1) { +void ready(void* data, + struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1) { is_capture_ready = 1; } -void failed(void *data, - struct ext_image_copy_capture_frame_v1 *ext_image_copy_capture_frame_v1, +void failed(void* data, + struct ext_image_copy_capture_frame_v1* ext_image_copy_capture_frame_v1, uint32_t reason) { switch(reason) { case 0: @@ -194,8 +204,90 @@ const struct ext_image_copy_capture_frame_v1_listener frame_listener = { .failed = failed }; +struct handle_data { + struct ext_foreign_toplevel_handle_v1* handle; + const char* title; + const char* app_id; + const char* identifier; +}; + +struct handle_data*** handles; +size_t handles_len = 0; + +void closed(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) { + // TODO handle properly +} +void title(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *title) { + struct handle_data* handle_data = (struct handle_data*)data; + size_t size = strlen(title)+1; + char* title_data = malloc(size); + memcpy(title_data, title, size); + handle_data->title = title_data; +} +void app_id(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *app_id) { + struct handle_data* handle_data = (struct handle_data*)data; + size_t size = strlen(app_id)+1; + char* app_id_data = malloc(size); + memcpy(app_id_data, app_id, size); + handle_data->app_id = app_id_data; +} +void identifier(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1, + const char *identifier) { + struct handle_data* handle_data = (struct handle_data*)data; + size_t size = strlen(identifier)+1; + char* identifier_data = malloc(size); + memcpy(identifier_data, identifier, size); + handle_data->identifier = identifier_data; +} +void toplevel_done(void *data, + struct ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel_handle_v1) { + struct handle_data* handle_data = (struct handle_data*)data; +} + +struct ext_foreign_toplevel_handle_v1_listener handle_listener = { + .closed = closed, + .done = toplevel_done, + .title = title, + .app_id = app_id, + .identifier = identifier, +}; + +void toplevel(void* data, + struct ext_foreign_toplevel_list_v1* ext_foreign_toplevel_list_v1, + struct ext_foreign_toplevel_handle_v1* toplevel) { + handles_len++; + *handles = realloc(*handles, handles_len*sizeof(void*)); + + struct handle_data* handle_data = malloc(sizeof(struct handle_data)); + + handle_data->handle = toplevel; + handle_data->title = NULL; + handle_data->app_id = NULL; + handle_data->identifier = NULL; + + (*handles)[handles_len-1] = handle_data; -int main() { + ext_foreign_toplevel_handle_v1_add_listener(toplevel, &handle_listener, handle_data); +} +void finished(void* data, + struct ext_foreign_toplevel_list_v1* ext_foreign_toplevel_list_v1) { + + // TODO handle exploding properly +} + +const struct ext_foreign_toplevel_list_v1_listener toplevel_listener = { + .toplevel = toplevel, + .finished = finished +}; + + +int main(int argc, char* argv[]) { struct wl_display* dpy = wl_display_connect(NULL); ASSERT(dpy != NULL, "Unable to connect to Wayland"); @@ -208,11 +300,59 @@ int main() { ASSERT(stream_output != NULL, "No outputs found!"); ASSERT(output_source_manager != NULL, "ext-image-capture-source-v1.ext_output_image_capture_source_manager_v1 not supported."); + + struct handle_data* toplevel_handle = NULL; + if(argc == 2) { + ASSERT(toplevel_list != NULL, "ext-foreign-toplevel-list-v1.ext_foreign_toplevel_list_v1 not supported."); + + handles = malloc(sizeof(void*)); + *handles = malloc(sizeof(void*)); + + handles_len = 0; + + ext_foreign_toplevel_list_v1_add_listener(toplevel_list, &toplevel_listener, NULL); + + if(strcmp(argv[1], "?") == 0) { + wl_display_roundtrip(dpy); + for(int i = 0; i < handles_len; i++) { + struct handle_data* handle_data = (*handles)[i]; + printf("title: %s, app_id: %s, identifier: %s\n", handle_data->title, handle_data->app_id, handle_data->identifier); + } + return 0; + } else { + wl_display_roundtrip(dpy); + for(int i = 0; i < handles_len; i++) { + if(strcmp((*handles)[i]->identifier, argv[1]) == 0) { + toplevel_handle = (*handles)[i]; + printf("Found handle: "); + printf("title: %s, app_id: %s, identifier: %s\n", toplevel_handle->title, toplevel_handle->app_id, toplevel_handle->identifier); + } + } + } + } else if(argc == 1) { + full_screenshot = 1; + wl_display_roundtrip(dpy); + } else { + ASSERT(argc >= 1, "Invalid exec!"); + printf("Usage: %s ?|\n"); + exit(-1); + } + ASSERT(capture_manager != NULL, "ext-image-copy-capture-v1.ext_image_copy_capture_manager_v1 not supported."); - struct ext_image_capture_source_v1* capture_source = - ext_output_image_capture_source_manager_v1_create_source(output_source_manager, stream_output); - ASSERT(capture_source != NULL, "Failed creating a source for the default output!") + struct ext_image_capture_source_v1* capture_source; + if(full_screenshot) { + ASSERT(output_source_manager != NULL, "ext-image-capture-source-v1.ext_output_image_capture_source_manager_v1 not supported."); + capture_source = + ext_output_image_capture_source_manager_v1_create_source(output_source_manager, stream_output); + ASSERT(capture_source != NULL, "Failed creating a source for the default output!") + } else { + ASSERT(toplevel_handle != NULL, "Invalid toplevel handle!"); + ASSERT(toplevel_source_manager != NULL, "ext-image-capture-source-v1.ext_foreign_toplevel_image_capture_source_manager_v1 not supported."); + capture_source = + ext_foreign_toplevel_image_capture_source_manager_v1_create_source(toplevel_source_manager, toplevel_handle->handle); + ASSERT(capture_source != NULL, "Failed creating a source for the toplevel!") + } struct ext_image_copy_capture_session_v1* capture_session = ext_image_copy_capture_manager_v1_create_session(capture_manager, capture_source, EXT_IMAGE_COPY_CAPTURE_MANAGER_V1_OPTIONS_PAINT_CURSORS);