github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/game/gamepad/xinput_windows.go (about) 1 package gamepad 2 3 import ( 4 "math" 5 "syscall" 6 "unsafe" 7 ) 8 9 type ID byte 10 11 type All [ControllerCount]State 12 13 func (all *All) Update() (firsterr error) { 14 for i := range all { 15 all[i].ID = ID(i) 16 err := all[i].Update() 17 if err != nil && firsterr == nil { 18 firsterr = err 19 } 20 } 21 return 22 } 23 24 type State struct { 25 ID ID 26 Connected bool 27 28 Packet uint32 29 Raw struct { 30 Buttons Button 31 LeftTrigger uint8 32 RightTrigger uint8 33 ThumbLX int16 34 ThumbLY int16 35 ThumbRX int16 36 ThumbRY int16 37 } 38 } 39 40 func (state *State) Pressed(button Button) bool { return state.Raw.Buttons&button != 0 } 41 42 func (state *State) Update() error { return Get(state.ID, state) } 43 44 type Thumb struct{ X, Y, Magnitude float32 } 45 46 func (state *State) RectDPad() (thumb Thumb) { 47 if state.Pressed(DPadUp) { 48 thumb.Y += 1 49 } 50 if state.Pressed(DPadDown) { 51 thumb.Y -= 1 52 } 53 if state.Pressed(DPadLeft) { 54 thumb.X -= 1 55 } 56 if state.Pressed(DPadRight) { 57 thumb.X += 1 58 } 59 if thumb.X != 0 || thumb.Y != 0 { 60 thumb.Magnitude = 1 61 } 62 return 63 } 64 65 func (state *State) RoundDPad() (thumb Thumb) { 66 thumb = state.RectDPad() 67 if thumb.X != 0 && thumb.Y != 0 { 68 thumb.X *= isqrt2 69 thumb.Y *= isqrt2 70 } 71 return 72 } 73 74 func round16(rx, ry, deadzone int16) (thumb Thumb) { 75 //TODO: use sqrt32 76 fx, fy := float64(rx), float64(ry) 77 thumb.Magnitude = float32(math.Sqrt(fx*fx + fy*fy)) 78 79 thumb.X = float32(rx) / thumb.Magnitude 80 thumb.Y = float32(ry) / thumb.Magnitude 81 82 if thumb.Magnitude > float32(deadzone) { 83 if thumb.Magnitude > 32767 { 84 thumb.Magnitude = 32767 85 } 86 thumb.Magnitude = (thumb.Magnitude - float32(deadzone)) / float32(32767-deadzone) 87 } else { 88 thumb.Magnitude = 0 89 } 90 91 thumb.X *= thumb.Magnitude 92 thumb.Y *= thumb.Magnitude 93 94 return 95 } 96 97 func (state *State) RoundLeft() Thumb { 98 return round16(state.Raw.ThumbLX, state.Raw.ThumbLY, LeftThumbDeadZone) 99 } 100 101 func (state *State) RoundRight() Thumb { 102 return round16(state.Raw.ThumbRX, state.Raw.ThumbRY, RightThumbDeadZone) 103 } 104 105 func linear16(v, deadzone int16) float32 { 106 if v < -deadzone { 107 return float32(v+deadzone) / float32(32767-deadzone) 108 } 109 if v > deadzone { 110 return float32(v-deadzone) / float32(32767-deadzone) 111 } 112 return 0 113 } 114 115 func rect16(rx, ry, deadzone int16) (thumb Thumb) { 116 thumb.X = linear16(rx, deadzone) 117 thumb.Y = linear16(ry, deadzone) 118 if thumb.X != 0 && thumb.Y != 0 { 119 thumb.Magnitude = 1 120 } 121 return 122 } 123 124 func (state *State) RectLeft() Thumb { 125 return rect16(state.Raw.ThumbLX, state.Raw.ThumbLY, LeftThumbDeadZone) 126 } 127 128 func (state *State) RectRight() Thumb { 129 return rect16(state.Raw.ThumbRX, state.Raw.ThumbRY, RightThumbDeadZone) 130 } 131 132 func (state *State) Vibrate(left, right uint16) { 133 if !state.Connected { 134 return 135 } 136 Vibrate(state.ID, &Vibration{left, right}) 137 } 138 139 type Vibration struct { 140 LeftMotor uint16 141 RightMotor uint16 142 } 143 144 const ( 145 ControllerCount = ID(4) 146 TriggerThreshold = 30 147 LeftThumbDeadZone = 7849 148 RightThumbDeadZone = 8689 149 150 sqrt2 = 1.4142135623730950488 151 isqrt2 = 1 / sqrt2 152 ) 153 154 type Button uint16 155 156 const ( 157 DPadUp Button = 0x0001 158 DPadDown = 0x0002 159 DPadLeft = 0x0004 160 DPadRight = 0x0008 161 162 Start Button = 0x0010 163 Back = 0x0020 164 165 LeftThumb Button = 0x0040 166 RightThumb = 0x0080 167 168 LeftShoulder Button = 0x0100 169 RightShoulder = 0x0200 170 171 ButtonA Button = 0x1000 172 ButtonB = 0x2000 173 ButtonX = 0x4000 174 ButtonY = 0x8000 175 ) 176 177 // Get retrieves the latest state of the controller. 178 func Get(id ID, state *State) error { 179 r, _, _ := procGetState.Call(uintptr(id), uintptr(unsafe.Pointer(&state.Packet))) 180 state.ID = id 181 state.Connected = r == 0 182 if r == 0 { 183 return nil 184 } 185 return syscall.Errno(r) 186 } 187 188 func Vibrate(id ID, vibration *Vibration) error { 189 r, _, _ := procSetState.Call(uintptr(id), uintptr(unsafe.Pointer(vibration))) 190 if r == 0 { 191 return nil 192 } 193 return syscall.Errno(r) 194 } 195 196 var ( 197 procGetState *syscall.Proc 198 procSetState *syscall.Proc 199 ) 200 201 func init() { 202 dll, err := syscall.LoadDLL("xinput1_4.dll") 203 defer func() { 204 if err != nil { 205 panic(err) 206 } 207 }() 208 209 if err != nil { 210 dll, err = syscall.LoadDLL("xinput1_3.dll") 211 if err != nil { 212 dll, err = syscall.LoadDLL("xinput9_1_0.dll") 213 return 214 } 215 } 216 217 procGetState = dll.MustFindProc("XInputGetState") 218 procSetState = dll.MustFindProc("XInputSetState") 219 }