input.go (10445B)
1 // Copyright 2015 Hajime Hoshi 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 //go:build (darwin || freebsd || linux || windows) && !android && !ios 16 // +build darwin freebsd linux windows 17 // +build !android 18 // +build !ios 19 20 //go:generate curl --location --remote-name https://raw.githubusercontent.com/gabomdq/SDL_GameControllerDB/master/gamecontrollerdb.txt 21 //go:generate file2byteslice -package glfw -input=./gamecontrollerdb.txt -output=./gamecontrollerdb.txt.go -var=gamecontrollerdbTxt 22 23 package glfw 24 25 import ( 26 "math" 27 "sync" 28 "unicode" 29 30 "github.com/hajimehoshi/ebiten/v2/internal/driver" 31 "github.com/hajimehoshi/ebiten/v2/internal/gamepaddb" 32 "github.com/hajimehoshi/ebiten/v2/internal/glfw" 33 ) 34 35 type gamepad struct { 36 valid bool 37 guid string 38 name string 39 axisNum int 40 axes [16]float64 41 buttonNum int 42 buttonPressed [256]bool 43 hatsNum int 44 hats [16]int 45 } 46 47 type Input struct { 48 keyPressed map[glfw.Key]bool 49 mouseButtonPressed map[glfw.MouseButton]bool 50 onceCallback sync.Once 51 scrollX float64 52 scrollY float64 53 cursorX int 54 cursorY int 55 gamepads [16]gamepad 56 touches map[driver.TouchID]pos // TODO: Implement this (#417) 57 runeBuffer []rune 58 ui *UserInterface 59 } 60 61 type pos struct { 62 X int 63 Y int 64 } 65 66 func (i *Input) CursorPosition() (x, y int) { 67 if !i.ui.isRunning() { 68 return 0, 0 69 } 70 71 i.ui.m.RLock() 72 defer i.ui.m.RUnlock() 73 return i.cursorX, i.cursorY 74 } 75 76 func (i *Input) AppendGamepadIDs(gamepadIDs []driver.GamepadID) []driver.GamepadID { 77 if !i.ui.isRunning() { 78 return nil 79 } 80 81 i.ui.m.RLock() 82 defer i.ui.m.RUnlock() 83 for id, g := range i.gamepads { 84 if g.valid { 85 gamepadIDs = append(gamepadIDs, driver.GamepadID(id)) 86 } 87 } 88 return gamepadIDs 89 } 90 91 func (i *Input) GamepadSDLID(id driver.GamepadID) string { 92 if !i.ui.isRunning() { 93 return "" 94 } 95 96 i.ui.m.RLock() 97 defer i.ui.m.RUnlock() 98 if len(i.gamepads) <= int(id) { 99 return "" 100 } 101 return i.gamepads[id].guid 102 } 103 104 func (i *Input) GamepadName(id driver.GamepadID) string { 105 if !i.ui.isRunning() { 106 return "" 107 } 108 109 i.ui.m.RLock() 110 defer i.ui.m.RUnlock() 111 if len(i.gamepads) <= int(id) { 112 return "" 113 } 114 return i.gamepads[id].name 115 } 116 117 func (i *Input) GamepadAxisNum(id driver.GamepadID) int { 118 if !i.ui.isRunning() { 119 return 0 120 } 121 122 i.ui.m.RLock() 123 defer i.ui.m.RUnlock() 124 if len(i.gamepads) <= int(id) { 125 return 0 126 } 127 return i.gamepads[id].axisNum 128 } 129 130 func (i *Input) GamepadAxisValue(id driver.GamepadID, axis int) float64 { 131 if !i.ui.isRunning() { 132 return 0 133 } 134 135 i.ui.m.RLock() 136 defer i.ui.m.RUnlock() 137 if len(i.gamepads) <= int(id) { 138 return 0 139 } 140 return i.gamepads[id].axes[axis] 141 } 142 143 func (i *Input) GamepadButtonNum(id driver.GamepadID) int { 144 if !i.ui.isRunning() { 145 return 0 146 } 147 148 i.ui.m.RLock() 149 defer i.ui.m.RUnlock() 150 if len(i.gamepads) <= int(id) { 151 return 0 152 } 153 return i.gamepads[id].buttonNum 154 } 155 156 func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool { 157 if !i.ui.isRunning() { 158 return false 159 } 160 161 i.ui.m.RLock() 162 defer i.ui.m.RUnlock() 163 if len(i.gamepads) <= int(id) { 164 return false 165 } 166 return i.gamepads[id].buttonPressed[button] 167 } 168 169 func (i *Input) AppendTouchIDs(touchIDs []driver.TouchID) []driver.TouchID { 170 if !i.ui.isRunning() { 171 return nil 172 } 173 174 i.ui.m.RLock() 175 defer i.ui.m.RUnlock() 176 for id := range i.touches { 177 touchIDs = append(touchIDs, id) 178 } 179 return touchIDs 180 } 181 182 func (i *Input) TouchPosition(id driver.TouchID) (x, y int) { 183 if !i.ui.isRunning() { 184 return 0, 0 185 } 186 187 i.ui.m.RLock() 188 defer i.ui.m.RUnlock() 189 for tid, pos := range i.touches { 190 if id == tid { 191 return pos.X, pos.Y 192 } 193 } 194 return 0, 0 195 } 196 197 func (i *Input) AppendInputChars(runes []rune) []rune { 198 if !i.ui.isRunning() { 199 return nil 200 } 201 202 i.ui.m.RLock() 203 defer i.ui.m.RUnlock() 204 return append(runes, i.runeBuffer...) 205 } 206 207 func (i *Input) resetForFrame() { 208 if !i.ui.isRunning() { 209 return 210 } 211 212 i.ui.m.Lock() 213 defer i.ui.m.Unlock() 214 i.runeBuffer = i.runeBuffer[:0] 215 i.scrollX, i.scrollY = 0, 0 216 } 217 218 func (i *Input) IsKeyPressed(key driver.Key) bool { 219 if !i.ui.isRunning() { 220 return false 221 } 222 223 i.ui.m.Lock() 224 defer i.ui.m.Unlock() 225 if i.keyPressed == nil { 226 i.keyPressed = map[glfw.Key]bool{} 227 } 228 gk, ok := driverKeyToGLFWKey[key] 229 return ok && i.keyPressed[gk] 230 } 231 232 func (i *Input) IsMouseButtonPressed(button driver.MouseButton) bool { 233 if !i.ui.isRunning() { 234 return false 235 } 236 237 i.ui.m.Lock() 238 defer i.ui.m.Unlock() 239 if i.mouseButtonPressed == nil { 240 i.mouseButtonPressed = map[glfw.MouseButton]bool{} 241 } 242 for gb, b := range glfwMouseButtonToMouseButton { 243 if b != button { 244 continue 245 } 246 if i.mouseButtonPressed[gb] { 247 return true 248 } 249 } 250 return false 251 } 252 253 func (i *Input) Wheel() (xoff, yoff float64) { 254 if !i.ui.isRunning() { 255 return 0, 0 256 } 257 258 i.ui.m.RLock() 259 defer i.ui.m.RUnlock() 260 return i.scrollX, i.scrollY 261 } 262 263 var glfwMouseButtonToMouseButton = map[glfw.MouseButton]driver.MouseButton{ 264 glfw.MouseButtonLeft: driver.MouseButtonLeft, 265 glfw.MouseButtonRight: driver.MouseButtonRight, 266 glfw.MouseButtonMiddle: driver.MouseButtonMiddle, 267 } 268 269 // update must be called from the main thread. 270 func (i *Input) update(window *glfw.Window, context driver.UIContext) { 271 i.ui.m.Lock() 272 defer i.ui.m.Unlock() 273 274 i.onceCallback.Do(func() { 275 window.SetCharModsCallback(glfw.ToCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) { 276 // As this function is called from GLFW callbacks, the current thread is main. 277 if !unicode.IsPrint(char) { 278 return 279 } 280 281 i.ui.m.Lock() 282 defer i.ui.m.Unlock() 283 i.runeBuffer = append(i.runeBuffer, char) 284 })) 285 window.SetScrollCallback(glfw.ToScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) { 286 // As this function is called from GLFW callbacks, the current thread is main. 287 i.ui.m.Lock() 288 defer i.ui.m.Unlock() 289 i.scrollX = xoff 290 i.scrollY = yoff 291 })) 292 }) 293 if i.keyPressed == nil { 294 i.keyPressed = map[glfw.Key]bool{} 295 } 296 for gk := range glfwKeyToDriverKey { 297 i.keyPressed[gk] = window.GetKey(gk) == glfw.Press 298 } 299 if i.mouseButtonPressed == nil { 300 i.mouseButtonPressed = map[glfw.MouseButton]bool{} 301 } 302 for gb := range glfwMouseButtonToMouseButton { 303 i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press 304 } 305 cx, cy := window.GetCursorPos() 306 // TODO: This is tricky. Rename the function? 307 s := i.ui.deviceScaleFactor() 308 cx = i.ui.fromGLFWPixel(cx) 309 cy = i.ui.fromGLFWPixel(cy) 310 cx, cy = context.AdjustPosition(cx, cy, s) 311 312 // AdjustPosition can return NaN at the initialization. 313 if !math.IsNaN(cx) && !math.IsNaN(cy) { 314 i.cursorX, i.cursorY = int(cx), int(cy) 315 } 316 317 for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ { 318 i.gamepads[id].valid = false 319 if !id.Present() { 320 continue 321 } 322 323 buttons := id.GetButtons() 324 325 // A gamepad can be detected even though there are not. Apparently, some special devices are 326 // recognized as gamepads by GLFW. In this case, the number of the 'buttons' can exceeds the 327 // maximum. Skip such devices as a tentative solution (#1173). 328 if len(buttons) > driver.GamepadButtonNum { 329 continue 330 } 331 332 i.gamepads[id].valid = true 333 334 i.gamepads[id].buttonNum = len(buttons) 335 for b := 0; b < len(i.gamepads[id].buttonPressed); b++ { 336 if len(buttons) <= b { 337 i.gamepads[id].buttonPressed[b] = false 338 continue 339 } 340 i.gamepads[id].buttonPressed[b] = glfw.Action(buttons[b]) == glfw.Press 341 } 342 343 axes32 := id.GetAxes() 344 i.gamepads[id].axisNum = len(axes32) 345 for a := 0; a < len(i.gamepads[id].axes); a++ { 346 if len(axes32) <= a { 347 i.gamepads[id].axes[a] = 0 348 continue 349 } 350 i.gamepads[id].axes[a] = float64(axes32[a]) 351 } 352 353 hats := id.GetHats() 354 i.gamepads[id].hatsNum = len(hats) 355 for h := 0; h < len(i.gamepads[id].hats); h++ { 356 if len(hats) <= h { 357 i.gamepads[id].hats[h] = 0 358 continue 359 } 360 i.gamepads[id].hats[h] = int(hats[h]) 361 } 362 363 // Note that GLFW's gamepad GUID follows SDL's GUID. 364 i.gamepads[id].guid = id.GetGUID() 365 i.gamepads[id].name = id.GetName() 366 } 367 } 368 369 func (i *Input) IsStandardGamepadLayoutAvailable(id driver.GamepadID) bool { 370 i.ui.m.Lock() 371 defer i.ui.m.Unlock() 372 373 if len(i.gamepads) <= int(id) { 374 return false 375 } 376 g := i.gamepads[int(id)] 377 return gamepaddb.HasStandardLayoutMapping(g.guid) 378 } 379 380 func (i *Input) StandardGamepadAxisValue(id driver.GamepadID, axis driver.StandardGamepadAxis) float64 { 381 i.ui.m.Lock() 382 defer i.ui.m.Unlock() 383 384 if len(i.gamepads) <= int(id) { 385 return 0 386 } 387 g := i.gamepads[int(id)] 388 return gamepaddb.AxisValue(g.guid, axis, gamepadState{&g}) 389 } 390 391 func (i *Input) StandardGamepadButtonValue(id driver.GamepadID, button driver.StandardGamepadButton) float64 { 392 i.ui.m.Lock() 393 defer i.ui.m.Unlock() 394 395 if len(i.gamepads) <= int(id) { 396 return 0 397 } 398 g := i.gamepads[int(id)] 399 return gamepaddb.ButtonValue(g.guid, button, gamepadState{&g}) 400 } 401 402 func (i *Input) IsStandardGamepadButtonPressed(id driver.GamepadID, button driver.StandardGamepadButton) bool { 403 i.ui.m.Lock() 404 defer i.ui.m.Unlock() 405 406 if len(i.gamepads) <= int(id) { 407 return false 408 } 409 g := i.gamepads[int(id)] 410 return gamepaddb.IsButtonPressed(g.guid, button, gamepadState{&g}) 411 } 412 413 func init() { 414 // Confirm that all the hat state values are the same. 415 if gamepaddb.HatUp != glfw.HatUp { 416 panic("glfw: gamepaddb.HatUp must equal to glfw.HatUp but not") 417 } 418 if gamepaddb.HatRight != glfw.HatRight { 419 panic("glfw: gamepaddb.HatRight must equal to glfw.HatRight but not") 420 } 421 if gamepaddb.HatDown != glfw.HatDown { 422 panic("glfw: gamepaddb.HatDown must equal to glfw.HatDown but not") 423 } 424 if gamepaddb.HatLeft != glfw.HatLeft { 425 panic("glfw: gamepaddb.HatLeft must equal to glfw.HatLeft but not") 426 } 427 } 428 429 type gamepadState struct { 430 g *gamepad 431 } 432 433 func (s gamepadState) Axis(index int) float64 { 434 return s.g.axes[index] 435 } 436 437 func (s gamepadState) Button(index int) bool { 438 return s.g.buttonPressed[index] 439 } 440 441 func (s gamepadState) Hat(index int) int { 442 return s.g.hats[index] 443 }