tests: Add screenshot recording capability to weston-test
authorBryce Harrington <bryce@osg.samsung.com>
Tue, 5 May 2015 22:13:20 +0000 (15:13 -0700)
committerBryce Harrington <bryce@osg.samsung.com>
Thu, 21 May 2015 22:05:59 +0000 (15:05 -0700)
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>
tests/weston-test.c

index 9f1f49b..8f8781f 100644 (file)
@@ -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