tests: Add unit test for touch event handling
[profile/ivi/clutter.git] / tests / conform / events-touch.c
1 /*
2  * Copyright (C) 2009 Red Hat, Inc.
3  * Copyright (C) 2012 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT ANY
11  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this program; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
18  * Boston, MA 02111-1307, USA.
19  *
20  */
21
22 #include "config.h"
23 #include <clutter/clutter.h>
24
25 #if defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2
26
27 #include <stdio.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <math.h>
33 #include <linux/input.h>
34 #include <linux/uinput.h>
35 #include <dlfcn.h>
36
37 #include <clutter/x11/clutter-x11.h>
38
39 #include "test-conform-common.h"
40
41 #define ABS_MAX_X 32768
42 #define ABS_MAX_Y 32768
43
44 #define TOUCH_POINTS 10
45
46 static ClutterPoint gesture_points[] = {
47   { 100., 100. },
48   { 110., 100. },
49   { 120., 100. },
50   { 130., 100. },
51   { 140., 100. },
52   { 150., 100. },
53   { 160., 100. },
54   { 170., 100. },
55   { 180., 100. },
56   { 190., 100. },
57 };
58
59 typedef struct _State State;
60
61 struct _State
62 {
63   gboolean      pass;
64   ClutterPoint  gesture_points_to_check[TOUCH_POINTS];
65   int           gesture_points;
66 };
67
68 static int fd = -1;
69
70 static void send_event(int fd, int type, int code, int value, int sec, int usec)
71 {
72     static int sec_offset = -1;
73     static long last_time = -1;
74     long newtime;
75     struct input_event event;
76
77     event.type  = type;
78     event.code  = code;
79     event.value = value;
80
81     if (sec_offset == -1)
82         sec_offset = sec;
83
84     sec -= sec_offset;
85     newtime = sec * 1000000 + usec;
86
87     if (last_time > 0)
88         usleep(newtime - last_time);
89
90     gettimeofday(&event.time, NULL);
91
92     if (write(fd, &event, sizeof(event)) < sizeof(event))
93         perror("Send event failed.");
94
95     last_time = newtime;
96 }
97
98 static gboolean
99 event_cb (ClutterActor *actor, ClutterEvent *event, State *state)
100 {
101   int i;
102
103   if (event->type != CLUTTER_TOUCH_BEGIN &&
104       event->type != CLUTTER_TOUCH_UPDATE)
105     return FALSE;
106
107   state->gesture_points_to_check[state->gesture_points].x = ceil (event->touch.x);
108   state->gesture_points_to_check[state->gesture_points].y = ceil (event->touch.y);
109   state->gesture_points++;
110
111   if (state->gesture_points == TOUCH_POINTS)
112     {
113       for (i = 0; i < TOUCH_POINTS; i++)
114         {
115           if (state->gesture_points_to_check[i].x != gesture_points[i].x ||
116               state->gesture_points_to_check[i].y != gesture_points[i].y)
117             {
118               if (g_test_verbose ())
119                 g_print ("error: expected (%d, %d) but found (%d, %d) at position %d\n",
120                          (int) gesture_points[i].x, (int) gesture_points[i].y,
121                          (int) state->gesture_points_to_check[i].x,
122                          (int) state->gesture_points_to_check[i].y,
123                          i);
124               state->pass = FALSE;
125               break;
126             }
127         }
128
129       clutter_main_quit ();
130     }
131
132   return TRUE;
133 }
134
135 static void
136 screen_coords_to_device (int screen_x, int screen_y,
137                          int *device_x, int *device_y)
138 {
139   int display_width = DisplayWidth (clutter_x11_get_default_display (),
140                                     clutter_x11_get_default_screen ());
141   int display_height = DisplayHeight (clutter_x11_get_default_display (),
142                                       clutter_x11_get_default_screen ());
143
144   *device_x = (screen_x * ABS_MAX_X) / display_width;
145   *device_y = (screen_y * ABS_MAX_Y) / display_height;
146 }
147
148 static gboolean
149 perform_gesture (gpointer data)
150 {
151   State *state = data;
152   int i;
153
154   for (i = 0; i < TOUCH_POINTS; i++)
155     {
156       int x = gesture_points[i].x;
157       int y = gesture_points[i].y;
158
159       screen_coords_to_device (x, y, &x, &y);
160
161       send_event(fd, EV_ABS, ABS_MT_SLOT, 0, 1, i * 100);
162       send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, 1, 1, i * 100 + 10);
163
164       send_event(fd, EV_ABS, ABS_MT_POSITION_X, x,  1, i * 100 + 20);
165       send_event(fd, EV_ABS, ABS_MT_POSITION_Y, y, 1, i * 100 + 30);
166       send_event(fd, EV_SYN, SYN_MT_REPORT, 0, 1, i * 100 + 40);
167       send_event(fd, EV_SYN, SYN_REPORT, 0, 1, i * 100 + 50);
168     }
169
170   send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, -1, 1, TOUCH_POINTS * 100 + 10);
171   send_event(fd, EV_SYN, SYN_MT_REPORT, 0, 1, TOUCH_POINTS * 100 + 20);
172   send_event(fd, EV_SYN, SYN_REPORT, 0, 1, TOUCH_POINTS * 100 + 30);
173
174   return G_SOURCE_REMOVE;
175 }
176
177 static int
178 setup (struct uinput_user_dev *dev, int fd)
179 {
180   strcpy (dev->name, "eGalax Touch Screen");
181   dev->id.bustype = 0x18;
182   dev->id.vendor = 0xeef;
183   dev->id.product = 0x20;
184   dev->id.version = 0x1;
185
186   if (ioctl (fd, UI_SET_EVBIT, EV_SYN) == -1)
187     goto error;
188
189   if (ioctl (fd, UI_SET_EVBIT, EV_KEY) == -1)
190     goto error;
191
192   if (ioctl (fd, UI_SET_KEYBIT, BTN_TOUCH) == -1)
193     goto error;
194
195   if (ioctl (fd, UI_SET_EVBIT, EV_ABS) == -1)
196     goto error;
197
198   if (ioctl (fd, UI_SET_ABSBIT, ABS_X) == -1)
199     goto error;
200   else
201     {
202       int idx = ABS_X;
203       dev->absmin[idx] = 0;
204       dev->absmax[idx] = ABS_MAX_X;
205       dev->absfuzz[idx] = 1;
206       dev->absflat[idx] = 0;
207
208       if (dev->absmin[idx] == dev->absmax[idx])
209         dev->absmax[idx]++;
210     }
211
212   if (ioctl (fd, UI_SET_ABSBIT, ABS_Y) == -1)
213     goto error;
214   else
215     {
216       int idx = ABS_Y;
217       dev->absmin[idx] = 0;
218       dev->absmax[idx] = ABS_MAX_Y;
219       dev->absfuzz[idx] = 1;
220       dev->absflat[idx] = 0;
221
222       if (dev->absmin[idx] == dev->absmax[idx])
223         dev->absmax[idx]++;
224     }
225
226   if (ioctl (fd, UI_SET_ABSBIT, ABS_PRESSURE) == -1)
227     goto error;
228   else
229     {
230       int idx = ABS_PRESSURE;
231       dev->absmin[idx] = 0;
232       dev->absmax[idx] = 0;
233       dev->absfuzz[idx] = 0;
234       dev->absflat[idx] = 0;
235
236       if (dev->absmin[idx] == dev->absmax[idx])
237         dev->absmax[idx]++;
238     }
239
240   if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR) == -1)
241     goto error;
242   else
243     {
244       int idx = ABS_MT_TOUCH_MAJOR;
245       dev->absmin[idx] = 0;
246       dev->absmax[idx] = 255;
247       dev->absfuzz[idx] = 1;
248       dev->absflat[idx] = 0;
249
250       if (dev->absmin[idx] == dev->absmax[idx])
251         dev->absmax[idx]++;
252     }
253
254   if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR) == -1)
255     goto error;
256   else
257     {
258       int idx = ABS_MT_WIDTH_MAJOR;
259       dev->absmin[idx] = 0;
260       dev->absmax[idx] = 255;
261       dev->absfuzz[idx] = 1;
262       dev->absflat[idx] = 0;
263
264       if (dev->absmin[idx] == dev->absmax[idx])
265         dev->absmax[idx]++;
266     }
267
268   if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_POSITION_X) == -1)
269     goto error;
270   else
271     {
272       int idx = ABS_MT_POSITION_X;
273       dev->absmin[idx] = 0;
274       dev->absmax[idx] = ABS_MAX_X;
275       dev->absfuzz[idx] = 1;
276       dev->absflat[idx] = 0;
277
278       if (dev->absmin[idx] == dev->absmax[idx])
279         dev->absmax[idx]++;
280     }
281
282   if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y) == -1)
283     goto error;
284   else
285     {
286       int idx = ABS_MT_POSITION_Y;
287       dev->absmin[idx] = 0;
288       dev->absmax[idx] = ABS_MAX_Y;
289       dev->absfuzz[idx] = 1;
290       dev->absflat[idx] = 0;
291
292       if (dev->absmin[idx] == dev->absmax[idx])
293         dev->absmax[idx]++;
294     }
295
296   if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID) == -1)
297     goto error;
298   else
299     {
300       int idx = ABS_MT_TRACKING_ID;
301       dev->absmin[idx] = 0;
302       dev->absmax[idx] = 5;
303       dev->absfuzz[idx] = 0;
304       dev->absflat[idx] = 0;
305
306       if (dev->absmin[idx] == dev->absmax[idx])
307         dev->absmax[idx]++;
308     }
309
310
311
312   return 0;
313 error:
314   perror ("ioctl failed.");
315   return -1;
316 }
317
318 static int
319 init_uinput ()
320 {
321   struct uinput_user_dev dev;
322
323   fd = open ("/dev/uinput", O_RDWR);
324   if (fd < 0 && errno == ENODEV)
325     fd = open ("/dev/input/uinput", O_RDWR);
326   if (fd < 0)
327     goto error;
328
329   memset (&dev, 0, sizeof (dev));
330   setup (&dev, fd);
331
332   if (write (fd, &dev, sizeof (dev)) < sizeof (dev))
333     goto error;
334   if (ioctl (fd, UI_DEV_CREATE, NULL) == -1)
335     goto error;
336
337   return 0;
338
339 error:
340   if (g_test_verbose ())
341     g_print ("error: %s\n", strerror (errno));
342
343   if (fd != -1)
344     close (fd);
345
346   return -1;
347 }
348
349 #endif /* defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 */
350
351 void
352 events_touch (void)
353 {
354 #if defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2
355   ClutterActor *stage;
356   State state;
357
358   state.pass = TRUE;
359   state.gesture_points = 0;
360
361   stage = clutter_stage_new ();
362   g_signal_connect (stage, "event", G_CALLBACK (event_cb), &state);
363   clutter_stage_set_fullscreen (CLUTTER_STAGE (stage), TRUE);
364   clutter_actor_show (stage);
365
366   g_assert (init_uinput () == 0);
367
368   clutter_threads_add_timeout (500, perform_gesture, &state);
369
370   clutter_main ();
371
372   if (g_test_verbose ())
373     g_print ("end result: %s\n", state.pass ? "pass" : "FAIL");
374
375   g_assert (state.pass);
376
377   clutter_actor_destroy (stage);
378 #endif /* defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 */
379 }