22#include "common/io.h"
33#include "common/stringUtils.h"
44
5- static bool isRealKeyboard (uint32_t index , FFstrbuf * path )
6- {
7- // Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
8- ffStrbufSetF (path , "/sys/class/input/event%u/device/capabilities/ev" , (unsigned ) index );
9- {
10- FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate ();
11- if (!ffReadFileBuffer (path -> chars , & caps ))
12- return false;
13-
14- ffStrbufTrimRightSpace (& caps );
15- unsigned long val = strtoul (caps .chars , NULL , 16 );
16- if (!(val & (1UL << 20 ))) // EV_REP
17- return false;
18- }
19-
20- // Check KEY_A (bit 30) to filter media remotes and headset controls.
21- // The key capability bitmap is space-separated hex longs, MSB first;
22- // KEY_A falls in the last (least significant) word on all architectures.
23- ffStrbufSetF (path , "/sys/class/input/event%u/device/capabilities/key" , (unsigned ) index );
24- {
25- FF_STRBUF_AUTO_DESTROY caps = ffStrbufCreate ();
26- if (!ffReadFileBuffer (path -> chars , & caps ))
27- return false;
28-
29- ffStrbufTrimRightSpace (& caps );
30- const char * lastWord = strrchr (caps .chars , ' ' );
31- lastWord = lastWord ? lastWord + 1 : caps .chars ;
32-
33- unsigned long val = strtoul (lastWord , NULL , 16 );
34- if (!(val & (1UL << 30 ))) // KEY_A
35- return false;
36- }
37-
38- return true;
39- }
5+ #include <linux/input-event-codes.h>
406
417const char * ffDetectKeyboard (FFlist * devices /* List of FFKeyboardDevice */ )
428{
@@ -46,58 +12,90 @@ const char* ffDetectKeyboard(FFlist* devices /* List of FFKeyboardDevice */)
4612 if (!ffAppendFileBuffer ("/proc/bus/input/devices" , & content ))
4713 return "ffAppendFileBuffer(\"/proc/bus/input/devices\") == NULL" ;
4814
49- uint64_t flags = 0 ;
50- FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate ();
5115 FFstrbuf kbd = ffStrbufCreateStatic ("kbd" );
5216
17+ FFKeyboardDevice device = {
18+ .name = ffStrbufCreate (),
19+ .serial = ffStrbufCreate ()
20+ };
21+
5322 char * line = NULL ;
5423 size_t len = 0 ;
5524 while (ffStrbufGetline (& line , & len , & content ))
5625 {
57- if (!ffStrStartsWith (line , "H: Handlers=" ))
58- continue ;
59-
60- const char * handlers = line + strlen ("H: Handlers=" );
61-
62- if (!ffStrbufMatchSeparatedS (& kbd , handlers , ' ' ))
63- continue ;
64-
65- // Find "eventN" token and extract the index
66- const char * eventStr = strstr (handlers , "event" );
67- if (!eventStr )
68- continue ;
69-
70- char * pend = NULL ;
71- uint32_t eventIndex = (uint32_t ) strtoul (eventStr + strlen ("event" ), & pend , 10 );
72- if (pend == eventStr + strlen ("event" ))
73- continue ;
74-
75- // Skip duplicates (dedup bitmap covers indices 0-63; higher indices are not deduped)
76- if (eventIndex < 64 && (flags & (1ULL << eventIndex )))
77- continue ;
78-
79- if (!isRealKeyboard (eventIndex , & path ))
80- continue ;
81-
82- ffStrbufSetF (& path , "/sys/class/input/event%u/device/name" , (unsigned ) eventIndex );
83-
84- FF_STRBUF_AUTO_DESTROY name = ffStrbufCreate ();
85- if (ffAppendFileBuffer (path .chars , & name ))
26+ switch (line [0 ])
8627 {
87- if (eventIndex < 64 )
88- flags |= (1ULL << eventIndex );
89-
90- ffStrbufTrimRightSpace (& name );
91- ffStrbufSubstrBefore (& path , path .length - (uint32_t ) strlen ("name" ));
92-
93- FFKeyboardDevice * device = (FFKeyboardDevice * ) ffListAdd (devices );
94- ffStrbufInitMove (& device -> name , & name );
95- ffStrbufInit (& device -> serial );
96-
97- ffStrbufAppendS (& path , "uniq" );
98- if (ffAppendFileBuffer (path .chars , & device -> serial ))
99- ffStrbufTrimRightSpace (& device -> serial );
28+ case 'N' : {
29+ const uint32_t prefixLen = strlen ("N: Name=" );
30+ if (__builtin_expect (len <= prefixLen , false)) continue ;
31+ const char * name = line + prefixLen ;
32+ const uint32_t nameLen = (uint32_t ) len - prefixLen ;
33+ ffStrbufSetNS (& device .name , nameLen , name );
34+ ffStrbufTrim (& device .name , '"' );
35+ continue ;
36+ }
37+ case 'H' : {
38+ const uint32_t prefixLen = strlen ("H: Handlers=" );
39+ if (__builtin_expect (len <= prefixLen , false)) continue ;
40+ const char * handlers = line + prefixLen ;
41+ const uint32_t handlersLen = (uint32_t ) len - prefixLen ;
42+ if (!ffStrbufMatchSeparatedNS (& kbd , handlersLen , handlers , ' ' ))
43+ goto skipDevice ;
44+ continue ;
45+ }
46+ case 'B' : {
47+ const char * bits = line + strlen ("B: " );
48+ if (ffStrStartsWith (bits , "EV=" ))
49+ {
50+ // Check EV_REP (auto-repeat, bit 20) to filter pseudo-keyboards (Power Button, PC Speaker).
51+ const char * evBits = bits + strlen ("EV=" );
52+ uint64_t val = strtoull (evBits , NULL , 16 );
53+ if (!(val & (1ULL << EV_REP )))
54+ goto skipDevice ;
55+ }
56+ else if (ffStrStartsWith (bits , "KEY=" ))
57+ {
58+ // Check KEY_A (bit 30) to filter media remotes and headset controls.
59+ // The key capability bitmap is space-separated hex longs, MSB first;
60+ // KEY_A falls in the last (least significant) word on all architectures.
61+ const char * keyBits = bits + strlen ("KEY=" );
62+ const char * lastWord = memrchr (keyBits , ' ' , len - (size_t ) (keyBits - line ));
63+ lastWord = lastWord ? lastWord + 1 : keyBits ;
64+
65+ uint64_t val = strtoull (lastWord , NULL , 16 );
66+ if (!(val & (1ULL << KEY_A )))
67+ goto skipDevice ;
68+ }
69+ continue ;
70+ }
71+ case 'U' : {
72+ const uint32_t prefixLen = strlen ("U: Uniq=" );
73+ if (__builtin_expect (len <= prefixLen , false)) continue ;
74+ const char * uniq = line + prefixLen ;
75+ const uint32_t uniqLen = (uint32_t ) len - prefixLen ;
76+ ffStrbufSetNS (& device .serial , uniqLen , uniq );
77+ continue ;
78+ }
79+ case '\0' :
80+ // End of device entry; add to list if it has a name.
81+ if (device .name .length > 0 )
82+ {
83+ FFKeyboardDevice * added = (FFKeyboardDevice * ) ffListAdd (devices );
84+ ffStrbufInitMove (& added -> name , & device .name );
85+ ffStrbufInitMove (& added -> serial , & device .serial );
86+ }
87+ continue ;
88+ default :
89+ continue ;
10090 }
91+
92+ skipDevice :
93+ // Skip to the end of the current device entry.
94+ while (line [0 ] != '\0' && ffStrbufGetline (& line , & len , & content ))
95+ ;
96+ // Despite the fn name, it resets the string buffer to initial state
97+ ffStrbufDestroy (& device .name );
98+ ffStrbufDestroy (& device .serial );
10199 }
102100
103101 return NULL ;
0 commit comments