From e2b1110bca5b2b208657a733a91fd713e5c38b71 Mon Sep 17 00:00:00 2001
From: cbdev <cb@cbcdn.com>
Date: Sat, 6 Mar 2021 19:39:51 +0100
Subject: Fix prefix-matching for evdev input names (#85)

---
 backends/evdev.c  | 27 ++++++++++++++++-----------
 backends/evdev.md |  3 +--
 2 files changed, 17 insertions(+), 13 deletions(-)

(limited to 'backends')

diff --git a/backends/evdev.c b/backends/evdev.c
index 3dbf837..8f7c4f9 100644
--- a/backends/evdev.c
+++ b/backends/evdev.c
@@ -113,12 +113,14 @@ static int evdev_attach(instance* inst, evdev_instance_data* data, char* node){
 static char* evdev_find(char* name){
 	int fd = -1;
 	struct dirent* file = NULL;
-	char file_path[PATH_MAX * 2];
+	char file_path[PATH_MAX * 2], *result = calloc(PATH_MAX * 2, sizeof(char));
 	DIR* nodes = opendir(INPUT_NODES);
-	char device_name[UINPUT_MAX_NAME_SIZE], *result = NULL;
+	char device_name[UINPUT_MAX_NAME_SIZE];
+	size_t min_distance = -1, distance = 0;
 
 	if(!nodes){
 		LOGPF("Failed to query input device nodes in %s: %s", INPUT_NODES, strerror(errno));
+		free(result);
 		return NULL;
 	}
 
@@ -141,20 +143,23 @@ static char* evdev_find(char* name){
 			close(fd);
 
 			if(!strncmp(device_name, name, strlen(name))){
-				LOGPF("Matched name %s for %s: %s", device_name, name, file_path);
-				break;
+				distance = strlen(device_name) - strlen(name);
+				LOGPF("Matched name %s as candidate (distance %" PRIsize_t ") for %s: %s", device_name, distance, name, file_path);
+				if(distance < min_distance){
+					strncpy(result, file_path, (PATH_MAX * 2) - 1);
+					min_distance = distance;
+				}
 			}
 		}
 	}
 
-	if(file){
-		result = calloc(strlen(file_path) + 1, sizeof(char));
-		if(result){
-			strncpy(result, file_path, strlen(file_path));
-		}
-	}
-
 	closedir(nodes);
+
+	if(!result[0]){
+		free(result);
+		return NULL;
+	}
+	LOGPF("Using %s for input name %s", result, name);
 	return result;
 }
 
diff --git a/backends/evdev.md b/backends/evdev.md
index bf192b0..e7ba3cc 100644
--- a/backends/evdev.md
+++ b/backends/evdev.md
@@ -16,7 +16,7 @@ This functionality may require elevated privileges (such as special group member
 | Option	| Example value		| Default value | Description						|
 |---------------|-----------------------|---------------|-------------------------------------------------------|
 | `device`	| `/dev/input/event1`	| none		| `evdev` device to use as input device			|
-| `input`	| `Xbox Wireless`	| none		| Presentation name of evdev device to use as input (prefix-matched), can be used instead of the `device` option |
+| `input`	| `Xbox Wireless`	| none		| Presentation name of evdev device to use as input (most-specific prefix matched), can be used instead of the `device` option |
 | `output`	| `My Input Device`	| none		| Output device presentation name. Setting this option enables the instance for output	|
 | `exclusive`	| `1`			| `0`		| Prevent other processes from using the device		|
 | `id`		| `0x1 0x2 0x3`		| none		| Set output device bus identification (Vendor, Product and Version), optional |
@@ -49,7 +49,6 @@ If relative axes are used without specifying their extents, the channel will gen
 of `0`, `0.5` and `1` for any input less than, equal to and greater than `0`, respectively. As for output, only
 the values `-1`, `0` and `1` are generated for the same interval.
 
-
 #### Channel specification
 
 A channel is specified by its event type and event code, separated by `.`. For a complete list of event types and codes
-- 
cgit v1.2.3