parent
							
								
									02dfa9101e
								
							
						
					
					
						commit
						73755ad348
					
				| @ -0,0 +1,232 @@ | |||||||
|  | /*
 | ||||||
|  |  * Copyright © 2008 Kristian Høgsberg | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a | ||||||
|  |  * copy of this software and associated documentation files (the "Software"), | ||||||
|  |  * to deal in the Software without restriction, including without limitation | ||||||
|  |  * the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||||
|  |  * and/or sell copies of the Software, and to permit persons to whom the | ||||||
|  |  * Software is furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice (including the next | ||||||
|  |  * paragraph) shall be included in all copies or substantial portions of the | ||||||
|  |  * Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||||
|  |  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||||
|  |  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||||
|  |  * DEALINGS IN THE SOFTWARE. | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #define _XOPEN_SOURCE 700 | ||||||
|  | #define _POSIX_C_SOURCE 199309L | ||||||
|  | #include <errno.h> | ||||||
|  | #include <fcntl.h> | ||||||
|  | #include <limits.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <sys/mman.h> | ||||||
|  | #include <sys/param.h> | ||||||
|  | #include <sys/wait.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <wayland-client-protocol.h> | ||||||
|  | #include "wlr-screencopy-unstable-v1-client-protocol.h" | ||||||
|  | 
 | ||||||
|  | static struct wl_shm *shm = NULL; | ||||||
|  | static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; | ||||||
|  | static struct wl_output *output = NULL; | ||||||
|  | 
 | ||||||
|  | static struct { | ||||||
|  | 	struct wl_buffer *wl_buffer; | ||||||
|  | 	void *data; | ||||||
|  | 	int width, height, stride; | ||||||
|  | } buffer; | ||||||
|  | bool buffer_copy_done = false; | ||||||
|  | 
 | ||||||
|  | static int backingfile(off_t size) { | ||||||
|  | 	char template[] = "/tmp/wlroots-shared-XXXXXX"; | ||||||
|  | 	int fd = mkstemp(template); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	int ret; | ||||||
|  | 	while ((ret = ftruncate(fd, size)) == EINTR) { | ||||||
|  | 		// No-op
 | ||||||
|  | 	} | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		close(fd); | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	unlink(template); | ||||||
|  | 	return fd; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wl_buffer *create_shm_buffer(int width, int height, | ||||||
|  | 		int *stride_out, void **data_out) { | ||||||
|  | 	int stride = width * 4; | ||||||
|  | 	int size = stride * height; | ||||||
|  | 
 | ||||||
|  | 	int fd = backingfile(size); | ||||||
|  | 	if (fd < 0) { | ||||||
|  | 		fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	void *data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); | ||||||
|  | 	if (data == MAP_FAILED) { | ||||||
|  | 		fprintf(stderr, "mmap failed: %m\n"); | ||||||
|  | 		close(fd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); | ||||||
|  | 	close(fd); | ||||||
|  | 	struct wl_buffer *buffer = wl_shm_pool_create_buffer(pool, 0, width, height, | ||||||
|  | 		stride, WL_SHM_FORMAT_XRGB8888); | ||||||
|  | 	wl_shm_pool_destroy(pool); | ||||||
|  | 
 | ||||||
|  | 	*data_out = data; | ||||||
|  | 	*stride_out = stride; | ||||||
|  | 	return buffer; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_buffer(void *data, | ||||||
|  | 		struct zwlr_screencopy_frame_v1 *frame, uint32_t width, uint32_t height, | ||||||
|  | 		uint32_t flags, uint32_t format, uint32_t stride) { | ||||||
|  | 	buffer.width = width; | ||||||
|  | 	buffer.height = height; | ||||||
|  | 	buffer.wl_buffer = | ||||||
|  | 		create_shm_buffer(width, height, &buffer.stride, &buffer.data); | ||||||
|  | 	if (buffer.wl_buffer == NULL) { | ||||||
|  | 		fprintf(stderr, "failed to create buffer\n"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_ready(void *data, | ||||||
|  | 		struct zwlr_screencopy_frame_v1 *frame) { | ||||||
|  | 	buffer_copy_done = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_failed(void *data, | ||||||
|  | 		struct zwlr_screencopy_frame_v1 *frame) { | ||||||
|  | 	fprintf(stderr, "failed to copy frame\n"); | ||||||
|  | 	exit(EXIT_FAILURE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zwlr_screencopy_frame_v1_listener frame_listener = { | ||||||
|  | 	.buffer = frame_handle_buffer, | ||||||
|  | 	.ready = frame_handle_ready, | ||||||
|  | 	.failed = frame_handle_failed, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void handle_global(void *data, struct wl_registry *registry, | ||||||
|  | 		uint32_t name, const char *interface, uint32_t version) { | ||||||
|  | 	if (strcmp(interface, wl_output_interface.name) == 0 && output == NULL) { | ||||||
|  | 		output = wl_registry_bind(registry, name, &wl_output_interface, 1); | ||||||
|  | 	} else if (strcmp(interface, wl_shm_interface.name) == 0) { | ||||||
|  | 		shm = wl_registry_bind(registry, name, &wl_shm_interface, 1); | ||||||
|  | 	} else if (strcmp(interface, zwlr_screencopy_manager_v1_interface.name) | ||||||
|  | 			== 0) { | ||||||
|  | 		screencopy_manager = wl_registry_bind(registry, name, | ||||||
|  | 			&zwlr_screencopy_manager_v1_interface, 1); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_global_remove(void *data, struct wl_registry *registry, | ||||||
|  | 		uint32_t name) { | ||||||
|  | 	// Who cares?
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct wl_registry_listener registry_listener = { | ||||||
|  | 	.global = handle_global, | ||||||
|  | 	.global_remove = handle_global_remove, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void write_image(const char *filename, int width, int height, int stride, | ||||||
|  | 		void *data) { | ||||||
|  | 	char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits
 | ||||||
|  | 	sprintf(size, "%dx%d+0", width, height); | ||||||
|  | 
 | ||||||
|  | 	int fd[2]; | ||||||
|  | 	if (pipe(fd) != 0) { | ||||||
|  | 		fprintf(stderr, "cannot create pipe: %s\n", strerror(errno)); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	pid_t child = fork(); | ||||||
|  | 	if (child < 0) { | ||||||
|  | 		fprintf(stderr, "fork() failed\n"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} else if (child != 0) { | ||||||
|  | 		close(fd[0]); | ||||||
|  | 		if (write(fd[1], data, stride * height) < 0) { | ||||||
|  | 			fprintf(stderr, "write() failed: %s\n", strerror(errno)); | ||||||
|  | 			exit(EXIT_FAILURE); | ||||||
|  | 		} | ||||||
|  | 		close(fd[1]); | ||||||
|  | 		waitpid(child, NULL, 0); | ||||||
|  | 	} else { | ||||||
|  | 		close(fd[1]); | ||||||
|  | 		if (dup2(fd[0], 0) != 0) { | ||||||
|  | 			fprintf(stderr, "cannot dup the pipe\n"); | ||||||
|  | 			exit(EXIT_FAILURE); | ||||||
|  | 		} | ||||||
|  | 		close(fd[0]); | ||||||
|  | 		// We requested WL_SHM_FORMAT_XRGB8888 in little endian, so that's BGRA
 | ||||||
|  | 		// in big endian.
 | ||||||
|  | 		execlp("convert", "convert", "-depth", "8", "-size", size, "bgra:-", | ||||||
|  | 			"-alpha", "opaque", filename, NULL); | ||||||
|  | 		fprintf(stderr, "cannot execute convert\n"); | ||||||
|  | 		exit(EXIT_FAILURE); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  | 	struct wl_display * display = wl_display_connect(NULL); | ||||||
|  | 	if (display == NULL) { | ||||||
|  | 		fprintf(stderr, "failed to create display: %m\n"); | ||||||
|  | 		return EXIT_FAILURE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_registry *registry = wl_display_get_registry(display); | ||||||
|  | 	wl_registry_add_listener(registry, ®istry_listener, NULL); | ||||||
|  | 	wl_display_dispatch(display); | ||||||
|  | 	wl_display_roundtrip(display); | ||||||
|  | 
 | ||||||
|  | 	if (shm == NULL) { | ||||||
|  | 		fprintf(stderr, "compositor is missing wl_shm\n"); | ||||||
|  | 		return EXIT_FAILURE; | ||||||
|  | 	} | ||||||
|  | 	if (screencopy_manager == NULL) { | ||||||
|  | 		fprintf(stderr, "compositor doesn't support wlr-screencopy-unstable-v1\n"); | ||||||
|  | 		return EXIT_FAILURE; | ||||||
|  | 	} | ||||||
|  | 	if (output == NULL) { | ||||||
|  | 		fprintf(stderr, "no output available\n"); | ||||||
|  | 		return EXIT_FAILURE; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct zwlr_screencopy_frame_v1 *frame = | ||||||
|  | 		zwlr_screencopy_manager_v1_capture_output(screencopy_manager, output); | ||||||
|  | 	zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL); | ||||||
|  | 
 | ||||||
|  | 	while (!buffer_copy_done) { | ||||||
|  | 		wl_display_roundtrip(display); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	write_image("wayland-screenshot.png", buffer.width, buffer.height, | ||||||
|  | 		buffer.stride, buffer.data); | ||||||
|  | 	wl_buffer_destroy(buffer.wl_buffer); | ||||||
|  | 
 | ||||||
|  | 	return EXIT_SUCCESS; | ||||||
|  | } | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | #ifndef WLR_TYPES_WLR_SCREENCOPY_V1_H | ||||||
|  | #define WLR_TYPES_WLR_SCREENCOPY_V1_H | ||||||
|  | 
 | ||||||
|  | #include <wayland-server.h> | ||||||
|  | 
 | ||||||
|  | struct wlr_screencopy_manager_v1 { | ||||||
|  | 	struct wl_global *global; | ||||||
|  | 	struct wl_list resources; // wl_resource
 | ||||||
|  | 	struct wl_list frames; // wlr_screencopy_frame_v1::link
 | ||||||
|  | 
 | ||||||
|  | 	struct wl_listener display_destroy; | ||||||
|  | 
 | ||||||
|  | 	void *data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wlr_screencopy_frame_v1 { | ||||||
|  | 	struct wl_resource *resource; | ||||||
|  | 	struct wlr_screencopy_manager_v1 *manager; | ||||||
|  | 	struct wl_list link; | ||||||
|  | 
 | ||||||
|  | 	int32_t width, height; | ||||||
|  | 
 | ||||||
|  | 	struct wl_shm_buffer *buffer; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_output *output; | ||||||
|  | 	struct wl_listener output_swap_buffers; | ||||||
|  | 
 | ||||||
|  | 	void *data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( | ||||||
|  | 	struct wl_display *display); | ||||||
|  | void wlr_screencopy_manager_v1_destroy( | ||||||
|  | 	struct wlr_screencopy_manager_v1 *screencopy); | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -0,0 +1,122 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <protocol name="wlr_screencopy_unstable_v1"> | ||||||
|  |   <copyright> | ||||||
|  |     Copyright © 2018 Simon Ser | ||||||
|  | 
 | ||||||
|  |     Permission is hereby granted, free of charge, to any person obtaining a | ||||||
|  |     copy of this software and associated documentation files (the "Software"), | ||||||
|  |     to deal in the Software without restriction, including without limitation | ||||||
|  |     the rights to use, copy, modify, merge, publish, distribute, sublicense, | ||||||
|  |     and/or sell copies of the Software, and to permit persons to whom the | ||||||
|  |     Software is furnished to do so, subject to the following conditions: | ||||||
|  | 
 | ||||||
|  |     The above copyright notice and this permission notice (including the next | ||||||
|  |     paragraph) shall be included in all copies or substantial portions of the | ||||||
|  |     Software. | ||||||
|  | 
 | ||||||
|  |     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL | ||||||
|  |     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | ||||||
|  |     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | ||||||
|  |     DEALINGS IN THE SOFTWARE. | ||||||
|  |   </copyright> | ||||||
|  | 
 | ||||||
|  |   <description summary="screen content capturing on client buffers"> | ||||||
|  |     This protocol allows clients to ask the compositor to copy part of the | ||||||
|  |     screen content to a client buffer. | ||||||
|  |   </description> | ||||||
|  | 
 | ||||||
|  |   <interface name="zwlr_screencopy_manager_v1" version="1"> | ||||||
|  |     <description summary="manager to inform clients and begin capturing"> | ||||||
|  |       This object is a manager which offers requests to start capturing from a | ||||||
|  |       source. | ||||||
|  |     </description> | ||||||
|  | 
 | ||||||
|  |     <request name="capture_output"> | ||||||
|  |       <description summary="start capturing"> | ||||||
|  |         Request to start capturing from the given output. | ||||||
|  |       </description> | ||||||
|  |       <arg name="frame" type="new_id" interface="zwlr_screencopy_frame_v1"/> | ||||||
|  |       <arg name="output" type="object" interface="wl_output"/> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <request name="destroy" type="destructor"> | ||||||
|  |       <description summary="destroy the manager"> | ||||||
|  |         All objects created by the manager will still remain valid, until their | ||||||
|  |         appropriate destroy request has been called. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  |   </interface> | ||||||
|  | 
 | ||||||
|  |   <interface name="zwlr_screencopy_frame_v1" version="1"> | ||||||
|  |     <description summary="a frame ready for copy"> | ||||||
|  |       This object represents a frame which is ready to have its resources | ||||||
|  |       fetched and used. | ||||||
|  |     </description> | ||||||
|  | 
 | ||||||
|  |     <enum name="flags" bitfield="true"> | ||||||
|  |       <entry name="y_invert" value="1" summary="contents are y-inverted"/> | ||||||
|  |     </enum> | ||||||
|  | 
 | ||||||
|  |     <event name="buffer"> | ||||||
|  |       <description summary="buffer information"> | ||||||
|  |         Provides information about the frame's buffer. This event is sent once | ||||||
|  |         as soon as the frame is created. | ||||||
|  | 
 | ||||||
|  |         The client should then create a buffer with the provided width and | ||||||
|  |         height, and send a copy request. It can optionally create a buffer with | ||||||
|  |         the preferred format and stride. | ||||||
|  |       </description> | ||||||
|  |       <arg name="width" type="uint" summary="buffer width"/> | ||||||
|  |       <arg name="height" type="uint" summary="buffer height"/> | ||||||
|  |       <arg name="flags" type="uint" enum="flags" summary="buffer flags"/> | ||||||
|  |       <arg name="format" type="uint" summary="preferred DRM_FORMAT"/> | ||||||
|  |       <arg name="stride" type="uint" summary="preferred stride"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="copy"> | ||||||
|  |       <description summary="copy the frame"> | ||||||
|  |         Copy the frame to the supplied buffer. The buffer must have a the | ||||||
|  |         correct size, see zwlr_screencopy_frame_v1.buffer. The buffer needs to | ||||||
|  |         have a supported format. | ||||||
|  | 
 | ||||||
|  |         If the frame is successfully copied, a ready event is sent. Otherwise, | ||||||
|  |         an abort event is sent. | ||||||
|  |       </description> | ||||||
|  |       <arg name="buffer" type="object" interface="wl_buffer"/> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <enum name="error"> | ||||||
|  |       <entry name="already_used" value="0" | ||||||
|  |         summary="the object has already been used to copy a wl_buffer"/> | ||||||
|  |       <entry name="invalid_format" value="1" summary="format not supported"/> | ||||||
|  |       <entry name="invalid_dimensions" value="2" | ||||||
|  |         summary="invalid width or height"/> | ||||||
|  |     </enum> | ||||||
|  | 
 | ||||||
|  |     <event name="ready"> | ||||||
|  |       <description summary="indicates frame is available for reading"> | ||||||
|  |         Called as soon as the frame is copied, indicating it is available | ||||||
|  |         for reading. | ||||||
|  | 
 | ||||||
|  |         Upon receiving this event, the client should destroy the object. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="failed"> | ||||||
|  |       <description summary="frame copy failed"> | ||||||
|  |         This event indicates that the attempted frame copy has failed. | ||||||
|  | 
 | ||||||
|  |         Upon receiving this event, the client should destroy the object. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="destroy" type="destructor"> | ||||||
|  |       <description summary="delete this object, used or not"> | ||||||
|  |         Destroys the frame. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  |   </interface> | ||||||
|  | </protocol> | ||||||
| @ -0,0 +1,250 @@ | |||||||
|  | #include <assert.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <wlr/render/wlr_renderer.h> | ||||||
|  | #include <wlr/types/wlr_output.h> | ||||||
|  | #include <wlr/types/wlr_screencopy_v1.h> | ||||||
|  | #include <wlr/backend.h> | ||||||
|  | #include "wlr-screencopy-unstable-v1-protocol.h" | ||||||
|  | 
 | ||||||
|  | #define SCREENCOPY_MANAGER_VERSION 1 | ||||||
|  | 
 | ||||||
|  | static const struct zwlr_screencopy_frame_v1_interface frame_impl; | ||||||
|  | 
 | ||||||
|  | static struct wlr_screencopy_frame_v1 *frame_from_resource( | ||||||
|  | 		struct wl_resource *resource) { | ||||||
|  | 	assert(wl_resource_instance_of(resource, | ||||||
|  | 		&zwlr_screencopy_frame_v1_interface, &frame_impl)); | ||||||
|  | 	return wl_resource_get_user_data(resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_output_swap_buffers(struct wl_listener *listener, | ||||||
|  | 		void *_data) { | ||||||
|  | 	struct wlr_screencopy_frame_v1 *frame = | ||||||
|  | 		wl_container_of(listener, frame, output_swap_buffers); | ||||||
|  | 	struct wlr_output *output = frame->output; | ||||||
|  | 	struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&frame->output_swap_buffers.link); | ||||||
|  | 	wl_list_init(&frame->output_swap_buffers.link); | ||||||
|  | 
 | ||||||
|  | 	if (output->width != frame->width || output->height != frame->height) { | ||||||
|  | 		zwlr_screencopy_frame_v1_send_failed(frame->resource); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_shm_buffer *buffer = frame->buffer; | ||||||
|  | 	assert(buffer != NULL); | ||||||
|  | 
 | ||||||
|  | 	enum wl_shm_format fmt = wl_shm_buffer_get_format(buffer); | ||||||
|  | 	int32_t width = wl_shm_buffer_get_width(buffer); | ||||||
|  | 	int32_t height = wl_shm_buffer_get_height(buffer); | ||||||
|  | 	int32_t stride = wl_shm_buffer_get_stride(buffer); | ||||||
|  | 
 | ||||||
|  | 	wl_shm_buffer_begin_access(buffer); | ||||||
|  | 	void *data = wl_shm_buffer_get_data(buffer); | ||||||
|  | 	bool ok = wlr_renderer_read_pixels(renderer, fmt, stride, width, height, | ||||||
|  | 		0, 0, 0, 0, data); | ||||||
|  | 	wl_shm_buffer_end_access(buffer); | ||||||
|  | 
 | ||||||
|  | 	if (!ok) { | ||||||
|  | 		zwlr_screencopy_frame_v1_send_failed(frame->resource); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	zwlr_screencopy_frame_v1_send_ready(frame->resource); | ||||||
|  | 
 | ||||||
|  | 	// TODO: make frame resource inert
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_copy(struct wl_client *client, | ||||||
|  | 		struct wl_resource *frame_resource, | ||||||
|  | 		struct wl_resource *buffer_resource) { | ||||||
|  | 	struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); | ||||||
|  | 	struct wlr_output *output = frame->output; | ||||||
|  | 	struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||||||
|  | 
 | ||||||
|  | 	struct wl_shm_buffer *buffer = wl_shm_buffer_get(buffer_resource); | ||||||
|  | 	if (buffer == NULL) { | ||||||
|  | 		zwlr_screencopy_frame_v1_send_failed(frame_resource); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	enum wl_shm_format fmt = wl_shm_buffer_get_format(buffer); | ||||||
|  | 	if (!wlr_renderer_format_supported(renderer, fmt)) { | ||||||
|  | 		wl_resource_post_error(frame->resource, | ||||||
|  | 			ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_FORMAT, | ||||||
|  | 			"unsupported format %"PRIu32, fmt); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (frame->width != wl_shm_buffer_get_width(buffer) || | ||||||
|  | 			frame->height != wl_shm_buffer_get_height(buffer)) { | ||||||
|  | 		wl_resource_post_error(frame->resource, | ||||||
|  | 			ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_DIMENSIONS, | ||||||
|  | 			"invalid width or height"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!wl_list_empty(&frame->output_swap_buffers.link) || | ||||||
|  | 			frame->buffer != NULL) { | ||||||
|  | 		wl_resource_post_error(frame->resource, | ||||||
|  | 			ZWLR_SCREENCOPY_FRAME_V1_ERROR_ALREADY_USED, | ||||||
|  | 			"frame already used"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	frame->buffer = buffer; | ||||||
|  | 
 | ||||||
|  | 	wl_signal_add(&output->events.swap_buffers, &frame->output_swap_buffers); | ||||||
|  | 	frame->output_swap_buffers.notify = frame_handle_output_swap_buffers; | ||||||
|  | 
 | ||||||
|  | 	// Schedule a buffer swap
 | ||||||
|  | 	output->needs_swap = true; | ||||||
|  | 	wlr_output_schedule_frame(output); | ||||||
|  | 
 | ||||||
|  | 	// TODO: listen to buffer destroy
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void frame_handle_destroy(struct wl_client *client, | ||||||
|  | 		struct wl_resource *frame_resource) { | ||||||
|  | 	wl_resource_destroy(frame_resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zwlr_screencopy_frame_v1_interface frame_impl = { | ||||||
|  | 	.copy = frame_handle_copy, | ||||||
|  | 	.destroy = frame_handle_destroy, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void frame_handle_resource_destroy(struct wl_resource *frame_resource) { | ||||||
|  | 	struct wlr_screencopy_frame_v1 *frame = frame_from_resource(frame_resource); | ||||||
|  | 	wl_list_remove(&frame->link); | ||||||
|  | 	wl_list_remove(&frame->output_swap_buffers.link); | ||||||
|  | 	free(frame); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static const struct zwlr_screencopy_manager_v1_interface manager_impl; | ||||||
|  | 
 | ||||||
|  | static struct wlr_screencopy_manager_v1 *manager_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 manager_handle_capture_output(struct wl_client *client, | ||||||
|  | 		struct wl_resource *manager_resource, uint32_t id, | ||||||
|  | 		struct wl_resource *output_resource) { | ||||||
|  | 	struct wlr_screencopy_manager_v1 *manager = | ||||||
|  | 		manager_from_resource(manager_resource); | ||||||
|  | 	struct wlr_output *output = wlr_output_from_resource(output_resource); | ||||||
|  | 
 | ||||||
|  | 	struct wlr_screencopy_frame_v1 *frame = | ||||||
|  | 		calloc(1, sizeof(struct wlr_screencopy_frame_v1)); | ||||||
|  | 	if (frame == NULL) { | ||||||
|  | 		wl_resource_post_no_memory(manager_resource); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	frame->manager = manager; | ||||||
|  | 
 | ||||||
|  | 	frame->output = output; | ||||||
|  | 
 | ||||||
|  | 	uint32_t version = wl_resource_get_version(manager_resource); | ||||||
|  | 	frame->resource = wl_resource_create(client, | ||||||
|  | 		&zwlr_screencopy_frame_v1_interface, version, id); | ||||||
|  | 	if (frame->resource == NULL) { | ||||||
|  | 		free(frame); | ||||||
|  | 		wl_client_post_no_memory(client); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	wl_resource_set_implementation(frame->resource, &frame_impl, frame, | ||||||
|  | 		frame_handle_resource_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&manager->frames, &frame->link); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&frame->output_swap_buffers.link); | ||||||
|  | 
 | ||||||
|  | 	frame->width = output->width; | ||||||
|  | 	frame->height = output->height; | ||||||
|  | 	// TODO: don't send zero
 | ||||||
|  | 	zwlr_screencopy_frame_v1_send_buffer(frame->resource, | ||||||
|  | 		frame->width, frame->height, 0, 0, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void manager_handle_destroy(struct wl_client *client, | ||||||
|  | 		struct wl_resource *manager_resource) { | ||||||
|  | 	wl_resource_destroy(manager_resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zwlr_screencopy_manager_v1_interface manager_impl = { | ||||||
|  | 	.capture_output = manager_handle_capture_output, | ||||||
|  | 	.destroy = manager_handle_destroy, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void manager_handle_resource_destroy(struct wl_resource *resource) { | ||||||
|  | 	wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void manager_bind(struct wl_client *client, void *data, uint32_t version, | ||||||
|  | 		uint32_t id) { | ||||||
|  | 	struct wlr_screencopy_manager_v1 *manager = data; | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource = wl_resource_create(client, | ||||||
|  | 		&zwlr_screencopy_manager_v1_interface, version, id); | ||||||
|  | 	if (resource == NULL) { | ||||||
|  | 		wl_client_post_no_memory(client); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	wl_resource_set_implementation(resource, &manager_impl, manager, | ||||||
|  | 		manager_handle_resource_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&manager->resources, wl_resource_get_link(resource)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void handle_display_destroy(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct wlr_screencopy_manager_v1 *manager = | ||||||
|  | 		wl_container_of(listener, manager, display_destroy); | ||||||
|  | 	wlr_screencopy_manager_v1_destroy(manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct wlr_screencopy_manager_v1 *wlr_screencopy_manager_v1_create( | ||||||
|  | 		struct wl_display *display) { | ||||||
|  | 	struct wlr_screencopy_manager_v1 *manager = | ||||||
|  | 		calloc(1, sizeof(struct wlr_screencopy_manager_v1)); | ||||||
|  | 	if (manager == NULL) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	manager->global = wl_global_create(display, | ||||||
|  | 		&zwlr_screencopy_manager_v1_interface, SCREENCOPY_MANAGER_VERSION, | ||||||
|  | 		manager, manager_bind); | ||||||
|  | 	if (manager->global == NULL) { | ||||||
|  | 		free(manager); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	wl_list_init(&manager->resources); | ||||||
|  | 	wl_list_init(&manager->frames); | ||||||
|  | 
 | ||||||
|  | 	manager->display_destroy.notify = handle_display_destroy; | ||||||
|  | 	wl_display_add_destroy_listener(display, &manager->display_destroy); | ||||||
|  | 
 | ||||||
|  | 	return manager; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void wlr_screencopy_manager_v1_destroy( | ||||||
|  | 		struct wlr_screencopy_manager_v1 *manager) { | ||||||
|  | 	if (manager == NULL) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	wl_list_remove(&manager->display_destroy.link); | ||||||
|  | 	struct wlr_screencopy_frame_v1 *frame, *tmp_frame; | ||||||
|  | 	wl_list_for_each_safe(frame, tmp_frame, &manager->frames, link) { | ||||||
|  | 		wl_resource_destroy(frame->resource); | ||||||
|  | 	} | ||||||
|  | 	struct wl_resource *resource, *tmp_resource; | ||||||
|  | 	wl_resource_for_each_safe(resource, tmp_resource, &manager->resources) { | ||||||
|  | 		wl_resource_destroy(resource); | ||||||
|  | 	} | ||||||
|  | 	wl_global_destroy(manager->global); | ||||||
|  | 	free(manager); | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue