package xinput_devnodes // #cgo CFLAGS: -g --std=gnu99 -pedantic -Wall // #cgo LDFLAGS: -lX11 -lXi // #include "devnodes.h" // #include // #include import "C" import ( "fmt" "unsafe" "regexp" "errors" "os" ) func FindKeyboardDevNode() string { xslaveinfos, freeMethod := GetXSlaveInfo() defer freeMethod() re := regexp.MustCompile(`(?i).*keyboard.*`) for _, xslaveinfo := range xslaveinfos { name, devNode := C.GoString(xslaveinfo.name), C.GoString(xslaveinfo.dev_node) if re.MatchString(name) { return devNode } } return "" } func Print() { xslaveinfos, freeMethod := GetXSlaveInfo() defer freeMethod() for _, xslaveinfo := range xslaveinfos { fmt.Printf("%s = %s\n", C.GoString(xslaveinfo.name), C.GoString(xslaveinfo.dev_node)) } } func GetXSlaveInfo() ([]C.XSlaveInfo, func()) { xDisplayName := C.CString(tryGetXDisplay()) defer C.free(unsafe.Pointer(xDisplayName)) var error C.int xslaves := C.get_slave_info(xDisplayName, &error) if (error != C.int(0)) { panic(convertErrorIntToGoError(error)) } arr := (*[1 << 30]C.XSlaveInfo)(unsafe.Pointer(xslaves.slaves)) slice := arr[:xslaves.length:xslaves.length] freeMethod := func() { C.free_xslaves(xslaves) } return slice, freeMethod } func tryGetXDisplay() string { display := os.Getenv("DISPLAY") if display == "" { panic(errors.New("DISPLAY envvar not set")) } return display } func convertErrorIntToGoError(err C.int) error { errorMsg := "" switch err { case C.MALLOCFAIL: errorMsg = "Memory allocation failed" case C.XSLAVESFULL: errorMsg = "XSlaves full" case C.BADDISPLAY: errorMsg = "Failed to open X display" case C.XINPUTEXTUNAVAIL: errorMsg = "XInput extension unavailable" case C.XINPUT20UNAVAIL: errorMsg = "XInput extension is not 2.0" case C.XINPUTQUERYVERSIONFAILED: errorMsg = "Failed to query XInput extension version" case C.XSLAVEINFOQUERYFAILED: errorMsg = "Failed to query XInput slaves" default: errorMsg = fmt.Sprintf("Unknown error from devnodes.h: %v", err) } return errors.New(errorMsg) }