Merge branch 'vulkan-icc-2' into 'master'

ext_image_capture_source_v1: render hidden sRGB for ICC correct

See merge request wlroots/wlroots!5357
This commit is contained in:
Furkan Sahin 2026-06-13 11:06:46 +00:00
commit 56bb41f9f3
4 changed files with 435 additions and 19 deletions

View file

@ -13,9 +13,12 @@
#include <wayland-server-core.h> #include <wayland-server-core.h>
#include <wlr/render/drm_format_set.h> #include <wlr/render/drm_format_set.h>
struct wlr_scene;
struct wlr_scene_node; struct wlr_scene_node;
struct wlr_allocator; struct wlr_allocator;
struct wlr_renderer; struct wlr_renderer;
struct wlr_output;
struct wlr_output_layout;
/** /**
* A screen capture source. * A screen capture source.
@ -79,6 +82,8 @@ struct wlr_ext_image_capture_source_v1_cursor {
*/ */
struct wlr_ext_output_image_capture_source_manager_v1 { struct wlr_ext_output_image_capture_source_manager_v1 {
struct wl_global *global; struct wl_global *global;
struct wlr_scene *scene;
struct wlr_output_layout *layout;
struct { struct {
struct wl_listener display_destroy; struct wl_listener display_destroy;
@ -122,6 +127,14 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_from_res
struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create( struct wlr_ext_output_image_capture_source_manager_v1 *wlr_ext_output_image_capture_source_manager_v1_create(
struct wl_display *display, uint32_t version); struct wl_display *display, uint32_t version);
void wlr_ext_output_image_capture_source_manager_v1_set_scene(
struct wlr_ext_output_image_capture_source_manager_v1 *manager,
struct wlr_scene *scene);
void wlr_ext_output_image_capture_source_manager_v1_set_layout(
struct wlr_ext_output_image_capture_source_manager_v1 *manager,
struct wlr_output_layout *layout);
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(struct wl_display *display, uint32_t version); wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(struct wl_display *display, uint32_t version);
@ -133,6 +146,10 @@ struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_w
struct wlr_scene_node *node, struct wl_event_loop *event_loop, struct wlr_scene_node *node, struct wl_event_loop *event_loop,
struct wlr_allocator *allocator, struct wlr_renderer *renderer); struct wlr_allocator *allocator, struct wlr_renderer *renderer);
struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_with_scene_output(
struct wlr_scene *scene, struct wlr_output *reference_output,
struct wlr_output_layout *layout);
/** /**
* Returns the corresponding wlr_output for a image capture source * Returns the corresponding wlr_output for a image capture source
* managed by wlr_ext_output_image_capture_source_manager_v1 * managed by wlr_ext_output_image_capture_source_manager_v1

View file

@ -37,12 +37,41 @@ struct wlr_ext_output_image_capture_source_v1 {
bool software_cursors_locked; bool software_cursors_locked;
}; };
struct scene_output_source_addon {
struct wlr_addon addon;
struct wlr_ext_image_capture_source_v1 *source;
struct wl_listener source_destroy;
};
struct wlr_ext_output_image_capture_source_v1_frame_event { struct wlr_ext_output_image_capture_source_v1_frame_event {
struct wlr_ext_image_capture_source_v1_frame_event base; struct wlr_ext_image_capture_source_v1_frame_event base;
struct wlr_buffer *buffer; struct wlr_buffer *buffer;
struct timespec when; struct timespec when;
}; };
static void scene_output_source_addon_handle_source_destroy(struct wl_listener *listener,
void *data) {
struct scene_output_source_addon *addon =
wl_container_of(listener, addon, source_destroy);
(void)data;
wl_list_remove(&addon->source_destroy.link);
wlr_addon_finish(&addon->addon);
free(addon);
}
static void scene_output_source_addon_destroy(struct wlr_addon *addon) {
struct scene_output_source_addon *scene_addon =
wl_container_of(addon, scene_addon, addon);
wl_list_remove(&scene_addon->source_destroy.link);
wlr_addon_finish(&scene_addon->addon);
free(scene_addon);
}
static const struct wlr_addon_interface scene_output_source_addon_impl = {
.name = "wlr_ext_output_image_capture_scene_source_v1",
.destroy = scene_output_source_addon_destroy,
};
static void output_source_start(struct wlr_ext_image_capture_source_v1 *base, static void output_source_start(struct wlr_ext_image_capture_source_v1 *base,
bool with_cursors) { bool with_cursors) {
struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base); struct wlr_ext_output_image_capture_source_v1 *source = wl_container_of(base, source, base);
@ -185,30 +214,66 @@ static void output_manager_handle_create_source(struct wl_client *client,
return; return;
} }
struct wlr_ext_output_image_capture_source_v1 *source; struct wlr_ext_output_image_capture_source_manager_v1 *manager =
struct wlr_addon *addon = wlr_addon_find(&output->addons, NULL, &output_addon_impl); wl_resource_get_user_data(manager_resource);
if (addon != NULL) { struct wlr_ext_image_capture_source_v1 *capture_source = NULL;
source = wl_container_of(addon, source, addon);
if (manager->scene != NULL) {
struct scene_output_source_addon *scene_addon = NULL;
struct wlr_addon *addon = wlr_addon_find(&output->addons,
NULL, &scene_output_source_addon_impl);
if (addon != NULL) {
scene_addon = wl_container_of(addon, scene_addon, addon);
capture_source = scene_addon->source;
} else {
scene_addon = calloc(1, sizeof(*scene_addon));
if (scene_addon == NULL) {
wl_resource_post_no_memory(manager_resource);
return;
}
capture_source = wlr_ext_image_capture_source_v1_create_with_scene_output(
manager->scene, output, manager->layout);
if (capture_source == NULL) {
free(scene_addon);
wl_resource_post_no_memory(manager_resource);
return;
}
scene_addon->source = capture_source;
scene_addon->source_destroy.notify = scene_output_source_addon_handle_source_destroy;
wl_signal_add(&capture_source->events.destroy, &scene_addon->source_destroy);
wlr_addon_init(&scene_addon->addon, &output->addons, NULL,
&scene_output_source_addon_impl);
}
} else { } else {
source = calloc(1, sizeof(*source)); struct wlr_ext_output_image_capture_source_v1 *source;
if (source == NULL) { struct wlr_addon *addon = wlr_addon_find(&output->addons, NULL, &output_addon_impl);
wl_resource_post_no_memory(manager_resource); if (addon != NULL) {
return; source = wl_container_of(addon, source, addon);
} else {
source = calloc(1, sizeof(*source));
if (source == NULL) {
wl_resource_post_no_memory(manager_resource);
return;
}
wlr_ext_image_capture_source_v1_init(&source->base, &output_source_impl);
wlr_addon_init(&source->addon, &output->addons, NULL, &output_addon_impl);
source->output = output;
source->output_commit.notify = source_handle_output_commit;
wl_signal_add(&output->events.commit, &source->output_commit);
source_update_buffer_constraints(source);
output_cursor_source_init(&source->cursor, output);
} }
wlr_ext_image_capture_source_v1_init(&source->base, &output_source_impl); capture_source = &source->base;
wlr_addon_init(&source->addon, &output->addons, NULL, &output_addon_impl);
source->output = output;
source->output_commit.notify = source_handle_output_commit;
wl_signal_add(&output->events.commit, &source->output_commit);
source_update_buffer_constraints(source);
output_cursor_source_init(&source->cursor, output);
} }
if (!wlr_ext_image_capture_source_v1_create_resource(&source->base, client, new_id)) { if (!wlr_ext_image_capture_source_v1_create_resource(capture_source, client, new_id)) {
return; return;
} }
} }
@ -236,6 +301,18 @@ static void output_manager_bind(struct wl_client *client, void *data,
wl_resource_set_implementation(resource, &output_manager_impl, manager, NULL); wl_resource_set_implementation(resource, &output_manager_impl, manager, NULL);
} }
void wlr_ext_output_image_capture_source_manager_v1_set_scene(
struct wlr_ext_output_image_capture_source_manager_v1 *manager,
struct wlr_scene *scene) {
manager->scene = scene;
}
void wlr_ext_output_image_capture_source_manager_v1_set_layout(
struct wlr_ext_output_image_capture_source_manager_v1 *manager,
struct wlr_output_layout *layout) {
manager->layout = layout;
}
static void output_manager_handle_display_destroy(struct wl_listener *listener, void *data) { static void output_manager_handle_display_destroy(struct wl_listener *listener, void *data) {
struct wlr_ext_output_image_capture_source_manager_v1 *manager = struct wlr_ext_output_image_capture_source_manager_v1 *manager =
wl_container_of(listener, manager, display_destroy); wl_container_of(listener, manager, display_destroy);

View file

@ -0,0 +1,321 @@
#include <assert.h>
#include <stdlib.h>
#include <pixman.h>
#include <wlr/backend/headless.h>
#include <wlr/interfaces/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_output.h>
#include <wlr/types/wlr_output_layout.h>
#include <wlr/types/wlr_scene.h>
struct scene_output_source {
struct wlr_ext_image_capture_source_v1 base;
struct wlr_scene *scene;
struct wlr_output *ref_output;
struct wlr_output_layout *layout;
struct wlr_backend *headless_backend;
struct wlr_output *headless;
struct wlr_scene_output *scene_output;
struct wl_listener headless_frame;
struct wl_listener headless_commit;
struct wl_listener scene_output_destroy;
struct wl_listener ref_output_commit;
struct wl_listener ref_output_destroy;
size_t num_started;
};
struct scene_output_source_frame_event {
struct wlr_ext_image_capture_source_v1_frame_event base;
struct wlr_buffer *buffer;
struct timespec when;
};
static void scene_output_source_update_constraints(struct scene_output_source *source) {
struct wlr_output *output = source->ref_output;
if (!output->enabled) {
return;
}
if (!wlr_output_configure_primary_swapchain(output, NULL, &output->swapchain)) {
return;
}
wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(&source->base,
output->swapchain, output->renderer);
}
static void scene_output_source_handle_ref_output_commit(struct wl_listener *listener,
void *data) {
struct scene_output_source *source = wl_container_of(listener, source, ref_output_commit);
struct wlr_output_event_commit *event = data;
if (event->state->committed & (WLR_OUTPUT_STATE_MODE |
WLR_OUTPUT_STATE_RENDER_FORMAT | WLR_OUTPUT_STATE_ENABLED)) {
scene_output_source_update_constraints(source);
}
}
static void scene_output_source_handle_scene_output_destroy(struct wl_listener *listener,
void *data) {
struct scene_output_source *source = wl_container_of(listener, source, scene_output_destroy);
(void)data;
source->scene_output = NULL;
wl_list_remove(&source->scene_output_destroy.link);
wl_list_init(&source->scene_output_destroy.link);
}
static void scene_output_source_handle_headless_frame(struct wl_listener *listener,
void *data) {
struct scene_output_source *source = wl_container_of(listener, source, headless_frame);
(void)data;
if (source->scene_output == NULL) {
return;
}
int width = source->ref_output->width;
int height = source->ref_output->height;
if (width <= 0 || height <= 0) {
return;
}
pixman_region32_t damage;
pixman_region32_init_rect(&damage, 0, 0, width, height);
pixman_region32_copy(&source->scene_output->pending_commit_damage, &damage);
pixman_region32_fini(&damage);
struct wlr_scene_output_state_options options = {
.color_transform = NULL,
};
struct wlr_output_state state;
wlr_output_state_init(&state);
wlr_output_state_set_enabled(&state, true);
wlr_output_state_set_custom_mode(&state, width, height, source->ref_output->refresh);
wlr_output_state_set_scale(&state, source->ref_output->scale);
wlr_output_state_set_transform(&state, source->ref_output->transform);
wlr_output_state_set_render_format(&state, source->ref_output->render_format);
if (!wlr_scene_output_build_state(source->scene_output, &state, &options)) {
wlr_output_state_finish(&state);
return;
}
wlr_output_commit_state(source->headless, &state);
wlr_output_state_finish(&state);
}
static void scene_output_source_handle_headless_commit(struct wl_listener *listener,
void *data) {
struct scene_output_source *source = wl_container_of(listener, source, headless_commit);
struct wlr_output_event_commit *event = data;
if (!(event->state->committed & WLR_OUTPUT_STATE_BUFFER)) {
return;
}
struct wlr_buffer *buffer = event->state->buffer;
pixman_region32_t full_damage;
const pixman_region32_t *damage;
if (event->state->committed & WLR_OUTPUT_STATE_DAMAGE) {
damage = &event->state->damage;
} else {
pixman_region32_init_rect(&full_damage, 0, 0, buffer->width, buffer->height);
damage = &full_damage;
}
struct scene_output_source_frame_event frame_event = {
.base = {
.damage = damage,
},
.buffer = buffer,
.when = event->when,
};
wl_signal_emit_mutable(&source->base.events.frame, &frame_event.base);
if (damage == &full_damage) {
pixman_region32_fini(&full_damage);
}
}
static void scene_output_source_start(struct wlr_ext_image_capture_source_v1 *base,
bool with_cursors) {
struct scene_output_source *source = wl_container_of(base, source, base);
(void)with_cursors;
source->num_started++;
if (source->num_started > 1) {
return;
}
bool created_backend = false;
if (source->headless_backend == NULL) {
source->headless_backend = wlr_headless_backend_create(source->ref_output->event_loop);
if (source->headless_backend == NULL) {
source->num_started--;
return;
}
created_backend = true;
if (!wlr_backend_start(source->headless_backend)) {
wlr_backend_destroy(source->headless_backend);
source->headless_backend = NULL;
source->num_started--;
return;
}
}
source->headless = wlr_headless_add_output(source->headless_backend,
source->ref_output->width, source->ref_output->height);
if (source->headless == NULL) {
if (created_backend) {
wlr_backend_destroy(source->headless_backend);
source->headless_backend = NULL;
}
source->num_started--;
return;
}
wlr_output_init_render(source->headless,
source->ref_output->allocator, source->ref_output->renderer);
source->scene_output = wlr_scene_output_create(source->scene, source->headless);
if (source->scene_output == NULL) {
wlr_output_destroy(source->headless);
source->headless = NULL;
if (created_backend) {
wlr_backend_destroy(source->headless_backend);
source->headless_backend = NULL;
}
source->num_started--;
return;
}
if (source->layout != NULL) {
struct wlr_box box;
wlr_output_layout_get_box(source->layout, source->ref_output, &box);
wlr_scene_output_set_position(source->scene_output, box.x, box.y);
}
source->headless_frame.notify = scene_output_source_handle_headless_frame;
wl_signal_add(&source->headless->events.frame, &source->headless_frame);
source->headless_commit.notify = scene_output_source_handle_headless_commit;
wl_signal_add(&source->headless->events.commit, &source->headless_commit);
source->scene_output_destroy.notify = scene_output_source_handle_scene_output_destroy;
wl_signal_add(&source->scene_output->events.destroy, &source->scene_output_destroy);
scene_output_source_update_constraints(source);
wl_signal_emit_mutable(&source->headless->events.frame, source->headless);
}
static void scene_output_source_stop(struct wlr_ext_image_capture_source_v1 *base) {
struct scene_output_source *source = wl_container_of(base, source, base);
assert(source->num_started > 0);
source->num_started--;
if (source->num_started > 0) {
return;
}
if (source->headless != NULL) {
wl_list_remove(&source->headless_frame.link);
wl_list_remove(&source->headless_commit.link);
if (source->scene_output != NULL) {
wl_list_remove(&source->scene_output_destroy.link);
wlr_scene_output_destroy(source->scene_output);
source->scene_output = NULL;
}
wlr_output_destroy(source->headless);
source->headless = NULL;
}
if (source->headless_backend != NULL) {
wlr_backend_destroy(source->headless_backend);
source->headless_backend = NULL;
}
}
static void scene_output_source_request_frame(struct wlr_ext_image_capture_source_v1 *base,
bool schedule_frame) {
struct scene_output_source *source = wl_container_of(base, source, base);
if (schedule_frame && source->headless != NULL) {
wlr_output_schedule_frame(source->headless);
}
}
static void scene_output_source_copy_frame(struct wlr_ext_image_capture_source_v1 *base,
struct wlr_ext_image_copy_capture_frame_v1 *frame,
struct wlr_ext_image_capture_source_v1_frame_event *base_event) {
struct scene_output_source *source = wl_container_of(base, source, base);
struct scene_output_source_frame_event *event = wl_container_of(base_event, event, base);
if (wlr_ext_image_copy_capture_frame_v1_copy_buffer(frame,
event->buffer, source->ref_output->renderer)) {
wlr_ext_image_copy_capture_frame_v1_ready(frame,
source->ref_output->transform, &event->when);
}
}
static const struct wlr_ext_image_capture_source_v1_interface scene_output_source_impl = {
.start = scene_output_source_start,
.stop = scene_output_source_stop,
.request_frame = scene_output_source_request_frame,
.copy_frame = scene_output_source_copy_frame,
};
static void scene_output_source_destroy(struct scene_output_source *source) {
if (source->num_started > 0) {
scene_output_source_stop(&source->base);
}
wl_list_remove(&source->ref_output_commit.link);
wl_list_remove(&source->ref_output_destroy.link);
wlr_ext_image_capture_source_v1_finish(&source->base);
free(source);
}
static void scene_output_source_handle_ref_output_destroy(struct wl_listener *listener,
void *data) {
struct scene_output_source *source = wl_container_of(listener, source, ref_output_destroy);
(void)data;
scene_output_source_destroy(source);
}
struct wlr_ext_image_capture_source_v1 *wlr_ext_image_capture_source_v1_create_with_scene_output(
struct wlr_scene *scene, struct wlr_output *reference_output,
struct wlr_output_layout *layout) {
struct scene_output_source *source = calloc(1, sizeof(*source));
if (source == NULL) {
return NULL;
}
*source = (struct scene_output_source){
.scene = scene,
.ref_output = reference_output,
.layout = layout,
};
wlr_ext_image_capture_source_v1_init(&source->base, &scene_output_source_impl);
wl_list_init(&source->headless_frame.link);
wl_list_init(&source->headless_commit.link);
wl_list_init(&source->scene_output_destroy.link);
source->ref_output_commit.notify = scene_output_source_handle_ref_output_commit;
wl_signal_add(&reference_output->events.commit, &source->ref_output_commit);
source->ref_output_destroy.notify = scene_output_source_handle_ref_output_destroy;
wl_signal_add(&reference_output->events.destroy, &source->ref_output_destroy);
scene_output_source_update_constraints(source);
return &source->base;
}

View file

@ -7,6 +7,7 @@ wlr_files += files(
'ext_image_capture_source_v1/output.c', 'ext_image_capture_source_v1/output.c',
'ext_image_capture_source_v1/foreign_toplevel.c', 'ext_image_capture_source_v1/foreign_toplevel.c',
'ext_image_capture_source_v1/scene.c', 'ext_image_capture_source_v1/scene.c',
'ext_image_capture_source_v1/scene_output.c',
'output/cursor.c', 'output/cursor.c',
'output/output.c', 'output/output.c',
'output/render.c', 'output/render.c',