[PATCH weston v2 4/7] surface-screenshot: a new manual test module

Pekka Paalanen ppaalanen at gmail.com
Wed Feb 25 03:06:00 PST 2015


From: Pekka Paalanen <pekka.paalanen at collabora.co.uk>

Add a new Weston plugin under tests/ for manual testing of the
surface-shooting API.

The debug key binding 'h' triggers a surface shot from the surface that
currently has the pointer focus. The shot is written in PAM format into
a file. PAM format was chosen because it is dead-simple to write from
scratch and can carry an RGBA format.

Changes in v2:
- check fprintf calls, fix a malloc without free
- remove stride and format arguments from the API

Signed-off-by: Pekka Paalanen <pekka.paalanen at collabora.co.uk>
v1 Tested-by: Nobuhiko Tanibata <NOBUHIKO_TANIBATA at xddp.denso.co.jp>
---
 Makefile.am                |  17 ++++
 tests/surface-screenshot.c | 218 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 235 insertions(+)
 create mode 100644 tests/surface-screenshot.c

diff --git a/Makefile.am b/Makefile.am
index 4e219e1..c509f6e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1104,6 +1104,23 @@ EXTRA_DIST +=					\
 	protocol/ivi-application.xml		\
 	protocol/ivi-hmi-controller.xml
 
+#
+# manual test modules in tests subdirectory
+#
+
+noinst_LTLIBRARIES +=				\
+	surface-screenshot.la
+
+surface_screenshot_la_LIBADD = $(COMPOSITOR_LIBS) libshared.la
+surface_screenshot_la_LDFLAGS = $(test_module_ldflags)
+surface_screenshot_la_CFLAGS = $(GCC_CFLAGS) $(COMPOSITOR_CFLAGS)
+surface_screenshot_la_SOURCES = tests/surface-screenshot.c
+
+
+#
+# Documentation
+#
+
 man_MANS = weston.1 weston.ini.5
 
 if ENABLE_DRM_COMPOSITOR
diff --git a/tests/surface-screenshot.c b/tests/surface-screenshot.c
new file mode 100644
index 0000000..57ec925
--- /dev/null
+++ b/tests/surface-screenshot.c
@@ -0,0 +1,218 @@
+/*
+ * Copyright © 2015 Collabora, Ltd.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission.  The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose.  It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include <linux/input.h>
+
+#include "compositor.h"
+#include "file-util.h"
+
+static char *
+encode_PAM_comment_line(const char *comment)
+{
+	size_t len = strlen(comment);
+	char *str = malloc(len + 2);
+	char *dst = str;
+	const char *src = comment;
+	const char *end = src + len;
+
+	*dst++ = '#';
+	*dst++ = ' ';
+	for (; src < end; src++, dst++) {
+		if (*src == '\n' || !isprint(*src))
+			*dst = '_';
+		else
+			*dst = *src;
+	}
+
+	return str;
+}
+
+/*
+ * PAM image format:
+ * http://en.wikipedia.org/wiki/Netpbm#PAM_graphics_format
+ * RGBA is in byte address order.
+ *
+ * ImageMagick 'convert' can convert a PAM image to a more common format.
+ * To view the image metadata: $ head -n7 image.pam
+ */
+static int
+write_PAM_image_rgba(FILE *fp, int width, int height,
+		     void *pixels, size_t size, const char *comment)
+{
+	char *str;
+	int ret;
+
+	assert(size == (size_t)4 * width * height);
+
+	ret = fprintf(fp, "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n"
+		      "TUPLTYPE RGB_ALPHA\n", width, height);
+	if (ret < 0)
+		return -1;
+
+	if (comment) {
+		str = encode_PAM_comment_line(comment);
+		ret = fprintf(fp, "%s\n", str);
+		free(str);
+
+		if (ret < 0)
+			return -1;
+	}
+
+	ret = fprintf(fp, "ENDHDR\n");
+	if (ret < 0)
+		return -1;
+
+	if (fwrite(pixels, 1, size, fp) != size)
+		return -1;
+
+	if (ferror(fp))
+		return -1;
+
+	return 0;
+}
+
+static uint32_t
+unmult(uint32_t c, uint32_t a)
+{
+	return (c * 255 + a / 2) / a;
+}
+
+static void
+unpremultiply_and_swap_a8b8g8r8_to_PAMrgba(void *pixels, size_t size)
+{
+	uint32_t *p = pixels;
+	uint32_t *end;
+
+	for (end = p + size / 4; p < end; p++) {
+		uint32_t v = *p;
+		uint32_t a;
+
+		a = (v & 0xff000000) >> 24;
+		if (a == 0) {
+			*p = 0;
+		} else {
+			uint8_t *dst = (uint8_t *)p;
+
+			dst[0] = unmult((v & 0x000000ff) >> 0, a);
+			dst[1] = unmult((v & 0x0000ff00) >> 8, a);
+			dst[2] = unmult((v & 0x00ff0000) >> 16, a);
+			dst[3] = a;
+		}
+	}
+}
+
+static void
+trigger_binding(struct weston_seat *seat, uint32_t time, uint32_t key,
+		void *data)
+{
+	const char *prefix = "surfaceshot-";
+	const char *suffix = ".pam";
+	char fname[1024];
+	struct weston_surface *surface;
+	int width, height;
+	char desc[512];
+	void *pixels;
+	const size_t bytespp = 4; /* PIXMAN_a8b8g8r8 */
+	size_t sz;
+	int ret;
+	FILE *fp;
+
+	if (seat->pointer_device_count == 0 ||
+	    !seat->pointer ||
+	    !seat->pointer->focus)
+		return;
+
+	surface = seat->pointer->focus->surface;
+
+	weston_surface_get_content_size(surface, &width, &height);
+
+	if (!surface->get_label ||
+	    surface->get_label(surface, desc, sizeof(desc)) < 0)
+		snprintf(desc, sizeof(desc), "(unknown)");
+
+	weston_log("surface screenshot of %p: '%s', %dx%d\n",
+		   surface, desc, width, height);
+
+	sz = width * bytespp * height;
+	if (sz == 0) {
+		weston_log("no content for %p\n", surface);
+		return;
+	}
+
+	pixels = malloc(sz);
+	if (!pixels) {
+		weston_log("%s: failed to malloc %zu B\n", __func__, sz);
+		return;
+	}
+
+	ret = weston_surface_copy_content(surface, pixels, sz,
+					  0, 0, width, height);
+	if (ret < 0) {
+		weston_log("shooting surface %p failed\n", surface);
+		goto out;
+	}
+
+	unpremultiply_and_swap_a8b8g8r8_to_PAMrgba(pixels, sz);
+
+	fp = file_create_dated(prefix, suffix, fname, sizeof(fname));
+	if (!fp) {
+		const char *msg;
+
+		switch (errno) {
+		case ETIME:
+			msg = "failure in datetime formatting";
+			break;
+		default:
+			msg = strerror(errno);
+		}
+
+		weston_log("Cannot open '%s*%s' for writing: %s\n",
+			   prefix, suffix, msg);
+		goto out;
+	}
+
+	ret = write_PAM_image_rgba(fp, width, height, pixels, sz, desc);
+	if (fclose(fp) != 0 || ret < 0)
+		weston_log("writing surface %p screenshot failed.\n", surface);
+	else
+		weston_log("successfully shot surface %p into '%s'\n",
+			   surface, fname);
+
+out:
+	free(pixels);
+}
+
+WL_EXPORT int
+module_init(struct weston_compositor *ec,
+	    int *argc, char *argv[])
+{
+	weston_compositor_add_debug_binding(ec, KEY_H, trigger_binding, ec);
+
+	return 0;
+}
-- 
2.0.5



More information about the wayland-devel mailing list