2015-01-02 07:20:05 +01:00
|
|
|
// Copyright 2015 Hajime Hoshi
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
// +build js
|
|
|
|
|
2019-04-07 11:55:56 +02:00
|
|
|
package js
|
2015-01-02 07:20:05 +01:00
|
|
|
|
2015-01-12 06:36:13 +01:00
|
|
|
import (
|
2020-02-04 15:59:57 +01:00
|
|
|
"encoding/hex"
|
2019-04-30 19:15:28 +02:00
|
|
|
"syscall/js"
|
2018-04-01 16:20:45 +02:00
|
|
|
"unicode"
|
|
|
|
|
2019-03-30 15:53:27 +01:00
|
|
|
"github.com/hajimehoshi/ebiten/internal/driver"
|
2019-12-18 16:03:35 +01:00
|
|
|
"github.com/hajimehoshi/ebiten/internal/jsutil"
|
2015-01-12 06:36:13 +01:00
|
|
|
)
|
|
|
|
|
2019-04-07 11:55:56 +02:00
|
|
|
type pos struct {
|
|
|
|
X int
|
|
|
|
Y int
|
|
|
|
}
|
2017-04-28 16:45:01 +02:00
|
|
|
|
2019-04-07 11:55:56 +02:00
|
|
|
type gamePad struct {
|
|
|
|
valid bool
|
2020-01-09 14:11:32 +01:00
|
|
|
name string
|
2019-04-07 11:55:56 +02:00
|
|
|
axisNum int
|
|
|
|
axes [16]float64
|
|
|
|
buttonNum int
|
|
|
|
buttonPressed [256]bool
|
|
|
|
}
|
2017-04-28 16:45:01 +02:00
|
|
|
|
|
|
|
type Input struct {
|
2017-04-13 20:02:38 +02:00
|
|
|
keyPressed map[string]bool
|
2017-11-09 16:35:46 +01:00
|
|
|
keyPressedEdge map[int]bool
|
2017-04-11 04:56:05 +02:00
|
|
|
mouseButtonPressed map[int]bool
|
|
|
|
cursorX int
|
|
|
|
cursorY int
|
2018-09-30 10:08:16 +02:00
|
|
|
wheelX float64
|
|
|
|
wheelY float64
|
2017-04-11 04:56:05 +02:00
|
|
|
gamepads [16]gamePad
|
2019-03-31 09:28:50 +02:00
|
|
|
touches map[int]pos
|
2017-08-14 21:11:51 +02:00
|
|
|
runeBuffer []rune
|
2019-04-07 12:27:30 +02:00
|
|
|
ui *UserInterface
|
2019-04-07 11:55:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) CursorPosition() (x, y int) {
|
2019-12-09 18:37:10 +01:00
|
|
|
xf, yf := i.ui.context.AdjustPosition(float64(i.cursorX), float64(i.cursorY))
|
|
|
|
return int(xf), int(yf)
|
2019-04-07 11:55:56 +02:00
|
|
|
}
|
|
|
|
|
2020-01-11 07:50:37 +01:00
|
|
|
func (i *Input) GamepadSDLID(id int) string {
|
2020-02-04 15:59:57 +01:00
|
|
|
// This emulates the implementation of EMSCRIPTEN_JoystickGetDeviceGUID.
|
|
|
|
// https://hg.libsdl.org/SDL/file/bc90ce38f1e2/src/joystick/emscripten/SDL_sysjoystick.c#l385
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
var sdlid [16]byte
|
|
|
|
copy(sdlid[:], []byte(i.gamepads[id].name))
|
|
|
|
return hex.EncodeToString(sdlid[:])
|
2020-01-09 14:11:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GamepadName returns a string containing some information about the controller.
|
|
|
|
// A PS2 controller returned "810-3-USB Gamepad" on Firefox
|
|
|
|
// A Xbox 360 controller returned "xinput" on Firefox and "Xbox 360 Controller (XInput STANDARD GAMEPAD)" on Chrome
|
|
|
|
func (i *Input) GamepadName(id int) string {
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return ""
|
|
|
|
}
|
|
|
|
return i.gamepads[id].name
|
|
|
|
}
|
|
|
|
|
2019-04-07 11:55:56 +02:00
|
|
|
func (i *Input) GamepadIDs() []int {
|
|
|
|
if len(i.gamepads) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
r := []int{}
|
|
|
|
for id, g := range i.gamepads {
|
|
|
|
if g.valid {
|
|
|
|
r = append(r, id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) GamepadAxisNum(id int) int {
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return i.gamepads[id].axisNum
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) GamepadAxis(id int, axis int) float64 {
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return i.gamepads[id].axes[axis]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) GamepadButtonNum(id int) int {
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
return i.gamepads[id].buttonNum
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) IsGamepadButtonPressed(id int, button driver.GamepadButton) bool {
|
|
|
|
if len(i.gamepads) <= id {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
return i.gamepads[id].buttonPressed[button]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) TouchIDs() []int {
|
|
|
|
if len(i.touches) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var ids []int
|
|
|
|
for id := range i.touches {
|
|
|
|
ids = append(ids, id)
|
|
|
|
}
|
|
|
|
return ids
|
|
|
|
}
|
|
|
|
|
|
|
|
func (i *Input) TouchPosition(id int) (x, y int) {
|
|
|
|
for tid, pos := range i.touches {
|
|
|
|
if id == tid {
|
2019-12-09 18:37:10 +01:00
|
|
|
x, y := i.ui.context.AdjustPosition(float64(pos.X), float64(pos.Y))
|
|
|
|
return int(x), int(y)
|
2019-04-07 11:55:56 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0, 0
|
2017-04-11 04:56:05 +02:00
|
|
|
}
|
|
|
|
|
2017-08-14 21:11:51 +02:00
|
|
|
func (i *Input) RuneBuffer() []rune {
|
|
|
|
return i.runeBuffer
|
|
|
|
}
|
|
|
|
|
2019-03-31 11:47:06 +02:00
|
|
|
func (i *Input) ResetForFrame() {
|
2018-04-01 16:20:45 +02:00
|
|
|
i.runeBuffer = nil
|
2018-09-30 10:08:16 +02:00
|
|
|
i.wheelX = 0
|
|
|
|
i.wheelY = 0
|
|
|
|
}
|
|
|
|
|
2019-03-30 15:53:27 +01:00
|
|
|
func (i *Input) IsKeyPressed(key driver.Key) bool {
|
2017-04-13 20:02:38 +02:00
|
|
|
if i.keyPressed != nil {
|
2020-02-19 02:42:42 +01:00
|
|
|
if i.keyPressed[driverKeyToJSKey[key]] {
|
2019-09-01 17:25:39 +02:00
|
|
|
return true
|
2017-04-11 04:56:05 +02:00
|
|
|
}
|
2017-04-13 20:02:38 +02:00
|
|
|
}
|
2017-11-09 16:35:46 +01:00
|
|
|
if i.keyPressedEdge != nil {
|
2020-02-19 02:42:42 +01:00
|
|
|
for c, k := range edgeKeyCodeToDriverKey {
|
2017-04-13 20:02:38 +02:00
|
|
|
if k != key {
|
|
|
|
continue
|
|
|
|
}
|
2017-11-09 16:35:46 +01:00
|
|
|
if i.keyPressedEdge[c] {
|
2017-04-13 20:02:38 +02:00
|
|
|
return true
|
|
|
|
}
|
2017-04-11 04:56:05 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2019-03-30 15:53:27 +01:00
|
|
|
var codeToMouseButton = map[int]driver.MouseButton{
|
|
|
|
0: driver.MouseButtonLeft,
|
|
|
|
1: driver.MouseButtonMiddle,
|
|
|
|
2: driver.MouseButtonRight,
|
2017-04-11 04:56:05 +02:00
|
|
|
}
|
|
|
|
|
2019-03-30 15:53:27 +01:00
|
|
|
func (i *Input) IsMouseButtonPressed(button driver.MouseButton) bool {
|
2017-04-11 04:56:05 +02:00
|
|
|
if i.mouseButtonPressed == nil {
|
|
|
|
i.mouseButtonPressed = map[int]bool{}
|
|
|
|
}
|
|
|
|
for c, b := range codeToMouseButton {
|
|
|
|
if b != button {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if i.mouseButtonPressed[c] {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-09-30 10:01:45 +02:00
|
|
|
func (i *Input) Wheel() (xoff, yoff float64) {
|
2018-09-30 10:08:16 +02:00
|
|
|
return i.wheelX, i.wheelY
|
2018-06-17 17:38:30 +02:00
|
|
|
}
|
|
|
|
|
2017-04-28 16:45:01 +02:00
|
|
|
func (i *Input) keyDown(code string) {
|
2017-04-11 04:56:05 +02:00
|
|
|
if i.keyPressed == nil {
|
2017-04-13 20:02:38 +02:00
|
|
|
i.keyPressed = map[string]bool{}
|
2015-01-07 15:02:58 +01:00
|
|
|
}
|
2017-04-11 04:56:05 +02:00
|
|
|
i.keyPressed[code] = true
|
2015-01-02 16:52:49 +01:00
|
|
|
}
|
|
|
|
|
2017-04-28 16:45:01 +02:00
|
|
|
func (i *Input) keyUp(code string) {
|
2017-04-11 04:56:05 +02:00
|
|
|
if i.keyPressed == nil {
|
2017-04-13 20:02:38 +02:00
|
|
|
i.keyPressed = map[string]bool{}
|
2015-01-07 15:02:58 +01:00
|
|
|
}
|
2017-04-11 04:56:05 +02:00
|
|
|
i.keyPressed[code] = false
|
2015-01-02 07:20:05 +01:00
|
|
|
}
|
2015-01-06 15:41:03 +01:00
|
|
|
|
2017-11-09 16:35:46 +01:00
|
|
|
func (i *Input) keyDownEdge(code int) {
|
|
|
|
if i.keyPressedEdge == nil {
|
|
|
|
i.keyPressedEdge = map[int]bool{}
|
2017-04-13 20:02:38 +02:00
|
|
|
}
|
2017-11-09 16:35:46 +01:00
|
|
|
i.keyPressedEdge[code] = true
|
2017-04-13 20:02:38 +02:00
|
|
|
}
|
|
|
|
|
2017-11-09 16:35:46 +01:00
|
|
|
func (i *Input) keyUpEdge(code int) {
|
|
|
|
if i.keyPressedEdge == nil {
|
|
|
|
i.keyPressedEdge = map[int]bool{}
|
2017-04-13 20:02:38 +02:00
|
|
|
}
|
2017-11-09 16:35:46 +01:00
|
|
|
i.keyPressedEdge[code] = false
|
2017-04-13 20:02:38 +02:00
|
|
|
}
|
|
|
|
|
2017-04-28 16:45:01 +02:00
|
|
|
func (i *Input) mouseDown(code int) {
|
2017-04-11 04:56:05 +02:00
|
|
|
if i.mouseButtonPressed == nil {
|
|
|
|
i.mouseButtonPressed = map[int]bool{}
|
2015-01-06 15:41:03 +01:00
|
|
|
}
|
2017-04-11 04:56:05 +02:00
|
|
|
i.mouseButtonPressed[code] = true
|
2015-01-06 15:41:03 +01:00
|
|
|
}
|
|
|
|
|
2017-04-28 16:45:01 +02:00
|
|
|
func (i *Input) mouseUp(code int) {
|
2017-04-11 04:56:05 +02:00
|
|
|
if i.mouseButtonPressed == nil {
|
|
|
|
i.mouseButtonPressed = map[int]bool{}
|
2015-01-06 15:41:03 +01:00
|
|
|
}
|
2017-04-11 04:56:05 +02:00
|
|
|
i.mouseButtonPressed[code] = false
|
2015-01-06 15:41:03 +01:00
|
|
|
}
|
|
|
|
|
2017-04-28 16:45:01 +02:00
|
|
|
func (i *Input) setMouseCursor(x, y int) {
|
2015-01-06 15:41:03 +01:00
|
|
|
i.cursorX, i.cursorY = x, y
|
|
|
|
}
|
2015-01-12 06:36:13 +01:00
|
|
|
|
2019-04-06 16:03:21 +02:00
|
|
|
func (i *Input) UpdateGamepads() {
|
2018-06-29 17:02:15 +02:00
|
|
|
nav := js.Global().Get("navigator")
|
2019-12-18 16:03:35 +01:00
|
|
|
if jsutil.Equal(nav.Get("getGamepads"), js.Undefined()) {
|
2015-01-12 06:55:28 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
gamepads := nav.Call("getGamepads")
|
2015-01-12 06:36:13 +01:00
|
|
|
l := gamepads.Get("length").Int()
|
|
|
|
for id := 0; id < l; id++ {
|
2017-10-25 19:59:06 +02:00
|
|
|
i.gamepads[id].valid = false
|
2015-01-12 06:36:13 +01:00
|
|
|
gamepad := gamepads.Index(id)
|
2019-12-18 16:03:35 +01:00
|
|
|
if jsutil.Equal(gamepad, js.Undefined()) || jsutil.Equal(gamepad, js.Null()) {
|
2015-01-12 06:36:13 +01:00
|
|
|
continue
|
|
|
|
}
|
2017-10-25 19:59:06 +02:00
|
|
|
i.gamepads[id].valid = true
|
2020-01-09 14:11:32 +01:00
|
|
|
i.gamepads[id].name = gamepad.Get("id").String()
|
2015-01-12 06:36:13 +01:00
|
|
|
|
|
|
|
axes := gamepad.Get("axes")
|
|
|
|
axesNum := axes.Get("length").Int()
|
|
|
|
i.gamepads[id].axisNum = axesNum
|
|
|
|
for a := 0; a < len(i.gamepads[id].axes); a++ {
|
|
|
|
if axesNum <= a {
|
|
|
|
i.gamepads[id].axes[a] = 0
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
i.gamepads[id].axes[a] = axes.Index(a).Float()
|
|
|
|
}
|
|
|
|
|
|
|
|
buttons := gamepad.Get("buttons")
|
|
|
|
buttonsNum := buttons.Get("length").Int()
|
|
|
|
i.gamepads[id].buttonNum = buttonsNum
|
|
|
|
for b := 0; b < len(i.gamepads[id].buttonPressed); b++ {
|
|
|
|
if buttonsNum <= b {
|
|
|
|
i.gamepads[id].buttonPressed[b] = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
i.gamepads[id].buttonPressed[b] = buttons.Index(b).Get("pressed").Bool()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-05-26 18:31:30 +02:00
|
|
|
|
2019-04-06 16:32:19 +02:00
|
|
|
func (i *Input) Update(e js.Value) {
|
|
|
|
switch e.Get("type").String() {
|
|
|
|
case "keydown":
|
|
|
|
c := e.Get("code")
|
2019-12-18 16:03:35 +01:00
|
|
|
if jsutil.Equal(c, js.Undefined()) {
|
2019-04-06 16:32:19 +02:00
|
|
|
code := e.Get("keyCode").Int()
|
2020-02-19 02:42:42 +01:00
|
|
|
if edgeKeyCodeToDriverKey[code] == driver.KeyUp ||
|
|
|
|
edgeKeyCodeToDriverKey[code] == driver.KeyDown ||
|
|
|
|
edgeKeyCodeToDriverKey[code] == driver.KeyLeft ||
|
|
|
|
edgeKeyCodeToDriverKey[code] == driver.KeyRight ||
|
|
|
|
edgeKeyCodeToDriverKey[code] == driver.KeyBackspace ||
|
|
|
|
edgeKeyCodeToDriverKey[code] == driver.KeyTab {
|
2019-04-06 16:32:19 +02:00
|
|
|
e.Call("preventDefault")
|
|
|
|
}
|
|
|
|
i.keyDownEdge(code)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
cs := c.String()
|
2020-02-19 02:42:42 +01:00
|
|
|
if cs == driverKeyToJSKey[driver.KeyUp] ||
|
|
|
|
cs == driverKeyToJSKey[driver.KeyDown] ||
|
|
|
|
cs == driverKeyToJSKey[driver.KeyLeft] ||
|
|
|
|
cs == driverKeyToJSKey[driver.KeyRight] ||
|
|
|
|
cs == driverKeyToJSKey[driver.KeyBackspace] ||
|
|
|
|
cs == driverKeyToJSKey[driver.KeyTab] {
|
2018-04-01 16:20:45 +02:00
|
|
|
e.Call("preventDefault")
|
|
|
|
}
|
2019-04-06 16:32:19 +02:00
|
|
|
i.keyDown(cs)
|
|
|
|
case "keypress":
|
|
|
|
if r := rune(e.Get("charCode").Int()); unicode.IsPrint(r) {
|
|
|
|
i.runeBuffer = append(i.runeBuffer, r)
|
|
|
|
}
|
|
|
|
case "keyup":
|
2019-12-18 16:03:35 +01:00
|
|
|
if jsutil.Equal(e.Get("code"), js.Undefined()) {
|
2019-04-06 16:32:19 +02:00
|
|
|
// Assume that UA is Edge.
|
|
|
|
code := e.Get("keyCode").Int()
|
|
|
|
i.keyUpEdge(code)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
code := e.Get("code").String()
|
|
|
|
i.keyUp(code)
|
|
|
|
case "mousedown":
|
|
|
|
button := e.Get("button").Int()
|
|
|
|
i.mouseDown(button)
|
|
|
|
i.setMouseCursorFromEvent(e)
|
|
|
|
case "mouseup":
|
|
|
|
button := e.Get("button").Int()
|
|
|
|
i.mouseUp(button)
|
|
|
|
i.setMouseCursorFromEvent(e)
|
|
|
|
case "mousemove":
|
|
|
|
i.setMouseCursorFromEvent(e)
|
|
|
|
case "wheel":
|
|
|
|
// TODO: What if e.deltaMode is not DOM_DELTA_PIXEL?
|
|
|
|
i.wheelX = -e.Get("deltaX").Float()
|
|
|
|
i.wheelY = -e.Get("deltaY").Float()
|
|
|
|
case "touchstart", "touchend", "touchmove":
|
|
|
|
i.updateTouches(e)
|
2018-04-01 16:20:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-06 16:32:19 +02:00
|
|
|
func (i *Input) setMouseCursorFromEvent(e js.Value) {
|
2018-04-01 16:20:45 +02:00
|
|
|
x, y := e.Get("clientX").Int(), e.Get("clientY").Int()
|
2019-04-06 16:32:19 +02:00
|
|
|
i.setMouseCursor(x, y)
|
2018-04-01 16:20:45 +02:00
|
|
|
}
|
|
|
|
|
2018-05-26 18:59:35 +02:00
|
|
|
func (i *Input) updateTouches(e js.Value) {
|
2018-04-01 16:20:45 +02:00
|
|
|
j := e.Get("targetTouches")
|
2019-03-31 09:28:50 +02:00
|
|
|
ts := map[int]pos{}
|
2019-04-14 19:56:46 +02:00
|
|
|
for i := 0; i < j.Length(); i++ {
|
2018-04-01 16:20:45 +02:00
|
|
|
jj := j.Call("item", i)
|
|
|
|
id := jj.Get("identifier").Int()
|
2019-03-31 09:28:50 +02:00
|
|
|
ts[id] = pos{
|
|
|
|
X: jj.Get("clientX").Int(),
|
|
|
|
Y: jj.Get("clientY").Int(),
|
2018-04-01 16:20:45 +02:00
|
|
|
}
|
|
|
|
}
|
2018-04-01 18:19:42 +02:00
|
|
|
i.touches = ts
|
2018-04-01 16:20:45 +02:00
|
|
|
}
|