github.com/holoplot/go-evdev@v0.0.0-20220721205823-d31c64b9d636/device.go (about) 1 package evdev 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "os" 7 "syscall" 8 ) 9 10 // InputDevice represent a Linux kernel input device in userspace. 11 // It can be used to query and write device properties, read input events, 12 // or grab it for exclusive access. 13 type InputDevice struct { 14 file *os.File 15 driverVersion int32 16 } 17 18 // Open creates a new InputDevice from the given path. Returns an error if 19 // the device node could not be opened or its properties failed to read. 20 func Open(path string) (*InputDevice, error) { 21 d := &InputDevice{} 22 23 var err error 24 d.file, err = os.OpenFile(path, os.O_RDWR, 0) 25 if err != nil { 26 return nil, err 27 } 28 29 d.driverVersion, err = ioctlEVIOCGVERSION(d.file.Fd()) 30 if err != nil { 31 return nil, fmt.Errorf("cannot get driver version: %v", err) 32 } 33 34 return d, nil 35 } 36 37 // Close releases the resources held by an InputDevice. After calling this 38 // function, the InputDevice is no longer operational. 39 func (d *InputDevice) Close() error { 40 return d.file.Close() 41 } 42 43 // Path returns the device's node path it was opened under. 44 func (d *InputDevice) Path() string { 45 return d.file.Name() 46 } 47 48 // DriverVersion returns the version of the Linux Evdev driver. 49 // The three ints returned by this function describe the major, minor and 50 // micro parts of the version code. 51 func (d *InputDevice) DriverVersion() (int, int, int) { 52 return int(d.driverVersion >> 16), 53 int((d.driverVersion >> 8) & 0xff), 54 int((d.driverVersion >> 0) & 0xff) 55 } 56 57 // Name returns the device's name as reported by the kernel. 58 func (d *InputDevice) Name() (string, error) { 59 return ioctlEVIOCGNAME(d.file.Fd()) 60 } 61 62 // PhysicalLocation returns the device's physical location as reported by the kernel. 63 func (d *InputDevice) PhysicalLocation() (string, error) { 64 return ioctlEVIOCGPHYS(d.file.Fd()) 65 } 66 67 // UniqueID returns the device's unique identifier as reported by the kernel. 68 func (d *InputDevice) UniqueID() (string, error) { 69 return ioctlEVIOCGUNIQ(d.file.Fd()) 70 } 71 72 // InputID returns the device's vendor/product/busType/version information as reported by the kernel. 73 func (d *InputDevice) InputID() (InputID, error) { 74 return ioctlEVIOCGID(d.file.Fd()) 75 } 76 77 // CapableTypes returns a slice of EvType that are the device supports 78 func (d *InputDevice) CapableTypes() []EvType { 79 var types []EvType 80 81 evBits, err := ioctlEVIOCGBIT(d.file.Fd(), 0) 82 if err != nil { 83 return []EvType{} 84 } 85 86 evBitmap := newBitmap(evBits) 87 88 for _, t := range evBitmap.setBits() { 89 types = append(types, EvType(t)) 90 } 91 92 return types 93 } 94 95 // CapableEvents returns a slice of EvCode that are the device supports for given EvType 96 func (d *InputDevice) CapableEvents(t EvType) []EvCode { 97 var codes []EvCode 98 99 evBits, err := ioctlEVIOCGBIT(d.file.Fd(), int(t)) 100 if err != nil { 101 return []EvCode{} 102 } 103 104 evBitmap := newBitmap(evBits) 105 106 for _, t := range evBitmap.setBits() { 107 codes = append(codes, EvCode(t)) 108 } 109 110 return codes 111 } 112 113 // Properties returns a slice of EvProp that are the device supports 114 func (d *InputDevice) Properties() []EvProp { 115 var props []EvProp 116 117 propBits, err := ioctlEVIOCGPROP(d.file.Fd()) 118 if err != nil { 119 return []EvProp{} 120 } 121 122 propBitmap := newBitmap(propBits) 123 124 for _, p := range propBitmap.setBits() { 125 props = append(props, EvProp(p)) 126 } 127 128 return props 129 } 130 131 // State return a StateMap for the given type. The map will be empty if the requested type 132 // is not supported by the device. 133 func (d *InputDevice) State(t EvType) (StateMap, error) { 134 fd := d.file.Fd() 135 136 evBits, err := ioctlEVIOCGBIT(fd, 0) 137 if err != nil { 138 return nil, fmt.Errorf("cannot get evBits: %v", err) 139 } 140 141 evBitmap := newBitmap(evBits) 142 143 if !evBitmap.bitIsSet(int(t)) { 144 return StateMap{}, nil 145 } 146 147 codeBits, err := ioctlEVIOCGBIT(fd, int(t)) 148 if err != nil { 149 return nil, fmt.Errorf("cannot get evBits: %v", err) 150 } 151 152 codeBitmap := newBitmap(codeBits) 153 154 var stateBits []byte 155 156 switch t { 157 case EV_KEY: 158 stateBits, err = ioctlEVIOCGKEY(fd) 159 case EV_SW: 160 stateBits, err = ioctlEVIOCGSW(fd) 161 case EV_LED: 162 stateBits, err = ioctlEVIOCGLED(fd) 163 case EV_SND: 164 stateBits, err = ioctlEVIOCGSND(fd) 165 default: 166 err = fmt.Errorf("unsupported evType %d", t) 167 } 168 169 if err != nil { 170 return nil, err 171 } 172 173 stateBitmap := newBitmap(stateBits) 174 st := StateMap{} 175 176 for _, code := range codeBitmap.setBits() { 177 st[EvCode(code)] = stateBitmap.bitIsSet(code) 178 } 179 180 return st, nil 181 } 182 183 // AbsInfos returns the AbsInfo struct for all axis the device supports. 184 func (d *InputDevice) AbsInfos() (map[EvCode]AbsInfo, error) { 185 a := make(map[EvCode]AbsInfo) 186 187 absBits, err := ioctlEVIOCGBIT(d.file.Fd(), EV_ABS) 188 if err != nil { 189 return nil, fmt.Errorf("cannot get absBits: %v", err) 190 } 191 192 absBitmap := newBitmap(absBits) 193 194 for _, abs := range absBitmap.setBits() { 195 absInfo, err := ioctlEVIOCGABS(d.file.Fd(), abs) 196 if err == nil { 197 a[EvCode(abs)] = absInfo 198 } 199 } 200 201 return a, nil 202 } 203 204 // Grab grabs the device for exclusive access. No other process will receive 205 // input events until the device instance is active. 206 func (d *InputDevice) Grab() error { 207 return ioctlEVIOCGRAB(d.file.Fd(), 1) 208 } 209 210 // Ungrab releases a previously taken exclusive use with Grab(). 211 func (d *InputDevice) Ungrab() error { 212 return ioctlEVIOCGRAB(d.file.Fd(), 0) 213 } 214 215 // Revoke revokes device access 216 func (d *InputDevice) Revoke() error { 217 return ioctlEVIOCREVOKE(d.file.Fd()) 218 } 219 220 // NonBlock sets file descriptor into nonblocking mode. 221 // This way it is possible to interrupt ReadOne call by closing the device. 222 // Note: file.Fd() call will set file descriptor back to blocking mode so make sure your program 223 // is not using any other method than ReadOne after NonBlock call. 224 func (d *InputDevice) NonBlock() error { 225 return syscall.SetNonblock(int(d.file.Fd()), true) 226 } 227 228 // ReadOne reads one InputEvent from the device. It blocks until an event has 229 // been received or an error has occurred. 230 func (d *InputDevice) ReadOne() (*InputEvent, error) { 231 event := InputEvent{} 232 233 err := binary.Read(d.file, binary.LittleEndian, &event) 234 if err != nil { 235 return nil, err 236 } 237 238 return &event, nil 239 } 240 241 // WriteOne writes one InputEvent to the device. 242 // Useful for controlling LEDs of the device 243 func (d *InputDevice) WriteOne(event *InputEvent) error { 244 return binary.Write(d.file, binary.LittleEndian, event) 245 }