input_js.go (8745B)
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 package js 16 17 import ( 18 "encoding/hex" 19 "syscall/js" 20 "unicode" 21 22 "github.com/hajimehoshi/ebiten/v2/internal/driver" 23 "github.com/hajimehoshi/ebiten/v2/internal/jsutil" 24 ) 25 26 type pos struct { 27 X int 28 Y int 29 } 30 31 type gamePad struct { 32 valid bool 33 name string 34 axisNum int 35 axes [16]float64 36 buttonNum int 37 buttonPressed [256]bool 38 } 39 40 type Input struct { 41 keyPressed map[string]bool 42 keyPressedEdge map[int]bool 43 mouseButtonPressed map[int]bool 44 cursorX int 45 cursorY int 46 wheelX float64 47 wheelY float64 48 gamepads [16]gamePad 49 touches map[driver.TouchID]pos 50 runeBuffer []rune 51 ui *UserInterface 52 } 53 54 func (i *Input) CursorPosition() (x, y int) { 55 xf, yf := i.ui.context.AdjustPosition(float64(i.cursorX), float64(i.cursorY)) 56 return int(xf), int(yf) 57 } 58 59 func (i *Input) GamepadSDLID(id driver.GamepadID) string { 60 // This emulates the implementation of EMSCRIPTEN_JoystickGetDeviceGUID. 61 // https://hg.libsdl.org/SDL/file/bc90ce38f1e2/src/joystick/emscripten/SDL_sysjoystick.c#l385 62 if len(i.gamepads) <= int(id) { 63 return "" 64 } 65 var sdlid [16]byte 66 copy(sdlid[:], []byte(i.gamepads[id].name)) 67 return hex.EncodeToString(sdlid[:]) 68 } 69 70 // GamepadName returns a string containing some information about the controller. 71 // A PS2 controller returned "810-3-USB Gamepad" on Firefox 72 // A Xbox 360 controller returned "xinput" on Firefox and "Xbox 360 Controller (XInput STANDARD GAMEPAD)" on Chrome 73 func (i *Input) GamepadName(id driver.GamepadID) string { 74 if len(i.gamepads) <= int(id) { 75 return "" 76 } 77 return i.gamepads[id].name 78 } 79 80 func (i *Input) GamepadIDs() []driver.GamepadID { 81 if len(i.gamepads) == 0 { 82 return nil 83 } 84 var r []driver.GamepadID 85 for id, g := range i.gamepads { 86 if g.valid { 87 r = append(r, driver.GamepadID(id)) 88 } 89 } 90 return r 91 } 92 93 func (i *Input) GamepadAxisNum(id driver.GamepadID) int { 94 if len(i.gamepads) <= int(id) { 95 return 0 96 } 97 return i.gamepads[id].axisNum 98 } 99 100 func (i *Input) GamepadAxis(id driver.GamepadID, axis int) float64 { 101 if len(i.gamepads) <= int(id) { 102 return 0 103 } 104 return i.gamepads[id].axes[axis] 105 } 106 107 func (i *Input) GamepadButtonNum(id driver.GamepadID) int { 108 if len(i.gamepads) <= int(id) { 109 return 0 110 } 111 return i.gamepads[id].buttonNum 112 } 113 114 func (i *Input) IsGamepadButtonPressed(id driver.GamepadID, button driver.GamepadButton) bool { 115 if len(i.gamepads) <= int(id) { 116 return false 117 } 118 return i.gamepads[id].buttonPressed[button] 119 } 120 121 func (i *Input) TouchIDs() []driver.TouchID { 122 if len(i.touches) == 0 { 123 return nil 124 } 125 126 var ids []driver.TouchID 127 for id := range i.touches { 128 ids = append(ids, id) 129 } 130 return ids 131 } 132 133 func (i *Input) TouchPosition(id driver.TouchID) (x, y int) { 134 for tid, pos := range i.touches { 135 if id == tid { 136 x, y := i.ui.context.AdjustPosition(float64(pos.X), float64(pos.Y)) 137 return int(x), int(y) 138 } 139 } 140 return 0, 0 141 } 142 143 func (i *Input) RuneBuffer() []rune { 144 return i.runeBuffer 145 } 146 147 func (i *Input) resetForFrame() { 148 i.runeBuffer = nil 149 i.wheelX = 0 150 i.wheelY = 0 151 } 152 153 func (i *Input) IsKeyPressed(key driver.Key) bool { 154 if i.keyPressed != nil { 155 if i.keyPressed[driverKeyToJSKey[key]] { 156 return true 157 } 158 } 159 if i.keyPressedEdge != nil { 160 for c, k := range edgeKeyCodeToDriverKey { 161 if k != key { 162 continue 163 } 164 if i.keyPressedEdge[c] { 165 return true 166 } 167 } 168 } 169 return false 170 } 171 172 var codeToMouseButton = map[int]driver.MouseButton{ 173 0: driver.MouseButtonLeft, 174 1: driver.MouseButtonMiddle, 175 2: driver.MouseButtonRight, 176 } 177 178 func (i *Input) IsMouseButtonPressed(button driver.MouseButton) bool { 179 if i.mouseButtonPressed == nil { 180 i.mouseButtonPressed = map[int]bool{} 181 } 182 for c, b := range codeToMouseButton { 183 if b != button { 184 continue 185 } 186 if i.mouseButtonPressed[c] { 187 return true 188 } 189 } 190 return false 191 } 192 193 func (i *Input) Wheel() (xoff, yoff float64) { 194 return i.wheelX, i.wheelY 195 } 196 197 func (i *Input) keyDown(code string) { 198 if i.keyPressed == nil { 199 i.keyPressed = map[string]bool{} 200 } 201 i.keyPressed[code] = true 202 } 203 204 func (i *Input) keyUp(code string) { 205 if i.keyPressed == nil { 206 i.keyPressed = map[string]bool{} 207 } 208 i.keyPressed[code] = false 209 } 210 211 func (i *Input) keyDownEdge(code int) { 212 if i.keyPressedEdge == nil { 213 i.keyPressedEdge = map[int]bool{} 214 } 215 i.keyPressedEdge[code] = true 216 } 217 218 func (i *Input) keyUpEdge(code int) { 219 if i.keyPressedEdge == nil { 220 i.keyPressedEdge = map[int]bool{} 221 } 222 i.keyPressedEdge[code] = false 223 } 224 225 func (i *Input) mouseDown(code int) { 226 if i.mouseButtonPressed == nil { 227 i.mouseButtonPressed = map[int]bool{} 228 } 229 i.mouseButtonPressed[code] = true 230 } 231 232 func (i *Input) mouseUp(code int) { 233 if i.mouseButtonPressed == nil { 234 i.mouseButtonPressed = map[int]bool{} 235 } 236 i.mouseButtonPressed[code] = false 237 } 238 239 func (i *Input) setMouseCursor(x, y int) { 240 i.cursorX, i.cursorY = x, y 241 } 242 243 func (i *Input) UpdateGamepads() { 244 nav := js.Global().Get("navigator") 245 if jsutil.Equal(nav.Get("getGamepads"), js.Undefined()) { 246 return 247 } 248 gamepads := nav.Call("getGamepads") 249 l := gamepads.Get("length").Int() 250 for id := 0; id < l; id++ { 251 i.gamepads[id].valid = false 252 gamepad := gamepads.Index(id) 253 if jsutil.Equal(gamepad, js.Undefined()) || jsutil.Equal(gamepad, js.Null()) { 254 continue 255 } 256 i.gamepads[id].valid = true 257 i.gamepads[id].name = gamepad.Get("id").String() 258 259 axes := gamepad.Get("axes") 260 axesNum := axes.Get("length").Int() 261 i.gamepads[id].axisNum = axesNum 262 for a := 0; a < len(i.gamepads[id].axes); a++ { 263 if axesNum <= a { 264 i.gamepads[id].axes[a] = 0 265 continue 266 } 267 i.gamepads[id].axes[a] = axes.Index(a).Float() 268 } 269 270 buttons := gamepad.Get("buttons") 271 buttonsNum := buttons.Get("length").Int() 272 i.gamepads[id].buttonNum = buttonsNum 273 for b := 0; b < len(i.gamepads[id].buttonPressed); b++ { 274 if buttonsNum <= b { 275 i.gamepads[id].buttonPressed[b] = false 276 continue 277 } 278 i.gamepads[id].buttonPressed[b] = buttons.Index(b).Get("pressed").Bool() 279 } 280 } 281 } 282 283 func (i *Input) Update(e js.Value) { 284 switch e.Get("type").String() { 285 case "keydown": 286 c := e.Get("code") 287 if jsutil.Equal(c, js.Undefined()) { 288 code := e.Get("keyCode").Int() 289 if edgeKeyCodeToDriverKey[code] == driver.KeyUp || 290 edgeKeyCodeToDriverKey[code] == driver.KeyDown || 291 edgeKeyCodeToDriverKey[code] == driver.KeyLeft || 292 edgeKeyCodeToDriverKey[code] == driver.KeyRight || 293 edgeKeyCodeToDriverKey[code] == driver.KeyBackspace || 294 edgeKeyCodeToDriverKey[code] == driver.KeyTab { 295 e.Call("preventDefault") 296 } 297 i.keyDownEdge(code) 298 return 299 } 300 cs := c.String() 301 if cs == driverKeyToJSKey[driver.KeyUp] || 302 cs == driverKeyToJSKey[driver.KeyDown] || 303 cs == driverKeyToJSKey[driver.KeyLeft] || 304 cs == driverKeyToJSKey[driver.KeyRight] || 305 cs == driverKeyToJSKey[driver.KeyBackspace] || 306 cs == driverKeyToJSKey[driver.KeyTab] { 307 e.Call("preventDefault") 308 } 309 i.keyDown(cs) 310 case "keypress": 311 if r := rune(e.Get("charCode").Int()); unicode.IsPrint(r) { 312 i.runeBuffer = append(i.runeBuffer, r) 313 } 314 case "keyup": 315 if jsutil.Equal(e.Get("code"), js.Undefined()) { 316 // Assume that UA is Edge. 317 code := e.Get("keyCode").Int() 318 i.keyUpEdge(code) 319 return 320 } 321 code := e.Get("code").String() 322 i.keyUp(code) 323 case "mousedown": 324 button := e.Get("button").Int() 325 i.mouseDown(button) 326 i.setMouseCursorFromEvent(e) 327 case "mouseup": 328 button := e.Get("button").Int() 329 i.mouseUp(button) 330 i.setMouseCursorFromEvent(e) 331 case "mousemove": 332 i.setMouseCursorFromEvent(e) 333 case "wheel": 334 // TODO: What if e.deltaMode is not DOM_DELTA_PIXEL? 335 i.wheelX = -e.Get("deltaX").Float() 336 i.wheelY = -e.Get("deltaY").Float() 337 case "touchstart", "touchend", "touchmove": 338 i.updateTouches(e) 339 } 340 } 341 342 func (i *Input) setMouseCursorFromEvent(e js.Value) { 343 x, y := e.Get("clientX").Int(), e.Get("clientY").Int() 344 i.setMouseCursor(x, y) 345 } 346 347 func (i *Input) updateTouches(e js.Value) { 348 j := e.Get("targetTouches") 349 ts := map[driver.TouchID]pos{} 350 for i := 0; i < j.Length(); i++ { 351 jj := j.Call("item", i) 352 id := driver.TouchID(jj.Get("identifier").Int()) 353 ts[id] = pos{ 354 X: jj.Get("clientX").Int(), 355 Y: jj.Get("clientY").Int(), 356 } 357 } 358 i.touches = ts 359 }