input.go (8310B)
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 // +build darwin freebsd linux windows 16 // +build !android 17 // +build !ios 18 19 package glfw 20 21 import ( 22 "sync" 23 "unicode" 24 25 "github.com/hajimehoshi/ebiten/v2/internal/driver" 26 "github.com/hajimehoshi/ebiten/v2/internal/glfw" 27 ) 28 29 type gamePad struct { 30 valid bool 31 guid string 32 name string 33 axisNum int 34 axes [16]float64 35 buttonNum int 36 buttonPressed [256]bool 37 } 38 39 type Input struct { 40 keyPressed map[glfw.Key]bool 41 mouseButtonPressed map[glfw.MouseButton]bool 42 onceCallback sync.Once 43 scrollX float64 44 scrollY float64 45 cursorX int 46 cursorY int 47 gamepads [16]gamePad 48 touches map[driver.TouchID]pos // TODO: Implement this (#417) 49 runeBuffer []rune 50 ui *UserInterface 51 } 52 53 type pos struct { 54 X int 55 Y int 56 } 57 58 func (i *Input) CursorPosition() (x, y int) { 59 if !i.ui.isRunning() { 60 return 0, 0 61 } 62 var cx, cy int 63 _ = i.ui.t.Call(func() error { 64 cx, cy = i.cursorX, i.cursorY 65 return nil 66 }) 67 return cx, cy 68 } 69 70 func (i *Input) GamepadIDs() []driver.GamepadID { 71 if !i.ui.isRunning() { 72 return nil 73 } 74 var r []driver.GamepadID 75 _ = i.ui.t.Call(func() error { 76 for id, g := range i.gamepads { 77 if g.valid { 78 r = append(r, driver.GamepadID(id)) 79 } 80 } 81 return nil 82 }) 83 return r 84 } 85 86 func (i *Input) GamepadSDLID(id driver.GamepadID) string { 87 if !i.ui.isRunning() { 88 return "" 89 } 90 var r string 91 _ = i.ui.t.Call(func() error { 92 if len(i.gamepads) <= int(id) { 93 return nil 94 } 95 r = i.gamepads[id].guid 96 return nil 97 }) 98 return r 99 } 100 101 func (i *Input) GamepadName(id driver.GamepadID) string { 102 if !i.ui.isRunning() { 103 return "" 104 } 105 var r string 106 _ = i.ui.t.Call(func() error { 107 if len(i.gamepads) <= int(id) { 108 return nil 109 } 110 r = i.gamepads[id].name 111 return nil 112 }) 113 return r 114 } 115 116 func (i *Input) GamepadAxisNum(id driver.GamepadID) int { 117 if !i.ui.isRunning() { 118 return 0 119 } 120 var r int 121 _ = i.ui.t.Call(func() error { 122 if len(i.gamepads) <= int(id) { 123 return nil 124 } 125 r = i.gamepads[id].axisNum 126 return nil 127 }) 128 return r 129 } 130 131 func (i *Input) GamepadAxis(id driver.GamepadID, axis int) float64 { 132 if !i.ui.isRunning() { 133 return 0 134 } 135 var r float64 136 _ = i.ui.t.Call(func() error { 137 if len(i.gamepads) <= int(id) { 138 return nil 139 } 140 r = i.gamepads[id].axes[axis] 141 return nil 142 }) 143 return r 144 } 145 146 func (i *Input) GamepadButtonNum(id driver.GamepadID) int { 147 if !i.ui.isRunning() { 148 return 0 149 } 150 var r int 151 _ = i.ui.t.Call(func() error { 152 if len(i.gamepads) <= int(id) { 153 return nil 154 } 155 r = i.gamepads[id].buttonNum 156 return nil 157 }) 158 return r 159 } 160 161 func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool { 162 if !i.ui.isRunning() { 163 return false 164 } 165 var r bool 166 _ = i.ui.t.Call(func() error { 167 if len(i.gamepads) <= int(id) { 168 return nil 169 } 170 r = i.gamepads[id].buttonPressed[button] 171 return nil 172 }) 173 return r 174 } 175 176 func (i *Input) TouchIDs() []driver.TouchID { 177 if !i.ui.isRunning() { 178 return nil 179 } 180 var ids []driver.TouchID 181 _ = i.ui.t.Call(func() error { 182 if len(i.touches) == 0 { 183 return nil 184 } 185 for id := range i.touches { 186 ids = append(ids, id) 187 } 188 return nil 189 }) 190 return ids 191 } 192 193 func (i *Input) TouchPosition(id driver.TouchID) (x, y int) { 194 if !i.ui.isRunning() { 195 return 0, 0 196 } 197 var found bool 198 var p pos 199 _ = i.ui.t.Call(func() error { 200 for tid, pos := range i.touches { 201 if id == tid { 202 p = pos 203 found = true 204 break 205 } 206 } 207 return nil 208 }) 209 if !found { 210 return 0, 0 211 } 212 return p.X, p.Y 213 } 214 215 func (i *Input) RuneBuffer() []rune { 216 if !i.ui.isRunning() { 217 return nil 218 } 219 var r []rune 220 _ = i.ui.t.Call(func() error { 221 r = i.runeBuffer 222 return nil 223 }) 224 return r 225 } 226 227 func (i *Input) resetForFrame() { 228 if !i.ui.isRunning() { 229 return 230 } 231 _ = i.ui.t.Call(func() error { 232 i.runeBuffer = i.runeBuffer[:0] 233 i.scrollX, i.scrollY = 0, 0 234 return nil 235 }) 236 } 237 238 func (i *Input) IsKeyPressed(key driver.Key) bool { 239 if !i.ui.isRunning() { 240 return false 241 } 242 var r bool 243 _ = i.ui.t.Call(func() error { 244 if i.keyPressed == nil { 245 i.keyPressed = map[glfw.Key]bool{} 246 } 247 gk, ok := driverKeyToGLFWKey[key] 248 if ok && i.keyPressed[gk] { 249 r = true 250 return nil 251 } 252 return nil 253 }) 254 return r 255 } 256 257 func (i *Input) IsMouseButtonPressed(button driver.MouseButton) bool { 258 if !i.ui.isRunning() { 259 return false 260 } 261 var r bool 262 _ = i.ui.t.Call(func() error { 263 if i.mouseButtonPressed == nil { 264 i.mouseButtonPressed = map[glfw.MouseButton]bool{} 265 } 266 for gb, b := range glfwMouseButtonToMouseButton { 267 if b != button { 268 continue 269 } 270 if i.mouseButtonPressed[gb] { 271 r = true 272 return nil 273 } 274 } 275 return nil 276 }) 277 return r 278 } 279 280 func (i *Input) Wheel() (xoff, yoff float64) { 281 if !i.ui.isRunning() { 282 return 0, 0 283 } 284 _ = i.ui.t.Call(func() error { 285 xoff, yoff = i.scrollX, i.scrollY 286 return nil 287 }) 288 return 289 } 290 291 var glfwMouseButtonToMouseButton = map[glfw.MouseButton]driver.MouseButton{ 292 glfw.MouseButtonLeft: driver.MouseButtonLeft, 293 glfw.MouseButtonRight: driver.MouseButtonRight, 294 glfw.MouseButtonMiddle: driver.MouseButtonMiddle, 295 } 296 297 func (i *Input) appendRuneBuffer(char rune) { 298 // As this function is called from GLFW callbacks, the current thread is main. 299 if !unicode.IsPrint(char) { 300 return 301 } 302 i.runeBuffer = append(i.runeBuffer, char) 303 } 304 305 func (i *Input) setWheel(xoff, yoff float64) { 306 // As this function is called from GLFW callbacks, the current thread is main. 307 i.scrollX = xoff 308 i.scrollY = yoff 309 } 310 311 func (i *Input) update(window *glfw.Window, context driver.UIContext) { 312 var cx, cy float64 313 _ = i.ui.t.Call(func() error { 314 i.onceCallback.Do(func() { 315 window.SetCharModsCallback(func(w *glfw.Window, char rune, mods glfw.ModifierKey) { 316 i.appendRuneBuffer(char) 317 }) 318 window.SetScrollCallback(func(w *glfw.Window, xoff float64, yoff float64) { 319 i.setWheel(xoff, yoff) 320 }) 321 }) 322 if i.keyPressed == nil { 323 i.keyPressed = map[glfw.Key]bool{} 324 } 325 for gk := range glfwKeyToDriverKey { 326 i.keyPressed[gk] = window.GetKey(gk) == glfw.Press 327 } 328 if i.mouseButtonPressed == nil { 329 i.mouseButtonPressed = map[glfw.MouseButton]bool{} 330 } 331 for gb := range glfwMouseButtonToMouseButton { 332 i.mouseButtonPressed[gb] = window.GetMouseButton(gb) == glfw.Press 333 } 334 cx, cy = window.GetCursorPos() 335 // TODO: This is tricky. Rename the function? 336 cx = i.ui.fromGLFWMonitorPixel(cx) 337 cy = i.ui.fromGLFWMonitorPixel(cy) 338 return nil 339 }) 340 341 cx, cy = context.AdjustPosition(cx, cy) 342 343 _ = i.ui.t.Call(func() error { 344 i.cursorX, i.cursorY = int(cx), int(cy) 345 346 for id := glfw.Joystick(0); id < glfw.Joystick(len(i.gamepads)); id++ { 347 i.gamepads[id].valid = false 348 if !id.Present() { 349 continue 350 } 351 352 buttons := id.GetButtons() 353 354 // A gamepad can be detected even though there are not. Apparently, some special devices are 355 // recognized as gamepads by GLFW. In this case, the number of the 'buttons' can exceeds the 356 // maximum. Skip such devices as a tentative solution (#1173). 357 if len(buttons) > driver.GamepadButtonNum { 358 continue 359 } 360 361 i.gamepads[id].valid = true 362 363 i.gamepads[id].buttonNum = len(buttons) 364 for b := 0; b < len(i.gamepads[id].buttonPressed); b++ { 365 if len(buttons) <= b { 366 i.gamepads[id].buttonPressed[b] = false 367 continue 368 } 369 i.gamepads[id].buttonPressed[b] = glfw.Action(buttons[b]) == glfw.Press 370 } 371 372 axes32 := id.GetAxes() 373 i.gamepads[id].axisNum = len(axes32) 374 for a := 0; a < len(i.gamepads[id].axes); a++ { 375 if len(axes32) <= a { 376 i.gamepads[id].axes[a] = 0 377 continue 378 } 379 i.gamepads[id].axes[a] = float64(axes32[a]) 380 } 381 382 // Note that GLFW's gamepad GUID follows SDL's GUID. 383 i.gamepads[id].guid = id.GetGUID() 384 i.gamepads[id].name = id.GetName() 385 } 386 return nil 387 }) 388 }