tests: Add screenshot recording capability to weston-test
Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=83981 Signed-off-by: Bryce Harrington <bryce@osg.samsung.com> Reviewed-By: Derek Foreman <derekf@osg.samsung.com>
This commit is contained in:
@@ -269,6 +269,248 @@ get_n_buffers(struct wl_client *client, struct wl_resource *resource)
|
|||||||
weston_test_send_n_egl_buffers(resource, n_buffers);
|
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 = {
|
static const struct weston_test_interface test_implementation = {
|
||||||
move_surface,
|
move_surface,
|
||||||
move_pointer,
|
move_pointer,
|
||||||
@@ -278,6 +520,7 @@ static const struct weston_test_interface test_implementation = {
|
|||||||
device_release,
|
device_release,
|
||||||
device_add,
|
device_add,
|
||||||
get_n_buffers,
|
get_n_buffers,
|
||||||
|
capture_screenshot,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|||||||
Reference in New Issue
Block a user