diff --git a/tests/weston-test.c b/tests/weston-test.c index 9f1f49b3..8f8781fc 100644 --- a/tests/weston-test.c +++ b/tests/weston-test.c @@ -269,6 +269,248 @@ get_n_buffers(struct wl_client *client, struct wl_resource *resource) weston_test_send_n_egl_buffers(resource, n_buffers); } +enum weston_test_screenshot_outcome { + WESTON_TEST_SCREENSHOT_SUCCESS, + WESTON_TEST_SCREENSHOT_NO_MEMORY, + WESTON_TEST_SCREENSHOT_BAD_BUFFER + }; + +typedef void (*weston_test_screenshot_done_func_t)(void *data, + enum weston_test_screenshot_outcome outcome); + +struct test_screenshot { + struct weston_compositor *compositor; + struct wl_global *global; + struct wl_client *client; + struct weston_process process; + struct wl_listener destroy_listener; +}; + +struct test_screenshot_frame_listener { + struct wl_listener listener; + struct weston_buffer *buffer; + weston_test_screenshot_done_func_t done; + void *data; +}; + +static void +copy_bgra_yflip(uint8_t *dst, uint8_t *src, int height, int stride) +{ + uint8_t *end; + + end = dst + height * stride; + while (dst < end) { + memcpy(dst, src, stride); + dst += stride; + src -= stride; + } +} + + +static void +copy_bgra(uint8_t *dst, uint8_t *src, int height, int stride) +{ + /* TODO: optimize this out */ + memcpy(dst, src, height * stride); +} + +static void +copy_row_swap_RB(void *vdst, void *vsrc, int bytes) +{ + uint32_t *dst = vdst; + uint32_t *src = vsrc; + uint32_t *end = dst + bytes / 4; + + while (dst < end) { + uint32_t v = *src++; + /* A R G B */ + uint32_t tmp = v & 0xff00ff00; + tmp |= (v >> 16) & 0x000000ff; + tmp |= (v << 16) & 0x00ff0000; + *dst++ = tmp; + } +} + +static void +copy_rgba_yflip(uint8_t *dst, uint8_t *src, int height, int stride) +{ + uint8_t *end; + + end = dst + height * stride; + while (dst < end) { + copy_row_swap_RB(dst, src, stride); + dst += stride; + src -= stride; + } +} + +static void +copy_rgba(uint8_t *dst, uint8_t *src, int height, int stride) +{ + uint8_t *end; + + end = dst + height * stride; + while (dst < end) { + copy_row_swap_RB(dst, src, stride); + dst += stride; + src += stride; + } +} + +static void +test_screenshot_frame_notify(struct wl_listener *listener, void *data) +{ + struct test_screenshot_frame_listener *l = + container_of(listener, + struct test_screenshot_frame_listener, listener); + struct weston_output *output = data; + struct weston_compositor *compositor = output->compositor; + int32_t stride; + uint8_t *pixels, *d, *s; + + output->disable_planes--; + wl_list_remove(&listener->link); + stride = l->buffer->width * (PIXMAN_FORMAT_BPP(compositor->read_format) / 8); + pixels = malloc(stride * l->buffer->height); + + if (pixels == NULL) { + l->done(l->data, WESTON_TEST_SCREENSHOT_NO_MEMORY); + free(l); + return; + } + + // FIXME: Needs to handle output transformations + + compositor->renderer->read_pixels(output, + compositor->read_format, + pixels, + 0, 0, + output->current_mode->width, + output->current_mode->height); + + stride = wl_shm_buffer_get_stride(l->buffer->shm_buffer); + + d = wl_shm_buffer_get_data(l->buffer->shm_buffer); + s = pixels + stride * (l->buffer->height - 1); + + wl_shm_buffer_begin_access(l->buffer->shm_buffer); + + /* XXX: It would be nice if we used Pixman to do all this rather + * than our own implementation + */ + switch (compositor->read_format) { + case PIXMAN_a8r8g8b8: + case PIXMAN_x8r8g8b8: + if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP) + copy_bgra_yflip(d, s, output->current_mode->height, stride); + else + copy_bgra(d, pixels, output->current_mode->height, stride); + break; + case PIXMAN_x8b8g8r8: + case PIXMAN_a8b8g8r8: + if (compositor->capabilities & WESTON_CAP_CAPTURE_YFLIP) + copy_rgba_yflip(d, s, output->current_mode->height, stride); + else + copy_rgba(d, pixels, output->current_mode->height, stride); + break; + default: + break; + } + + wl_shm_buffer_end_access(l->buffer->shm_buffer); + + l->done(l->data, WESTON_TEST_SCREENSHOT_SUCCESS); + free(pixels); + free(l); +} + +static bool +weston_test_screenshot_shoot(struct weston_output *output, + struct weston_buffer *buffer, + weston_test_screenshot_done_func_t done, + void *data) +{ + struct test_screenshot_frame_listener *l; + + /* Get the shm buffer resource the client created */ + if (!wl_shm_buffer_get(buffer->resource)) { + done(data, WESTON_TEST_SCREENSHOT_BAD_BUFFER); + return false; + } + + buffer->shm_buffer = wl_shm_buffer_get(buffer->resource); + buffer->width = wl_shm_buffer_get_width(buffer->shm_buffer); + buffer->height = wl_shm_buffer_get_height(buffer->shm_buffer); + + /* Verify buffer is big enough */ + if (buffer->width < output->current_mode->width || + buffer->height < output->current_mode->height) { + done(data, WESTON_TEST_SCREENSHOT_BAD_BUFFER); + return false; + } + + /* allocate the frame listener */ + l = malloc(sizeof *l); + if (l == NULL) { + done(data, WESTON_TEST_SCREENSHOT_NO_MEMORY); + return false; + } + + /* Set up the listener */ + l->buffer = buffer; + l->done = done; + l->data = data; + l->listener.notify = test_screenshot_frame_notify; + wl_signal_add(&output->frame_signal, &l->listener); + + /* Fire off a repaint */ + output->disable_planes++; + weston_output_schedule_repaint(output); + + return true; +} + +static void +capture_screenshot_done(void *data, enum weston_test_screenshot_outcome outcome) +{ + struct wl_resource *resource = data; + + switch (outcome) { + case WESTON_TEST_SCREENSHOT_SUCCESS: + weston_test_send_capture_screenshot_done(resource); + break; + case WESTON_TEST_SCREENSHOT_NO_MEMORY: + wl_resource_post_no_memory(resource); + break; + default: + break; + } +} + + +/** + * Grabs a snapshot of the screen. + */ +static void +capture_screenshot(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *output_resource, + struct wl_resource *buffer_resource) +{ + struct weston_output *output = + wl_resource_get_user_data(output_resource); + struct weston_buffer *buffer = + weston_buffer_from_resource(buffer_resource); + + if (buffer == NULL) { + wl_resource_post_no_memory(resource); + return; + } + + weston_test_screenshot_shoot(output, buffer, + capture_screenshot_done, resource); +} + static const struct weston_test_interface test_implementation = { move_surface, move_pointer, @@ -278,6 +520,7 @@ static const struct weston_test_interface test_implementation = { device_release, device_add, get_n_buffers, + capture_screenshot, }; static void