mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-11-03 09:01:40 -05:00 
			
		
		
		
	screencopy: Implement damage reporting
This commit is contained in:
		
							parent
							
								
									2a63f4fc61
								
							
						
					
					
						commit
						61a6f2b928
					
				
					 2 changed files with 208 additions and 28 deletions
				
			
		| 
						 | 
				
			
			@ -27,9 +27,15 @@ struct wlr_screencopy_manager_v1 {
 | 
			
		|||
	void *data;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct wlr_screencopy_v1_client {
 | 
			
		||||
	int ref;
 | 
			
		||||
	struct wlr_screencopy_manager_v1 *manager;
 | 
			
		||||
	struct wl_list damages;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct wlr_screencopy_frame_v1 {
 | 
			
		||||
	struct wl_resource *resource;
 | 
			
		||||
	struct wlr_screencopy_manager_v1 *manager;
 | 
			
		||||
	struct wlr_screencopy_v1_client *client;
 | 
			
		||||
	struct wl_list link;
 | 
			
		||||
 | 
			
		||||
	enum wl_shm_format format;
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +44,8 @@ struct wlr_screencopy_frame_v1 {
 | 
			
		|||
 | 
			
		||||
	bool overlay_cursor, cursor_locked;
 | 
			
		||||
 | 
			
		||||
	bool with_damage;
 | 
			
		||||
 | 
			
		||||
	struct wl_shm_buffer *buffer;
 | 
			
		||||
	struct wl_listener buffer_destroy;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,10 +8,119 @@
 | 
			
		|||
#include "wlr-screencopy-unstable-v1-protocol.h"
 | 
			
		||||
#include "util/signal.h"
 | 
			
		||||
 | 
			
		||||
#define SCREENCOPY_MANAGER_VERSION 1
 | 
			
		||||
#define SCREENCOPY_MANAGER_VERSION 2
 | 
			
		||||
 | 
			
		||||
struct screencopy_damage {
 | 
			
		||||
	struct wl_list link;
 | 
			
		||||
	struct wlr_output *output;
 | 
			
		||||
	struct pixman_region32 damage;
 | 
			
		||||
	struct wl_listener output_precommit;
 | 
			
		||||
	struct wl_listener output_destroy;
 | 
			
		||||
	uint32_t last_commit_seq;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const struct zwlr_screencopy_frame_v1_interface frame_impl;
 | 
			
		||||
 | 
			
		||||
static struct screencopy_damage *screencopy_damage_find(
 | 
			
		||||
		struct wlr_screencopy_v1_client *client,
 | 
			
		||||
		struct wlr_output *output) {
 | 
			
		||||
	struct screencopy_damage *damage;
 | 
			
		||||
 | 
			
		||||
	wl_list_for_each(damage, &client->damages, link) {
 | 
			
		||||
		if (damage->output == output) {
 | 
			
		||||
			return damage;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screencopy_damage_accumulate(struct screencopy_damage *damage) {
 | 
			
		||||
	struct pixman_region32 *region = &damage->damage;
 | 
			
		||||
	struct wlr_output *output = damage->output;
 | 
			
		||||
 | 
			
		||||
	/* This check is done so damage that has been added and cleared in the
 | 
			
		||||
	 * frame precommit handler is not added again after it has been handled.
 | 
			
		||||
	 */
 | 
			
		||||
	if (damage->last_commit_seq == output->commit_seq) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pixman_region32_union(region, region, &output->pending.damage);
 | 
			
		||||
	pixman_region32_intersect_rect(region, region, 0, 0, output->width,
 | 
			
		||||
		output->height);
 | 
			
		||||
	damage->last_commit_seq = output->commit_seq;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screencopy_damage_handle_output_precommit(
 | 
			
		||||
		struct wl_listener *listener, void *data) {
 | 
			
		||||
	struct screencopy_damage *damage =
 | 
			
		||||
		wl_container_of(listener, damage, output_precommit);
 | 
			
		||||
	screencopy_damage_accumulate(damage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screencopy_damage_destroy(struct screencopy_damage *damage) {
 | 
			
		||||
	wl_list_remove(&damage->output_destroy.link);
 | 
			
		||||
	wl_list_remove(&damage->output_precommit.link);
 | 
			
		||||
	wl_list_remove(&damage->link);
 | 
			
		||||
	pixman_region32_fini(&damage->damage);
 | 
			
		||||
	free(damage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void screencopy_damage_handle_output_destroy(
 | 
			
		||||
		struct wl_listener *listener, void *data) {
 | 
			
		||||
	struct screencopy_damage *damage =
 | 
			
		||||
		wl_container_of(listener, damage, output_destroy);
 | 
			
		||||
	screencopy_damage_destroy(damage);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct screencopy_damage *screencopy_damage_create(
 | 
			
		||||
		struct wlr_screencopy_v1_client *client,
 | 
			
		||||
		struct wlr_output *output) {
 | 
			
		||||
	struct screencopy_damage *damage =
 | 
			
		||||
		calloc(1, sizeof(struct screencopy_damage));
 | 
			
		||||
	if (!damage) {
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	damage->output = output;
 | 
			
		||||
	damage->last_commit_seq = output->commit_seq - 1;
 | 
			
		||||
	pixman_region32_init_rect(&damage->damage, 0, 0, output->width,
 | 
			
		||||
		output->height);
 | 
			
		||||
	wl_list_insert(&client->damages, &damage->link);
 | 
			
		||||
 | 
			
		||||
	wl_signal_add(&output->events.precommit, &damage->output_precommit);
 | 
			
		||||
	damage->output_precommit.notify =
 | 
			
		||||
		screencopy_damage_handle_output_precommit;
 | 
			
		||||
 | 
			
		||||
	wl_signal_add(&output->events.destroy, &damage->output_destroy);
 | 
			
		||||
	damage->output_destroy.notify = screencopy_damage_handle_output_destroy;
 | 
			
		||||
 | 
			
		||||
	return damage;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct screencopy_damage *screencopy_damage_get_or_create(
 | 
			
		||||
		struct wlr_screencopy_v1_client *client,
 | 
			
		||||
		struct wlr_output *output) {
 | 
			
		||||
	struct screencopy_damage *damage = screencopy_damage_find(client, output);
 | 
			
		||||
	return damage ? damage : screencopy_damage_create(client, output);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void client_unref(struct wlr_screencopy_v1_client *client) {
 | 
			
		||||
	assert(client->ref > 0);
 | 
			
		||||
 | 
			
		||||
	if (--client->ref != 0) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct screencopy_damage *damage, *tmp_damage;
 | 
			
		||||
	wl_list_for_each_safe(damage, tmp_damage, &client->damages, link) {
 | 
			
		||||
		screencopy_damage_destroy(damage);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	free(client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct wlr_screencopy_frame_v1 *frame_from_resource(
 | 
			
		||||
		struct wl_resource *resource) {
 | 
			
		||||
	assert(wl_resource_instance_of(resource,
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +145,7 @@ static void frame_destroy(struct wlr_screencopy_frame_v1 *frame) {
 | 
			
		|||
	wl_list_remove(&frame->buffer_destroy.link);
 | 
			
		||||
	// Make the frame resource inert
 | 
			
		||||
	wl_resource_set_user_data(frame->resource, NULL);
 | 
			
		||||
	client_unref(frame->client);
 | 
			
		||||
	free(frame);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +162,17 @@ static void frame_handle_output_precommit(struct wl_listener *listener,
 | 
			
		|||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct screencopy_damage *damage = NULL;
 | 
			
		||||
	if (frame->with_damage) {
 | 
			
		||||
		damage = screencopy_damage_get_or_create(frame->client, output);
 | 
			
		||||
		if (damage) {
 | 
			
		||||
			screencopy_damage_accumulate(damage);
 | 
			
		||||
			if (!pixman_region32_not_empty(&damage->damage)) {
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wl_list_remove(&frame->output_precommit.link);
 | 
			
		||||
	wl_list_init(&frame->output_precommit.link);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,6 +202,22 @@ static void frame_handle_output_precommit(struct wl_listener *listener,
 | 
			
		|||
 | 
			
		||||
	zwlr_screencopy_frame_v1_send_flags(frame->resource, flags);
 | 
			
		||||
 | 
			
		||||
	// TODO: send fine-grained damage events
 | 
			
		||||
	if (damage) {
 | 
			
		||||
		struct pixman_box32 *damage_box =
 | 
			
		||||
			pixman_region32_extents(&damage->damage);
 | 
			
		||||
 | 
			
		||||
		int damage_x = damage_box->x1;
 | 
			
		||||
		int damage_y = damage_box->y1;
 | 
			
		||||
		int damage_width = damage_box->x2 - damage_box->x1;
 | 
			
		||||
		int damage_height = damage_box->y2 - damage_box->y1;
 | 
			
		||||
 | 
			
		||||
		zwlr_screencopy_frame_v1_send_damage(frame->resource,
 | 
			
		||||
			damage_x, damage_y, damage_width, damage_height);
 | 
			
		||||
 | 
			
		||||
		pixman_region32_clear(&damage->damage);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	time_t tv_sec = event->when->tv_sec;
 | 
			
		||||
	uint32_t tv_sec_hi = (sizeof(tv_sec) > 4) ? tv_sec >> 32 : 0;
 | 
			
		||||
	uint32_t tv_sec_lo = tv_sec & 0xFFFFFFFF;
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +253,7 @@ static void frame_handle_buffer_destroy(struct wl_listener *listener,
 | 
			
		|||
	frame_destroy(frame);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void frame_handle_copy(struct wl_client *client,
 | 
			
		||||
static void frame_handle_copy(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *frame_resource,
 | 
			
		||||
		struct wl_resource *buffer_resource) {
 | 
			
		||||
	struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource);
 | 
			
		||||
| 
						 | 
				
			
			@ -185,7 +322,18 @@ static void frame_handle_copy(struct wl_client *client,
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void frame_handle_destroy(struct wl_client *client,
 | 
			
		||||
static void frame_handle_copy_with_damage(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *frame_resource,
 | 
			
		||||
		struct wl_resource *buffer_resource) {
 | 
			
		||||
	struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource);
 | 
			
		||||
	if (frame == NULL) {
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	frame->with_damage = true;
 | 
			
		||||
	frame_handle_copy(wl_client, frame_resource, buffer_resource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void frame_handle_destroy(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *frame_resource) {
 | 
			
		||||
	wl_resource_destroy(frame_resource);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -193,6 +341,7 @@ static void frame_handle_destroy(struct wl_client *client,
 | 
			
		|||
static const struct zwlr_screencopy_frame_v1_interface frame_impl = {
 | 
			
		||||
	.copy = frame_handle_copy,
 | 
			
		||||
	.destroy = frame_handle_destroy,
 | 
			
		||||
	.copy_with_damage = frame_handle_copy_with_damage,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void frame_handle_resource_destroy(struct wl_resource *frame_resource) {
 | 
			
		||||
| 
						 | 
				
			
			@ -203,38 +352,41 @@ static void frame_handle_resource_destroy(struct wl_resource *frame_resource) {
 | 
			
		|||
 | 
			
		||||
static const struct zwlr_screencopy_manager_v1_interface manager_impl;
 | 
			
		||||
 | 
			
		||||
static struct wlr_screencopy_manager_v1 *manager_from_resource(
 | 
			
		||||
static struct wlr_screencopy_v1_client *client_from_resource(
 | 
			
		||||
		struct wl_resource *resource) {
 | 
			
		||||
	assert(wl_resource_instance_of(resource,
 | 
			
		||||
		&zwlr_screencopy_manager_v1_interface, &manager_impl));
 | 
			
		||||
	return wl_resource_get_user_data(resource);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void capture_output(struct wl_client *client,
 | 
			
		||||
		struct wlr_screencopy_manager_v1 *manager, uint32_t version, uint32_t id,
 | 
			
		||||
		int32_t overlay_cursor, struct wlr_output *output,
 | 
			
		||||
static void capture_output(struct wl_client *wl_client,
 | 
			
		||||
		struct wlr_screencopy_v1_client *client, uint32_t version,
 | 
			
		||||
		uint32_t id, int32_t overlay_cursor, struct wlr_output *output,
 | 
			
		||||
		const struct wlr_box *box) {
 | 
			
		||||
	struct wlr_screencopy_frame_v1 *frame =
 | 
			
		||||
		calloc(1, sizeof(struct wlr_screencopy_frame_v1));
 | 
			
		||||
	if (frame == NULL) {
 | 
			
		||||
		wl_client_post_no_memory(client);
 | 
			
		||||
		wl_client_post_no_memory(wl_client);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	frame->manager = manager;
 | 
			
		||||
	frame->output = output;
 | 
			
		||||
	frame->overlay_cursor = !!overlay_cursor;
 | 
			
		||||
 | 
			
		||||
	frame->resource = wl_resource_create(client,
 | 
			
		||||
	frame->resource = wl_resource_create(wl_client,
 | 
			
		||||
		&zwlr_screencopy_frame_v1_interface, version, id);
 | 
			
		||||
	if (frame->resource == NULL) {
 | 
			
		||||
		free(frame);
 | 
			
		||||
		wl_client_post_no_memory(client);
 | 
			
		||||
		wl_client_post_no_memory(wl_client);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	frame->client = client;
 | 
			
		||||
	client->ref++;
 | 
			
		||||
 | 
			
		||||
	wl_resource_set_implementation(frame->resource, &frame_impl, frame,
 | 
			
		||||
		frame_handle_resource_destroy);
 | 
			
		||||
 | 
			
		||||
	wl_list_insert(&manager->frames, &frame->link);
 | 
			
		||||
	wl_list_insert(&client->manager->frames, &frame->link);
 | 
			
		||||
 | 
			
		||||
	wl_list_init(&frame->output_precommit.link);
 | 
			
		||||
	wl_list_init(&frame->output_enable.link);
 | 
			
		||||
| 
						 | 
				
			
			@ -283,23 +435,24 @@ error:
 | 
			
		|||
	frame_destroy(frame);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void manager_handle_capture_output(struct wl_client *client,
 | 
			
		||||
static void manager_handle_capture_output(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *manager_resource, uint32_t id,
 | 
			
		||||
		int32_t overlay_cursor, struct wl_resource *output_resource) {
 | 
			
		||||
	struct wlr_screencopy_manager_v1 *manager =
 | 
			
		||||
		manager_from_resource(manager_resource);
 | 
			
		||||
	struct wlr_screencopy_v1_client *client =
 | 
			
		||||
		client_from_resource(manager_resource);
 | 
			
		||||
	uint32_t version = wl_resource_get_version(manager_resource);
 | 
			
		||||
	struct wlr_output *output = wlr_output_from_resource(output_resource);
 | 
			
		||||
 | 
			
		||||
	capture_output(client, manager, version, id, overlay_cursor, output, NULL);
 | 
			
		||||
	capture_output(wl_client, client, version, id, overlay_cursor, output,
 | 
			
		||||
		NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void manager_handle_capture_output_region(struct wl_client *client,
 | 
			
		||||
static void manager_handle_capture_output_region(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *manager_resource, uint32_t id,
 | 
			
		||||
		int32_t overlay_cursor, struct wl_resource *output_resource,
 | 
			
		||||
		int32_t x, int32_t y, int32_t width, int32_t height) {
 | 
			
		||||
	struct wlr_screencopy_manager_v1 *manager =
 | 
			
		||||
		manager_from_resource(manager_resource);
 | 
			
		||||
	struct wlr_screencopy_v1_client *client =
 | 
			
		||||
		client_from_resource(manager_resource);
 | 
			
		||||
	uint32_t version = wl_resource_get_version(manager_resource);
 | 
			
		||||
	struct wlr_output *output = wlr_output_from_resource(output_resource);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -309,10 +462,11 @@ static void manager_handle_capture_output_region(struct wl_client *client,
 | 
			
		|||
		.width = width,
 | 
			
		||||
		.height = height,
 | 
			
		||||
	};
 | 
			
		||||
	capture_output(client, manager, version, id, overlay_cursor, output, &box);
 | 
			
		||||
	capture_output(wl_client, client, version, id, overlay_cursor, output,
 | 
			
		||||
		&box);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void manager_handle_destroy(struct wl_client *client,
 | 
			
		||||
static void manager_handle_destroy(struct wl_client *wl_client,
 | 
			
		||||
		struct wl_resource *manager_resource) {
 | 
			
		||||
	wl_resource_destroy(manager_resource);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -324,23 +478,41 @@ static const struct zwlr_screencopy_manager_v1_interface manager_impl = {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
void manager_handle_resource_destroy(struct wl_resource *resource) {
 | 
			
		||||
	struct wlr_screencopy_v1_client *client =
 | 
			
		||||
		client_from_resource(resource);
 | 
			
		||||
	client_unref(client);
 | 
			
		||||
	wl_list_remove(wl_resource_get_link(resource));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void manager_bind(struct wl_client *client, void *data, uint32_t version,
 | 
			
		||||
		uint32_t id) {
 | 
			
		||||
static void manager_bind(struct wl_client *wl_client, void *data,
 | 
			
		||||
		uint32_t version, uint32_t id) {
 | 
			
		||||
	struct wlr_screencopy_manager_v1 *manager = data;
 | 
			
		||||
 | 
			
		||||
	struct wl_resource *resource = wl_resource_create(client,
 | 
			
		||||
	struct wlr_screencopy_v1_client *client =
 | 
			
		||||
		calloc(1, sizeof(struct wlr_screencopy_v1_client));
 | 
			
		||||
	if (client == NULL) {
 | 
			
		||||
		goto failure;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct wl_resource *resource = wl_resource_create(wl_client,
 | 
			
		||||
		&zwlr_screencopy_manager_v1_interface, version, id);
 | 
			
		||||
	if (resource == NULL) {
 | 
			
		||||
		wl_client_post_no_memory(client);
 | 
			
		||||
		return;
 | 
			
		||||
		goto failure;
 | 
			
		||||
	}
 | 
			
		||||
	wl_resource_set_implementation(resource, &manager_impl, manager,
 | 
			
		||||
 | 
			
		||||
	client->ref = 1;
 | 
			
		||||
	client->manager = manager;
 | 
			
		||||
	wl_list_init(&client->damages);
 | 
			
		||||
 | 
			
		||||
	wl_resource_set_implementation(resource, &manager_impl, client,
 | 
			
		||||
		manager_handle_resource_destroy);
 | 
			
		||||
 | 
			
		||||
	wl_list_insert(&manager->resources, wl_resource_get_link(resource));
 | 
			
		||||
 | 
			
		||||
	return;
 | 
			
		||||
failure:
 | 
			
		||||
	free(client);
 | 
			
		||||
	wl_client_post_no_memory(wl_client);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_display_destroy(struct wl_listener *listener, void *data) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue