Simulate a mouse click on embedded linux

The basic idea of this approach is to send an input event to evdev, such as /dev/input/event1, so that the running Qt embedded app can receive a mouse press/release event.

The key for sending an input event is the struct input_event. Our approach only needs to click based on an absolute position, thus we use the input event type EV_ABS with code ABS_X/ABS_Y/ABS_PRESSURE. In the last, we use the input event type EV_SYN with code SYN_REPORT as a synchronization event.

The code is as below. Note that because I also tried to send input events to uinput but with no luck, the code is a little long. I already marked the codes for uinput, so you don't need to worry about the result.

 //  
 // This program is used to simulate the mouse click event via input system.  
 // Please ensure the uinput is loaded and the /dev/input/uinput is available.  
 //  
 // Usage: PROGRAM_NAME [X] [Y] [DOWN], where X and Y is the coordination we want a mouse to click in,  
 //                   and DOWN is the down status of the mouse  
 //   
 #include <stdio.h>  
 #include <stdlib.h>  
 #include <string.h>  
 #include <errno.h>  
 #include <sys/types.h>  
 #include <sys/stat.h>  
 #include <fcntl.h>  
 #include <linux/input.h>  
 #include <linux/uinput.h>  
 #include <sys/time.h>  
 #include <unistd.h>  
 #define USE_UINPUT  0  
 #define X_AXIS_MAX  1280  
 #define X_AXIS_MIN  0  
 #define Y_AXIS_MAX  960  
 #define Y_AXIS_MIN  0  
 #define PRESSURE_MAX  15000  
 #define PRESSURE_MIN  0  
 #define MAX_BUTTONS 5  
 static double a[7];  
 static int g_fd = -1;  
 static int debug_lv = 0;  
 static void load_tslib_cal(void)   
 {  
   FILE *f;  
   char line[1024], *p;  
   int i;  
   /* /etc/pointercal -528 33408 -3417516 -44200 408 40292028 56541 */  
   /* this is the identity transformation: */  
   a[0] = 1.0;  
   a[1] = 0.0;  
   a[2] = 0.0;  
   a[3] = 0.0;  
   a[4] = 1.0;  
   a[5] = 0.0;  
   a[6] = 1.0;  
   char tslib_cal[128] = {0};  
   if (getenv("TSLIB_CALIBFILE"))  
   {  
     sprintf(tslib_cal, "%s", getenv("TSLIB_CALIBFILE"));  
   }  
   else  
   {  
     strcpy(tslib_cal, "/etc/pointercal");  
   }  
   if (tslib_cal == NULL)   
   {  
     return;  
   }  
   if (debug_lv) fprintf(stderr, "load_tslib_cal: reading %s\n", tslib_cal);  
   f = fopen(tslib_cal, "r");  
   if (f == NULL)   
   {  
     if (debug_lv) fprintf(stderr, "load_tslib_cal: fopen");  
     exit(1);  
   }  
   if (fgets(line, sizeof(line), f) == NULL)   
   {  
     if (debug_lv) fprintf(stderr, "load_tslib_cal: fgets");  
     exit(1);  
   }  
   fclose(f);  
   p = strtok(line, " \t");  
   i = 0;  
   while (p)   
   {  
     a[i] = (double) atoi(p);  
     if (debug_lv) fprintf(stderr, "load_tslib_cal: a[%d] %.3f\n", i, a[i]);  
     p = strtok(NULL, " \t");  
     i++;  
     if (i >= 7)   
     {  
       break;  
     }  
   }  
   if (i != 7)   
   {  
     if (debug_lv) fprintf(stderr, "load_tslib_cal: invalid tslib file format: i=%d %s\n",  
       i, tslib_cal);  
     exit(1);  
   }  
 }  
 static void apply_tslib(int *x, int *y)   
 {  
   double x1 = *x, y1 = *y, x2, y2;  
   /* this is the inverse of the tslib linear transform: */  
   x2 = (a[4] * (a[6] * x1 - a[2]) - a[1] * (a[6] * y1 - a[5]))/(a[4]*a[0] - a[1]*a[3]);  
   y2 = (a[0] * (a[6] * y1 - a[5]) - a[3] * (a[6] * x1 - a[2]))/(a[4]*a[0] - a[1]*a[3]);  
   *x = (int) x2;  
   *y = (int) y2;  
 }  
 static void ptr_abs(int x, int y, int p)   
 {  
   struct input_event ev;  
   int x0, y0;  
   int d = g_fd;  
   memset(&ev, 0, sizeof(ev));  
   x0 = x;  
   y0 = y;  
   apply_tslib(&x, &y);  
   if (debug_lv) fprintf(stderr, "ptr_abs(%d, %d => %d %d, p=%d) fd=%d\n", x0, y0, x, y, p, d);  
   gettimeofday(&ev.time, NULL);  
   ev.type = EV_ABS;  
   ev.code = ABS_Y;  
   ev.value = y;  
   write(d, &ev, sizeof(ev));  
   ev.type = EV_ABS;  
   ev.code = ABS_X;  
   ev.value = x;  
   write(d, &ev, sizeof(ev));  
   if (p >= 0)   
   {  
     ev.type = EV_ABS;  
     ev.code = ABS_PRESSURE;  
     ev.value = p;  
     write(d, &ev, sizeof(ev));  
   }  
   ev.type = EV_SYN;  
   ev.code = SYN_REPORT;  
   ev.value = 0;  
   write(d, &ev, sizeof(ev));  
 }  
 int main(int argc, char *argv[])  
 {   
   int retcode = -1;  
   struct uinput_user_dev dev;  
   // check required argument number  
   if (argc < 4)  
   {  
     fprintf(stderr, "Please speicfy X and Y and down state.\n");  
     return 1;  
   }  
   // get debug level  
   if (getenv("HMI_MOUSEEMU_DEBUG"))  
   {  
     debug_lv = atoi(getenv("HMI_MOUSEEMU_DEBUG"));  
   }  
   // get argument data  
   int nClickX = atoi(argv[1]);  
   int nClickY = atoi(argv[2]);  
   int nDown = atoi(argv[3]);  
   if (debug_lv) fprintf(stderr, "Mouse clicks on (%d, %d), down=%d\n", nClickX, nClickY, nDown);  
   // open device  
 #if USE_UINPUT  
   g_fd = open("/dev/uinput", O_WRONLY | O_NDELAY );  
 #else  
   g_fd = open("/dev/input/event1", O_WRONLY | O_NDELAY );  
 #endif  
   if (g_fd == 0)   
   {  
     if (debug_lv) fprintf(stderr, "Could not open device.\n");  
     return -1;  
   }  
 #if USE_UINPUT  
   memset(&dev, 0, sizeof(dev));  
   strncpy(dev.name, "HMIMouse Injector", 20);  
   dev.id.bustype = BUS_USB;  
   dev.id.version = 4;  
   dev.absmax[ABS_X] = 480;  
   dev.absmin[ABS_X] = X_AXIS_MIN;  
   dev.absmax[ABS_Y] = 272;  
   dev.absmin[ABS_Y] = Y_AXIS_MIN;  
   // activate the support event types  
   retcode = ioctl(g_fd, UI_SET_EVBIT, EV_KEY);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   retcode = ioctl(g_fd, UI_SET_EVBIT, EV_SYN);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   retcode = ioctl(g_fd, UI_SET_EVBIT, EV_ABS);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   //ioctl(g_fd, UI_SET_EVBIT, EV_REL);  
   // enable the keycode  
   retcode = ioctl(g_fd, UI_SET_ABSBIT, ABS_X);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   retcode = ioctl(g_fd, UI_SET_ABSBIT, ABS_Y);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   retcode = ioctl(g_fd, UI_SET_ABSBIT, ABS_PRESSURE);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   //ioctl(g_fd, UI_SET_RELBIT, REL_X);  
   //ioctl(g_fd, UI_SET_RELBIT, REL_Y);  
   retcode = ioctl(g_fd, UI_SET_KEYBIT, BTN_TOUCH);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   retcode = ioctl(g_fd, UI_SET_KEYBIT, BTN_MOUSE);  
   if (retcode != 0)  
   {  
     printf("failure of ioctl.\n");  
   }  
   // create input device in input subsystem  
   retcode = write(g_fd, &dev, sizeof(dev));  
   printf("First write returned %d.\n", retcode);  
   // create the device  
   retcode = ioctl(g_fd, UI_DEV_CREATE);  
   printf("ioctl UI_DEV_CREATE returned %d.\n", retcode);  
   if (retcode != 0)   
   {  
     printf("Error create uinput device %d.\n", retcode);  
     return 1;  
   }  
 #endif  
   // load tslib calibration file  
   load_tslib_cal();  
   // send a abs pointer event  
   ptr_abs(nClickX, nClickY, nDown);  
   //ptr_abs(nClickX, nClickY, 0);  
   // destroy the device  
 #if USE_UINPUT  
   retcode = ioctl(g_fd, UI_DEV_DESTROY);  
   if (retcode != 0)  
   {  
     printf("failed to destroy device.\n");  
   }  
 #endif  
   close(g_fd);  
   return 0;  
 }  

A very import part of above code is to use tslib for calibration. Without it the point sent to evdev won't be correctly reflected. Also the code is modifed from the source code of x11vnc, so you will see it's very similar to uinput.c of x11vnc. :)

If you are interested in the uinput, the following link is a very informative article: http://thiemonge.org/getting-started-with-uinput

留言

熱門文章