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  }