[call for testing] clients: add cliptest program

Pekka Paalanen ppaalanen at gmail.com
Wed Sep 5 06:37:53 PDT 2012


Cliptest is for controlled testing of the calculate_edges() function in
compositor.c. The function is copied verbatim into cliptest.c.

Signed-off-by: Pekka Paalanen <ppaalanen at gmail.com>
---
 clients/Makefile.am |    5 +
 clients/cliptest.c  |  819 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 824 insertions(+), 0 deletions(-)
 create mode 100644 clients/cliptest.c

Hi all,

this patch contains a new polygon clipping algorithm for the rectangle-rectangle
intersection computation, a.k.a calculate_edges() function. Right now, it is
only in the cliptest program for testing.

I would like to ask people to try to break the algorithm.

Apply this patch, and run cliptest in a Wayland compositor. You will see a blue
rectangle, which is the clipping area, and a red rectangle which represents
a surface. The red dot indicates the orientation of the surface.

The algorithm computes the intersection of these two rectangles. In the
image, the algorithm produces the green line with vertex indices. The
green line should be exactly the boundary of the intersection (purple). See:
http://people.collabora.com/~pq/geometry-test-8.png

If you can produce an image, where the green line is wrong, like it was in
http://people.collabora.com/~pq/geometry-test-5.png
please, take a screenshot and send it to me.

You can control the rectangles by:
 *	clip box position: mouse left drag, keys: w a s d
 *	clip box size: mouse right drag, keys: i j k l
 *	surface orientation: mouse wheel, keys: n m
 *	surface transform disable key: r

Also, if you try this, and cannot break it, let me know you tried, still.


Thanks,
pq

diff --git a/clients/Makefile.am b/clients/Makefile.am
index 1b7fa10..a777772 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -48,6 +48,7 @@ terminal = weston-terminal
 clients_programs =				\
 	flower					\
 	image					\
+	cliptest				\
 	dnd					\
 	smoke					\
 	resizor					\
@@ -88,6 +89,10 @@ weston_terminal_LDADD = $(toolkit_libs) -lutil
 image_SOURCES = image.c
 image_LDADD = $(toolkit_libs)
 
+cliptest_SOURCES = cliptest.c
+cliptest_CPPFLAGS = $(AM_CPPFLAGS) $(PIXMAN_CFLAGS)
+cliptest_LDADD = $(toolkit_libs) $(PIXMAN_LIBS)
+
 dnd_SOURCES = dnd.c
 dnd_LDADD = $(toolkit_libs)
 
diff --git a/clients/cliptest.c b/clients/cliptest.c
new file mode 100644
index 0000000..9531728
--- /dev/null
+++ b/clients/cliptest.c
@@ -0,0 +1,819 @@
+/*
+ * Copyright © 2012 Collabora, Ltd.
+ * Copyright © 2012 Rob Clark
+ *
+ * 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.
+ */
+
+/* cliptest: for debugging calculate_edges() function, which is copied
+ * from compositor.c.
+ * controls:
+ *	clip box position: mouse left drag, keys: w a s d
+ *	clip box size: mouse right drag, keys: i j k l
+ *	surface orientation: mouse wheel, keys: n m
+ *	surface transform disable key: r
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <pixman.h>
+#include <cairo.h>
+#include <assert.h>
+
+#include <linux/input.h>
+#include <wayland-client.h>
+
+#include "window.h"
+
+typedef float GLfloat;
+
+struct geometry {
+	pixman_box32_t clip;
+
+	pixman_box32_t surf;
+	float s; /* sin phi */
+	float c; /* cos phi */
+	float phi;
+};
+
+struct weston_surface {
+	struct {
+		int enabled;
+	} transform;
+
+	struct geometry *geometry;
+};
+
+static void
+weston_surface_to_global_float(struct weston_surface *surface,
+			       GLfloat sx, GLfloat sy, GLfloat *x, GLfloat *y)
+{
+	struct geometry *g = surface->geometry;
+
+	/* pure rotation around origin by sine and cosine */
+	*x = g->c * sx + g->s * sy;
+	*y = -g->s * sx + g->c * sy;
+}
+
+/* ---------------------- copied begins -----------------------*/
+
+struct polygon8 {
+	GLfloat x[8];
+	GLfloat y[8];
+	int n;
+};
+
+struct clip_context {
+	struct {
+		GLfloat x;
+		GLfloat y;
+	} prev;
+
+	struct {
+		GLfloat x1, y1;
+		GLfloat x2, y2;
+	} clip;
+
+	struct {
+		GLfloat *x;
+		GLfloat *y;
+	} vertices;
+};
+
+static GLfloat
+clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+		 GLfloat x_arg)
+{
+	/* homogenenous line equation, ax + by + c = 0, l = (a, b, c)^T */
+	GLfloat a, b, c;
+
+	/* l = p1 x p2, vector cross product */
+	a = p1y - p2y;
+	b = p2x - p1x;
+	c = p1x * p2y - p1y * p2x;
+
+	/* intersection of lines l and x = x_arg; l x (-1, 0, x_arg)^T */
+	return -(c + a * x_arg) / b;
+}
+
+static GLfloat
+clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
+		 GLfloat y_arg)
+{
+	/* homogenenous line equation, ax + by + c = 0, l = (a, b, c)^T */
+	GLfloat a, b, c;
+
+	/* l = p1 x p2, vector cross product */
+	a = p1y - p2y;
+	b = p2x - p1x;
+	c = p1x * p2y - p1y * p2x;
+
+	/* intersection of lines l and y = y_arg; l x (0, -1, y_arg)^T */
+	return -(b * y_arg + c) / a;
+}
+
+enum path_transition {
+	PATH_TRANSITION_OUT_TO_OUT = 0,
+	PATH_TRANSITION_OUT_TO_IN = 1,
+	PATH_TRANSITION_IN_TO_OUT = 2,
+	PATH_TRANSITION_IN_TO_IN = 3,
+};
+
+static void
+clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+	*ctx->vertices.x++ = x;
+	*ctx->vertices.y++ = y;
+}
+
+static enum path_transition
+path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+	return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
+}
+
+static enum path_transition
+path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+	return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
+}
+
+static enum path_transition
+path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+	return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
+}
+
+static enum path_transition
+path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
+{
+	return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
+}
+
+static void
+clip_polygon_leftright(struct clip_context *ctx,
+		       enum path_transition transition,
+		       GLfloat x, GLfloat y, GLfloat clip_x)
+{
+	GLfloat yi;
+
+	switch (transition) {
+	case PATH_TRANSITION_IN_TO_IN:
+		clip_append_vertex(ctx, x, y);
+		break;
+	case PATH_TRANSITION_IN_TO_OUT:
+		yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+		clip_append_vertex(ctx, clip_x, yi);
+		break;
+	case PATH_TRANSITION_OUT_TO_IN:
+		yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
+		clip_append_vertex(ctx, clip_x, yi);
+		clip_append_vertex(ctx, x, y);
+		break;
+	case PATH_TRANSITION_OUT_TO_OUT:
+		/* nothing */
+		break;
+	default:
+		assert(0 && "bad enum path_transition");
+	}
+
+	ctx->prev.x = x;
+	ctx->prev.y = y;
+}
+
+static void
+clip_polygon_topbottom(struct clip_context *ctx,
+		       enum path_transition transition,
+		       GLfloat x, GLfloat y, GLfloat clip_y)
+{
+	GLfloat xi;
+
+	switch (transition) {
+	case PATH_TRANSITION_IN_TO_IN:
+		clip_append_vertex(ctx, x, y);
+		break;
+	case PATH_TRANSITION_IN_TO_OUT:
+		xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+		clip_append_vertex(ctx, xi, clip_y);
+		break;
+	case PATH_TRANSITION_OUT_TO_IN:
+		xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
+		clip_append_vertex(ctx, xi, clip_y);
+		clip_append_vertex(ctx, x, y);
+		break;
+	case PATH_TRANSITION_OUT_TO_OUT:
+		/* nothing */
+		break;
+	default:
+		assert(0 && "bad enum path_transition");
+	}
+
+	ctx->prev.x = x;
+	ctx->prev.y = y;
+}
+
+static void
+clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
+		      GLfloat *dst_x, GLfloat *dst_y)
+{
+	ctx->prev.x = src->x[src->n - 1];
+	ctx->prev.y = src->y[src->n - 1];
+	ctx->vertices.x = dst_x;
+	ctx->vertices.y = dst_y;
+}
+
+static int
+clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
+		  GLfloat *dst_x, GLfloat *dst_y)
+{
+	enum path_transition trans;
+	int i;
+
+	clip_context_prepare(ctx, src, dst_x, dst_y);
+	for (i = 0; i < src->n; i++) {
+		trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
+		clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+				       ctx->clip.x1);
+	}
+	return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
+		   GLfloat *dst_x, GLfloat *dst_y)
+{
+	enum path_transition trans;
+	int i;
+
+	clip_context_prepare(ctx, src, dst_x, dst_y);
+	for (i = 0; i < src->n; i++) {
+		trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
+		clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
+				       ctx->clip.x2);
+	}
+	return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
+		 GLfloat *dst_x, GLfloat *dst_y)
+{
+	enum path_transition trans;
+	int i;
+
+	clip_context_prepare(ctx, src, dst_x, dst_y);
+	for (i = 0; i < src->n; i++) {
+		trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
+		clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+				       ctx->clip.y1);
+	}
+	return ctx->vertices.x - dst_x;
+}
+
+static int
+clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
+		    GLfloat *dst_x, GLfloat *dst_y)
+{
+	enum path_transition trans;
+	int i;
+
+	clip_context_prepare(ctx, src, dst_x, dst_y);
+	for (i = 0; i < src->n; i++) {
+		trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
+		clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
+				       ctx->clip.y2);
+	}
+	return ctx->vertices.x - dst_x;
+}
+
+#define max(a, b) (((a) > (b)) ? (a) : (b))
+#define min(a, b) (((a) > (b)) ? (b) : (a))
+#define clip(x, a, b)  min(max(x, a), b)
+
+static int
+calculate_edges(struct weston_surface *es, pixman_box32_t *rect,
+		pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
+{
+	struct polygon8 polygon;
+	struct clip_context ctx;
+	int i, n = 0;
+	GLfloat min_x, max_x, min_y, max_y;
+	struct polygon8 surf = {
+		{ surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
+		{ surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
+		4
+	};
+
+	ctx.clip.x1 = rect->x1;
+	ctx.clip.y1 = rect->y1;
+	ctx.clip.x2 = rect->x2;
+	ctx.clip.y2 = rect->y2;
+
+	/* transform surface to screen space: */
+	for (i = 0; i < surf.n; i++)
+		weston_surface_to_global_float(es, surf.x[i], surf.y[i],
+					       &surf.x[i], &surf.y[i]);
+
+	/* find bounding box: */
+	min_x = max_x = surf.x[0];
+	min_y = max_y = surf.y[0];
+
+	for (i = 1; i < surf.n; i++) {
+		min_x = min(min_x, surf.x[i]);
+		max_x = max(max_x, surf.x[i]);
+		min_y = min(min_y, surf.y[i]);
+		max_y = max(max_y, surf.y[i]);
+	}
+
+	/* First, simple bounding box check to discard early transformed
+	 * surface rects that do not intersect with the clip region:
+	 */
+	if ((min_x > ctx.clip.x2) || (max_x < ctx.clip.x1) ||
+	    (min_y > ctx.clip.y2) || (max_y < ctx.clip.y1))
+		return 0;
+
+	/* Simple case, bounding box edges are parallel to surface edges,
+	 * there will be only four edges.  We just need to clip the surface
+	 * vertices to the clip rect bounds:
+	 */
+	if (!es->transform.enabled) {
+		for (i = 0; i < surf.n; i++) {
+			ex[n] = clip(surf.x[i], ctx.clip.x1, ctx.clip.x2);
+			ey[n] = clip(surf.y[i], ctx.clip.y1, ctx.clip.y2);
+			n++;
+		}
+		return surf.n;
+	}
+
+	/* Transformed case: use a general polygon clipping algorithm to
+	 * clip the surface rectangle with each side of 'rect'.
+	 * The algorithm is Sutherland-Hodgman, as explained in
+	 * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
+	 * but without looking at any of that code.
+	 */
+	polygon.n = clip_polygon_left(&ctx, &surf, polygon.x, polygon.y);
+	surf.n = clip_polygon_right(&ctx, &polygon, surf.x, surf.y);
+	polygon.n = clip_polygon_top(&ctx, &surf, polygon.x, polygon.y);
+	return clip_polygon_bottom(&ctx, &polygon, ex, ey);
+}
+
+
+/* ---------------------- copied ends -----------------------*/
+
+static void
+geometry_set_phi(struct geometry *g, float phi)
+{
+	g->phi = phi;
+	g->s = sin(phi);
+	g->c = cos(phi);
+}
+
+static void
+geometry_init(struct geometry *g)
+{
+	g->clip.x1 = -50;
+	g->clip.y1 = -50;
+	g->clip.x2 = -10;
+	g->clip.y2 = -10;
+
+	g->surf.x1 = -20;
+	g->surf.y1 = -20;
+	g->surf.x2 = 20;
+	g->surf.y2 = 20;
+
+	geometry_set_phi(g, 0.0);
+}
+
+struct ui_state {
+	uint32_t button;
+	int down;
+
+	int down_pos[2];
+	struct geometry geometry;
+};
+
+struct cliptest {
+	struct window *window;
+	struct widget *widget;
+	struct display *display;
+	int fullscreen;
+
+	struct ui_state ui;
+
+	struct geometry geometry;
+	struct weston_surface surface;
+};
+
+static void
+draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+	int i;
+
+	cairo_move_to(cr, x[0], y[0]);
+	for (i = 1; i < n; i++)
+		cairo_line_to(cr, x[i], y[i]);
+	cairo_line_to(cr, x[0], y[0]);
+}
+
+static void
+draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
+{
+	char str[16];
+	int i;
+
+	for (i = 0; i < n; i++) {
+		snprintf(str, 16, "%d", i);
+		cairo_move_to(cr, x[i], y[i]);
+		cairo_show_text(cr, str);
+	}
+}
+
+static void
+draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_surface *surface)
+{
+	GLfloat x[4], y[4];
+
+	if (surface) {
+		weston_surface_to_global_float(surface, box->x1, box->y1, &x[0], &y[0]);
+		weston_surface_to_global_float(surface, box->x2, box->y1, &x[1], &y[1]);
+		weston_surface_to_global_float(surface, box->x2, box->y2, &x[2], &y[2]);
+		weston_surface_to_global_float(surface, box->x1, box->y2, &x[3], &y[3]);
+	} else {
+		x[0] = box->x1; y[0] = box->y1;
+		x[1] = box->x2; y[1] = box->y1;
+		x[2] = box->x2; y[2] = box->y2;
+		x[3] = box->x1; y[3] = box->y2;
+	}
+
+	draw_polygon_closed(cr, x, y, 4);
+}
+
+static void
+draw_geometry(cairo_t *cr, struct weston_surface *surface)
+{
+	struct geometry *g = surface->geometry;
+	GLfloat cx, cy;
+	GLfloat ex[8], ey[8];
+	int n;
+
+	draw_box(cr, &g->surf, surface);
+	cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
+	cairo_fill(cr);
+	weston_surface_to_global_float(surface, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
+	cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
+	if (surface->transform.enabled == 0)
+		cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
+	cairo_fill(cr);
+
+	draw_box(cr, &g->clip, NULL);
+	cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
+	cairo_fill(cr);
+
+	n = calculate_edges(surface, &g->clip, &g->surf, ex, ey);
+	draw_polygon_closed(cr, ex, ey, n);
+	cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
+	cairo_stroke(cr);
+
+	cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
+	draw_polygon_labels(cr, ex, ey, n);
+}
+
+static void
+redraw_handler(struct widget *widget, void *data)
+{
+	struct cliptest *cliptest = data;
+	struct rectangle allocation;
+	cairo_t *cr;
+	cairo_surface_t *surface;
+
+	widget_get_allocation(cliptest->widget, &allocation);
+
+	surface = window_get_surface(cliptest->window);
+	cr = cairo_create(surface);
+	widget_get_allocation(cliptest->widget, &allocation);
+	cairo_rectangle(cr, allocation.x, allocation.y,
+			allocation.width, allocation.height);
+	cairo_clip(cr);
+	cairo_push_group(cr);
+	cairo_translate(cr, allocation.x + allocation.width / 2.0,
+			allocation.y + allocation.height / 2.0);
+	cairo_scale(cr, 4.0, 4.0);
+
+	cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
+	cairo_set_source_rgba(cr, 0, 0, 0, 1);
+	cairo_paint(cr);
+
+	cairo_set_line_width(cr, 0.25);
+	cairo_move_to(cr, -allocation.width / 2.0, 0.0);
+	cairo_line_to(cr, allocation.width / 2.0, 0.0);
+	cairo_move_to(cr, 0.0, -allocation.height / 2.0);
+	cairo_line_to(cr, 0.0, allocation.height / 2.0);
+	cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
+	cairo_stroke(cr);
+
+	cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
+	cairo_set_line_width(cr, 0.5);
+	cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
+	cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
+			       CAIRO_FONT_WEIGHT_BOLD);
+	cairo_set_font_size(cr, 5.0);
+	draw_geometry(cr, &cliptest->surface);
+
+	cairo_pop_group_to_source(cr);
+	cairo_paint(cr);
+	cairo_destroy(cr);
+
+	cairo_surface_destroy(surface);
+}
+
+static int
+motion_handler(struct widget *widget, struct input *input,
+	       uint32_t time, float x, float y, void *data)
+{
+	struct cliptest *cliptest = data;
+	struct ui_state *ui = &cliptest->ui;
+	struct geometry *ref = &ui->geometry;
+	struct geometry *geom = &cliptest->geometry;
+	float dx, dy;
+
+	if (!ui->down)
+		return CURSOR_LEFT_PTR;
+
+	dx = (x - ui->down_pos[0]) * 0.25;
+	dy = (y - ui->down_pos[1]) * 0.25;
+
+	switch (ui->button) {
+	case BTN_LEFT:
+		geom->clip.x1 = ref->clip.x1 + dx;
+		geom->clip.y1 = ref->clip.y1 + dy;
+		/* fall through */
+	case BTN_RIGHT:
+		geom->clip.x2 = ref->clip.x2 + dx;
+		geom->clip.y2 = ref->clip.y2 + dy;
+		break;
+	default:
+		return CURSOR_LEFT_PTR;
+	}
+
+	widget_schedule_redraw(cliptest->widget);
+	return CURSOR_BLANK;
+}
+
+static void
+button_handler(struct widget *widget, struct input *input,
+	       uint32_t time, uint32_t button,
+	       enum wl_pointer_button_state state, void *data)
+{
+	struct cliptest *cliptest = data;
+	struct ui_state *ui = &cliptest->ui;
+
+	ui->button = button;
+
+	if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
+		ui->down = 1;
+		input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]);
+	} else {
+		ui->down = 0;
+		ui->geometry = cliptest->geometry;
+	}
+}
+
+static void
+axis_handler(struct widget *widget, struct input *input, uint32_t time,
+	     uint32_t axis, wl_fixed_t value, void *data)
+{
+	struct cliptest *cliptest = data;
+	struct geometry *geom = &cliptest->geometry;
+
+	if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
+		return;
+
+	geometry_set_phi(geom, geom->phi +
+				(M_PI / 12.0) * wl_fixed_to_double(value));
+	cliptest->surface.transform.enabled = 1;
+
+	widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+key_handler(struct window *window, struct input *input, uint32_t time,
+	    uint32_t key, uint32_t sym,
+	    enum wl_keyboard_key_state state, void *data)
+{
+	struct cliptest *cliptest = data;
+	struct geometry *g = &cliptest->geometry;
+
+	if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
+		return;
+
+	switch (sym) {
+	case XKB_KEY_Escape:
+		display_exit(cliptest->display);
+		return;
+	case XKB_KEY_w:
+		g->clip.y1 -= 1;
+		g->clip.y2 -= 1;
+		break;
+	case XKB_KEY_a:
+		g->clip.x1 -= 1;
+		g->clip.x2 -= 1;
+		break;
+	case XKB_KEY_s:
+		g->clip.y1 += 1;
+		g->clip.y2 += 1;
+		break;
+	case XKB_KEY_d:
+		g->clip.x1 += 1;
+		g->clip.x2 += 1;
+		break;
+	case XKB_KEY_i:
+		g->clip.y2 -= 1;
+		break;
+	case XKB_KEY_j:
+		g->clip.x2 -= 1;
+		break;
+	case XKB_KEY_k:
+		g->clip.y2 += 1;
+		break;
+	case XKB_KEY_l:
+		g->clip.x2 += 1;
+		break;
+	case XKB_KEY_n:
+		geometry_set_phi(g, g->phi + (M_PI / 24.0));
+		cliptest->surface.transform.enabled = 1;
+		break;
+	case XKB_KEY_m:
+		geometry_set_phi(g, g->phi - (M_PI / 24.0));
+		cliptest->surface.transform.enabled = 1;
+		break;
+	case XKB_KEY_r:
+		geometry_set_phi(g, 0.0);
+		cliptest->surface.transform.enabled = 0;
+		break;
+	default:
+		return;
+	}
+
+	widget_schedule_redraw(cliptest->widget);
+}
+
+static void
+keyboard_focus_handler(struct window *window,
+		       struct input *device, void *data)
+{
+	struct cliptest *cliptest = data;
+
+	window_schedule_redraw(cliptest->window);
+}
+
+static void
+fullscreen_handler(struct window *window, void *data)
+{
+	struct cliptest *cliptest = data;
+
+	cliptest->fullscreen ^= 1;
+	window_set_fullscreen(window, cliptest->fullscreen);
+}
+
+static struct cliptest *
+cliptest_create(struct display *display)
+{
+	struct cliptest *cliptest;
+
+	cliptest = malloc(sizeof *cliptest);
+	if (cliptest == NULL)
+		return cliptest;
+	memset(cliptest, 0, sizeof *cliptest);
+
+	cliptest->surface.geometry = &cliptest->geometry;
+	cliptest->surface.transform.enabled = 0;
+	geometry_init(&cliptest->geometry);
+	geometry_init(&cliptest->ui.geometry);
+
+	cliptest->window = window_create(display);
+	cliptest->widget = frame_create(cliptest->window, cliptest);
+	window_set_title(cliptest->window, "cliptest");
+	cliptest->display = display;
+
+	window_set_user_data(cliptest->window, cliptest);
+	widget_set_redraw_handler(cliptest->widget, redraw_handler);
+	widget_set_button_handler(cliptest->widget, button_handler);
+	widget_set_motion_handler(cliptest->widget, motion_handler);
+	widget_set_axis_handler(cliptest->widget, axis_handler);
+
+	window_set_keyboard_focus_handler(cliptest->window,
+					  keyboard_focus_handler);
+	window_set_key_handler(cliptest->window, key_handler);
+	window_set_fullscreen_handler(cliptest->window, fullscreen_handler);
+
+	/* set minimum size */
+	widget_schedule_resize(cliptest->widget, 200, 100);
+
+	/* set current size */
+	widget_schedule_resize(cliptest->widget, 500, 400);
+
+	return cliptest;
+}
+
+static struct timespec begin_time;
+
+static void
+reset_timer(void)
+{
+	clock_gettime(CLOCK_MONOTONIC, &begin_time);
+}
+
+static double
+read_timer(void)
+{
+	struct timespec t;
+
+	clock_gettime(CLOCK_MONOTONIC, &t);
+	return (double)(t.tv_sec - begin_time.tv_sec) +
+	       1e-9 * (t.tv_nsec - begin_time.tv_nsec);
+}
+
+static int
+benchmark(void)
+{
+	struct weston_surface surface;
+	struct geometry geom;
+	GLfloat ex[8], ey[8];
+	int i;
+	double t;
+	const int N = 1000000;
+
+	geom.clip.x1 = -19;
+	geom.clip.y1 = -19;
+	geom.clip.x2 = 19;
+	geom.clip.y2 = 19;
+
+	geom.surf.x1 = -20;
+	geom.surf.y1 = -20;
+	geom.surf.x2 = 20;
+	geom.surf.y2 = 20;
+
+	geometry_set_phi(&geom, 0.0);
+
+	surface.transform.enabled = 1;
+	surface.geometry = &geom;
+
+	reset_timer();
+	for (i = 0; i < N; i++) {
+		geometry_set_phi(&geom, (float)i / 360.0f);
+		calculate_edges(&surface, &geom.clip, &geom.surf, ex, ey);
+	}
+	t = read_timer();
+
+	printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6);
+
+	return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+	struct display *d;
+	struct cliptest *cliptest;
+
+	if (argc > 1)
+		return benchmark();
+
+	d = display_create(argc, argv);
+	if (d == NULL) {
+		fprintf(stderr, "failed to create display: %m\n");
+		return -1;
+	}
+
+	cliptest = cliptest_create(d);
+	display_run(d);
+
+	widget_destroy(cliptest->widget);
+	window_destroy(cliptest->window);
+	free(cliptest);
+
+	return 0;
+}
-- 
1.7.8.6



More information about the wayland-devel mailing list