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:
parent
83d61b6dfb
commit
f280d11274
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user