mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-10-29 05:40:12 -04:00 
			
		
		
		
	screencopy-v1: add basic implementation
This commit is contained in:
		
							parent
							
								
									02dfa9101e
								
							
						
					
					
						commit
						73755ad348
					
				
					 9 changed files with 655 additions and 1 deletions
				
			
		|  | @ -56,3 +56,9 @@ if libavutil.found() and libavcodec.found() and libavformat.found() | |||
| 				libavformat, wlroots, threads ] | ||||
| 	) | ||||
| endif | ||||
| 
 | ||||
| executable( | ||||
| 	'screencopy', | ||||
| 	'screencopy.c', | ||||
| 	dependencies: [wayland_client, wlr_protos, wlroots] | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										232
									
								
								examples/screencopy.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										232
									
								
								examples/screencopy.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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; | ||||
| } | ||||
|  | @ -22,6 +22,7 @@ | |||
| #include <wlr/types/wlr_list.h> | ||||
| #include <wlr/types/wlr_idle.h> | ||||
| #include <wlr/types/wlr_idle_inhibit_v1.h> | ||||
| #include <wlr/types/wlr_screencopy_v1.h> | ||||
| #include "rootston/view.h" | ||||
| #include "rootston/config.h" | ||||
| #include "rootston/output.h" | ||||
|  | @ -54,6 +55,7 @@ struct roots_desktop { | |||
| 	struct wlr_linux_dmabuf *linux_dmabuf; | ||||
| 	struct wlr_layer_shell *layer_shell; | ||||
| 	struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard; | ||||
| 	struct wlr_screencopy_manager_v1 *screencopy; | ||||
| 
 | ||||
| 	struct wl_listener new_output; | ||||
| 	struct wl_listener layout_change; | ||||
|  |  | |||
							
								
								
									
										36
									
								
								include/wlr/types/wlr_screencopy_v1.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								include/wlr/types/wlr_screencopy_v1.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -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 | ||||
|  | @ -42,6 +42,7 @@ protocols = [ | |||
| 	'wlr-export-dmabuf-unstable-v1.xml', | ||||
| 	'wlr-input-inhibitor-unstable-v1.xml', | ||||
| 	'wlr-layer-shell-unstable-v1.xml', | ||||
| 	'wlr-screencopy-unstable-v1.xml', | ||||
| ] | ||||
| 
 | ||||
| client_protocols = [ | ||||
|  | @ -51,8 +52,9 @@ client_protocols = [ | |||
| 	'idle.xml', | ||||
| 	'screenshooter.xml', | ||||
| 	'wlr-export-dmabuf-unstable-v1.xml', | ||||
| 	'wlr-layer-shell-unstable-v1.xml', | ||||
| 	'wlr-input-inhibitor-unstable-v1.xml', | ||||
| 	'wlr-layer-shell-unstable-v1.xml', | ||||
| 	'wlr-screencopy-unstable-v1.xml', | ||||
| ] | ||||
| 
 | ||||
| wl_protos_src = [] | ||||
|  |  | |||
							
								
								
									
										122
									
								
								protocol/wlr-screencopy-unstable-v1.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								protocol/wlr-screencopy-unstable-v1.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -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> | ||||
|  | @ -875,6 +875,9 @@ struct roots_desktop *desktop_create(struct roots_server *server, | |||
| 	wl_signal_add(&desktop->virtual_keyboard->events.new_virtual_keyboard, | ||||
| 		&desktop->virtual_keyboard_new); | ||||
| 	desktop->virtual_keyboard_new.notify = handle_virtual_keyboard; | ||||
| 
 | ||||
| 	desktop->screencopy = wlr_screencopy_manager_v1_create(server->wl_display); | ||||
| 
 | ||||
| 	return desktop; | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -50,6 +50,7 @@ lib_wlr_types = static_library( | |||
| 		'wlr_wl_shell.c', | ||||
| 		'wlr_xcursor_manager.c', | ||||
| 		'wlr_xdg_output.c', | ||||
| 		'wlr_screencopy_v1.c', | ||||
| 	), | ||||
| 	include_directories: wlr_inc, | ||||
| 	dependencies: [pixman, xkbcommon, wayland_server, wlr_protos], | ||||
|  |  | |||
							
								
								
									
										250
									
								
								types/wlr_screencopy_v1.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										250
									
								
								types/wlr_screencopy_v1.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 emersion
						emersion