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