gobot.io/x/gobot@v1.16.0/platforms/joystick/joystick_driver.go (about) 1 package joystick 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "io/ioutil" 7 "time" 8 9 "github.com/veandco/go-sdl2/sdl" 10 "gobot.io/x/gobot" 11 ) 12 13 const ( 14 // Dualshock3 joystick configuration. 15 Dualshock3 = "dualshock3" 16 17 // Dualshock4 joystick configuration. 18 Dualshock4 = "dualshock4" 19 20 // TFlightHotasX flight stick configuration. 21 TFlightHotasX = "tflightHotasX" 22 23 // Xbox360 joystick configuration. 24 Xbox360 = "xbox360" 25 26 // Xbox360RockBandDrums controller configuration. 27 Xbox360RockBandDrums = "xbox360RockBandDrums" 28 29 // Nvidia Shield TV Controller 30 Shield = "shield" 31 ) 32 33 // Driver represents a joystick 34 type Driver struct { 35 name string 36 interval time.Duration 37 connection gobot.Connection 38 configPath string 39 config joystickConfig 40 poll func() sdl.Event 41 halt chan bool 42 gobot.Eventer 43 } 44 45 // pair is a JSON representation of name and id 46 type pair struct { 47 Name string `json:"name"` 48 ID int `json:"id"` 49 } 50 51 // hat is a JSON representation of hat, name and id 52 type hat struct { 53 Hat int `json:"hat"` 54 Name string `json:"name"` 55 ID int `json:"id"` 56 } 57 58 // joystickConfig is a JSON representation of configuration values 59 type joystickConfig struct { 60 Name string `json:"name"` 61 GUID string `json:"guid"` 62 Axis []pair `json:"axis"` 63 Buttons []pair `json:"buttons"` 64 Hats []hat `json:"Hats"` 65 } 66 67 // NewDriver returns a new Driver with a polling interval of 68 // 10 Milliseconds given a Joystick Adaptor and json button configuration 69 // file location. 70 // 71 // Optionally accepts: 72 // time.Duration: Interval at which the Driver is polled for new information 73 func NewDriver(a *Adaptor, config string, v ...time.Duration) *Driver { 74 d := &Driver{ 75 name: gobot.DefaultName("Joystick"), 76 connection: a, 77 Eventer: gobot.NewEventer(), 78 configPath: config, 79 poll: func() sdl.Event { 80 return sdl.PollEvent() 81 }, 82 interval: 10 * time.Millisecond, 83 halt: make(chan bool, 0), 84 } 85 86 if len(v) > 0 { 87 d.interval = v[0] 88 } 89 90 d.AddEvent("error") 91 return d 92 } 93 94 // Name returns the Drivers name 95 func (j *Driver) Name() string { return j.name } 96 97 // SetName sets the Drivers name 98 func (j *Driver) SetName(n string) { j.name = n } 99 100 // Connection returns the Drivers connection 101 func (j *Driver) Connection() gobot.Connection { return j.connection } 102 103 // adaptor returns joystick adaptor 104 func (j *Driver) adaptor() *Adaptor { 105 return j.Connection().(*Adaptor) 106 } 107 108 // Start and polls the state of the joystick at the given interval. 109 // 110 // Emits the Events: 111 // Error error - On button error 112 // Events defined in the json button configuration file. 113 // They will have the format: 114 // [button]_press 115 // [button]_release 116 // [axis] 117 func (j *Driver) Start() (err error) { 118 switch j.configPath { 119 case "dualshock3": 120 j.config = dualshock3Config 121 case "dualshock4": 122 j.config = dualshock4Config 123 case "tflightHotasX": 124 j.config = tflightHotasXConfig 125 case "xbox360": 126 j.config = xbox360Config 127 case "xbox360RockBandDrums": 128 j.config = xbox360RockBandDrumsConfig 129 case "shield": 130 j.config = shieldConfig 131 default: 132 err := j.loadFile() 133 if err != nil { 134 return err 135 } 136 } 137 138 for _, value := range j.config.Buttons { 139 j.AddEvent(fmt.Sprintf("%s_press", value.Name)) 140 j.AddEvent(fmt.Sprintf("%s_release", value.Name)) 141 } 142 for _, value := range j.config.Axis { 143 j.AddEvent(value.Name) 144 } 145 for _, value := range j.config.Hats { 146 j.AddEvent(fmt.Sprintf("%s_press", value.Name)) 147 j.AddEvent(fmt.Sprintf("%s_release", value.Name)) 148 } 149 150 go func() { 151 for { 152 for event := j.poll(); event != nil; event = j.poll() { 153 if errs := j.handleEvent(event); errs != nil { 154 j.Publish(j.Event("error"), errs) 155 } 156 } 157 select { 158 case <-time.After(j.interval): 159 case <-j.halt: 160 return 161 } 162 } 163 }() 164 return 165 } 166 167 // Halt stops joystick driver 168 func (j *Driver) Halt() (err error) { 169 j.halt <- true 170 return 171 } 172 173 var previousHat = "" 174 175 // HandleEvent publishes an specific event according to data received 176 func (j *Driver) handleEvent(event sdl.Event) error { 177 switch data := event.(type) { 178 case *sdl.JoyAxisEvent: 179 if data.Which == j.adaptor().joystick.InstanceID() { 180 axis := j.findName(data.Axis, j.config.Axis) 181 if axis == "" { 182 return fmt.Errorf("Unknown Axis: %v", data.Axis) 183 } 184 j.Publish(j.Event(axis), data.Value) 185 } 186 case *sdl.JoyButtonEvent: 187 if data.Which == j.adaptor().joystick.InstanceID() { 188 button := j.findName(data.Button, j.config.Buttons) 189 if button == "" { 190 return fmt.Errorf("Unknown Button: %v", data.Button) 191 } 192 if data.State == 1 { 193 j.Publish(j.Event(fmt.Sprintf("%s_press", button)), nil) 194 } else { 195 j.Publish(j.Event(fmt.Sprintf("%s_release", button)), nil) 196 } 197 } 198 case *sdl.JoyHatEvent: 199 if data.Which == j.adaptor().joystick.InstanceID() { 200 hat := j.findHatName(data.Value, data.Hat, j.config.Hats) 201 if hat == "" { 202 return fmt.Errorf("Unknown Hat: %v %v", data.Hat, data.Value) 203 } else if hat == "released" { 204 hat = previousHat 205 j.Publish(j.Event(fmt.Sprintf("%s_release", hat)), true) 206 } else { 207 previousHat = hat 208 j.Publish(j.Event(fmt.Sprintf("%s_press", hat)), true) 209 } 210 } 211 } 212 return nil 213 } 214 215 func (j *Driver) findName(id uint8, list []pair) string { 216 for _, value := range list { 217 if int(id) == value.ID { 218 return value.Name 219 } 220 } 221 return "" 222 } 223 224 // findHatName returns name from hat found by id in provided list 225 func (j *Driver) findHatName(id uint8, hat uint8, list []hat) string { 226 for _, lHat := range list { 227 if int(id) == lHat.ID && int(hat) == lHat.Hat { 228 return lHat.Name 229 } 230 } 231 return "" 232 } 233 234 // loadFile load the joystick config from a .json file 235 func (j *Driver) loadFile() error { 236 file, e := ioutil.ReadFile(j.configPath) 237 if e != nil { 238 return e 239 } 240 241 var jsontype joystickConfig 242 json.Unmarshal(file, &jsontype) 243 j.config = jsontype 244 return nil 245 }