// +build linux package hid import ( "encoding/binary" "io" "io/ioutil" "os" "math" "time" ) const ( BUTTON1 = byte(1 << 0) BUTTON2 = byte(1 << 1) BUTTON3 = byte(1 << 2) ) //mat.Round() doesn't exist before go 1.10 func round(f float64) float64 { return math.Floor(f + .5) } type Mouse struct { lastChangeWasAbsolute bool buttons [3]bool axis [2]int devicePath string deviceFile *os.File } func NewMouse(devicePath string) (mouse *Mouse, err error) { //ToDo: check existence of deviceFile (+ is writable) mouse = &Mouse{ devicePath: devicePath, } mouse.deviceFile, err = os.OpenFile(devicePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.ModePerm) if err != nil { return nil,err } return } func (m *Mouse) Close() { if m.deviceFile != nil { m.deviceFile.Close() } } func (m *Mouse) writeReport(filepath string) error { report, err := generateMouseReport(m.lastChangeWasAbsolute, m.buttons, m.axis) if err != nil { return err } //fmt.Printf("Writing %+v to %s\n", report, filepath) return ioutil.WriteFile(filepath, report, os.ModePerm) //Serialize Report and write to specified file } func (m *Mouse) writeReportToFile(file *os.File) (err error) { data, err := generateMouseReport(m.lastChangeWasAbsolute, m.buttons, m.axis) if err != nil { return err } //fmt.Printf("Writing %+v to %s\n", report, file) n, err := file.Write(data) if err == nil && n < len(data) { err = io.ErrShortWrite } return err } func (m* Mouse) SetButtons(bt1,bt2,bt3 bool) (err error) { change := false if m.buttons[0] != bt1 { m.buttons[0] = bt1 change = true } if m.buttons[1] != bt2 { m.buttons[1] = bt2 change = true } if m.buttons[2] != bt3 { m.buttons[2] = bt3 change = true } if change { m.lastChangeWasAbsolute = false m.axis[0] = 0 //No (repeated) movement on button change m.axis[1] = 0 //No (repeated) movement on button change return m.writeReportToFile(m.deviceFile) } else { //no state change, no new mouse report return nil } } func (m* Mouse) Click(bt1,bt2,bt3 bool) (err error) { m.SetButtons(bt1,bt2,bt3) m.SetButtons(false,false,false ) //release all button (including other buttons in pressed state, before doing the click) return } func (m* Mouse) DoubleClick(bt1,bt2,bt3 bool) (err error) { m.Click(bt1,bt2,bt3) time.Sleep(100 * time.Millisecond) // delay between clicks m.Click(bt1,bt2,bt3) return } func (m* Mouse) Move(x,y int8) (err error) { m.axis[0] = int(x) m.axis[1] = int(y) m.lastChangeWasAbsolute = false return m.writeReportToFile(m.deviceFile) } func scaleAbs(fVal float64) int { ival := int(float64(0x7FFF) * fVal) //ival -= 32768 if ival < -32768 { ival = -32768 } if ival > 32767 { ival = 32767 } return ival } func (m* Mouse) MoveTo(x,y float64) (err error) { m.axis[0] = scaleAbs(x) m.axis[1] = scaleAbs(y) m.lastChangeWasAbsolute = true return m.writeReportToFile(m.deviceFile) } func (m* Mouse) MoveStepped(x,y int16) (err error) { xf := float64(x) yf := float64(y) steps := math.Max(math.Abs(xf), math.Abs(yf)) dx := xf / steps dy := yf / steps curX := int16(0) curY := int16(0) for curStep := 1; curStep <= int(steps); curStep++ { desiredX := int16(round(dx * float64(curStep))) desiredY := int16(round(dy * float64(curStep))) stepX := desiredX - curX stepY := desiredY - curY //start Lock here m.axis[0] = int(stepX) m.axis[1] = int(stepY) m.lastChangeWasAbsolute = false err = m.writeReportToFile(m.deviceFile) if err != nil { m.axis[0] = 0 m.axis[1] = 0 //unlock return err } //unlock curX += stepX curY += stepY } //Lock m.axis[0] = 0 m.axis[1] = 0 //Unlock return nil } func generateMouseReport(absolute bool, buttons [3]bool, axis [2]int) (report []byte, err error) { var outdata [6]byte if absolute { outdata[0] = 0x02 } else { outdata[0] = 0x01 } if buttons[0] { outdata[1] |= BUTTON1 } if buttons[1] { outdata[1] |= BUTTON2 } if buttons[2] { outdata[1] |= BUTTON3 } if absolute { binary.LittleEndian.PutUint16(outdata[2:], uint16(axis[0])) binary.LittleEndian.PutUint16(outdata[4:], uint16(axis[1])) } else { outdata[2] = uint8(axis[0]) outdata[3] = uint8(axis[1]) } return outdata[:], nil }