Update snapshot
[profile/ivi/weston.git] / clients / cliptest.c
1 /*
2  * Copyright © 2012 Collabora, Ltd.
3  * Copyright © 2012 Rob Clark
4  *
5  * Permission to use, copy, modify, distribute, and sell this software and its
6  * documentation for any purpose is hereby granted without fee, provided that
7  * the above copyright notice appear in all copies and that both that copyright
8  * notice and this permission notice appear in supporting documentation, and
9  * that the name of the copyright holders not be used in advertising or
10  * publicity pertaining to distribution of the software without specific,
11  * written prior permission.  The copyright holders make no representations
12  * about the suitability of this software for any purpose.  It is provided "as
13  * is" without express or implied warranty.
14  *
15  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
17  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
19  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
20  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
21  * OF THIS SOFTWARE.
22  */
23
24 /* cliptest: for debugging calculate_edges() function, which is copied
25  * from compositor.c.
26  * controls:
27  *      clip box position: mouse left drag, keys: w a s d
28  *      clip box size: mouse right drag, keys: i j k l
29  *      surface orientation: mouse wheel, keys: n m
30  *      surface transform disable key: r
31  */
32
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <fcntl.h>
38 #include <libgen.h>
39 #include <unistd.h>
40 #include <math.h>
41 #include <time.h>
42 #include <pixman.h>
43 #include <cairo.h>
44 #include <float.h>
45 #include <assert.h>
46
47 #include <linux/input.h>
48 #include <wayland-client.h>
49
50 #include "window.h"
51
52 typedef float GLfloat;
53
54 struct geometry {
55         pixman_box32_t clip;
56
57         pixman_box32_t surf;
58         float s; /* sin phi */
59         float c; /* cos phi */
60         float phi;
61 };
62
63 struct weston_surface {
64         struct {
65                 int enabled;
66         } transform;
67
68         struct geometry *geometry;
69 };
70
71 static void
72 weston_surface_to_global_float(struct weston_surface *surface,
73                                GLfloat sx, GLfloat sy, GLfloat *x, GLfloat *y)
74 {
75         struct geometry *g = surface->geometry;
76
77         /* pure rotation around origin by sine and cosine */
78         *x = g->c * sx + g->s * sy;
79         *y = -g->s * sx + g->c * sy;
80 }
81
82 /* ---------------------- copied begins -----------------------*/
83
84 struct polygon8 {
85         GLfloat x[8];
86         GLfloat y[8];
87         int n;
88 };
89
90 struct clip_context {
91         struct {
92                 GLfloat x;
93                 GLfloat y;
94         } prev;
95
96         struct {
97                 GLfloat x1, y1;
98                 GLfloat x2, y2;
99         } clip;
100
101         struct {
102                 GLfloat *x;
103                 GLfloat *y;
104         } vertices;
105 };
106
107 static GLfloat
108 float_difference(GLfloat a, GLfloat b)
109 {
110         /* http://www.altdevblogaday.com/2012/02/22/comparing-floating-point-numbers-2012-edition/ */
111         static const GLfloat max_diff = 4.0f * FLT_MIN;
112         static const GLfloat max_rel_diff = 4.0e-5;
113         GLfloat diff = a - b;
114         GLfloat adiff = fabsf(diff);
115
116         if (adiff <= max_diff)
117                 return 0.0f;
118
119         a = fabsf(a);
120         b = fabsf(b);
121         if (adiff <= (a > b ? a : b) * max_rel_diff)
122                 return 0.0f;
123
124         return diff;
125 }
126
127 /* A line segment (p1x, p1y)-(p2x, p2y) intersects the line x = x_arg.
128  * Compute the y coordinate of the intersection.
129  */
130 static GLfloat
131 clip_intersect_y(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
132                  GLfloat x_arg)
133 {
134         GLfloat a;
135         GLfloat diff = float_difference(p1x, p2x);
136
137         /* Practically vertical line segment, yet the end points have already
138          * been determined to be on different sides of the line. Therefore
139          * the line segment is part of the line and intersects everywhere.
140          * Return the end point, so we use the whole line segment.
141          */
142         if (diff == 0.0f)
143                 return p2y;
144
145         a = (x_arg - p2x) / diff;
146         return p2y + (p1y - p2y) * a;
147 }
148
149 /* A line segment (p1x, p1y)-(p2x, p2y) intersects the line y = y_arg.
150  * Compute the x coordinate of the intersection.
151  */
152 static GLfloat
153 clip_intersect_x(GLfloat p1x, GLfloat p1y, GLfloat p2x, GLfloat p2y,
154                  GLfloat y_arg)
155 {
156         GLfloat a;
157         GLfloat diff = float_difference(p1y, p2y);
158
159         /* Practically horizontal line segment, yet the end points have already
160          * been determined to be on different sides of the line. Therefore
161          * the line segment is part of the line and intersects everywhere.
162          * Return the end point, so we use the whole line segment.
163          */
164         if (diff == 0.0f)
165                 return p2x;
166
167         a = (y_arg - p2y) / diff;
168         return p2x + (p1x - p2x) * a;
169 }
170
171 enum path_transition {
172         PATH_TRANSITION_OUT_TO_OUT = 0,
173         PATH_TRANSITION_OUT_TO_IN = 1,
174         PATH_TRANSITION_IN_TO_OUT = 2,
175         PATH_TRANSITION_IN_TO_IN = 3,
176 };
177
178 static void
179 clip_append_vertex(struct clip_context *ctx, GLfloat x, GLfloat y)
180 {
181         *ctx->vertices.x++ = x;
182         *ctx->vertices.y++ = y;
183 }
184
185 static enum path_transition
186 path_transition_left_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
187 {
188         return ((ctx->prev.x >= ctx->clip.x1) << 1) | (x >= ctx->clip.x1);
189 }
190
191 static enum path_transition
192 path_transition_right_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
193 {
194         return ((ctx->prev.x < ctx->clip.x2) << 1) | (x < ctx->clip.x2);
195 }
196
197 static enum path_transition
198 path_transition_top_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
199 {
200         return ((ctx->prev.y >= ctx->clip.y1) << 1) | (y >= ctx->clip.y1);
201 }
202
203 static enum path_transition
204 path_transition_bottom_edge(struct clip_context *ctx, GLfloat x, GLfloat y)
205 {
206         return ((ctx->prev.y < ctx->clip.y2) << 1) | (y < ctx->clip.y2);
207 }
208
209 static void
210 clip_polygon_leftright(struct clip_context *ctx,
211                        enum path_transition transition,
212                        GLfloat x, GLfloat y, GLfloat clip_x)
213 {
214         GLfloat yi;
215
216         switch (transition) {
217         case PATH_TRANSITION_IN_TO_IN:
218                 clip_append_vertex(ctx, x, y);
219                 break;
220         case PATH_TRANSITION_IN_TO_OUT:
221                 yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
222                 clip_append_vertex(ctx, clip_x, yi);
223                 break;
224         case PATH_TRANSITION_OUT_TO_IN:
225                 yi = clip_intersect_y(ctx->prev.x, ctx->prev.y, x, y, clip_x);
226                 clip_append_vertex(ctx, clip_x, yi);
227                 clip_append_vertex(ctx, x, y);
228                 break;
229         case PATH_TRANSITION_OUT_TO_OUT:
230                 /* nothing */
231                 break;
232         default:
233                 assert(0 && "bad enum path_transition");
234         }
235
236         ctx->prev.x = x;
237         ctx->prev.y = y;
238 }
239
240 static void
241 clip_polygon_topbottom(struct clip_context *ctx,
242                        enum path_transition transition,
243                        GLfloat x, GLfloat y, GLfloat clip_y)
244 {
245         GLfloat xi;
246
247         switch (transition) {
248         case PATH_TRANSITION_IN_TO_IN:
249                 clip_append_vertex(ctx, x, y);
250                 break;
251         case PATH_TRANSITION_IN_TO_OUT:
252                 xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
253                 clip_append_vertex(ctx, xi, clip_y);
254                 break;
255         case PATH_TRANSITION_OUT_TO_IN:
256                 xi = clip_intersect_x(ctx->prev.x, ctx->prev.y, x, y, clip_y);
257                 clip_append_vertex(ctx, xi, clip_y);
258                 clip_append_vertex(ctx, x, y);
259                 break;
260         case PATH_TRANSITION_OUT_TO_OUT:
261                 /* nothing */
262                 break;
263         default:
264                 assert(0 && "bad enum path_transition");
265         }
266
267         ctx->prev.x = x;
268         ctx->prev.y = y;
269 }
270
271 static void
272 clip_context_prepare(struct clip_context *ctx, const struct polygon8 *src,
273                       GLfloat *dst_x, GLfloat *dst_y)
274 {
275         ctx->prev.x = src->x[src->n - 1];
276         ctx->prev.y = src->y[src->n - 1];
277         ctx->vertices.x = dst_x;
278         ctx->vertices.y = dst_y;
279 }
280
281 static int
282 clip_polygon_left(struct clip_context *ctx, const struct polygon8 *src,
283                   GLfloat *dst_x, GLfloat *dst_y)
284 {
285         enum path_transition trans;
286         int i;
287
288         clip_context_prepare(ctx, src, dst_x, dst_y);
289         for (i = 0; i < src->n; i++) {
290                 trans = path_transition_left_edge(ctx, src->x[i], src->y[i]);
291                 clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
292                                        ctx->clip.x1);
293         }
294         return ctx->vertices.x - dst_x;
295 }
296
297 static int
298 clip_polygon_right(struct clip_context *ctx, const struct polygon8 *src,
299                    GLfloat *dst_x, GLfloat *dst_y)
300 {
301         enum path_transition trans;
302         int i;
303
304         clip_context_prepare(ctx, src, dst_x, dst_y);
305         for (i = 0; i < src->n; i++) {
306                 trans = path_transition_right_edge(ctx, src->x[i], src->y[i]);
307                 clip_polygon_leftright(ctx, trans, src->x[i], src->y[i],
308                                        ctx->clip.x2);
309         }
310         return ctx->vertices.x - dst_x;
311 }
312
313 static int
314 clip_polygon_top(struct clip_context *ctx, const struct polygon8 *src,
315                  GLfloat *dst_x, GLfloat *dst_y)
316 {
317         enum path_transition trans;
318         int i;
319
320         clip_context_prepare(ctx, src, dst_x, dst_y);
321         for (i = 0; i < src->n; i++) {
322                 trans = path_transition_top_edge(ctx, src->x[i], src->y[i]);
323                 clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
324                                        ctx->clip.y1);
325         }
326         return ctx->vertices.x - dst_x;
327 }
328
329 static int
330 clip_polygon_bottom(struct clip_context *ctx, const struct polygon8 *src,
331                     GLfloat *dst_x, GLfloat *dst_y)
332 {
333         enum path_transition trans;
334         int i;
335
336         clip_context_prepare(ctx, src, dst_x, dst_y);
337         for (i = 0; i < src->n; i++) {
338                 trans = path_transition_bottom_edge(ctx, src->x[i], src->y[i]);
339                 clip_polygon_topbottom(ctx, trans, src->x[i], src->y[i],
340                                        ctx->clip.y2);
341         }
342         return ctx->vertices.x - dst_x;
343 }
344
345 #define max(a, b) (((a) > (b)) ? (a) : (b))
346 #define min(a, b) (((a) > (b)) ? (b) : (a))
347 #define clip(x, a, b)  min(max(x, a), b)
348
349 /*
350  * Compute the boundary vertices of the intersection of the global coordinate
351  * aligned rectangle 'rect', and an arbitrary quadrilateral produced from
352  * 'surf_rect' when transformed from surface coordinates into global coordinates.
353  * The vertices are written to 'ex' and 'ey', and the return value is the
354  * number of vertices. Vertices are produced in clockwise winding order.
355  * Guarantees to produce either zero vertices, or 3-8 vertices with non-zero
356  * polygon area.
357  */
358 static int
359 calculate_edges(struct weston_surface *es, pixman_box32_t *rect,
360                 pixman_box32_t *surf_rect, GLfloat *ex, GLfloat *ey)
361 {
362         struct polygon8 polygon;
363         struct clip_context ctx;
364         int i, n;
365         GLfloat min_x, max_x, min_y, max_y;
366         struct polygon8 surf = {
367                 { surf_rect->x1, surf_rect->x2, surf_rect->x2, surf_rect->x1 },
368                 { surf_rect->y1, surf_rect->y1, surf_rect->y2, surf_rect->y2 },
369                 4
370         };
371
372         ctx.clip.x1 = rect->x1;
373         ctx.clip.y1 = rect->y1;
374         ctx.clip.x2 = rect->x2;
375         ctx.clip.y2 = rect->y2;
376
377         /* transform surface to screen space: */
378         for (i = 0; i < surf.n; i++)
379                 weston_surface_to_global_float(es, surf.x[i], surf.y[i],
380                                                &surf.x[i], &surf.y[i]);
381
382         /* find bounding box: */
383         min_x = max_x = surf.x[0];
384         min_y = max_y = surf.y[0];
385
386         for (i = 1; i < surf.n; i++) {
387                 min_x = min(min_x, surf.x[i]);
388                 max_x = max(max_x, surf.x[i]);
389                 min_y = min(min_y, surf.y[i]);
390                 max_y = max(max_y, surf.y[i]);
391         }
392
393         /* First, simple bounding box check to discard early transformed
394          * surface rects that do not intersect with the clip region:
395          */
396         if ((min_x >= ctx.clip.x2) || (max_x <= ctx.clip.x1) ||
397             (min_y >= ctx.clip.y2) || (max_y <= ctx.clip.y1))
398                 return 0;
399
400         /* Simple case, bounding box edges are parallel to surface edges,
401          * there will be only four edges.  We just need to clip the surface
402          * vertices to the clip rect bounds:
403          */
404         if (!es->transform.enabled) {
405                 for (i = 0; i < surf.n; i++) {
406                         ex[i] = clip(surf.x[i], ctx.clip.x1, ctx.clip.x2);
407                         ey[i] = clip(surf.y[i], ctx.clip.y1, ctx.clip.y2);
408                 }
409                 return surf.n;
410         }
411
412         /* Transformed case: use a general polygon clipping algorithm to
413          * clip the surface rectangle with each side of 'rect'.
414          * The algorithm is Sutherland-Hodgman, as explained in
415          * http://www.codeguru.com/cpp/misc/misc/graphics/article.php/c8965/Polygon-Clipping.htm
416          * but without looking at any of that code.
417          */
418         polygon.n = clip_polygon_left(&ctx, &surf, polygon.x, polygon.y);
419         surf.n = clip_polygon_right(&ctx, &polygon, surf.x, surf.y);
420         polygon.n = clip_polygon_top(&ctx, &surf, polygon.x, polygon.y);
421         surf.n = clip_polygon_bottom(&ctx, &polygon, surf.x, surf.y);
422
423         /* Get rid of duplicate vertices */
424         ex[0] = surf.x[0];
425         ey[0] = surf.y[0];
426         n = 1;
427         for (i = 1; i < surf.n; i++) {
428                 if (float_difference(ex[n - 1], surf.x[i]) == 0.0f &&
429                     float_difference(ey[n - 1], surf.y[i]) == 0.0f)
430                         continue;
431                 ex[n] = surf.x[i];
432                 ey[n] = surf.y[i];
433                 n++;
434         }
435         if (float_difference(ex[n - 1], surf.x[0]) == 0.0f &&
436             float_difference(ey[n - 1], surf.y[0]) == 0.0f)
437                 n--;
438
439         if (n < 3)
440                 return 0;
441
442         return n;
443 }
444
445
446 /* ---------------------- copied ends -----------------------*/
447
448 static void
449 geometry_set_phi(struct geometry *g, float phi)
450 {
451         g->phi = phi;
452         g->s = sin(phi);
453         g->c = cos(phi);
454 }
455
456 static void
457 geometry_init(struct geometry *g)
458 {
459         g->clip.x1 = -50;
460         g->clip.y1 = -50;
461         g->clip.x2 = -10;
462         g->clip.y2 = -10;
463
464         g->surf.x1 = -20;
465         g->surf.y1 = -20;
466         g->surf.x2 = 20;
467         g->surf.y2 = 20;
468
469         geometry_set_phi(g, 0.0);
470 }
471
472 struct ui_state {
473         uint32_t button;
474         int down;
475
476         int down_pos[2];
477         struct geometry geometry;
478 };
479
480 struct cliptest {
481         struct window *window;
482         struct widget *widget;
483         struct display *display;
484         int fullscreen;
485
486         struct ui_state ui;
487
488         struct geometry geometry;
489         struct weston_surface surface;
490 };
491
492 static void
493 draw_polygon_closed(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
494 {
495         int i;
496
497         cairo_move_to(cr, x[0], y[0]);
498         for (i = 1; i < n; i++)
499                 cairo_line_to(cr, x[i], y[i]);
500         cairo_line_to(cr, x[0], y[0]);
501 }
502
503 static void
504 draw_polygon_labels(cairo_t *cr, GLfloat *x, GLfloat *y, int n)
505 {
506         char str[16];
507         int i;
508
509         for (i = 0; i < n; i++) {
510                 snprintf(str, 16, "%d", i);
511                 cairo_move_to(cr, x[i], y[i]);
512                 cairo_show_text(cr, str);
513         }
514 }
515
516 static void
517 draw_coordinates(cairo_t *cr, double ox, double oy, GLfloat *x, GLfloat *y, int n)
518 {
519         char str[64];
520         int i;
521         cairo_font_extents_t ext;
522
523         cairo_font_extents(cr, &ext);
524         for (i = 0; i < n; i++) {
525                 snprintf(str, 64, "%d: %14.9f, %14.9f", i, x[i], y[i]);
526                 cairo_move_to(cr, ox, oy + ext.height * (i + 1));
527                 cairo_show_text(cr, str);
528         }
529 }
530
531 static void
532 draw_box(cairo_t *cr, pixman_box32_t *box, struct weston_surface *surface)
533 {
534         GLfloat x[4], y[4];
535
536         if (surface) {
537                 weston_surface_to_global_float(surface, box->x1, box->y1, &x[0], &y[0]);
538                 weston_surface_to_global_float(surface, box->x2, box->y1, &x[1], &y[1]);
539                 weston_surface_to_global_float(surface, box->x2, box->y2, &x[2], &y[2]);
540                 weston_surface_to_global_float(surface, box->x1, box->y2, &x[3], &y[3]);
541         } else {
542                 x[0] = box->x1; y[0] = box->y1;
543                 x[1] = box->x2; y[1] = box->y1;
544                 x[2] = box->x2; y[2] = box->y2;
545                 x[3] = box->x1; y[3] = box->y2;
546         }
547
548         draw_polygon_closed(cr, x, y, 4);
549 }
550
551 static void
552 draw_geometry(cairo_t *cr, struct weston_surface *surface,
553               GLfloat *ex, GLfloat *ey, int n)
554 {
555         struct geometry *g = surface->geometry;
556         GLfloat cx, cy;
557
558         draw_box(cr, &g->surf, surface);
559         cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.4);
560         cairo_fill(cr);
561         weston_surface_to_global_float(surface, g->surf.x1 - 4, g->surf.y1 - 4, &cx, &cy);
562         cairo_arc(cr, cx, cy, 1.5, 0.0, 2.0 * M_PI);
563         if (surface->transform.enabled == 0)
564                 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.8);
565         cairo_fill(cr);
566
567         draw_box(cr, &g->clip, NULL);
568         cairo_set_source_rgba(cr, 0.0, 0.0, 1.0, 0.4);
569         cairo_fill(cr);
570
571         draw_polygon_closed(cr, ex, ey, n);
572         cairo_set_source_rgb(cr, 0.0, 1.0, 0.0);
573         cairo_stroke(cr);
574
575         cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 0.5);
576         draw_polygon_labels(cr, ex, ey, n);
577 }
578
579 static void
580 redraw_handler(struct widget *widget, void *data)
581 {
582         struct cliptest *cliptest = data;
583         struct geometry *g = cliptest->surface.geometry;
584         struct rectangle allocation;
585         cairo_t *cr;
586         cairo_surface_t *surface;
587         GLfloat ex[8];
588         GLfloat ey[8];
589         int n;
590
591         n = calculate_edges(&cliptest->surface, &g->clip, &g->surf, ex, ey);
592
593         widget_get_allocation(cliptest->widget, &allocation);
594
595         surface = window_get_surface(cliptest->window);
596         cr = cairo_create(surface);
597         widget_get_allocation(cliptest->widget, &allocation);
598         cairo_rectangle(cr, allocation.x, allocation.y,
599                         allocation.width, allocation.height);
600         cairo_clip(cr);
601
602         cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
603         cairo_set_source_rgba(cr, 0, 0, 0, 1);
604         cairo_paint(cr);
605
606         cairo_translate(cr, allocation.x, allocation.y);
607         cairo_set_line_width(cr, 1.0);
608         cairo_move_to(cr, allocation.width / 2.0, 0.0);
609         cairo_line_to(cr, allocation.width / 2.0, allocation.height);
610         cairo_move_to(cr, 0.0, allocation.height / 2.0);
611         cairo_line_to(cr, allocation.width, allocation.height / 2.0);
612         cairo_set_source_rgba(cr, 0.5, 0.5, 0.5, 1.0);
613         cairo_stroke(cr);
614
615         cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
616         cairo_push_group(cr);
617                 cairo_translate(cr, allocation.width / 2.0,
618                                 allocation.height / 2.0);
619                 cairo_scale(cr, 4.0, 4.0);
620                 cairo_set_line_width(cr, 0.5);
621                 cairo_set_line_join(cr, CAIRO_LINE_JOIN_BEVEL);
622                 cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
623                                        CAIRO_FONT_WEIGHT_BOLD);
624                 cairo_set_font_size(cr, 5.0);
625                 draw_geometry(cr, &cliptest->surface, ex, ey, n);
626         cairo_pop_group_to_source(cr);
627         cairo_paint(cr);
628
629         cairo_set_source_rgba(cr, 0.0, 1.0, 0.0, 1.0);
630         cairo_select_font_face(cr, "monospace", CAIRO_FONT_SLANT_NORMAL,
631                                CAIRO_FONT_WEIGHT_NORMAL);
632         cairo_set_font_size(cr, 12.0);
633         draw_coordinates(cr, 10.0, 10.0, ex, ey, n);
634
635         cairo_destroy(cr);
636
637         cairo_surface_destroy(surface);
638 }
639
640 static int
641 motion_handler(struct widget *widget, struct input *input,
642                uint32_t time, float x, float y, void *data)
643 {
644         struct cliptest *cliptest = data;
645         struct ui_state *ui = &cliptest->ui;
646         struct geometry *ref = &ui->geometry;
647         struct geometry *geom = &cliptest->geometry;
648         float dx, dy;
649
650         if (!ui->down)
651                 return CURSOR_LEFT_PTR;
652
653         dx = (x - ui->down_pos[0]) * 0.25;
654         dy = (y - ui->down_pos[1]) * 0.25;
655
656         switch (ui->button) {
657         case BTN_LEFT:
658                 geom->clip.x1 = ref->clip.x1 + dx;
659                 geom->clip.y1 = ref->clip.y1 + dy;
660                 /* fall through */
661         case BTN_RIGHT:
662                 geom->clip.x2 = ref->clip.x2 + dx;
663                 geom->clip.y2 = ref->clip.y2 + dy;
664                 break;
665         default:
666                 return CURSOR_LEFT_PTR;
667         }
668
669         widget_schedule_redraw(cliptest->widget);
670         return CURSOR_BLANK;
671 }
672
673 static void
674 button_handler(struct widget *widget, struct input *input,
675                uint32_t time, uint32_t button,
676                enum wl_pointer_button_state state, void *data)
677 {
678         struct cliptest *cliptest = data;
679         struct ui_state *ui = &cliptest->ui;
680
681         ui->button = button;
682
683         if (state == WL_POINTER_BUTTON_STATE_PRESSED) {
684                 ui->down = 1;
685                 input_get_position(input, &ui->down_pos[0], &ui->down_pos[1]);
686         } else {
687                 ui->down = 0;
688                 ui->geometry = cliptest->geometry;
689         }
690 }
691
692 static void
693 axis_handler(struct widget *widget, struct input *input, uint32_t time,
694              uint32_t axis, wl_fixed_t value, void *data)
695 {
696         struct cliptest *cliptest = data;
697         struct geometry *geom = &cliptest->geometry;
698
699         if (axis != WL_POINTER_AXIS_VERTICAL_SCROLL)
700                 return;
701
702         geometry_set_phi(geom, geom->phi +
703                                 (M_PI / 12.0) * wl_fixed_to_double(value));
704         cliptest->surface.transform.enabled = 1;
705
706         widget_schedule_redraw(cliptest->widget);
707 }
708
709 static void
710 key_handler(struct window *window, struct input *input, uint32_t time,
711             uint32_t key, uint32_t sym,
712             enum wl_keyboard_key_state state, void *data)
713 {
714         struct cliptest *cliptest = data;
715         struct geometry *g = &cliptest->geometry;
716
717         if (state == WL_KEYBOARD_KEY_STATE_RELEASED)
718                 return;
719
720         switch (sym) {
721         case XKB_KEY_Escape:
722                 display_exit(cliptest->display);
723                 return;
724         case XKB_KEY_w:
725                 g->clip.y1 -= 1;
726                 g->clip.y2 -= 1;
727                 break;
728         case XKB_KEY_a:
729                 g->clip.x1 -= 1;
730                 g->clip.x2 -= 1;
731                 break;
732         case XKB_KEY_s:
733                 g->clip.y1 += 1;
734                 g->clip.y2 += 1;
735                 break;
736         case XKB_KEY_d:
737                 g->clip.x1 += 1;
738                 g->clip.x2 += 1;
739                 break;
740         case XKB_KEY_i:
741                 g->clip.y2 -= 1;
742                 break;
743         case XKB_KEY_j:
744                 g->clip.x2 -= 1;
745                 break;
746         case XKB_KEY_k:
747                 g->clip.y2 += 1;
748                 break;
749         case XKB_KEY_l:
750                 g->clip.x2 += 1;
751                 break;
752         case XKB_KEY_n:
753                 geometry_set_phi(g, g->phi + (M_PI / 24.0));
754                 cliptest->surface.transform.enabled = 1;
755                 break;
756         case XKB_KEY_m:
757                 geometry_set_phi(g, g->phi - (M_PI / 24.0));
758                 cliptest->surface.transform.enabled = 1;
759                 break;
760         case XKB_KEY_r:
761                 geometry_set_phi(g, 0.0);
762                 cliptest->surface.transform.enabled = 0;
763                 break;
764         default:
765                 return;
766         }
767
768         widget_schedule_redraw(cliptest->widget);
769 }
770
771 static void
772 keyboard_focus_handler(struct window *window,
773                        struct input *device, void *data)
774 {
775         struct cliptest *cliptest = data;
776
777         window_schedule_redraw(cliptest->window);
778 }
779
780 static void
781 fullscreen_handler(struct window *window, void *data)
782 {
783         struct cliptest *cliptest = data;
784
785         cliptest->fullscreen ^= 1;
786         window_set_fullscreen(window, cliptest->fullscreen);
787 }
788
789 static struct cliptest *
790 cliptest_create(struct display *display)
791 {
792         struct cliptest *cliptest;
793
794         cliptest = malloc(sizeof *cliptest);
795         if (cliptest == NULL)
796                 return cliptest;
797         memset(cliptest, 0, sizeof *cliptest);
798
799         cliptest->surface.geometry = &cliptest->geometry;
800         cliptest->surface.transform.enabled = 0;
801         geometry_init(&cliptest->geometry);
802         geometry_init(&cliptest->ui.geometry);
803
804         cliptest->window = window_create(display);
805         cliptest->widget = frame_create(cliptest->window, cliptest);
806         window_set_title(cliptest->window, "cliptest");
807         cliptest->display = display;
808
809         window_set_user_data(cliptest->window, cliptest);
810         widget_set_redraw_handler(cliptest->widget, redraw_handler);
811         widget_set_button_handler(cliptest->widget, button_handler);
812         widget_set_motion_handler(cliptest->widget, motion_handler);
813         widget_set_axis_handler(cliptest->widget, axis_handler);
814
815         window_set_keyboard_focus_handler(cliptest->window,
816                                           keyboard_focus_handler);
817         window_set_key_handler(cliptest->window, key_handler);
818         window_set_fullscreen_handler(cliptest->window, fullscreen_handler);
819
820         /* set minimum size */
821         widget_schedule_resize(cliptest->widget, 200, 100);
822
823         /* set current size */
824         widget_schedule_resize(cliptest->widget, 500, 400);
825
826         return cliptest;
827 }
828
829 static struct timespec begin_time;
830
831 static void
832 reset_timer(void)
833 {
834         clock_gettime(CLOCK_MONOTONIC, &begin_time);
835 }
836
837 static double
838 read_timer(void)
839 {
840         struct timespec t;
841
842         clock_gettime(CLOCK_MONOTONIC, &t);
843         return (double)(t.tv_sec - begin_time.tv_sec) +
844                1e-9 * (t.tv_nsec - begin_time.tv_nsec);
845 }
846
847 static int
848 benchmark(void)
849 {
850         struct weston_surface surface;
851         struct geometry geom;
852         GLfloat ex[8], ey[8];
853         int i;
854         double t;
855         const int N = 1000000;
856
857         geom.clip.x1 = -19;
858         geom.clip.y1 = -19;
859         geom.clip.x2 = 19;
860         geom.clip.y2 = 19;
861
862         geom.surf.x1 = -20;
863         geom.surf.y1 = -20;
864         geom.surf.x2 = 20;
865         geom.surf.y2 = 20;
866
867         geometry_set_phi(&geom, 0.0);
868
869         surface.transform.enabled = 1;
870         surface.geometry = &geom;
871
872         reset_timer();
873         for (i = 0; i < N; i++) {
874                 geometry_set_phi(&geom, (float)i / 360.0f);
875                 calculate_edges(&surface, &geom.clip, &geom.surf, ex, ey);
876         }
877         t = read_timer();
878
879         printf("%d calls took %g s, average %g us/call\n", N, t, t / N * 1e6);
880
881         return 0;
882 }
883
884 int
885 main(int argc, char *argv[])
886 {
887         struct display *d;
888         struct cliptest *cliptest;
889
890         if (argc > 1)
891                 return benchmark();
892
893         d = display_create(argc, argv);
894         if (d == NULL) {
895                 fprintf(stderr, "failed to create display: %m\n");
896                 return -1;
897         }
898
899         cliptest = cliptest_create(d);
900         display_run(d);
901
902         widget_destroy(cliptest->widget);
903         window_destroy(cliptest->window);
904         free(cliptest);
905
906         return 0;
907 }