From cd653e9ea150b260d0b3df45a0e7046fb0d014fe Mon Sep 17 00:00:00 2001 From: Ron Evans Date: Sat, 23 Sep 2023 12:32:31 +0200 Subject: [PATCH] joystick(core): replace sdl with 0xcafed00d/joystick package (#988) --- .circleci/config.yml | 12 +- Makefile | 6 +- appveyor.yml | 4 + examples/ardrone_ps3.go | 2 +- examples/bebop_ps3.go | 2 +- examples/bebop_ps3_video.go | 2 +- examples/joystick_ps3.go | 26 ++- examples/joystick_ps4.go | 2 +- examples/joystick_ps5.go | 2 +- examples/joystick_xbox360.go | 2 +- examples/joystick_xbox360_rock_band_drums.go | 2 +- examples/joystick_xboxone.go | 2 +- examples/minidrone_mambo_ps3.go | 2 +- examples/minidrone_ps3.go | 2 +- examples/tello_facetracker.go | 2 +- examples/tello_ps3.go | 2 +- go.mod | 4 +- go.sum | 8 +- platforms/joystick/LICENSE | 2 +- platforms/joystick/README.md | 52 +---- platforms/joystick/bin/scanner.go | 140 +++++++----- platforms/joystick/configs/dualshock3.json | 12 +- platforms/joystick/doc.go | 12 +- platforms/joystick/joystick_adaptor.go | 33 +-- platforms/joystick/joystick_adaptor_test.go | 19 +- platforms/joystick/joystick_driver.go | 178 ++++++++------- platforms/joystick/joystick_driver_test.go | 211 ++++++++++++------ platforms/joystick/joystick_dualsense.go | 1 - ...hock3.go => joystick_dualshock3_darwin.go} | 35 ++- .../joystick/joystick_dualshock3_linux.go | 94 ++++++++ .../joystick/joystick_dualshock3_windows.go | 86 +++++++ .../joystick/joystick_dualshock4_darwin.go | 90 ++++++++ ...shock4.go => joystick_dualshock4_linux.go} | 35 +-- .../joystick/joystick_dualshock4_windows.go | 94 ++++++++ .../joystick/joystick_nintendo_joycon.go | 1 - platforms/joystick/joystick_shield.go | 27 --- .../joystick/joystick_tflight_hotas_x.go | 7 - platforms/joystick/joystick_xbox360.go | 47 ---- .../joystick_xbox360_rock_band_drums.go | 1 - platforms/joystick/joystick_xboxone.go | 27 --- platforms/joystick/test_helper.go | 15 +- 41 files changed, 839 insertions(+), 464 deletions(-) rename platforms/joystick/{joystick_dualshock3.go => joystick_dualshock3_darwin.go} (94%) create mode 100644 platforms/joystick/joystick_dualshock3_linux.go create mode 100644 platforms/joystick/joystick_dualshock3_windows.go create mode 100644 platforms/joystick/joystick_dualshock4_darwin.go rename platforms/joystick/{joystick_dualshock4.go => joystick_dualshock4_linux.go} (78%) create mode 100644 platforms/joystick/joystick_dualshock4_windows.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 2727f9153..e09013af6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -38,10 +38,10 @@ jobs: name: Debug version command: go version - run: - # digispark needs libusb, joystick needs sdl2, opencv needs opencv - name: Platform tests (except digispark, joystick, opencv) + # digispark needs libusb, opencv needs opencv + name: Platform tests (except digispark and opencv) command: | - go test -v $(go list ./platforms/... | grep -v platforms/digispark | grep -v platforms/joystick | grep -v platforms/opencv) + go test -v $(go list ./platforms/... | grep -v platforms/digispark | grep -v platforms/opencv) "check_examples": docker: @@ -52,11 +52,11 @@ jobs: name: Debug version command: go version - run: - # digispark needs libusb, joystick needs sdl2, opencv needs opencv - name: Check examples (except digispark, joystick, opencv) + # digispark needs libusb, opencv needs opencv + name: Check examples (except digispark, opencv) command: | ALL=$(grep -l -r --include "*.go" 'build example' ./) - SOME=$(grep -L 'digispark' $(grep -L 'joystick' $(grep -L 'gocv' ${ALL}))) + SOME=$(grep -L 'digispark' $(grep -L 'gocv' ${ALL})) for e in ${SOME} ; do go vet "${e}" ; done workflows: diff --git a/Makefile b/Makefile index 4d7c4b56d..7454c03ba 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,8 @@ ALL_EXAMPLES := $(shell grep -l -r --include "*.go" 'build example' ./) # prevent examples with gocv (opencv) dependencies EXAMPLES_NO_GOCV := $(shell grep -L 'gocv' $(ALL_EXAMPLES)) -# prevent examples with joystick (sdl2) dependencies -EXAMPLES_NO_JOYSTICK := $(shell grep -L 'joystick' $(ALL_EXAMPLES)) -# prevent examples with joystick (sdl2) and gocv (opencv) dependencies -EXAMPLES_NO_GOCV_JOYSTICK := $(shell grep -L 'joystick' $$(grep -L 'gocv' $(EXAMPLES_NO_GOCV))) # used examples -EXAMPLES := $(EXAMPLES_NO_GOCV_JOYSTICK) +EXAMPLES := $(EXAMPLES_NO_GOCV) .PHONY: test test_race test_cover robeaux version_check fmt_check fmt_fix examples examples_check $(EXAMPLES) diff --git a/appveyor.yml b/appveyor.yml index a2abb7d93..1bc2f7642 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,10 @@ build_script: - go test -v -cpu=2 . - go test -v -cpu=2 ./drivers/aio/... - go test -v -cpu=2 ./drivers/i2c/... + - go test -v -cpu=2 ./platforms/dji/... - go test -v -cpu=2 ./platforms/firmata/... - go test -v -cpu=2 ./platforms/ble/... + - go test -v -cpu=2 ./platforms/joystick/... + - go test -v -cpu=2 ./platforms/parrot/... + - go test -v -cpu=2 ./platforms/sphero/... - cd .. diff --git a/examples/ardrone_ps3.go b/examples/ardrone_ps3.go index d7aad83e0..37dd20131 100644 --- a/examples/ardrone_ps3.go +++ b/examples/ardrone_ps3.go @@ -25,7 +25,7 @@ var leftX, leftY, rightX, rightY atomic.Value const offset = 32767.0 func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3") ardroneAdaptor := ardrone.NewAdaptor() diff --git a/examples/bebop_ps3.go b/examples/bebop_ps3.go index 93b514348..743b7b0f2 100644 --- a/examples/bebop_ps3.go +++ b/examples/bebop_ps3.go @@ -25,7 +25,7 @@ var leftX, leftY, rightX, rightY atomic.Value const offset = 32767.0 func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3") bebopAdaptor := bebop.NewAdaptor() diff --git a/examples/bebop_ps3_video.go b/examples/bebop_ps3_video.go index 74b38b672..007e782e3 100644 --- a/examples/bebop_ps3_video.go +++ b/examples/bebop_ps3_video.go @@ -83,7 +83,7 @@ func ffmpeg() (stdin io.WriteCloser, stderr io.ReadCloser, err error) { } func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3") bebopAdaptor := bebop.NewAdaptor() diff --git a/examples/joystick_ps3.go b/examples/joystick_ps3.go index 2cdb55bc4..1d2f3a2ce 100644 --- a/examples/joystick_ps3.go +++ b/examples/joystick_ps3.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, joystick.Dualshock3) work := func() { @@ -64,15 +64,27 @@ func main() { stick.On(joystick.RightPress, func(data interface{}) { fmt.Println("right_press") }) + stick.On(joystick.RightRelease, func(data interface{}) { + fmt.Println("right_release") + }) stick.On(joystick.LeftPress, func(data interface{}) { fmt.Println("left_press") }) + stick.On(joystick.LeftRelease, func(data interface{}) { + fmt.Println("left_release") + }) stick.On(joystick.UpPress, func(data interface{}) { fmt.Println("up_press") }) + stick.On(joystick.UpRelease, func(data interface{}) { + fmt.Println("up_release") + }) stick.On(joystick.DownPress, func(data interface{}) { fmt.Println("down_press") }) + stick.On(joystick.DownRelease, func(data interface{}) { + fmt.Println("down_release") + }) // joysticks stick.On(joystick.LeftX, func(data interface{}) { @@ -92,15 +104,27 @@ func main() { stick.On(joystick.R1Press, func(data interface{}) { fmt.Println("R1Press", data) }) + stick.On(joystick.R1Release, func(data interface{}) { + fmt.Println("R1Release", data) + }) stick.On(joystick.R2Press, func(data interface{}) { fmt.Println("R2Press", data) }) + stick.On(joystick.R2Release, func(data interface{}) { + fmt.Println("R2Release", data) + }) stick.On(joystick.L1Press, func(data interface{}) { fmt.Println("L1Press", data) }) + stick.On(joystick.L1Release, func(data interface{}) { + fmt.Println("L1Release", data) + }) stick.On(joystick.L2Press, func(data interface{}) { fmt.Println("L2Press", data) }) + stick.On(joystick.L2Release, func(data interface{}) { + fmt.Println("L2Release", data) + }) } robot := gobot.NewRobot("joystickBot", diff --git a/examples/joystick_ps4.go b/examples/joystick_ps4.go index 046c7bd0a..c824090d4 100644 --- a/examples/joystick_ps4.go +++ b/examples/joystick_ps4.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, joystick.Dualshock4) work := func() { diff --git a/examples/joystick_ps5.go b/examples/joystick_ps5.go index fe8d1063b..d5cbf631b 100644 --- a/examples/joystick_ps5.go +++ b/examples/joystick_ps5.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, joystick.Dualsense) work := func() { diff --git a/examples/joystick_xbox360.go b/examples/joystick_xbox360.go index 655cb3889..dff41efb3 100644 --- a/examples/joystick_xbox360.go +++ b/examples/joystick_xbox360.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, joystick.Xbox360) work := func() { diff --git a/examples/joystick_xbox360_rock_band_drums.go b/examples/joystick_xbox360_rock_band_drums.go index a83c6993b..05c6bf0a2 100644 --- a/examples/joystick_xbox360_rock_band_drums.go +++ b/examples/joystick_xbox360_rock_band_drums.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, joystick.Xbox360RockBandDrums) work := func() { diff --git a/examples/joystick_xboxone.go b/examples/joystick_xboxone.go index d29737c90..a680529e6 100644 --- a/examples/joystick_xboxone.go +++ b/examples/joystick_xboxone.go @@ -14,7 +14,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") joystick := joystick.NewDriver(joystickAdaptor, joystick.XboxOne) work := func() { diff --git a/examples/minidrone_mambo_ps3.go b/examples/minidrone_mambo_ps3.go index d38e4af6a..b7ccd9b3a 100644 --- a/examples/minidrone_mambo_ps3.go +++ b/examples/minidrone_mambo_ps3.go @@ -43,7 +43,7 @@ var leftX, leftY, rightX, rightY atomic.Value const offset = 32767.0 func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "./platforms/joystick/configs/dualshock3.json", ) diff --git a/examples/minidrone_ps3.go b/examples/minidrone_ps3.go index a09959ec9..7afd96053 100644 --- a/examples/minidrone_ps3.go +++ b/examples/minidrone_ps3.go @@ -43,7 +43,7 @@ var leftX, leftY, rightX, rightY atomic.Value const offset = 32767.0 func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3") droneAdaptor := ble.NewClientAdaptor(os.Args[1]) diff --git a/examples/tello_facetracker.go b/examples/tello_facetracker.go index 1a65dfd65..38cc2bcc6 100644 --- a/examples/tello_facetracker.go +++ b/examples/tello_facetracker.go @@ -74,7 +74,7 @@ var ( flightData *tello.FlightData // joystick - joyAdaptor = joystick.NewAdaptor() + joyAdaptor = joystick.NewAdaptor("0") stick = joystick.NewDriver(joyAdaptor, "dualshock4") leftX, leftY, rightX, rightY atomic.Value ) diff --git a/examples/tello_ps3.go b/examples/tello_ps3.go index 5d4f998d0..fb0e2a7f1 100644 --- a/examples/tello_ps3.go +++ b/examples/tello_ps3.go @@ -37,7 +37,7 @@ var leftX, leftY, rightX, rightY atomic.Value const offset = 32767.0 func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3") drone := tello.NewDriver("8888") diff --git a/go.mod b/go.mod index 044643eea..4338b5512 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module gobot.io/x/gobot/v2 go 1.18 require ( + github.com/0xcafed00d/joystick v1.0.1 github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f github.com/donovanhide/eventsource v0.0.0-20210830082556-c59027999da0 github.com/eclipse/paho.mqtt.golang v1.4.2 @@ -11,9 +12,9 @@ require ( github.com/hybridgroup/go-ardrone v0.0.0-20140402002621-b9750d8d7b78 github.com/hybridgroup/mjpeg v0.0.0-20140228234708-4680f319790e github.com/nats-io/nats.go v1.27.1 + github.com/nsf/termbox-go v1.1.1 github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f github.com/stretchr/testify v1.8.4 - github.com/veandco/go-sdl2 v0.4.35 github.com/warthog618/gpiod v0.8.1 go.bug.st/serial v1.5.0 gocv.io/x/gocv v0.33.0 @@ -34,6 +35,7 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/klauspost/compress v1.16.7 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/muka/go-bluetooth v0.0.0-20221213043340-85dc80edc4e1 // indirect github.com/nats-io/nats-server/v2 v2.7.4 // indirect github.com/nats-io/nkeys v0.4.4 // indirect diff --git a/go.sum b/go.sum index f590e4b61..e77271270 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/0xcafed00d/joystick v1.0.1 h1:r4p2cRp4MHJWu1gArhGtumbkPxmr3tcOUTFqybEhplM= +github.com/0xcafed00d/joystick v1.0.1/go.mod h1:gzszjNgzP6jtCAeSdC9OqPVO5rO7TJuaw4P7eAjNzx8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/bgould/http v0.0.0-20190627042742-d268792bdee7/go.mod h1:BTqvVegvwifopl4KTEDth6Zezs9eR+lCWhvGKvkxJHE= github.com/bmizerany/pat v0.0.0-20210406213842-e4b6760bdd6f h1:gOO/tNZMjjvTKZWpY7YnXC72ULNLErRtp94LountVE8= @@ -52,6 +54,8 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/muka/go-bluetooth v0.0.0-20220830075246-0746e3a1ea53/go.mod h1:dMCjicU6vRBk34dqOmIZm0aod6gUwZXOXzBROqGous0= github.com/muka/go-bluetooth v0.0.0-20221213043340-85dc80edc4e1 h1:BuVRHr4HHJbk1DHyWkArJ7E8J/VA8ncCr/VLnQFazBo= @@ -67,6 +71,8 @@ github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nsf/termbox-go v1.1.1 h1:nksUPLCb73Q++DwbYUBEglYBRPZyoXJdrj5L+TkjyZY= +github.com/nsf/termbox-go v1.1.1/go.mod h1:T0cTdVuOwf7pHQNtfhnEbzHbcNyCEcVU4YPpouCbVxo= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/paypal/gatt v0.0.0-20151011220935-4ae819d591cf/go.mod h1:+AwQL2mK3Pd3S+TUwg0tYQjid0q1txyNUJuuSmz8Kdk= github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys= @@ -100,8 +106,6 @@ github.com/tdakkota/win32metadata v0.1.0/go.mod h1:77e6YvX0LIVW+O81fhWLnXAxxcyu/ github.com/tinygo-org/cbgo v0.0.4 h1:3D76CRYbH03Rudi8sEgs/YO0x3JIMdyq8jlQtk/44fU= github.com/tinygo-org/cbgo v0.0.4/go.mod h1:7+HgWIHd4nbAz0ESjGlJ1/v9LDU1Ox8MGzP9mah/fLk= github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/veandco/go-sdl2 v0.4.35 h1:NohzsfageDWGtCd9nf7Pc3sokMK/MOK+UA2QMJARWzQ= -github.com/veandco/go-sdl2 v0.4.35/go.mod h1:OROqMhHD43nT4/i9crJukyVecjPNYYuCofep6SNiAjY= github.com/warthog618/gpiod v0.8.1 h1:+8iHpHd3fljAd6l4AT8jPbMDQNKdvBIpW/hmLgAcHiM= github.com/warthog618/gpiod v0.8.1/go.mod h1:A7v1hGR2eTsnkN+e9RoAPYgJG9bLJWtwyIIK+pgqC7s= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/platforms/joystick/LICENSE b/platforms/joystick/LICENSE index 257d22e97..f90ffa280 100644 --- a/platforms/joystick/LICENSE +++ b/platforms/joystick/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2018 The Hybrid Group +Copyright (c) 2014-2023 The Hybrid Group Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/platforms/joystick/README.md b/platforms/joystick/README.md index 5e4b9d2cc..9f8f06740 100644 --- a/platforms/joystick/README.md +++ b/platforms/joystick/README.md @@ -1,6 +1,6 @@ # Joystick -You can use Gobot with any USB joystick or game controller that is compatible with [Simple DirectMedia Layer](http://www.libsdl.org/). +You can use Gobot with many USB joysticks and game controllers. Current configurations included: @@ -14,41 +14,24 @@ Current configurations included: ## How to Install -This package requires `sdl2` to be installed on your system +Any platform specific info here... ### macOS -To install `sdl2` on macOS using Homebrew: - -```sh -brew install sdl2 -``` - -To use an XBox360 controller on macOS, you will most likely need to install additional software such as [https://github.com/360Controller/360Controller](https://github.com/360Controller/360Controller). ### Linux (Ubuntu and Raspbian) -Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md) -You must be running a Linux kernel that is v4.14+ in order for the various controller mappings to work as expected. +### Windows -Then you must install the latest SDL2 v2.0.8 or greater: - -```sh -wget https://www.libsdl.org/release/SDL2-2.0.8.tar.gz -tar -zxvf SDL2-2.0.8.tar.gz -cd SDL2-2.0.8/ -./configure && make && sudo make install -``` ## How to Use -Controller configurations are stored in Gobot it, but you can also use external file in JSON format. Take a look at the -`configs` directory for examples. +Controller configurations are stored in Gobot, but you can also use external file in JSON format. Take a look at the `configs` directory for examples. ## How to Connect -Plug your USB joystick or game controller into your USB port. If your device is supported by SDL, you are now ready. +Plug your USB joystick or game controller into your USB port. If your device is supported by your operating system, it might prompt you to install some system drivers. For the Dualshock4, you must pair the device with your computers Bluetooth interface first, before running your Gobot program. @@ -67,7 +50,7 @@ import ( ) func main() { - joystickAdaptor := joystick.NewAdaptor() + joystickAdaptor := joystick.NewAdaptor("0") stick := joystick.NewDriver(joystickAdaptor, "dualshock3", ) @@ -151,25 +134,4 @@ func main() { ## How to Add A New Joystick -In the `bin` directory for this package is a CLI utility program that scans for SDL joystick events, and displays the ID -and value: - -```sh -$ go run ./platforms/joystick/bin/scanner.go -Joystick 0 connected -[6625 ms] Axis: 1 value:-22686 -[6641 ms] Axis: 1 value:-32768 -[6836 ms] Axis: 1 value:-18317 -[6852 ms] Axis: 1 value:0 -[8663 ms] Axis: 3 value:-32768 -[8873 ms] Axis: 3 value:0 -[10183 ms] Axis: 0 value:-24703 -[10183 ms] Axis: 0 value:-32768 -[10313 ms] Axis: 1 value:-3193 -[10329 ms] Axis: 1 value:0 -[10345 ms] Axis: 0 value:0 -``` - -You can use the output from this program to create a JSON file for the various buttons and axes on your joystick/gamepad. -You could also create a file similar to `joystick_dualshock3.go` and submit a pull request with the new configuration so -others can use it as well. +You can create a file similar to `joystick_dualshock3.go` and submit a pull request with the new configuration so others can use it as well. diff --git a/platforms/joystick/bin/scanner.go b/platforms/joystick/bin/scanner.go index 66eff923d..ec74112c9 100644 --- a/platforms/joystick/bin/scanner.go +++ b/platforms/joystick/bin/scanner.go @@ -5,68 +5,108 @@ // Do not build by default. // // Joystick scanner -// Based on original code from Jacky Boen -// https://github.com/veandco/go-sdl2/blob/master/examples/events/events.go - +// Based on original code from +// https://github.com/0xcafed00d/joystick/blob/master/joysticktest/joysticktest.go +// Simple program that displays the state of the specified joystick +// +// go run joysticktest.go 2 +// displays state of joystick id 2 package main import ( "fmt" + "github.com/nsf/termbox-go" + "github.com/0xcafed00d/joystick" "os" - - "github.com/veandco/go-sdl2/sdl" + "strconv" + "time" ) -var joysticks [16]*sdl.Joystick - -func run() int { - var event sdl.Event - var running bool - - sdl.Init(sdl.INIT_JOYSTICK) - defer sdl.Quit() - - sdl.JoystickEventState(sdl.ENABLE) - - running = true - for running { - for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent() { - switch t := event.(type) { - case *sdl.QuitEvent: - running = false - case *sdl.JoyAxisEvent: - fmt.Printf("[%d ms] Axis: %d\tvalue:%d\n", - t.Timestamp, t.Axis, t.Value) - case *sdl.JoyBallEvent: - fmt.Printf("[%d ms] Ball:%d\txrel:%d\tyrel:%d\n", - t.Timestamp, t.Ball, t.XRel, t.YRel) - case *sdl.JoyButtonEvent: - fmt.Printf("[%d ms] Button:%d\tstate:%d\n", - t.Timestamp, t.Button, t.State) - case *sdl.JoyHatEvent: - fmt.Printf("[%d ms] Hat:%d\tvalue:%d\n", - t.Timestamp, t.Hat, t.Value) - case *sdl.JoyDeviceAddedEvent: - joysticks[int(t.Which)] = sdl.JoystickOpen(int(t.Which)) - if joysticks[int(t.Which)] != nil { - fmt.Printf("Joystick %d connected\n", t.Which) - } - case *sdl.JoyDeviceRemovedEvent: - if joystick := joysticks[int(t.Which)]; joystick != nil { - joystick.Close() - } - fmt.Printf("Joystick %d disconnected\n", t.Which) - default: - fmt.Printf("Unknown event\n") - } +func printAt(x, y int, s string) { + for _, r := range s { + termbox.SetCell(x, y, r, termbox.ColorDefault, termbox.ColorDefault) + x++ + } +} + +func readJoystick(js joystick.Joystick) { + jinfo, err := js.Read() + + if err != nil { + printAt(1, 5, "Error: "+err.Error()) + return + } + + printAt(1, 5, "Buttons:") + for button := 0; button < js.ButtonCount(); button++ { + if jinfo.Buttons&(1< 1 { + i, err := strconv.Atoi(os.Args[1]) + if err != nil { + fmt.Println(err) + return + } + jsid = i + } + + js, jserr := joystick.Open(jsid) + + if jserr != nil { + fmt.Println(jserr) + return + } + + err := termbox.Init() + if err != nil { + panic(err) + } + defer termbox.Close() + + eventQueue := make(chan termbox.Event) + go func() { + for { + eventQueue <- termbox.PollEvent() + } + }() + + ticker := time.NewTicker(time.Millisecond * 40) + + for doQuit := false; !doQuit; { + select { + case ev := <-eventQueue: + if ev.Type == termbox.EventKey { + if ev.Ch == 'q' { + doQuit = true + } + } + if ev.Type == termbox.EventResize { + termbox.Flush() + } + + case <-ticker.C: + printAt(1, 0, "-- Press 'q' to Exit --") + printAt(1, 1, fmt.Sprintf("Joystick Name: %s", js.Name())) + printAt(1, 2, fmt.Sprintf(" Axis Count: %d", js.AxisCount())) + printAt(1, 3, fmt.Sprintf(" Button Count: %d", js.ButtonCount())) + readJoystick(js) + termbox.Flush() + } + } } diff --git a/platforms/joystick/configs/dualshock3.json b/platforms/joystick/configs/dualshock3.json index f74f5b963..24195932e 100644 --- a/platforms/joystick/configs/dualshock3.json +++ b/platforms/joystick/configs/dualshock3.json @@ -11,12 +11,20 @@ "id": 1 }, { - "name": "right_x", + "name": "l2", "id": 2 }, { - "name": "right_y", + "name": "right_x", "id": 3 + }, + { + "name": "right_y", + "id": 4 + }, + { + "name": "r2", + "id": 5 } ], "buttons": [ diff --git a/platforms/joystick/doc.go b/platforms/joystick/doc.go index c0f7c8f60..3b5fcd64e 100644 --- a/platforms/joystick/doc.go +++ b/platforms/joystick/doc.go @@ -1,11 +1,9 @@ /* -Package joystick provides the Gobot adaptor and drivers for game controllers that are compatible with SDL. +Package joystick provides the Gobot adaptor and drivers for game controllers and joysticks. Installing: - This package requires `sdl2` to be installed on your system - - Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md) + Please refer to the main [README.md](https://github.com/hybridgroup/gobot/blob/release/README.md) Example: @@ -19,10 +17,8 @@ Example: ) func main() { - joystickAdaptor := joystick.NewAdaptor() - joystick := joystick.NewDriver(joystickAdaptor, - "./platforms/joystick/configs/dualshock3.json", - ) + joystickAdaptor := joystick.NewAdaptor("0") + joystick := joystick.NewDriver(joystickAdaptor, "dualshock3") work := func() { joystick.On(joystick.Event("square_press"), func(data interface{}) { diff --git a/platforms/joystick/joystick_adaptor.go b/platforms/joystick/joystick_adaptor.go index 2d5f93789..fba07f6bd 100644 --- a/platforms/joystick/joystick_adaptor.go +++ b/platforms/joystick/joystick_adaptor.go @@ -1,38 +1,41 @@ package joystick import ( - "errors" + "fmt" + "strconv" "gobot.io/x/gobot/v2" - "github.com/veandco/go-sdl2/sdl" + js "github.com/0xcafed00d/joystick" ) -type joystick interface { - Close() - InstanceID() sdl.JoystickID -} - // Adaptor represents a connection to a joystick type Adaptor struct { name string - joystick joystick + id string + joystick js.Joystick connect func(*Adaptor) error } // NewAdaptor returns a new Joystick Adaptor. -func NewAdaptor() *Adaptor { +// Pass in the ID of the joystick you wish to connect to. +func NewAdaptor(id string) *Adaptor { return &Adaptor{ name: gobot.DefaultName("Joystick"), connect: func(j *Adaptor) error { - if err := sdl.Init(sdl.INIT_JOYSTICK); err != nil { - return err + i, err := strconv.Atoi(id) + if err != nil { + return fmt.Errorf("Invalid joystick ID: %v", err) } - if sdl.NumJoysticks() > 0 { - j.joystick = sdl.JoystickOpen(0) - return nil + + joy, err := js.Open(i) + if err != nil { + return fmt.Errorf("No joystick available: %v", err) } - return errors.New("No joystick available") + + j.id = id + j.joystick = joy + return nil }, } } diff --git a/platforms/joystick/joystick_adaptor_test.go b/platforms/joystick/joystick_adaptor_test.go index 4f11ca06a..3a96a2031 100644 --- a/platforms/joystick/joystick_adaptor_test.go +++ b/platforms/joystick/joystick_adaptor_test.go @@ -1,18 +1,18 @@ package joystick import ( - "errors" "strings" "testing" + "github.com/stretchr/testify/assert" + "gobot.io/x/gobot/v2" - "gobot.io/x/gobot/v2/gobottest" ) var _ gobot.Adaptor = (*Adaptor)(nil) func initTestAdaptor() *Adaptor { - a := NewAdaptor() + a := NewAdaptor("6") a.connect = func(j *Adaptor) (err error) { j.joystick = &testJoystick{} return nil @@ -22,21 +22,22 @@ func initTestAdaptor() *Adaptor { func TestJoystickAdaptorName(t *testing.T) { a := initTestAdaptor() - gobottest.Assert(t, strings.HasPrefix(a.Name(), "Joystick"), true) + assert.True(t, strings.HasPrefix(a.Name(), "Joystick")) a.SetName("NewName") - gobottest.Assert(t, a.Name(), "NewName") + assert.Equal(t, a.Name(), "NewName") } func TestAdaptorConnect(t *testing.T) { a := initTestAdaptor() - gobottest.Assert(t, a.Connect(), nil) + assert.Nil(t, a.Connect()) - a = NewAdaptor() - gobottest.Assert(t, a.Connect(), errors.New("No joystick available")) + a = NewAdaptor("6") + err := a.Connect() + assert.True(t, strings.HasPrefix(err.Error(), "No joystick available")) } func TestAdaptorFinalize(t *testing.T) { a := initTestAdaptor() _ = a.Connect() - gobottest.Assert(t, a.Finalize(), nil) + assert.Nil(t, a.Finalize()) } diff --git a/platforms/joystick/joystick_driver.go b/platforms/joystick/joystick_driver.go index 09634c234..c313f362d 100644 --- a/platforms/joystick/joystick_driver.go +++ b/platforms/joystick/joystick_driver.go @@ -6,7 +6,7 @@ import ( "os" "time" - "github.com/veandco/go-sdl2/sdl" + js "github.com/0xcafed00d/joystick" "gobot.io/x/gobot/v2" ) @@ -41,13 +41,15 @@ const ( // Driver represents a joystick type Driver struct { - name string - interval time.Duration - connection gobot.Connection - configPath string - config joystickConfig - poll func() sdl.Event - halt chan bool + name string + interval time.Duration + connection gobot.Connection + configPath string + config joystickConfig + buttonState map[int]bool + axisState map[int]int + + halt chan bool gobot.Eventer } @@ -57,20 +59,12 @@ type pair struct { ID int `json:"id"` } -// hat is a JSON representation of hat, name and id -type hat struct { - Hat int `json:"hat"` - Name string `json:"name"` - ID int `json:"id"` -} - // joystickConfig is a JSON representation of configuration values type joystickConfig struct { Name string `json:"name"` GUID string `json:"guid"` Axis []pair `json:"axis"` Buttons []pair `json:"buttons"` - Hats []hat `json:"Hats"` } // NewDriver returns a new Driver with a polling interval of @@ -82,13 +76,13 @@ type joystickConfig struct { // time.Duration: Interval at which the Driver is polled for new information func NewDriver(a *Adaptor, config string, v ...time.Duration) *Driver { d := &Driver{ - name: gobot.DefaultName("Joystick"), - connection: a, - Eventer: gobot.NewEventer(), - configPath: config, - poll: func() sdl.Event { - return sdl.PollEvent() - }, + name: gobot.DefaultName("Joystick"), + connection: a, + Eventer: gobot.NewEventer(), + configPath: config, + buttonState: make(map[int]bool), + axisState: make(map[int]int), + interval: 10 * time.Millisecond, halt: make(chan bool), } @@ -125,7 +119,43 @@ func (j *Driver) adaptor() *Adaptor { // [button]_press // [button]_release // [axis] -func (j *Driver) Start() (err error) { +func (j *Driver) Start() error { + if err := j.initConfig(); err != nil { + return err + } + + j.initEvents() + + go func() { + for { + state, err := j.adaptor().joystick.Read() + if err != nil { + j.Publish(j.Event("error"), err) + break + } + + // might just be missing a button definition, so keep going + if err := j.handleButtons(state); err != nil { + j.Publish(j.Event("error"), err) + } + + // might just be missing an axis definition, so keep going + if err := j.handleAxes(state); err != nil { + j.Publish(j.Event("error"), err) + } + + select { + case <-time.After(j.interval): + case <-j.halt: + return + } + } + }() + + return nil +} + +func (j *Driver) initConfig() error { switch j.configPath { case Dualshock3: j.config = dualshock3Config @@ -148,10 +178,14 @@ func (j *Driver) Start() (err error) { default: err := j.loadFile() if err != nil { - return err + return fmt.Errorf("loadfile error: %w", err) } } + return nil +} + +func (j *Driver) initEvents() { for _, value := range j.config.Buttons { j.AddEvent(fmt.Sprintf("%s_press", value.Name)) j.AddEvent(fmt.Sprintf("%s_release", value.Name)) @@ -159,26 +193,6 @@ func (j *Driver) Start() (err error) { for _, value := range j.config.Axis { j.AddEvent(value.Name) } - for _, value := range j.config.Hats { - j.AddEvent(fmt.Sprintf("%s_press", value.Name)) - j.AddEvent(fmt.Sprintf("%s_release", value.Name)) - } - - go func() { - for { - for event := j.poll(); event != nil; event = j.poll() { - if errs := j.handleEvent(event); errs != nil { - j.Publish(j.Event("error"), errs) - } - } - select { - case <-time.After(j.interval): - case <-j.halt: - return - } - } - }() - return } // Halt stops joystick driver @@ -187,48 +201,44 @@ func (j *Driver) Halt() (err error) { return } -var previousHat = "" - -// HandleEvent publishes an specific event according to data received -func (j *Driver) handleEvent(event sdl.Event) error { - switch data := event.(type) { - case *sdl.JoyAxisEvent: - if data.Which == j.adaptor().joystick.InstanceID() { - axis := j.findName(data.Axis, j.config.Axis) - if axis == "" { - return fmt.Errorf("Unknown Axis: %v", data.Axis) +func (j *Driver) handleButtons(state js.State) error { + for button := 0; button < j.adaptor().joystick.ButtonCount(); button++ { + buttonPressed := state.Buttons&(1<