mirror of
https://github.com/X11Libre/xserver.git
synced 2026-03-24 05:54:08 +00:00
kdrive/linux: Improve evdev driver autodetection
It should now work out of the box on most setups with one mouse and one keyboard. It probably still breaks on some single mouse and keyboard setups, and on setups with more than one mouse or keyboard. Signed-off-by: stefan11111 <stefan11111@shitposting.expert>
This commit is contained in:
committed by
Enrico Weigelt
parent
a2c58b9934
commit
cc152cf964
@@ -28,6 +28,7 @@
|
|||||||
#include "inputstr.h"
|
#include "inputstr.h"
|
||||||
#include "scrnintstr.h"
|
#include "scrnintstr.h"
|
||||||
#include "kdrive.h"
|
#include "kdrive.h"
|
||||||
|
#include "evdev.h"
|
||||||
|
|
||||||
#define NUM_EVENTS 128
|
#define NUM_EVENTS 128
|
||||||
#define ABS_UNSET -65535
|
#define ABS_UNSET -65535
|
||||||
@@ -183,21 +184,11 @@ EvdevPtrRead(int evdevPort, void *closure)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define NUM_DEFAULT_EVDEV 32
|
|
||||||
|
|
||||||
static Status
|
static Status
|
||||||
EvdevPtrInit(KdPointerInfo * pi)
|
EvdevPtrInit(KdPointerInfo * pi)
|
||||||
{
|
{
|
||||||
if (!pi->path) {
|
if (!pi->path) {
|
||||||
char default_device[] = "/dev/input/eventxx";
|
pi->path = EvdevDefaultPtr();
|
||||||
for (int i = 0; i < NUM_DEFAULT_EVDEV; i++) {
|
|
||||||
sprintf(default_device, "/dev/input/event%d", i);
|
|
||||||
fd = open(default_device, O_RDWR);
|
|
||||||
if (fd >= 0) {
|
|
||||||
pi->path = strdup(default_device);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int fd = open(pi->path, O_RDWR);
|
int fd = open(pi->path, O_RDWR);
|
||||||
@@ -364,15 +355,7 @@ static Status
|
|||||||
EvdevKbdInit(KdKeyboardInfo * ki)
|
EvdevKbdInit(KdKeyboardInfo * ki)
|
||||||
{
|
{
|
||||||
if (!ki->path) {
|
if (!ki->path) {
|
||||||
char default_device[] = "/dev/input/eventxx";
|
ki->path = EvdevDefaultKbd();
|
||||||
for (int i = 0; i < NUM_DEFAULT_EVDEV; i++) {
|
|
||||||
sprintf(default_device, "/dev/input/event%d", i);
|
|
||||||
fd = open(default_device, O_RDWR);
|
|
||||||
if (fd >= 0) {
|
|
||||||
ki->path = strdup(default_device);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int fd = open(ki->path, O_RDWR);
|
int fd = open(ki->path, O_RDWR);
|
||||||
12
hw/kdrive/linux/evdev/evdev.h
Normal file
12
hw/kdrive/linux/evdev/evdev.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR X11
|
||||||
|
*
|
||||||
|
* Copyright © 2026 stefan11111 <stefan11111@shitposting.expert>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __EVDEH_H__
|
||||||
|
#define __EVDEH_H__
|
||||||
|
|
||||||
|
char* EvdevDefaultPtr(void);
|
||||||
|
char* EvdevDefaultKbd(void);
|
||||||
|
|
||||||
|
#endif /* __EVDEH_H__ */
|
||||||
355
hw/kdrive/linux/evdev/evdev_autodetect.c
Normal file
355
hw/kdrive/linux/evdev/evdev_autodetect.c
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
/* SPDX-License-Identifier: MIT OR X11
|
||||||
|
*
|
||||||
|
* Copyright © 2026 stefan11111 <stefan11111@shitposting.expert>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <kdrive-config.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "kdrive.h"
|
||||||
|
#include "evdev.h"
|
||||||
|
|
||||||
|
#define EVDEV_FMT "/dev/input/event%d"
|
||||||
|
|
||||||
|
#define PROC_DEVICES "/proc/bus/input/devices"
|
||||||
|
|
||||||
|
#define PHYS_MAX 64 /* Busid + device id */
|
||||||
|
|
||||||
|
#define MOUSE_EV (1 << 2)
|
||||||
|
#define KBD_EV 0x120013
|
||||||
|
|
||||||
|
enum {
|
||||||
|
EVDEV_KEYBOARD = 0,
|
||||||
|
EVDEV_MOUSE = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_FALLBACK_EVDEV 32
|
||||||
|
|
||||||
|
/* Simple fallback that was already here */
|
||||||
|
static char*
|
||||||
|
FallbackEvdevCheck(void)
|
||||||
|
{
|
||||||
|
char fallback_dev[] = "/dev/input/eventxx";
|
||||||
|
|
||||||
|
for (int i = 0; i < NUM_FALLBACK_EVDEV; i++) {
|
||||||
|
sprintf(fallback_dev, EVDEV_FMT, i);
|
||||||
|
int fd = open(fallback_dev, O_RDWR);
|
||||||
|
if (fd >= 0) {
|
||||||
|
close(fd);
|
||||||
|
return strdup(fallback_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* All numbers read are in base 16 */
|
||||||
|
static inline uint64_t
|
||||||
|
read_val(const char *val)
|
||||||
|
{
|
||||||
|
return strtol(val, NULL, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t Bus; /* Bus= */
|
||||||
|
uint32_t Vendor; /* Vendor= */
|
||||||
|
uint32_t Product; /* Product= */
|
||||||
|
uint32_t Version; /* Version= */
|
||||||
|
} EvdevOptionalInfo;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* Info that should be unique across physical devices,
|
||||||
|
* but not across logical devices.
|
||||||
|
*/
|
||||||
|
EvdevOptionalInfo info; /* I: */
|
||||||
|
/* char *Name; */ /* N: Name = */
|
||||||
|
char Phys[PHYS_MAX]; /* P: Phys = */
|
||||||
|
/* char *Sysfs; */ /* S: Sysfs= */
|
||||||
|
uint64_t Uniq; /* U: Uniq= */
|
||||||
|
|
||||||
|
int EventNo; /* H: Handlers=... eventxx ... */
|
||||||
|
|
||||||
|
/* If checking for these 2 ever causes problems, remove them */
|
||||||
|
int is_mouse; /* H: Handlers=... mousexx ... */
|
||||||
|
int is_kbd; /* H: handlers=... kbd ... */
|
||||||
|
|
||||||
|
uint64_t EV; /* B: EV= */
|
||||||
|
int is_read;
|
||||||
|
} EventDevice;
|
||||||
|
|
||||||
|
static EventDevice DefaultPtr = {0};
|
||||||
|
static EventDevice DefaultKbd = {0};
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ReadOptInfo(EvdevOptionalInfo *dst, const char* data)
|
||||||
|
{
|
||||||
|
const char *val = NULL;
|
||||||
|
|
||||||
|
val = strstr(data, "Bus=");
|
||||||
|
if (val) {
|
||||||
|
val += sizeof("Bus=") - 1;
|
||||||
|
dst->Bus = read_val(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = strstr(data, "Vendor=");
|
||||||
|
if (val) {
|
||||||
|
val += sizeof("Vendor=") - 1;
|
||||||
|
dst->Vendor = read_val(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = strstr(data, "Product=");
|
||||||
|
if (val) {
|
||||||
|
val += sizeof("Product=") - 1;
|
||||||
|
dst->Product = read_val(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = strstr(data, "Version=");
|
||||||
|
if (val) {
|
||||||
|
val += sizeof("Version=") - 1;
|
||||||
|
dst->Version = read_val(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ReadPhys(char *dst, const char* data)
|
||||||
|
{
|
||||||
|
char *p = dst;
|
||||||
|
|
||||||
|
data = strstr(data, "Phys=");
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sizeof("Phys=") - 1;
|
||||||
|
while (*data && *data != '/' && p - dst < PHYS_MAX - 1) {
|
||||||
|
*p = *data;
|
||||||
|
p++;
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ReadUniq(uint64_t *dst, const char* data)
|
||||||
|
{
|
||||||
|
data = strstr(data, "Uniq=");
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sizeof("Uniq=") - 1;
|
||||||
|
*dst = read_val(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ReadHandlers(EventDevice *dst, const char* data)
|
||||||
|
{
|
||||||
|
dst->is_mouse = !!strstr(data, "mouse");
|
||||||
|
dst->is_kbd = !!strstr(data, "kbd");
|
||||||
|
|
||||||
|
data = strstr(data, "event");
|
||||||
|
if (!data) {
|
||||||
|
/* If this one is missing, we really can't do anything */
|
||||||
|
dst->EventNo = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sizeof("event") - 1;
|
||||||
|
|
||||||
|
/* This one is base10 */
|
||||||
|
dst->EventNo = strtol(data, NULL, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
ReadEV(uint64_t *EV, const char* data)
|
||||||
|
{
|
||||||
|
data = strstr(data, "EV=");
|
||||||
|
if (!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
data += sizeof("EV=") - 1;
|
||||||
|
*EV = read_val(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
ReadEvdev(EventDevice *dst, FILE *f)
|
||||||
|
{
|
||||||
|
for (;;) {
|
||||||
|
char *line = NULL;
|
||||||
|
char *end = NULL;
|
||||||
|
char *data = NULL;
|
||||||
|
size_t unused = 0;
|
||||||
|
|
||||||
|
if (getline(&line, &unused, f) < 0) {
|
||||||
|
free(line);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
end = strchr(line, '\n');
|
||||||
|
if (end) {
|
||||||
|
*end = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[0] == '\0') {
|
||||||
|
free(line);
|
||||||
|
dst->is_read = TRUE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line[1] != ':' ||
|
||||||
|
line[2] == '\0' || line[3] == '\0') {
|
||||||
|
/* Skip this line */
|
||||||
|
free(line);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = line + 3;
|
||||||
|
|
||||||
|
switch (line[0]) {
|
||||||
|
case 'I': /* Optional info I: */
|
||||||
|
ReadOptInfo(&dst->info, data);
|
||||||
|
break;
|
||||||
|
case 'P': /* P: Phys= */
|
||||||
|
ReadPhys(dst->Phys, data);
|
||||||
|
break;
|
||||||
|
case 'U': /* U; Uniq= */
|
||||||
|
ReadUniq(&dst->Uniq, data);
|
||||||
|
break;
|
||||||
|
case 'H': /* H: Handlers= */
|
||||||
|
ReadHandlers(dst, data);
|
||||||
|
break;
|
||||||
|
case 'B': /* B: ... */
|
||||||
|
ReadEV(&dst->EV, data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Bool
|
||||||
|
EvdevIsKbd(EventDevice *dev)
|
||||||
|
{
|
||||||
|
return dev->is_kbd && ((dev->EV & KBD_EV) == KBD_EV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Bool
|
||||||
|
EvdevIsPtr(EventDevice *dev)
|
||||||
|
{
|
||||||
|
return dev->is_mouse && ((dev->EV & MOUSE_EV) == MOUSE_EV);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Bool
|
||||||
|
EvdevDifferentDevices(EventDevice *a, EventDevice *b)
|
||||||
|
{
|
||||||
|
#define IS_DIFFERENT(x, y) ((x) && (y) && (x) != (y))
|
||||||
|
#define IS_DIFF(f) if (IS_DIFFERENT(a->f, b->f)) { return TRUE; }
|
||||||
|
|
||||||
|
if (!a->is_read || !b->is_read) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
IS_DIFF(Uniq);
|
||||||
|
|
||||||
|
IS_DIFF(info.Bus);
|
||||||
|
IS_DIFF(info.Vendor);
|
||||||
|
IS_DIFF(info.Product);
|
||||||
|
IS_DIFF(info.Version);
|
||||||
|
|
||||||
|
if (a->Phys[0] && b->Phys[0] && strcmp(a->Phys, b->Phys)) {
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char*
|
||||||
|
EvdevDefaultDevice(int type)
|
||||||
|
{
|
||||||
|
char *ret = NULL;
|
||||||
|
FILE *f = NULL;
|
||||||
|
EventDevice read_dev = {0};
|
||||||
|
|
||||||
|
EventDevice *desired = (type == EVDEV_KEYBOARD) ?
|
||||||
|
&DefaultKbd : &DefaultPtr;
|
||||||
|
|
||||||
|
EventDevice *other = (type == EVDEV_KEYBOARD) ?
|
||||||
|
&DefaultPtr : &DefaultKbd;
|
||||||
|
|
||||||
|
if (desired->is_read) {
|
||||||
|
if (asprintf(&ret, EVDEV_FMT, desired->EventNo) < 0) {
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
f = fopen(PROC_DEVICES, "r");
|
||||||
|
if (!f) {
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (feof(f)) {
|
||||||
|
fclose(f);
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&read_dev, 0, sizeof(read_dev));
|
||||||
|
|
||||||
|
if (!ReadEvdev(&read_dev, f)) {
|
||||||
|
fclose(f);
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_dev.EventNo == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == EVDEV_KEYBOARD && !EvdevIsKbd(&read_dev)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == EVDEV_MOUSE && !EvdevIsPtr(&read_dev)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sometimes, modern mice advertise themselved as keyboards.
|
||||||
|
* As such, we have to check that the mouse and keyboard
|
||||||
|
* are separate physical devices.
|
||||||
|
*
|
||||||
|
* Keyboards rarely advertise themselves as mice,
|
||||||
|
* but it doesn't hurt to check them too.
|
||||||
|
*/
|
||||||
|
if (EvdevDifferentDevices(&read_dev, other)) {
|
||||||
|
memcpy(desired, &read_dev, sizeof(read_dev));
|
||||||
|
fclose(f);
|
||||||
|
if (asprintf(&ret, EVDEV_FMT, desired->EventNo) < 0) {
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unreachable */
|
||||||
|
fclose(f);
|
||||||
|
return FallbackEvdevCheck();
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
EvdevDefaultKbd(void)
|
||||||
|
{
|
||||||
|
return EvdevDefaultDevice(EVDEV_KEYBOARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
char*
|
||||||
|
EvdevDefaultPtr(void)
|
||||||
|
{
|
||||||
|
return EvdevDefaultDevice(EVDEV_MOUSE);
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ if build_kdrive_mouse
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
if build_kdrive_evdev
|
if build_kdrive_evdev
|
||||||
srcs_linux += 'evdev.c'
|
srcs_linux += 'evdev/evdev.c'
|
||||||
|
srcs_linux += 'evdev/evdev_autodetect.c'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if build_kdrive_tslib
|
if build_kdrive_tslib
|
||||||
|
|||||||
Reference in New Issue
Block a user