Author: Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>
Description: rescan event devices on disconnects

Note @q66: modified to use /run instead of /var/run

--- a/mouseemu.8
+++ b/mouseemu.8
@@ -52,13 +52,20 @@
 .B -nofork
 don't run in the background
 .TP
+.B -autorescan
+Automatically scan every 5s for new devices. This is normally not need, as udev should
+inform mouseemu about new devices.
+.TP
 .B -help
 show usage message
 .PP
 The key codes for the buttons and modifiers are key scancodes. They can be found in 
 include/linux/input.h in the kernel headers or by using `showkey` in a console. The 
 keycodes must be given as decimal values (`showkey` displays hex values!).
-
+.PP
+Mouseemu does normally not automatically scan for new devices. An udev rule is used
+to trigger a rescan when new devices are connected. You can also trigger a rescan
+manually by sending a HUP signal to the mouseemu process.
 .SH EXAMPLES
 .PP
 To have the same behaviour as in MacOS X (CTRL-click for right mouse button and no
@@ -69,7 +76,12 @@
 .RE
 .PP
 The code for the (left) mouse button is 272 (0x110 in hex). The code for CTRL is 29. 
-
+.PP
+Trigger a rescan for newly attached devices:
+.PP
+.RS 4
+.B kill -HUP `cat /run/mouseemu.pid`
+.RE
 .SH AUTHOR
 Mouseemu was written by  Colin Leroy 
 .nh 
--- a/mouseemu.c
+++ b/mouseemu.c
@@ -19,6 +19,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <string.h>
 #include <signal.h>
@@ -51,12 +52,14 @@
 
 static int running 		= -1;
 volatile sig_atomic_t		answer = 1;
+volatile sig_atomic_t		rescan = 0;
 pid_t pid		= -1;
-#define EVENT_DEVS 6
+#define EVENT_DEVS 32
 static kdev eventdevs[EVENT_DEVS];
 static input_handler ihandler[EVENT_DEVS];
 
-
+static int debug	= 0;
+static int autorescan   = 0;
 
 static void send_event(int fd, int type, int code, int value)
 {
@@ -224,12 +227,18 @@
 			ioctl(fd, EVIOCGBIT(0, EV_MAX), bit);
 			if (test_bit(EV_KEY, bit) && test_bit(EV_REP, bit)) {
 				ioctl(fd, EVIOCGID, id);
+				/* our own virtual keyboard (on rescans)*/
+				if (id[ID_PRODUCT] == 0x1F && id[ID_VENDOR] == 0x1F) {
+					close(fd);
+					continue;
+				}
 				if (id[ID_PRODUCT] != eventdevs[m].product ||
 					id[ID_VENDOR]  != eventdevs[m].vendor) {
 					if (eventdevs[m].handle >= 0) {
 						unregister_inputhandler(eventdevs[m].handle);
 						close(eventdevs[m].handle);
 					}
+					if (debug) fprintf(stderr, "keyboard: fd %d event%d, vendor %4x product %4x\n", fd, n, id[ID_VENDOR], id[ID_PRODUCT]);
 					eventdevs[m].handle= fd;
 					eventdevs[m].product = id[ID_PRODUCT];
 					eventdevs[m].vendor = id[ID_VENDOR];
@@ -238,12 +247,18 @@
 				m++;
 			} else if (test_bit(EV_REL, bit)) {
 				ioctl(fd, EVIOCGID, id);
+				/* our own virtual mouse (on rescans)*/
+				if (id[ID_PRODUCT] == 0x1E && id[ID_VENDOR] == 0x1F) {
+					close(fd);
+					continue;
+				}
 				if (id[ID_PRODUCT] != eventdevs[m].product ||
 					id[ID_VENDOR]  != eventdevs[m].vendor) {
 					if (eventdevs[m].handle >= 0) {
 						unregister_inputhandler(eventdevs[m].handle);
 						close(eventdevs[m].handle);
 					}
+					if (debug) fprintf(stderr, "mouse   : fd %d event%d, vendor %4x product %4x\n", fd, n, id[ID_VENDOR], id[ID_PRODUCT]);
 					eventdevs[m].handle= fd;
 					eventdevs[m].product = id[ID_PRODUCT];
 					eventdevs[m].vendor = id[ID_VENDOR];
@@ -261,6 +276,27 @@
 	}
 }
 
+void rescan_devs()
+{
+	int i, cfd;
+
+        for (i=0; i<EVENT_DEVS; i++) {
+		if (ihandler[i].fd != -1) {
+			cfd=ihandler[i].fd;
+			unregister_inputhandler(ihandler[i].fd);
+			close(cfd);
+			i--;
+		}
+	}
+        for (i=0; i<EVENT_DEVS; i++) {
+		eventdevs[i].product = 0;
+		eventdevs[i].vendor  = 0;
+		eventdevs[i].handle  = -1;
+	}
+	usleep(100);
+	scan_for_devs();
+}
+
 int register_inputhandler (int fd, void (*func)(int fd), int grab)
 {
 	int n;
@@ -300,7 +336,7 @@
 
 	FD_ZERO(watchset);
 	for (maxfd=n=0; n < EVENT_DEVS; n++) {
-		if (ihandler[n].fd == -1) break;
+		if (ihandler[n].fd == -1) continue;
 		FD_SET(ihandler[n].fd, watchset);
 		if (ihandler[n].fd > maxfd)
 			maxfd = ihandler[n].fd;
@@ -313,7 +349,7 @@
 	int n;
 
 	for (n=0; n < EVENT_DEVS; n++) {
-		if (ihandler[n].fd == -1) break;
+		if (ihandler[n].fd == -1) continue;
 		if (FD_ISSET(ihandler[n].fd, inset))
 			ihandler[n].handler (ihandler[n].fd);
 	}
@@ -461,7 +497,7 @@
 
 void uinput_cleanup()
 {
-	int i;
+	int i, cfd;
 	
 	printf("mouseemu: cleaning...\n");
 
@@ -470,8 +506,9 @@
 
         for (i=0; i<EVENT_DEVS; i++) {
 		if (ihandler[i].fd != -1) {
+			cfd=ihandler[i].fd;
 			unregister_inputhandler(ihandler[i].fd);
-			close(ihandler[i].fd);
+			close(cfd);
 		}
 	}
 
@@ -489,6 +526,11 @@
 {
 	if (sig_num == SIGUSR1) {
 		answer = 1;
+	} else if (sig_num == SIGHUP) {
+		rescan = 1;
+	} else if (sig_num == SIGALRM) {
+		rescan = 1;
+		alarm(5);
 	} else {
 		//printf("mouseemu: aborting on sig %i \n",sig_num);	
 		/*terminate the parent:*/
@@ -512,10 +554,13 @@
 
 	/*SIGUSR1 for process communication
 	 *SIGTERM and SIGCHLD for quitting
+	 *SIGHUP and SIGALRM for rescaning devices
 	 */
 	sigaction(SIGUSR1, &usr_action, NULL);
 	sigaction(SIGTERM, &usr_action, NULL);
+	sigaction(SIGHUP,  &usr_action, NULL);
 	sigaction(SIGCHLD, &usr_action, NULL);
+	sigaction(SIGALRM, &usr_action, NULL);
 	
 	sigprocmask(SIG_UNBLOCK, &mask, 0);	
 
@@ -529,7 +574,8 @@
 	                "\t[-scroll SCROLL_MOD]\n"
 	                "\t[-typing-block DELAY]\n"
 	                "\t[-device UINPUT_DEVICE]\n"
-	                "\t[-nofork]\n",
+	                "\t[-nofork]\n"
+			"\t[-autorescan]\n",
 					argv[0]);
 	fprintf(stream, "All modifier and button key arguments are\n"
 	                "key scancodes. They can be found in \n"
@@ -643,6 +689,11 @@
 					nofork=1;
 					i += 1;
 					continue;
+				}
+				else if (!strcmp(argv[i], "-autorescan")) {
+					autorescan=1;
+					i += 1;
+					continue;
 				} else {
 					usage(stderr, argv);
                 }
@@ -680,8 +731,15 @@
 		 * 
 		 */
 			
+		struct sigaction sa;
 		sigset_t mask, oldmask;
 			       
+		/* SIGHUP and SIGALRM are only useful in the child */
+		memset(&sa, 0, sizeof(sa));
+		sa.sa_handler = SIG_IGN;
+		sigaction(SIGHUP, &sa, NULL);
+		sigaction(SIGALRM, &sa, NULL);
+
 		/*we start only after we received the first sigusr1 from child:*/
 
 		sigemptyset(&mask);
@@ -715,6 +773,9 @@
 	
 	//strncpy(argv[0],"mouseemu",argv0size);
 startops:
+	if (nofork)
+		debug = 1;
+
 	for (i=0; i<EVENT_DEVS; i++) {
 		eventdevs[i].handle = -1;
 		eventdevs[i].vendor = 0;
@@ -736,18 +797,35 @@
                                                          
 
 	chdir("/");
-
+	
+	if (autorescan) 
+		alarm(5);
+	
 	/*main loop*/
 	
         while(running > 0) {
 	
 		tv.tv_sec = 1; tv.tv_usec = 0;
 		maxfd = create_fdset(&inset);
-		if ((val = select (maxfd+1, &inset, NULL, NULL, &tv)) >= 0) {
+		val = select (maxfd+1, &inset, NULL, NULL, &tv);
+		/* signal received, so rescan for devices when idle*/
+		if (val == 0 && rescan) {
+			rescan = 0;
+			rescan_devs();
+		}
+		if (val >= 0) {
 			if (val == 0)
 				usleep(10);
-			else
-				call_inputhandler(&inset);
+			else {
+				if (errno == ENODEV) {
+					if (debug) fprintf(stderr, "select returned %d, errno %d, rescanning devices\n", val, errno);
+					errno = 0;
+					rescan_devs();
+					usleep(500);
+				} else {
+					call_inputhandler(&inset);
+				}
+			}
 		}
 		/* tell the parent we are running without problems */
 		/* What should we do if the parent is dead? */
