package gamepad import ( "fmt" "log" "math" "github.com/gvalkov/golang-evdev" ) // event types const ( Button = iota AxisX AxisY AxisZ ) type Event struct { Type int Value int32 Max int32 } func (e Event) IsAxis() bool { switch e.Type { case AxisX, AxisY, AxisZ: return true default: return false } } func (e Event) IsAxisX() bool { return e.Type == AxisX } func (e Event) IsAxisY() bool { return e.Type == AxisY } func (e Event) IsAxisZ() bool { return e.Type == AxisZ } func (e Event) IsButton() bool { return e.Type == Button } func (e Event) IsButtonPress() bool { if e.IsButton() && e.Value > 0 { return true } return false } func (e Event) Fraction() float64 { if e.IsAxis() { fraction := float64(e.Value) / float64(e.Max) if math.Abs(fraction) < 0.01 { return 0 } return fraction } return float64(e.Value) } func (e Event) String() string { var code string switch e.Type { case Button: code = "button" case AxisX: code = "X" case AxisY: code = "Y" case AxisZ: code = "Z" } return fmt.Sprintf("%v %v", code, e.Value) } // Logitech gamepad button channels const ( LeftAnalog = 0x001 RightAnalog = 0x002 DPad = 0x004 A = 0x008 B = 0x010 X = 0x020 Y = 0x040 Left = 0x080 Right = 0x100 Back = 0x200 Start = 0x400 Logitech = 0x800 All = 0xfff ) const ( analogXYMax = 32767 analogZMax = 255 dpadMax = 1 ) const channelBufferSize = 16 type Gamepad struct { channels int16 device *evdev.InputDevice LeftAnalog, RightAnalog, DPad chan Event A, B, X, Y chan Event Left, Right chan Event Back, Start, Logitech chan Event } func NewGamepad(device string, channels int16) (*Gamepad, error) { g := &Gamepad{channels: channels} if !evdev.IsInputDevice(device) { return nil, fmt.Errorf("%v is not an input device", device) } var err error g.device, err = evdev.Open(device) if err != nil { return nil, err } if channels&LeftAnalog != 0 { g.LeftAnalog = make(chan Event, channelBufferSize) } if channels&RightAnalog != 0 { g.RightAnalog = make(chan Event, channelBufferSize) } if channels&DPad != 0 { g.DPad = make(chan Event, channelBufferSize) } if channels&A != 0 { g.A = make(chan Event, channelBufferSize) } if channels&B != 0 { g.B = make(chan Event, channelBufferSize) } if channels&X != 0 { g.X = make(chan Event, channelBufferSize) } if channels&Y != 0 { g.Y = make(chan Event, channelBufferSize) } if channels&Left != 0 { g.Left = make(chan Event, channelBufferSize) } if channels&Right != 0 { g.Right = make(chan Event, channelBufferSize) } if channels&Back != 0 { g.Back = make(chan Event, channelBufferSize) } if channels&Start != 0 { g.Start = make(chan Event, channelBufferSize) } if channels&Logitech != 0 { g.Logitech = make(chan Event, channelBufferSize) } return g, nil } // Logitech gamepad codes const ( leftAnalogX = 0 leftAnalogY = 1 leftAnalogZ = 2 rightAnalogX = 3 rightAnalogY = 4 rightAnalogZ = 5 dPadX = 16 dPadY = 17 a = 304 b = 305 x = 307 y = 308 left = 310 right = 311 back = 314 start = 315 logitech = 316 leftAnalog = 317 rightAnalog = 318 ) func (g *Gamepad) Read() error { log.Print("gamepad read loop started") for { e, err := g.device.ReadOne() if err != nil { return err } switch e.Type { case evdev.EV_ABS: switch e.Code { case leftAnalogX: if g.channels&LeftAnalog != 0 { g.LeftAnalog <- Event{Type: AxisX, Value: e.Value, Max: analogXYMax} } case leftAnalogY: if g.channels&LeftAnalog != 0 { g.LeftAnalog <- Event{Type: AxisY, Value: e.Value, Max: analogXYMax} } case leftAnalogZ: if g.channels&LeftAnalog != 0 { g.LeftAnalog <- Event{Type: AxisZ, Value: e.Value, Max: analogZMax} } case rightAnalogX: if g.channels&RightAnalog != 0 { g.RightAnalog <- Event{Type: AxisX, Value: e.Value, Max: analogXYMax} } case rightAnalogY: if g.channels&RightAnalog != 0 { g.RightAnalog <- Event{Type: AxisY, Value: e.Value, Max: analogXYMax} } case rightAnalogZ: if g.channels&RightAnalog != 0 { g.RightAnalog <- Event{Type: AxisZ, Value: e.Value, Max: analogZMax} } case dPadX: if g.channels&DPad != 0 { g.DPad <- Event{Type: AxisX, Value: e.Value, Max: dpadMax} } case dPadY: if g.channels&DPad != 0 { g.DPad <- Event{Type: AxisY, Value: e.Value, Max: dpadMax} } } case evdev.EV_KEY: switch e.Code { case a: if g.channels&A != 0 { g.A <- Event{Type: Button, Value: e.Value} } case b: if g.channels&B != 0 { g.B <- Event{Type: Button, Value: e.Value} } case x: if g.channels&X != 0 { g.X <- Event{Type: Button, Value: e.Value} } case y: if g.channels&Y != 0 { g.Y <- Event{Type: Button, Value: e.Value} } case left: if g.channels&Left != 0 { g.Left <- Event{Type: Button, Value: e.Value} } case right: if g.channels&Right != 0 { g.Right <- Event{Type: Button, Value: e.Value} } case back: if g.channels&Back != 0 { g.Back <- Event{Type: Button, Value: e.Value} } case start: if g.channels&Start != 0 { g.Start <- Event{Type: Button, Value: e.Value} } case logitech: if g.channels&Logitech != 0 { g.Logitech <- Event{Type: Button, Value: e.Value} } case leftAnalog: if g.channels&LeftAnalog != 0 { g.LeftAnalog <- Event{Type: Button, Value: e.Value} } case rightAnalog: if g.channels&LeftAnalog != 0 { g.RightAnalog <- Event{Type: Button, Value: e.Value} } } } } }