ebiten/mobile/ebitenmobileview/input_android.go
divVerent ede787c228
mobile/ebitenmobileview: make runes a parameter. (#2683)
This prevents duplicate input characters due to concurrent touch events.

Closes #2682
2023-06-24 13:40:29 +09:00

211 lines
7.2 KiB
Go

// Copyright 2016 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.
package ebitenmobileview
import (
"encoding/hex"
"hash/crc32"
"unicode"
"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
// https://developer.android.com/reference/android/view/KeyEvent
const (
keycodeButtonA = 0x00000060
keycodeButtonB = 0x00000061
keycodeButtonC = 0x00000062
keycodeButtonX = 0x00000063
keycodeButtonY = 0x00000064
keycodeButtonZ = 0x00000065
keycodeButtonL1 = 0x00000066
keycodeButtonR1 = 0x00000067
keycodeButtonL2 = 0x00000068
keycodeButtonR2 = 0x00000069
keycodeButtonThumbl = 0x0000006a
keycodeButtonThumbr = 0x0000006b
keycodeButtonStart = 0x0000006c
keycodeButtonSelect = 0x0000006d
keycodeButtonMode = 0x0000006e
keycodeButton1 = 0x000000bc
keycodeButton2 = 0x000000bd
keycodeButton3 = 0x000000be
keycodeButton4 = 0x000000bf
keycodeButton5 = 0x000000c0
keycodeButton6 = 0x000000c1
keycodeButton7 = 0x000000c2
keycodeButton8 = 0x000000c3
keycodeButton9 = 0x000000c4
keycodeButton10 = 0x000000c5
keycodeButton11 = 0x000000c6
keycodeButton12 = 0x000000c7
keycodeButton13 = 0x000000c8
keycodeButton14 = 0x000000c9
keycodeButton15 = 0x000000ca
keycodeButton16 = 0x000000cb
keycodeDpadUp = 0x00000013
keycodeDpadDown = 0x00000014
keycodeDpadLeft = 0x00000015
keycodeDpadRight = 0x00000016
keycodeDpadCenter = 0x00000017
keycodeBack = 0x00000004
keycodeMenu = 0x00000052
)
// https://developer.android.com/reference/android/view/InputDevice
const (
sourceKeyboard = 0x00000101
sourceGamepad = 0x00000401
sourceJoystick = 0x01000010
)
// See https://github.com/libsdl-org/SDL/blob/47f2373dc13b66c48bf4024fcdab53cd0bdd59bb/src/joystick/android/SDL_sysjoystick.c#L71-L172
// TODO: This exceeds gamepad.SDLControllerButtonMax. Is that OK?
var androidKeyToSDL = map[int]int{
keycodeButtonA: gamepaddb.SDLControllerButtonA,
keycodeButtonB: gamepaddb.SDLControllerButtonB,
keycodeButtonX: gamepaddb.SDLControllerButtonX,
keycodeButtonY: gamepaddb.SDLControllerButtonY,
keycodeButtonL1: gamepaddb.SDLControllerButtonLeftShoulder,
keycodeButtonR1: gamepaddb.SDLControllerButtonRightShoulder,
keycodeButtonThumbl: gamepaddb.SDLControllerButtonLeftStick,
keycodeButtonThumbr: gamepaddb.SDLControllerButtonRightStick,
keycodeMenu: gamepaddb.SDLControllerButtonStart,
keycodeButtonStart: gamepaddb.SDLControllerButtonStart,
keycodeBack: gamepaddb.SDLControllerButtonBack,
keycodeButtonSelect: gamepaddb.SDLControllerButtonBack,
keycodeButtonMode: gamepaddb.SDLControllerButtonGuide,
keycodeButtonL2: 15,
keycodeButtonR2: 16,
keycodeButtonC: 17,
keycodeButtonZ: 18,
keycodeDpadUp: gamepaddb.SDLControllerButtonDpadUp,
keycodeDpadDown: gamepaddb.SDLControllerButtonDpadDown,
keycodeDpadLeft: gamepaddb.SDLControllerButtonDpadLeft,
keycodeDpadRight: gamepaddb.SDLControllerButtonDpadRight,
keycodeDpadCenter: gamepaddb.SDLControllerButtonA,
keycodeButton1: 20,
keycodeButton2: 21,
keycodeButton3: 22,
keycodeButton4: 23,
keycodeButton5: 24,
keycodeButton6: 25,
keycodeButton7: 26,
keycodeButton8: 27,
keycodeButton9: 28,
keycodeButton10: 29,
keycodeButton11: 30,
keycodeButton12: 31,
keycodeButton13: 32,
keycodeButton14: 33,
keycodeButton15: 34,
keycodeButton16: 35,
}
func UpdateTouchesOnAndroid(action int, id int, x, y int) {
switch action {
case 0x00, 0x05, 0x02: // ACTION_DOWN, ACTION_POINTER_DOWN, ACTION_MOVE
touches[ui.TouchID(id)] = position{x, y}
updateInput(nil)
case 0x01, 0x06: // ACTION_UP, ACTION_POINTER_UP
delete(touches, ui.TouchID(id))
updateInput(nil)
}
}
func OnKeyDownOnAndroid(keyCode int, unicodeChar int, source int, deviceID int) {
switch {
case source&sourceGamepad == sourceGamepad:
// A gamepad can be detected as a keyboard. Detect the device as a gamepad first.
if button, ok := androidKeyToSDL[keyCode]; ok {
gamepad.UpdateAndroidGamepadButton(deviceID, gamepad.Button(button), true)
}
case source&sourceJoystick == sourceJoystick:
// DPAD keys can come here, but they are also treated as an axis at a motion event. Ignore them.
case source&sourceKeyboard == sourceKeyboard:
if key, ok := androidKeyToUIKey[keyCode]; ok {
keys[key] = struct{}{}
var runes []rune
if r := rune(unicodeChar); r != 0 && unicode.IsPrint(r) {
runes = []rune{r}
}
updateInput(runes)
}
}
}
func OnKeyUpOnAndroid(keyCode int, source int, deviceID int) {
switch {
case source&sourceGamepad == sourceGamepad:
// A gamepad can be detected as a keyboard. Detect the device as a gamepad first.
if button, ok := androidKeyToSDL[keyCode]; ok {
gamepad.UpdateAndroidGamepadButton(deviceID, gamepad.Button(button), false)
}
case source&sourceJoystick == sourceJoystick:
// DPAD keys can come here, but they are also treated as an axis at a motion event. Ignore them.
case source&sourceKeyboard == sourceKeyboard:
if key, ok := androidKeyToUIKey[keyCode]; ok {
delete(keys, key)
updateInput(nil)
}
}
}
func OnGamepadAxisChanged(deviceID int, axisID int, value float32) {
gamepad.UpdateAndroidGamepadAxis(deviceID, axisID, float64(value))
}
func OnGamepadHatChanged(deviceID int, hatID int, xValue, yValue int) {
gamepad.UpdateAndroidGamepadHat(deviceID, hatID, xValue, yValue)
}
func OnGamepadAdded(deviceID int, name string, axisCount int, hatCount int, descriptor string, vendorID int, productID int, buttonMask int, axisMask int) {
// This emulates the implementation of Android_AddJoystick.
// https://github.com/libsdl-org/SDL/blob/0e9560aea22818884921e5e5064953257bfe7fa7/src/joystick/android/SDL_sysjoystick.c#L386
const SDL_HARDWARE_BUS_BLUETOOTH = 0x05
var sdlid [16]byte
sdlid[0] = byte(SDL_HARDWARE_BUS_BLUETOOTH)
sdlid[1] = byte(SDL_HARDWARE_BUS_BLUETOOTH >> 8)
if vendorID != 0 && productID != 0 {
sdlid[4] = byte(vendorID)
sdlid[5] = byte(vendorID >> 8)
sdlid[8] = byte(productID)
sdlid[9] = byte(productID >> 8)
} else {
crc := crc32.ChecksumIEEE(([]byte)(descriptor))
copy(sdlid[4:8], ([]byte)(descriptor))
sdlid[8] = byte(crc)
sdlid[9] = byte(crc >> 8)
sdlid[10] = byte(crc >> 16)
sdlid[11] = byte(crc >> 24)
}
sdlid[12] = byte(buttonMask)
sdlid[13] = byte(buttonMask >> 8)
sdlid[14] = byte(axisMask)
sdlid[15] = byte(axisMask >> 8)
gamepad.AddAndroidGamepad(deviceID, name, hex.EncodeToString(sdlid[:]), axisCount, hatCount)
}
func OnInputDeviceRemoved(deviceID int) {
gamepad.RemoveAndroidGamepad(deviceID)
}