From 73755ad348657fdf8d9e0f9a48ee13a2ab947e35 Mon Sep 17 00:00:00 2001 From: emersion Date: Tue, 19 Jun 2018 19:07:29 +0100 Subject: screencopy-v1: add basic implementation --- examples/meson.build | 6 ++ examples/screencopy.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 examples/screencopy.c (limited to 'examples') diff --git a/examples/meson.build b/examples/meson.build index d8df0bfc..a290dc82 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -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] +) diff --git a/examples/screencopy.c b/examples/screencopy.c new file mode 100644 index 00000000..98df09b5 --- /dev/null +++ b/examples/screencopy.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} -- cgit v1.2.3 From bd8be19b792797925f5cca14049a5ab2d0f6d06c Mon Sep 17 00:00:00 2001 From: emersion Date: Fri, 22 Jun 2018 22:31:33 +0100 Subject: screencopy: add presentation timestamp --- examples/screencopy.c | 5 ++-- protocol/wlr-screencopy-unstable-v1.xml | 48 ++++++++++++++++++++++++++++----- types/wlr_screencopy_v1.c | 8 ++++-- 3 files changed, 50 insertions(+), 11 deletions(-) (limited to 'examples') diff --git a/examples/screencopy.c b/examples/screencopy.c index 98df09b5..8b064a15 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -113,7 +113,8 @@ static void frame_handle_buffer(void *data, } static void frame_handle_ready(void *data, - struct zwlr_screencopy_frame_v1 *frame) { + struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, + uint32_t tv_sec_lo, uint32_t tv_nsec) { buffer_copy_done = true; } @@ -217,7 +218,7 @@ int main(int argc, char *argv[]) { } struct zwlr_screencopy_frame_v1 *frame = - zwlr_screencopy_manager_v1_capture_output(screencopy_manager, output); + zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output); zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL); while (!buffer_copy_done) { diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml index 6bd5a75f..52f3385e 100644 --- a/protocol/wlr-screencopy-unstable-v1.xml +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -26,6 +26,15 @@ This protocol allows clients to ask the compositor to copy part of the screen content to a client buffer. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible changes + may be added together with the corresponding interface version bump. + Backward incompatible changes are done by bumping the version number in + the protocol and interface names and resetting the interface version. + Once the protocol is to be declared stable, the 'z' prefix and the + version number in the protocol and interface names are removed and the + interface version number is reset. @@ -36,9 +45,11 @@ - Request to start capturing from the given output. + Capture the next frame of a an entire output. + @@ -52,8 +63,17 @@ - This object represents a frame which is ready to have its resources - fetched and used. + This object represents a single frame. + + When created, a "buffer" event will be sent. The client will then be able + to send a "copy" request. If the capture is successful, the compositor + will finally a "ready" event. + + If the capture failed, the "failed" event is sent. This can happen anytime + before the "ready" event. + + Once either a "ready" or a "failed" event is received, the client should + destroy the frame. @@ -99,23 +119,37 @@ Called as soon as the frame is copied, indicating it is available - for reading. + for reading. This event includes the time at which presentation happened + at. + + The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, + each component being an unsigned 32-bit value. Whole seconds are in + tv_sec which is a 64-bit value combined from tv_sec_hi and tv_sec_lo, + and the additional fractional part in tv_nsec as nanoseconds. Hence, + for valid timestamps tv_nsec must be in [0, 999999999]. The seconds part + may have an arbitrary offset at start. - Upon receiving this event, the client should destroy the object. + After receiving this event, the client should destroy the object. + + + This event indicates that the attempted frame copy has failed. - Upon receiving this event, the client should destroy the object. + After receiving this event, the client should destroy the object. - Destroys the frame. + Destroys the frame. This request can be sent at any time by the client. diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 197ba330..6b6472e1 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -21,6 +21,7 @@ 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_event_swap_buffers *event = _data; struct wlr_output *output = frame->output; struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); @@ -51,7 +52,10 @@ static void frame_handle_output_swap_buffers(struct wl_listener *listener, return; } - zwlr_screencopy_frame_v1_send_ready(frame->resource); + uint32_t tv_sec_hi = event->when->tv_sec >> 32; + uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF; + zwlr_screencopy_frame_v1_send_ready(frame->resource, + tv_sec_hi, tv_sec_lo, event->when->tv_nsec); // TODO: make frame resource inert } @@ -134,7 +138,7 @@ static struct wlr_screencopy_manager_v1 *manager_from_resource( static void manager_handle_capture_output(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, - struct wl_resource *output_resource) { + int32_t overlay_cursor, 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); -- cgit v1.2.3 From 2b9cbaddf3d285ce439bd5da9f19995a0ddbac3a Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 23 Jun 2018 14:02:43 +0100 Subject: screencopy: add support for frame flags --- backend/drm/drm.c | 2 +- examples/screencopy.c | 32 ++++++++++++++++++++++++-------- include/wlr/render/interface.h | 2 +- include/wlr/render/wlr_renderer.h | 9 ++++++++- protocol/wlr-screencopy-unstable-v1.xml | 17 ++++++++++++----- render/gles2/renderer.c | 28 ++++++++++++++++++++-------- render/wlr_renderer.c | 6 +++--- types/wlr_screencopy_v1.c | 9 ++++++--- types/wlr_screenshooter.c | 4 ++-- 9 files changed, 77 insertions(+), 32 deletions(-) (limited to 'examples') diff --git a/backend/drm/drm.c b/backend/drm/drm.c index f4a971a2..ae462568 100644 --- a/backend/drm/drm.c +++ b/backend/drm/drm.c @@ -683,7 +683,7 @@ static bool drm_connector_set_cursor(struct wlr_output *output, wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); wlr_renderer_end(rend); - wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, bo_stride, + wlr_renderer_read_pixels(rend, WL_SHM_FORMAT_ARGB8888, NULL, bo_stride, plane->surf.width, plane->surf.height, 0, 0, 0, 0, bo_data); swap_drm_surface_buffers(&plane->surf, NULL); diff --git a/examples/screencopy.c b/examples/screencopy.c index 8b064a15..7be06d68 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -45,6 +45,7 @@ static struct { struct wl_buffer *wl_buffer; void *data; int width, height, stride; + bool y_invert; } buffer; bool buffer_copy_done = false; @@ -99,7 +100,7 @@ static struct wl_buffer *create_shm_buffer(int width, int height, 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) { + uint32_t format, uint32_t stride) { buffer.width = width; buffer.height = height; buffer.wl_buffer = @@ -112,6 +113,11 @@ static void frame_handle_buffer(void *data, zwlr_screencopy_frame_v1_copy(frame, buffer.wl_buffer); } +static void frame_handle_flags(void *data, + struct zwlr_screencopy_frame_v1 *frame, uint32_t flags) { + buffer.y_invert = flags & ZWLR_SCREENCOPY_FRAME_V1_FLAGS_Y_INVERT; +} + static void frame_handle_ready(void *data, struct zwlr_screencopy_frame_v1 *frame, uint32_t tv_sec_hi, uint32_t tv_sec_lo, uint32_t tv_nsec) { @@ -126,6 +132,7 @@ static void frame_handle_failed(void *data, static const struct zwlr_screencopy_frame_v1_listener frame_listener = { .buffer = frame_handle_buffer, + .flags = frame_handle_flags, .ready = frame_handle_ready, .failed = frame_handle_failed, }; @@ -153,8 +160,8 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -static void write_image(const char *filename, int width, int height, int stride, - void *data) { +static void write_image(char *filename, int width, int height, int stride, + bool y_invert, void *data) { char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits sprintf(size, "%dx%d+0", width, height); @@ -183,10 +190,19 @@ static void write_image(const char *filename, int width, int height, int stride, 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); + char *argv[11] = {"convert", "-depth", "8", "-size", size, "bgra:-", + "-alpha", "opaque", filename, NULL}; + if (y_invert) { + argv[8] = "-flip"; + argv[9] = filename; + argv[10] = NULL; + } + + execvp("convert", argv); + fprintf(stderr, "cannot execute convert\n"); exit(EXIT_FAILURE); } @@ -221,12 +237,12 @@ int main(int argc, char *argv[]) { zwlr_screencopy_manager_v1_capture_output(screencopy_manager, 0, output); zwlr_screencopy_frame_v1_add_listener(frame, &frame_listener, NULL); - while (!buffer_copy_done) { - wl_display_roundtrip(display); + while (!buffer_copy_done && wl_display_dispatch(display) != -1) { + // This space is intentionally left blank } write_image("wayland-screenshot.png", buffer.width, buffer.height, - buffer.stride, buffer.data); + buffer.stride, buffer.y_invert, buffer.data); wl_buffer_destroy(buffer.wl_buffer); return EXIT_SUCCESS; diff --git a/include/wlr/render/interface.h b/include/wlr/render/interface.h index fb427c89..1b138ea3 100644 --- a/include/wlr/render/interface.h +++ b/include/wlr/render/interface.h @@ -34,7 +34,7 @@ struct wlr_renderer_impl { int (*get_dmabuf_modifiers)(struct wlr_renderer *renderer, int format, uint64_t **modifiers); bool (*read_pixels)(struct wlr_renderer *renderer, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); bool (*format_supported)(struct wlr_renderer *renderer, diff --git a/include/wlr/render/wlr_renderer.h b/include/wlr/render/wlr_renderer.h index c715e4b0..0650bf1b 100644 --- a/include/wlr/render/wlr_renderer.h +++ b/include/wlr/render/wlr_renderer.h @@ -7,6 +7,10 @@ #include #include +enum wlr_renderer_read_pixels_flags { + WLR_RENDERER_READ_PIXELS_Y_INVERT = 1, +}; + struct wlr_renderer_impl; struct wlr_renderer { @@ -87,9 +91,12 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *renderer, int format, /** * Reads out of pixels of the currently bound surface into data. `stride` is in * bytes. + * + * If `flags` is not NULl, the caller indicates that it accepts frame flags + * defined in `enum wlr_renderer_read_pixels_flags`. */ bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data); /** * Checks if a format is supported. diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml index 52f3385e..e62e3ff0 100644 --- a/protocol/wlr-screencopy-unstable-v1.xml +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -76,10 +76,6 @@ destroy the frame. - - - - Provides information about the frame's buffer. This event is sent once @@ -91,7 +87,6 @@ - @@ -116,6 +111,18 @@ summary="invalid width or height"/> + + + + + + + Provides flags about the frame. This event is sent once before the + "ready" event. + + + + Called as soon as the frame is copied, indicating it is available diff --git a/render/gles2/renderer.c b/render/gles2/renderer.c index 05426fa9..0c57bfa1 100644 --- a/render/gles2/renderer.c +++ b/render/gles2/renderer.c @@ -250,9 +250,9 @@ static int gles2_get_dmabuf_modifiers(struct wlr_renderer *wlr_renderer, } static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, - enum wl_shm_format wl_fmt, uint32_t stride, uint32_t width, - uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, - uint32_t dst_y, void *data) { + enum wl_shm_format wl_fmt, uint32_t *flags, uint32_t stride, + uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, + uint32_t dst_x, uint32_t dst_y, void *data) { gles2_get_renderer_in_context(wlr_renderer); const struct wlr_gles2_pixel_format *fmt = get_gles2_format_from_wl(wl_fmt); @@ -266,12 +266,24 @@ static bool gles2_read_pixels(struct wlr_renderer *wlr_renderer, // Make sure any pending drawing is finished before we try to read it glFinish(); - // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read - // the lines out row by row unsigned char *p = data + dst_y * stride; - for (size_t i = src_y; i < src_y + height; ++i) { - glReadPixels(src_x, src_y + height - i - 1, width, 1, fmt->gl_format, - fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); + uint32_t pack_stride = width * fmt->bpp / 8; + if (pack_stride == stride && dst_x == 0 && flags != NULL) { + // Under these particular conditions, we can read the pixels with only + // one glReadPixels call + glReadPixels(src_x, src_y, width, height, fmt->gl_format, + fmt->gl_type, p); + *flags = WLR_RENDERER_READ_PIXELS_Y_INVERT; + } else { + // Unfortunately GLES2 doesn't support GL_PACK_*, so we have to read + // the lines out row by row + for (size_t i = src_y; i < src_y + height; ++i) { + glReadPixels(src_x, src_y + height - i - 1, width, 1, fmt->gl_format, + fmt->gl_type, p + i * stride + dst_x * fmt->bpp / 8); + } + if (flags != NULL) { + *flags = 0; + } } POP_GLES2_DEBUG; diff --git a/render/wlr_renderer.c b/render/wlr_renderer.c index 00f1e411..d66edc13 100644 --- a/render/wlr_renderer.c +++ b/render/wlr_renderer.c @@ -139,14 +139,14 @@ int wlr_renderer_get_dmabuf_modifiers(struct wlr_renderer *r, int format, } bool wlr_renderer_read_pixels(struct wlr_renderer *r, enum wl_shm_format fmt, - uint32_t stride, uint32_t width, uint32_t height, + uint32_t *flags, uint32_t stride, uint32_t width, uint32_t height, uint32_t src_x, uint32_t src_y, uint32_t dst_x, uint32_t dst_y, void *data) { if (!r->impl->read_pixels) { return false; } - return r->impl->read_pixels(r, fmt, stride, width, height, src_x, src_y, - dst_x, dst_y, data); + return r->impl->read_pixels(r, fmt, flags, stride, width, height, + src_x, src_y, dst_x, dst_y, data); } bool wlr_renderer_format_supported(struct wlr_renderer *r, diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index 6b6472e1..89680a48 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -43,8 +43,9 @@ static void frame_handle_output_swap_buffers(struct wl_listener *listener, 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); + uint32_t flags = 0; + bool ok = wlr_renderer_read_pixels(renderer, fmt, &flags, stride, + width, height, 0, 0, 0, 0, data); wl_shm_buffer_end_access(buffer); if (!ok) { @@ -52,6 +53,8 @@ static void frame_handle_output_swap_buffers(struct wl_listener *listener, return; } + zwlr_screencopy_frame_v1_send_flags(frame->resource, flags); + uint32_t tv_sec_hi = event->when->tv_sec >> 32; uint32_t tv_sec_lo = event->when->tv_sec & 0xFFFFFFFF; zwlr_screencopy_frame_v1_send_ready(frame->resource, @@ -172,7 +175,7 @@ static void manager_handle_capture_output(struct wl_client *client, frame->height = output->height; // TODO: don't send zero zwlr_screencopy_frame_v1_send_buffer(frame->resource, - frame->width, frame->height, 0, 0, 0); + frame->width, frame->height, 0, 0); } static void manager_handle_destroy(struct wl_client *client, diff --git a/types/wlr_screenshooter.c b/types/wlr_screenshooter.c index e1386be6..5d9e9de3 100644 --- a/types/wlr_screenshooter.c +++ b/types/wlr_screenshooter.c @@ -50,8 +50,8 @@ static void output_handle_frame(struct wl_listener *listener, void *_data) { int32_t stride = wl_shm_buffer_get_stride(shm_buffer); wl_shm_buffer_begin_access(shm_buffer); void *data = wl_shm_buffer_get_data(shm_buffer); - bool ok = wlr_renderer_read_pixels(renderer, format, stride, width, height, - 0, 0, 0, 0, data); + bool ok = wlr_renderer_read_pixels(renderer, format, NULL, stride, + width, height, 0, 0, 0, 0, data); wl_shm_buffer_end_access(shm_buffer); if (!ok) { -- cgit v1.2.3 From c421700f3da6333652488e2951c2b0cbb8e35e51 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 23 Jun 2018 18:41:40 +0100 Subject: screncopy: update protocol --- examples/screencopy.c | 56 ++++++++++++++++++++++++--------- include/wlr/types/wlr_screencopy_v1.h | 4 ++- protocol/wlr-screencopy-unstable-v1.xml | 29 +++++++---------- types/wlr_screencopy_v1.c | 37 ++++++++++------------ 4 files changed, 72 insertions(+), 54 deletions(-) (limited to 'examples') diff --git a/examples/screencopy.c b/examples/screencopy.c index 7be06d68..dc1984e6 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -44,11 +44,24 @@ static struct wl_output *output = NULL; static struct { struct wl_buffer *wl_buffer; void *data; + enum wl_shm_format format; int width, height, stride; bool y_invert; } buffer; bool buffer_copy_done = false; +// wl_shm_format describes little-endian formats, ImageMagick uses big-endian +// formats. +static const struct { + enum wl_shm_format wl_format; + char *str_format; +} formats[] = { + {WL_SHM_FORMAT_XRGB8888, "BGRA"}, + {WL_SHM_FORMAT_ARGB8888, "BGRA"}, + {WL_SHM_FORMAT_XBGR8888, "RGBA"}, + {WL_SHM_FORMAT_ABGR8888, "RGBA"}, +}; + static int backingfile(off_t size) { char template[] = "/tmp/wlroots-shared-XXXXXX"; int fd = mkstemp(template); @@ -69,9 +82,8 @@ static int backingfile(off_t size) { return fd; } -static struct wl_buffer *create_shm_buffer(int width, int height, - int *stride_out, void **data_out) { - int stride = width * 4; +static struct wl_buffer *create_shm_buffer(enum wl_shm_format fmt, + int width, int height, int stride, void **data_out) { int size = stride * height; int fd = backingfile(size); @@ -90,21 +102,22 @@ static struct wl_buffer *create_shm_buffer(int width, int height, 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); + stride, fmt); 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 format, uint32_t stride) { + struct zwlr_screencopy_frame_v1 *frame, uint32_t format, + uint32_t width, uint32_t height, uint32_t stride) { + buffer.format = format; buffer.width = width; buffer.height = height; + buffer.stride = stride; buffer.wl_buffer = - create_shm_buffer(width, height, &buffer.stride, &buffer.data); + create_shm_buffer(format, width, height, stride, &buffer.data); if (buffer.wl_buffer == NULL) { fprintf(stderr, "failed to create buffer\n"); exit(EXIT_FAILURE); @@ -160,11 +173,26 @@ static const struct wl_registry_listener registry_listener = { .global_remove = handle_global_remove, }; -static void write_image(char *filename, int width, int height, int stride, - bool y_invert, void *data) { +static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, + int height, int stride, bool y_invert, void *data) { char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits sprintf(size, "%dx%d+0", width, height); + const char *fmt_str = NULL; + for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { + if (formats[i].wl_format == wl_fmt) { + fmt_str = formats[i].str_format; + break; + } + } + if (fmt_str == NULL) { + fprintf(stderr, "unsupported format %"PRIu32"\n", wl_fmt); + exit(EXIT_FAILURE); + } + char convert[strlen(fmt_str) + 3]; + memcpy(convert, fmt_str, strlen(fmt_str) + 1); + strcat(convert, ":-"); + int fd[2]; if (pipe(fd) != 0) { fprintf(stderr, "cannot create pipe: %s\n", strerror(errno)); @@ -191,9 +219,7 @@ static void write_image(char *filename, int width, int height, int stride, } close(fd[0]); - // We requested WL_SHM_FORMAT_XRGB8888 in little endian, so that's BGRA - // in big endian. - char *argv[11] = {"convert", "-depth", "8", "-size", size, "bgra:-", + char *argv[11] = {"convert", "-depth", "8", "-size", size, convert, "-alpha", "opaque", filename, NULL}; if (y_invert) { argv[8] = "-flip"; @@ -241,8 +267,8 @@ int main(int argc, char *argv[]) { // This space is intentionally left blank } - write_image("wayland-screenshot.png", buffer.width, buffer.height, - buffer.stride, buffer.y_invert, buffer.data); + write_image("wayland-screenshot.png", buffer.format, buffer.width, + buffer.height, buffer.stride, buffer.y_invert, buffer.data); wl_buffer_destroy(buffer.wl_buffer); return EXIT_SUCCESS; diff --git a/include/wlr/types/wlr_screencopy_v1.h b/include/wlr/types/wlr_screencopy_v1.h index 4766d680..69f62437 100644 --- a/include/wlr/types/wlr_screencopy_v1.h +++ b/include/wlr/types/wlr_screencopy_v1.h @@ -18,7 +18,9 @@ struct wlr_screencopy_frame_v1 { struct wlr_screencopy_manager_v1 *manager; struct wl_list link; - struct wlr_box buffer_box; + enum wl_shm_format format; + struct wlr_box box; + int stride; struct wl_shm_buffer *buffer; diff --git a/protocol/wlr-screencopy-unstable-v1.xml b/protocol/wlr-screencopy-unstable-v1.xml index c54b7268..a7a2d172 100644 --- a/protocol/wlr-screencopy-unstable-v1.xml +++ b/protocol/wlr-screencopy-unstable-v1.xml @@ -43,11 +43,6 @@ source. - - - - Capture the next frame of an entire output. @@ -63,8 +58,8 @@ Capture the next frame of an output's region. The region is given in output logical coordinates, see - xdg_output.logical_size. Trying to capture a region spanning outside the - output extents is a protocol error. + xdg_output.logical_size. The region will be clipped to the output's + extents. + - - + @@ -120,8 +114,8 @@ 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. + If the frame is successfully copied, a "flags" and a "ready" events are + sent. Otherwise, a "failed" event is sent. @@ -129,9 +123,8 @@ - - + diff --git a/types/wlr_screencopy_v1.c b/types/wlr_screencopy_v1.c index a600a716..027d4b51 100644 --- a/types/wlr_screencopy_v1.c +++ b/types/wlr_screencopy_v1.c @@ -28,8 +28,8 @@ static void frame_handle_output_swap_buffers(struct wl_listener *listener, wl_list_remove(&frame->output_swap_buffers.link); wl_list_init(&frame->output_swap_buffers.link); - int x = frame->buffer_box.x; - int y = frame->buffer_box.y; + int x = frame->box.x; + int y = frame->box.y; struct wl_shm_buffer *buffer = frame->buffer; assert(buffer != NULL); @@ -66,27 +66,24 @@ static void frame_handle_copy(struct wl_client *client, 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); + ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, + "unsupported buffer type"); return; } - if (frame->buffer_box.width != wl_shm_buffer_get_width(buffer) || - frame->buffer_box.height != wl_shm_buffer_get_height(buffer)) { + 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); + if (fmt != frame->format || width != frame->box.width || + height != frame->box.height || stride != frame->stride) { wl_resource_post_error(frame->resource, - ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_DIMENSIONS, - "invalid width or height"); + ZWLR_SCREENCOPY_FRAME_V1_ERROR_INVALID_BUFFER, + "invalid buffer attributes"); return; } @@ -165,7 +162,6 @@ static void capture_output(struct wl_client *client, return; } frame->manager = manager; - frame->output = output; frame->resource = wl_resource_create(client, @@ -182,10 +178,11 @@ static void capture_output(struct wl_client *client, wl_list_init(&frame->output_swap_buffers.link); - frame->buffer_box = buffer_box; - zwlr_screencopy_frame_v1_send_buffer(frame->resource, - frame->buffer_box.width, frame->buffer_box.height, - WL_SHM_FORMAT_XRGB8888, 4 * frame->buffer_box.width); + frame->format = WL_SHM_FORMAT_XRGB8888; + frame->box = buffer_box; + frame->stride = 4 * buffer_box.width; + zwlr_screencopy_frame_v1_send_buffer(frame->resource, frame->format, + buffer_box.width, buffer_box.height, frame->stride); } static void manager_handle_capture_output(struct wl_client *client, -- cgit v1.2.3 From a7a96d7644ee00b29f9376576a0da8a26cf3cba6 Mon Sep 17 00:00:00 2001 From: emersion Date: Sat, 30 Jun 2018 23:16:53 +0100 Subject: examples/screencopy: use libpng --- examples/meson.build | 14 ++++--- examples/screencopy.c | 101 ++++++++++++++++++++++++-------------------------- 2 files changed, 57 insertions(+), 58 deletions(-) (limited to 'examples') diff --git a/examples/meson.build b/examples/meson.build index a290dc82..18f44fde 100644 --- a/examples/meson.build +++ b/examples/meson.build @@ -1,6 +1,8 @@ threads = dependency('threads') wayland_cursor = dependency('wayland-cursor') +libpng = dependency('libpng', required: false) + # These versions correspond to ffmpeg 4.0 libavutil = dependency('libavutil', version: '>=56.14.100', required: false) libavcodec = dependency('libavcodec', version: '>=58.18.100', required: false) @@ -57,8 +59,10 @@ if libavutil.found() and libavcodec.found() and libavformat.found() ) endif -executable( - 'screencopy', - 'screencopy.c', - dependencies: [wayland_client, wlr_protos, wlroots] -) +if libpng.found() + executable( + 'screencopy', + 'screencopy.c', + dependencies: [wayland_client, wlr_protos, wlroots, libpng] + ) +endif diff --git a/examples/screencopy.c b/examples/screencopy.c index dc1984e6..4e58cd3b 100644 --- a/examples/screencopy.c +++ b/examples/screencopy.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -37,6 +38,11 @@ #include #include "wlr-screencopy-unstable-v1-client-protocol.h" +struct format { + enum wl_shm_format wl_format; + bool is_bgr; +}; + static struct wl_shm *shm = NULL; static struct zwlr_screencopy_manager_v1 *screencopy_manager = NULL; static struct wl_output *output = NULL; @@ -50,16 +56,13 @@ static struct { } buffer; bool buffer_copy_done = false; -// wl_shm_format describes little-endian formats, ImageMagick uses big-endian -// formats. -static const struct { - enum wl_shm_format wl_format; - char *str_format; -} formats[] = { - {WL_SHM_FORMAT_XRGB8888, "BGRA"}, - {WL_SHM_FORMAT_ARGB8888, "BGRA"}, - {WL_SHM_FORMAT_XBGR8888, "RGBA"}, - {WL_SHM_FORMAT_ABGR8888, "RGBA"}, +// wl_shm_format describes little-endian formats, libpng uses big-endian +// formats (so Wayland's ABGR is libpng's RGBA). +static const struct format formats[] = { + {WL_SHM_FORMAT_XRGB8888, true}, + {WL_SHM_FORMAT_ARGB8888, true}, + {WL_SHM_FORMAT_XBGR8888, false}, + {WL_SHM_FORMAT_ABGR8888, false}, }; static int backingfile(off_t size) { @@ -156,8 +159,8 @@ static void handle_global(void *data, struct wl_registry *registry, 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) { + } else if (strcmp(interface, + zwlr_screencopy_manager_v1_interface.name) == 0) { screencopy_manager = wl_registry_bind(registry, name, &zwlr_screencopy_manager_v1_interface, 1); } @@ -174,64 +177,56 @@ static const struct wl_registry_listener registry_listener = { }; static void write_image(char *filename, enum wl_shm_format wl_fmt, int width, - int height, int stride, bool y_invert, void *data) { - char size[10 + 1 + 10 + 2 + 1]; // int32_t are max 10 digits - sprintf(size, "%dx%d+0", width, height); - - const char *fmt_str = NULL; + int height, int stride, bool y_invert, png_bytep data) { + const struct format *fmt = NULL; for (size_t i = 0; i < sizeof(formats) / sizeof(formats[0]); ++i) { if (formats[i].wl_format == wl_fmt) { - fmt_str = formats[i].str_format; + fmt = &formats[i]; break; } } - if (fmt_str == NULL) { + if (fmt == NULL) { fprintf(stderr, "unsupported format %"PRIu32"\n", wl_fmt); exit(EXIT_FAILURE); } - char convert[strlen(fmt_str) + 3]; - memcpy(convert, fmt_str, strlen(fmt_str) + 1); - strcat(convert, ":-"); - int fd[2]; - if (pipe(fd) != 0) { - fprintf(stderr, "cannot create pipe: %s\n", strerror(errno)); + FILE *f = fopen(filename, "wb"); + if (f == NULL) { + fprintf(stderr, "failed to open output file\n"); 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]); + png_structp png = + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_infop info = png_create_info_struct(png); + + png_init_io(png, f); - char *argv[11] = {"convert", "-depth", "8", "-size", size, convert, - "-alpha", "opaque", filename, NULL}; + png_set_IHDR(png, info, width, height, 8, PNG_COLOR_TYPE_RGBA, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + + if (fmt->is_bgr) { + png_set_bgr(png); + } + + png_write_info(png, info); + + for (size_t i = 0; i < (size_t)height; ++i) { + png_bytep row; if (y_invert) { - argv[8] = "-flip"; - argv[9] = filename; - argv[10] = NULL; + row = data + (height - i - 1) * stride; + } else { + row = data + i * stride; } + png_write_row(png, row); + } - execvp("convert", argv); + png_write_end(png, NULL); - fprintf(stderr, "cannot execute convert\n"); - exit(EXIT_FAILURE); - } + png_destroy_write_struct(&png, &info); + + fclose(f); } int main(int argc, char *argv[]) { -- cgit v1.2.3