From 0a012b7efdb93f98e1d37b1194f16ba308a7a7e7 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 7 Jun 2012 11:08:49 +0200 Subject: tests: Add unit test for touch event handling For now, it just generates a simple horizontal slide (by writing to /dev/uinput) and checks that the stage gets the events at the expected coordinates. The test won't run if it doesn't have read/write permissions to /dev/uinput. It also adds OS_LINUX to config.h. --- configure.ac | 5 + tests/conform/Makefile.am | 5 + tests/conform/events-touch.c | 379 ++++++++++++++++++++++++++++++++++++ tests/conform/test-conform-common.c | 2 + tests/conform/test-conform-main.c | 9 + 5 files changed, 400 insertions(+) create mode 100644 tests/conform/events-touch.c diff --git a/configure.ac b/configure.ac index 2c72cf3c8..d91bbf344 100644 --- a/configure.ac +++ b/configure.ac @@ -436,6 +436,11 @@ AS_IF([test "x$CLUTTER_BACKENDS" = "x"], AC_MSG_ERROR([No backend enabled. You need to enable at least one backend.]) ]) +AS_IF([test "x$platform_linux" = "xyes"], + [ + AC_DEFINE([OS_LINUX], [1], [Define to 1 if building for Linux]) + ]) + # additional input backends AC_ARG_ENABLE([tslib-input], diff --git a/tests/conform/Makefile.am b/tests/conform/Makefile.am index 5d5cf3b76..d23efaf79 100644 --- a/tests/conform/Makefile.am +++ b/tests/conform/Makefile.am @@ -82,6 +82,11 @@ units_sources += \ cally-text.c \ $(NULL) +# events tests +units_sources += \ + events-touch.c \ + $(NULL) + test_conformance_SOURCES = $(common_sources) $(units_sources) if OS_WIN32 diff --git a/tests/conform/events-touch.c b/tests/conform/events-touch.c new file mode 100644 index 000000000..80a0d1572 --- /dev/null +++ b/tests/conform/events-touch.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2009 Red Hat, Inc. + * Copyright (C) 2012 Collabora Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for + * more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include + +#if defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "test-conform-common.h" + +#define ABS_MAX_X 32768 +#define ABS_MAX_Y 32768 + +#define TOUCH_POINTS 10 + +static ClutterPoint gesture_points[] = { + { 100., 100. }, + { 110., 100. }, + { 120., 100. }, + { 130., 100. }, + { 140., 100. }, + { 150., 100. }, + { 160., 100. }, + { 170., 100. }, + { 180., 100. }, + { 190., 100. }, +}; + +typedef struct _State State; + +struct _State +{ + gboolean pass; + ClutterPoint gesture_points_to_check[TOUCH_POINTS]; + int gesture_points; +}; + +static int fd = -1; + +static void send_event(int fd, int type, int code, int value, int sec, int usec) +{ + static int sec_offset = -1; + static long last_time = -1; + long newtime; + struct input_event event; + + event.type = type; + event.code = code; + event.value = value; + + if (sec_offset == -1) + sec_offset = sec; + + sec -= sec_offset; + newtime = sec * 1000000 + usec; + + if (last_time > 0) + usleep(newtime - last_time); + + gettimeofday(&event.time, NULL); + + if (write(fd, &event, sizeof(event)) < sizeof(event)) + perror("Send event failed."); + + last_time = newtime; +} + +static gboolean +event_cb (ClutterActor *actor, ClutterEvent *event, State *state) +{ + int i; + + if (event->type != CLUTTER_TOUCH_BEGIN && + event->type != CLUTTER_TOUCH_UPDATE) + return FALSE; + + state->gesture_points_to_check[state->gesture_points].x = ceil (event->touch.x); + state->gesture_points_to_check[state->gesture_points].y = ceil (event->touch.y); + state->gesture_points++; + + if (state->gesture_points == TOUCH_POINTS) + { + for (i = 0; i < TOUCH_POINTS; i++) + { + if (state->gesture_points_to_check[i].x != gesture_points[i].x || + state->gesture_points_to_check[i].y != gesture_points[i].y) + { + if (g_test_verbose ()) + g_print ("error: expected (%d, %d) but found (%d, %d) at position %d\n", + (int) gesture_points[i].x, (int) gesture_points[i].y, + (int) state->gesture_points_to_check[i].x, + (int) state->gesture_points_to_check[i].y, + i); + state->pass = FALSE; + break; + } + } + + clutter_main_quit (); + } + + return TRUE; +} + +static void +screen_coords_to_device (int screen_x, int screen_y, + int *device_x, int *device_y) +{ + int display_width = DisplayWidth (clutter_x11_get_default_display (), + clutter_x11_get_default_screen ()); + int display_height = DisplayHeight (clutter_x11_get_default_display (), + clutter_x11_get_default_screen ()); + + *device_x = (screen_x * ABS_MAX_X) / display_width; + *device_y = (screen_y * ABS_MAX_Y) / display_height; +} + +static gboolean +perform_gesture (gpointer data) +{ + State *state = data; + int i; + + for (i = 0; i < TOUCH_POINTS; i++) + { + int x = gesture_points[i].x; + int y = gesture_points[i].y; + + screen_coords_to_device (x, y, &x, &y); + + send_event(fd, EV_ABS, ABS_MT_SLOT, 0, 1, i * 100); + send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, 1, 1, i * 100 + 10); + + send_event(fd, EV_ABS, ABS_MT_POSITION_X, x, 1, i * 100 + 20); + send_event(fd, EV_ABS, ABS_MT_POSITION_Y, y, 1, i * 100 + 30); + send_event(fd, EV_SYN, SYN_MT_REPORT, 0, 1, i * 100 + 40); + send_event(fd, EV_SYN, SYN_REPORT, 0, 1, i * 100 + 50); + } + + send_event(fd, EV_ABS, ABS_MT_TRACKING_ID, -1, 1, TOUCH_POINTS * 100 + 10); + send_event(fd, EV_SYN, SYN_MT_REPORT, 0, 1, TOUCH_POINTS * 100 + 20); + send_event(fd, EV_SYN, SYN_REPORT, 0, 1, TOUCH_POINTS * 100 + 30); + + return G_SOURCE_REMOVE; +} + +static int +setup (struct uinput_user_dev *dev, int fd) +{ + strcpy (dev->name, "eGalax Touch Screen"); + dev->id.bustype = 0x18; + dev->id.vendor = 0xeef; + dev->id.product = 0x20; + dev->id.version = 0x1; + + if (ioctl (fd, UI_SET_EVBIT, EV_SYN) == -1) + goto error; + + if (ioctl (fd, UI_SET_EVBIT, EV_KEY) == -1) + goto error; + + if (ioctl (fd, UI_SET_KEYBIT, BTN_TOUCH) == -1) + goto error; + + if (ioctl (fd, UI_SET_EVBIT, EV_ABS) == -1) + goto error; + + if (ioctl (fd, UI_SET_ABSBIT, ABS_X) == -1) + goto error; + else + { + int idx = ABS_X; + dev->absmin[idx] = 0; + dev->absmax[idx] = ABS_MAX_X; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_Y) == -1) + goto error; + else + { + int idx = ABS_Y; + dev->absmin[idx] = 0; + dev->absmax[idx] = ABS_MAX_Y; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_PRESSURE) == -1) + goto error; + else + { + int idx = ABS_PRESSURE; + dev->absmin[idx] = 0; + dev->absmax[idx] = 0; + dev->absfuzz[idx] = 0; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_TOUCH_MAJOR) == -1) + goto error; + else + { + int idx = ABS_MT_TOUCH_MAJOR; + dev->absmin[idx] = 0; + dev->absmax[idx] = 255; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_WIDTH_MAJOR) == -1) + goto error; + else + { + int idx = ABS_MT_WIDTH_MAJOR; + dev->absmin[idx] = 0; + dev->absmax[idx] = 255; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_POSITION_X) == -1) + goto error; + else + { + int idx = ABS_MT_POSITION_X; + dev->absmin[idx] = 0; + dev->absmax[idx] = ABS_MAX_X; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_POSITION_Y) == -1) + goto error; + else + { + int idx = ABS_MT_POSITION_Y; + dev->absmin[idx] = 0; + dev->absmax[idx] = ABS_MAX_Y; + dev->absfuzz[idx] = 1; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + if (ioctl (fd, UI_SET_ABSBIT, ABS_MT_TRACKING_ID) == -1) + goto error; + else + { + int idx = ABS_MT_TRACKING_ID; + dev->absmin[idx] = 0; + dev->absmax[idx] = 5; + dev->absfuzz[idx] = 0; + dev->absflat[idx] = 0; + + if (dev->absmin[idx] == dev->absmax[idx]) + dev->absmax[idx]++; + } + + + + return 0; +error: + perror ("ioctl failed."); + return -1; +} + +static int +init_uinput () +{ + struct uinput_user_dev dev; + + fd = open ("/dev/uinput", O_RDWR); + if (fd < 0 && errno == ENODEV) + fd = open ("/dev/input/uinput", O_RDWR); + if (fd < 0) + goto error; + + memset (&dev, 0, sizeof (dev)); + setup (&dev, fd); + + if (write (fd, &dev, sizeof (dev)) < sizeof (dev)) + goto error; + if (ioctl (fd, UI_DEV_CREATE, NULL) == -1) + goto error; + + return 0; + +error: + if (g_test_verbose ()) + g_print ("error: %s\n", strerror (errno)); + + if (fd != -1) + close (fd); + + return -1; +} + +#endif /* defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 */ + +void +events_touch (void) +{ +#if defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 + ClutterActor *stage; + State state; + + state.pass = TRUE; + state.gesture_points = 0; + + stage = clutter_stage_new (); + g_signal_connect (stage, "event", G_CALLBACK (event_cb), &state); + clutter_stage_set_fullscreen (CLUTTER_STAGE (stage), TRUE); + clutter_actor_show (stage); + + g_assert (init_uinput () == 0); + + clutter_threads_add_timeout (500, perform_gesture, &state); + + clutter_main (); + + if (g_test_verbose ()) + g_print ("end result: %s\n", state.pass ? "pass" : "FAIL"); + + g_assert (state.pass); + + clutter_actor_destroy (stage); +#endif /* defined CLUTTER_WINDOWING_X11 && OS_LINUX && HAVE_XINPUT_2_2 */ +} diff --git a/tests/conform/test-conform-common.c b/tests/conform/test-conform-common.c index e7cd42bc4..2a8016639 100644 --- a/tests/conform/test-conform-common.c +++ b/tests/conform/test-conform-common.c @@ -45,6 +45,8 @@ test_conform_simple_fixture_setup (TestConformSimpleFixture *fixture, } #endif + clutter_x11_enable_xinput (); + g_assert (clutter_init (shared_state->argc_addr, shared_state->argv_addr) == CLUTTER_INIT_SUCCESS); diff --git a/tests/conform/test-conform-main.c b/tests/conform/test-conform-main.c index 04536ae97..ce61c3a38 100644 --- a/tests/conform/test-conform-main.c +++ b/tests/conform/test-conform-main.c @@ -116,6 +116,13 @@ clutter_test_init (gint *argc, shared_state->argv_addr = argv; } +static int +can_write_to_uinput () +{ + return g_access ("/dev/uinput", R_OK | W_OK) || + g_access ("/dev/input/uinput", R_OK | W_OK); +} + int main (int argc, char **argv) { @@ -252,6 +259,8 @@ main (int argc, char **argv) TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_interleved); TEST_CONFORM_SIMPLE ("/cogl/vertex-buffer", test_cogl_vertex_buffer_mutability); + TEST_CONFORM_SKIP (can_write_to_uinput (), "/events", events_touch); + /* left to the end because they aren't currently very orthogonal and tend to * break subsequent tests! */ TEST_CONFORM_SIMPLE ("/cogl", test_cogl_viewport); -- cgit v1.2.1