internal/gamepaddb: enable the database for Android

Before this fix, the button and axis IDs are from the OS. These
didn't match with the SDL game controller databaes unfortunately.

This fix changes the assignments of the buttons and the axes to match
with the database.

Closes #2312
This commit is contained in:
Hajime Hoshi 2022-09-08 12:29:02 +09:00
parent 6b1502ee71
commit 47558d20c5
6 changed files with 273 additions and 348 deletions

View File

@ -354,6 +354,11 @@ const viewJava = `// Code generated by ebitenmobile. DO NOT EDIT.
package {{.JavaPkg}}.{{.PrefixLower}};
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Handler;
@ -371,6 +376,32 @@ import android.view.WindowManager;
import {{.JavaPkg}}.ebitenmobileview.Ebitenmobileview;
public class EbitenView extends ViewGroup implements InputManager.InputDeviceListener {
static class Gamepad {
public int deviceId;
public ArrayList<InputDevice.MotionRange> axes;
public ArrayList<InputDevice.MotionRange> hats;
}
// See https://github.com/libsdl-org/SDL/blob/2df2da11f627299c6e05b7e0aff407c915043372/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java#L154-L173
static class RangeComparator implements Comparator<InputDevice.MotionRange> {
@Override
public int compare(InputDevice.MotionRange arg0, InputDevice.MotionRange arg1) {
int arg0Axis = arg0.getAxis();
int arg1Axis = arg1.getAxis();
if (arg0Axis == MotionEvent.AXIS_GAS) {
arg0Axis = MotionEvent.AXIS_BRAKE;
} else if (arg0Axis == MotionEvent.AXIS_BRAKE) {
arg0Axis = MotionEvent.AXIS_GAS;
}
if (arg1Axis == MotionEvent.AXIS_GAS) {
arg1Axis = MotionEvent.AXIS_BRAKE;
} else if (arg1Axis == MotionEvent.AXIS_BRAKE) {
arg1Axis = MotionEvent.AXIS_GAS;
}
return arg0Axis - arg1Axis;
}
}
private static double pxToDp(double x) {
return x / Ebitenmobileview.deviceScale();
}
@ -386,6 +417,8 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
}
private void initialize(Context context) {
this.gamepads = new ArrayList<Gamepad>();
this.ebitenSurfaceView = new EbitenSurfaceView(getContext());
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
addView(this.ebitenSurfaceView, params);
@ -433,75 +466,14 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
return true;
}
// The order must be the same as mobile/ebitenmobileview/input_android.go.
static int[] gamepadButtons = {
KeyEvent.KEYCODE_BUTTON_A,
KeyEvent.KEYCODE_BUTTON_B,
KeyEvent.KEYCODE_BUTTON_C,
KeyEvent.KEYCODE_BUTTON_X,
KeyEvent.KEYCODE_BUTTON_Y,
KeyEvent.KEYCODE_BUTTON_Z,
KeyEvent.KEYCODE_BUTTON_L1,
KeyEvent.KEYCODE_BUTTON_R1,
KeyEvent.KEYCODE_BUTTON_L2,
KeyEvent.KEYCODE_BUTTON_R2,
KeyEvent.KEYCODE_BUTTON_THUMBL,
KeyEvent.KEYCODE_BUTTON_THUMBR,
KeyEvent.KEYCODE_BUTTON_START,
KeyEvent.KEYCODE_BUTTON_SELECT,
KeyEvent.KEYCODE_BUTTON_MODE,
KeyEvent.KEYCODE_BUTTON_1,
KeyEvent.KEYCODE_BUTTON_2,
KeyEvent.KEYCODE_BUTTON_3,
KeyEvent.KEYCODE_BUTTON_4,
KeyEvent.KEYCODE_BUTTON_5,
KeyEvent.KEYCODE_BUTTON_6,
KeyEvent.KEYCODE_BUTTON_7,
KeyEvent.KEYCODE_BUTTON_8,
KeyEvent.KEYCODE_BUTTON_9,
KeyEvent.KEYCODE_BUTTON_10,
KeyEvent.KEYCODE_BUTTON_11,
KeyEvent.KEYCODE_BUTTON_12,
KeyEvent.KEYCODE_BUTTON_13,
KeyEvent.KEYCODE_BUTTON_14,
KeyEvent.KEYCODE_BUTTON_15,
KeyEvent.KEYCODE_BUTTON_16,
};
// The order must be the same as mobile/ebitenmobileview/input_android.go.
static int[] axes = {
MotionEvent.AXIS_X,
MotionEvent.AXIS_Y,
MotionEvent.AXIS_Z,
MotionEvent.AXIS_RX,
MotionEvent.AXIS_RY,
MotionEvent.AXIS_RZ,
MotionEvent.AXIS_HAT_X,
MotionEvent.AXIS_HAT_Y,
MotionEvent.AXIS_LTRIGGER,
MotionEvent.AXIS_RTRIGGER,
MotionEvent.AXIS_THROTTLE,
MotionEvent.AXIS_RUDDER,
MotionEvent.AXIS_WHEEL,
MotionEvent.AXIS_GAS,
MotionEvent.AXIS_BRAKE,
MotionEvent.AXIS_GENERIC_1,
MotionEvent.AXIS_GENERIC_2,
MotionEvent.AXIS_GENERIC_3,
MotionEvent.AXIS_GENERIC_4,
MotionEvent.AXIS_GENERIC_5,
MotionEvent.AXIS_GENERIC_6,
MotionEvent.AXIS_GENERIC_7,
MotionEvent.AXIS_GENERIC_8,
MotionEvent.AXIS_GENERIC_9,
MotionEvent.AXIS_GENERIC_10,
MotionEvent.AXIS_GENERIC_11,
MotionEvent.AXIS_GENERIC_12,
MotionEvent.AXIS_GENERIC_13,
MotionEvent.AXIS_GENERIC_14,
MotionEvent.AXIS_GENERIC_15,
MotionEvent.AXIS_GENERIC_16,
};
private Gamepad getGamepad(int deviceId) {
for (Gamepad gamepad : this.gamepads) {
if (gamepad.deviceId == deviceId) {
return gamepad;
}
}
return null;
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
@ -511,17 +483,24 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
if (event.getAction() != MotionEvent.ACTION_MOVE) {
return super.onGenericMotionEvent(event);
}
InputDevice inputDevice = this.inputManager.getInputDevice(event.getDeviceId());
for (int axis : axes) {
InputDevice.MotionRange motionRange = inputDevice.getMotionRange(axis, event.getSource());
float value = 0.0f;
if (motionRange != null) {
value = event.getAxisValue(axis);
if (Math.abs(value) <= motionRange.getFlat()) {
value = 0.0f;
}
}
Ebitenmobileview.onGamepadAxesOrHatsChanged(event.getDeviceId(), axis, value);
// See https://github.com/libsdl-org/SDL/blob/2df2da11f627299c6e05b7e0aff407c915043372/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java#L256-L277
Gamepad gamepad = this.getGamepad(event.getDeviceId());
if (gamepad == null) {
return true;
}
int actionPointerIndex = event.getActionIndex();
for (int i = 0; i < gamepad.axes.size(); i++) {
InputDevice.MotionRange range = gamepad.axes.get(i);
float axisValue = event.getAxisValue(range.getAxis(), actionPointerIndex);
float value = (axisValue - range.getMin()) / range.getRange() * 2.0f - 1.0f;
Ebitenmobileview.onGamepadAxisChanged(gamepad.deviceId, i, value);
}
for (int i = 0; i < gamepad.hats.size() / 2; i++) {
int hatX = Math.round(event.getAxisValue(gamepad.hats.get(2*i).getAxis(), actionPointerIndex));
int hatY = Math.round(event.getAxisValue(gamepad.hats.get(2*i+1).getAxis(), actionPointerIndex));
Ebitenmobileview.onGamepadHatChanged(gamepad.deviceId, i, hatX, hatY);
}
return true;
}
@ -545,34 +524,43 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
return;
}
int naxes = 0;
int nhats2 = 0;
for (int i = 0; i < axes.length; i++) {
InputDevice.MotionRange range = inputDevice.getMotionRange(axes[i], InputDevice.SOURCE_JOYSTICK);
if (range == null) {
continue;
}
// See https://github.com/libsdl-org/SDL/blob/2df2da11f627299c6e05b7e0aff407c915043372/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java#L182-L216
List<InputDevice.MotionRange> ranges = inputDevice.getMotionRanges();
Collections.sort(ranges, new RangeComparator());
Gamepad gamepad = new Gamepad();
gamepad.deviceId = deviceId;
gamepad.axes = new ArrayList<InputDevice.MotionRange>();
gamepad.hats = new ArrayList<InputDevice.MotionRange>();
for (InputDevice.MotionRange range : ranges) {
if (range.getAxis() == MotionEvent.AXIS_HAT_X || range.getAxis() == MotionEvent.AXIS_HAT_Y) {
nhats2++;
gamepad.hats.add(range);
} else {
naxes++;
gamepad.axes.add(range);
}
}
this.gamepads.add(gamepad);
String descriptor = inputDevice.getDescriptor();
int vendorId = inputDevice.getVendorId();
int productId = inputDevice.getProductId();
// These values are required to calculate SDL's GUID.
int buttonMask = getButtonMask(inputDevice, nhats2/2);
int buttonMask = getButtonMask(inputDevice, gamepad.hats.size()/2);
int axisMask = getAxisMask(inputDevice);
Ebitenmobileview.onGamepadAdded(deviceId, inputDevice.getName(), naxes, nhats2/2, descriptor, vendorId, productId, buttonMask, axisMask);
Ebitenmobileview.onGamepadAdded(deviceId, inputDevice.getName(), gamepad.axes.size(), gamepad.hats.size()/2, descriptor, vendorId, productId, buttonMask, axisMask);
// Initialize the trigger axes values explicitly, or the initial button values would be 0.5 instead of 0.
if (gamepad.axes.size() >= 6) {
Ebitenmobileview.onGamepadAxisChanged(deviceId, 4, -1);
Ebitenmobileview.onGamepadAxisChanged(deviceId, 5, -1);
}
}
// The implementation is copied from SDL:
// https://github.com/libsdl-org/SDL/blob/0e9560aea22818884921e5e5064953257bfe7fa7/android-project/app/src/main/java/org/libsdl/app/SDLControllerManager.java#L308
private int getButtonMask(InputDevice joystickDevice, int nhats) {
private static int getButtonMask(InputDevice joystickDevice, int nhats) {
int buttonMask = 0;
int[] keys = new int[] {
KeyEvent.KEYCODE_BUTTON_A,
@ -672,7 +660,7 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
return buttonMask;
}
private int getAxisMask(InputDevice joystickDevice) {
private static int getAxisMask(InputDevice joystickDevice) {
final int SDL_CONTROLLER_AXIS_LEFTX = 0;
final int SDL_CONTROLLER_AXIS_LEFTY = 1;
final int SDL_CONTROLLER_AXIS_RIGHTX = 2;
@ -712,6 +700,7 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
public void onInputDeviceRemoved(int deviceId) {
// Do not call inputManager.getInputDevice(), which returns null (#1185).
Ebitenmobileview.onInputDeviceRemoved(deviceId);
this.gamepads.remove(this.getGamepad(deviceId));
}
// suspendGame suspends the game.
@ -748,6 +737,7 @@ public class EbitenView extends ViewGroup implements InputManager.InputDeviceLis
private EbitenSurfaceView ebitenSurfaceView;
private InputManager inputManager;
private ArrayList<Gamepad> gamepads;
}
`

File diff suppressed because one or more lines are too long

View File

@ -18,14 +18,7 @@
package gamepad
import (
"fmt"
)
type AndroidHatDirection int
const (
AndroidHatDirectionX AndroidHatDirection = iota
AndroidHatDirectionY
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
)
func AddAndroidGamepad(androidDeviceID int, name, sdlID string, axisCount, hatCount int) {
@ -44,8 +37,8 @@ func UpdateAndroidGamepadButton(androidDeviceID int, button Button, pressed bool
theGamepads.updateAndroidGamepadButton(androidDeviceID, button, pressed)
}
func UpdateAndroidGamepadHat(androidDeviceID int, hat int, dir AndroidHatDirection, value int) {
theGamepads.updateAndroidGamepadHat(androidDeviceID, hat, dir, value)
func UpdateAndroidGamepadHat(androidDeviceID int, hat int, xValue, yValue int) {
theGamepads.updateAndroidGamepadHat(androidDeviceID, hat, xValue, yValue)
}
func (g *gamepads) addAndroidGamepad(androidDeviceID int, name, sdlID string, axisCount, hatCount int) {
@ -56,7 +49,7 @@ func (g *gamepads) addAndroidGamepad(androidDeviceID int, name, sdlID string, ax
gp.native = &nativeGamepadImpl{
androidDeviceID: androidDeviceID,
axes: make([]float64, axisCount),
buttons: make([]bool, ButtonCount),
buttons: make([]bool, gamepaddb.SDLControllerButtonMax+1),
hats: make([]int, hatCount),
}
}
@ -96,7 +89,7 @@ func (g *gamepads) updateAndroidGamepadButton(androidDeviceID int, button Button
gp.updateAndroidGamepadButton(button, pressed)
}
func (g *gamepads) updateAndroidGamepadHat(androidDeviceID int, hat int, dir AndroidHatDirection, value int) {
func (g *gamepads) updateAndroidGamepadHat(androidDeviceID int, hat int, xValue, yValue int) {
g.m.Lock()
defer g.m.Unlock()
@ -106,7 +99,7 @@ func (g *gamepads) updateAndroidGamepadHat(androidDeviceID int, hat int, dir And
if gp == nil {
return
}
gp.updateAndroidGamepadHat(hat, dir, value)
gp.updateAndroidGamepadHat(hat, xValue, yValue)
}
func (g *Gamepad) updateAndroidGamepadAxis(axis int, value float64) {
@ -131,7 +124,7 @@ func (g *Gamepad) updateAndroidGamepadButton(button Button, pressed bool) {
n.buttons[button] = pressed
}
func (g *Gamepad) updateAndroidGamepadHat(hat int, dir AndroidHatDirection, value int) {
func (g *Gamepad) updateAndroidGamepadHat(hat int, xValue, yValue int) {
g.m.Lock()
defer g.m.Unlock()
@ -139,32 +132,43 @@ func (g *Gamepad) updateAndroidGamepadHat(hat int, dir AndroidHatDirection, valu
if hat < 0 || hat >= len(n.hats) {
return
}
v := n.hats[hat]
switch dir {
case AndroidHatDirectionX:
switch {
case value < 0:
v |= hatLeft
v &^= hatRight
case value > 0:
v &^= hatLeft
v |= hatRight
default:
v &^= (hatLeft | hatRight)
}
case AndroidHatDirectionY:
switch {
case value < 0:
v |= hatUp
v &^= hatDown
case value > 0:
v &^= hatUp
v |= hatDown
default:
v &^= (hatUp | hatDown)
}
default:
panic(fmt.Sprintf("gamepad: invalid direction: %d", dir))
var v int
switch {
case xValue < 0:
v |= hatLeft
case xValue > 0:
v |= hatRight
}
switch {
case yValue < 0:
v |= hatUp
case yValue > 0:
v |= hatDown
}
n.hats[hat] = v
// Update the gamepad buttons in addition to hats.
// See https://github.com/libsdl-org/SDL/blob/47f2373dc13b66c48bf4024fcdab53cd0bdd59bb/src/joystick/android/SDL_sysjoystick.c#L290-L301
switch {
case xValue < 0:
n.buttons[gamepaddb.SDLControllerButtonDpadLeft] = true
n.buttons[gamepaddb.SDLControllerButtonDpadRight] = false
case xValue > 0:
n.buttons[gamepaddb.SDLControllerButtonDpadLeft] = false
n.buttons[gamepaddb.SDLControllerButtonDpadRight] = true
default:
n.buttons[gamepaddb.SDLControllerButtonDpadLeft] = false
n.buttons[gamepaddb.SDLControllerButtonDpadRight] = false
}
switch {
case yValue < 0:
n.buttons[gamepaddb.SDLControllerButtonDpadUp] = true
n.buttons[gamepaddb.SDLControllerButtonDpadDown] = false
case yValue > 0:
n.buttons[gamepaddb.SDLControllerButtonDpadUp] = false
n.buttons[gamepaddb.SDLControllerButtonDpadDown] = true
default:
n.buttons[gamepaddb.SDLControllerButtonDpadUp] = false
n.buttons[gamepaddb.SDLControllerButtonDpadDown] = false
}
}

View File

@ -341,30 +341,26 @@ func toStandardGamepadAxis(str string) (StandardAxis, bool) {
}
func buttonMappings(id string) map[StandardButton]*mapping {
// TODO: Use the database instead of the original mapping (#2308).
// The buttons and axes assignments should be fixed.
if m, ok := gamepadButtonMappings[id]; ok {
return m
}
if currentPlatform == platformAndroid {
if addAndroidDefaultMappings(id) {
return gamepadButtonMappings[id]
}
}
if m, ok := gamepadButtonMappings[id]; ok {
return m
}
return nil
}
func axisMappings(id string) map[StandardAxis]*mapping {
// TODO: Use the database instead of the original mapping (#2308).
// The buttons and axes assignments should be fixed.
if m, ok := gamepadAxisMappings[id]; ok {
return m
}
if currentPlatform == platformAndroid {
if addAndroidDefaultMappings(id) {
return gamepadAxisMappings[id]
}
}
if m, ok := gamepadAxisMappings[id]; ok {
return m
}
return nil
}
@ -585,35 +581,6 @@ func Update(mappingData []byte) error {
}
func addAndroidDefaultMappings(id string) bool {
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L655-L680
const (
SDLControllerButtonA = 0
SDLControllerButtonB = 1
SDLControllerButtonX = 2
SDLControllerButtonY = 3
SDLControllerButtonBack = 4
SDLControllerButtonGuide = 5
SDLControllerButtonStart = 6
SDLControllerButtonLeftStick = 7
SDLControllerButtonRightStick = 8
SDLControllerButtonLeftShoulder = 9
SDLControllerButtonRightShoulder = 10
SDLControllerButtonDpadUp = 11
SDLControllerButtonDpadDown = 12
SDLControllerButtonDpadLeft = 13
SDLControllerButtonDpadRight = 14
)
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L550-L560
const (
SDLControllerAxisLeftX = 0
SDLControllerAxisLeftY = 1
SDLControllerAxisRightX = 2
SDLControllerAxisRightY = 3
SDLControllerAxisTriggerLeft = 4
SDLControllerAxisTriggerRight = 5
)
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/src/joystick/SDL_gamecontroller.c#L468-L568
const faceButtonMask = ((1 << SDLControllerButtonA) |
@ -642,38 +609,38 @@ func addAndroidDefaultMappings(id string) bool {
if buttonMask&(1<<SDLControllerButtonA) != 0 {
gamepadButtonMappings[id][StandardButtonRightBottom] = &mapping{
Type: mappingTypeButton,
Index: 0,
Index: SDLControllerButtonA,
}
}
if buttonMask&(1<<SDLControllerButtonB) != 0 {
gamepadButtonMappings[id][StandardButtonRightRight] = &mapping{
Type: mappingTypeButton,
Index: 1,
Index: SDLControllerButtonB,
}
} else {
// Use the back button as "B" for easy UI navigation with TV remotes.
gamepadButtonMappings[id][StandardButtonRightRight] = &mapping{
Type: mappingTypeButton,
Index: 13,
Index: SDLControllerButtonBack,
}
buttonMask &^= uint16(1) << SDLControllerButtonBack
}
if buttonMask&(1<<SDLControllerButtonX) != 0 {
gamepadButtonMappings[id][StandardButtonRightLeft] = &mapping{
Type: mappingTypeButton,
Index: 3,
Index: SDLControllerButtonX,
}
}
if buttonMask&(1<<SDLControllerButtonY) != 0 {
gamepadButtonMappings[id][StandardButtonRightTop] = &mapping{
Type: mappingTypeButton,
Index: 4,
Index: SDLControllerButtonY,
}
}
if buttonMask&(1<<SDLControllerButtonBack) != 0 {
gamepadButtonMappings[id][StandardButtonCenterLeft] = &mapping{
Type: mappingTypeButton,
Index: 13,
Index: SDLControllerButtonBack,
}
}
if buttonMask&(1<<SDLControllerButtonGuide) != 0 {
@ -687,67 +654,63 @@ func addAndroidDefaultMappings(id string) bool {
if buttonMask&(1<<SDLControllerButtonStart) != 0 {
gamepadButtonMappings[id][StandardButtonCenterRight] = &mapping{
Type: mappingTypeButton,
Index: 12,
Index: SDLControllerButtonStart,
}
}
if buttonMask&(1<<SDLControllerButtonLeftStick) != 0 {
gamepadButtonMappings[id][StandardButtonLeftStick] = &mapping{
Type: mappingTypeButton,
Index: 10,
Index: SDLControllerButtonLeftStick,
}
}
if buttonMask&(1<<SDLControllerButtonRightStick) != 0 {
gamepadButtonMappings[id][StandardButtonRightStick] = &mapping{
Type: mappingTypeButton,
Index: 11,
Index: SDLControllerButtonRightStick,
}
}
if buttonMask&(1<<SDLControllerButtonLeftShoulder) != 0 {
gamepadButtonMappings[id][StandardButtonFrontTopLeft] = &mapping{
Type: mappingTypeButton,
Index: 6,
Index: SDLControllerButtonLeftShoulder,
}
}
if buttonMask&(1<<SDLControllerButtonRightShoulder) != 0 {
gamepadButtonMappings[id][StandardButtonFrontTopRight] = &mapping{
Type: mappingTypeButton,
Index: 7,
Index: SDLControllerButtonRightShoulder,
}
}
if buttonMask&(1<<SDLControllerButtonDpadUp) != 0 {
gamepadButtonMappings[id][StandardButtonLeftTop] = &mapping{
Type: mappingTypeHat,
Index: 0,
HatState: HatUp,
Type: mappingTypeButton,
Index: SDLControllerButtonDpadUp,
}
}
if buttonMask&(1<<SDLControllerButtonDpadDown) != 0 {
gamepadButtonMappings[id][StandardButtonLeftBottom] = &mapping{
Type: mappingTypeHat,
Index: 0,
HatState: HatDown,
Type: mappingTypeButton,
Index: SDLControllerButtonDpadDown,
}
}
if buttonMask&(1<<SDLControllerButtonDpadLeft) != 0 {
gamepadButtonMappings[id][StandardButtonLeftLeft] = &mapping{
Type: mappingTypeHat,
Index: 0,
HatState: HatLeft,
Type: mappingTypeButton,
Index: SDLControllerButtonDpadLeft,
}
}
if buttonMask&(1<<SDLControllerButtonDpadRight) != 0 {
gamepadButtonMappings[id][StandardButtonLeftRight] = &mapping{
Type: mappingTypeHat,
Index: 0,
HatState: HatRight,
Type: mappingTypeButton,
Index: SDLControllerButtonDpadRight,
}
}
if axisMask&(1<<SDLControllerAxisLeftX) != 0 {
gamepadAxisMappings[id][StandardAxisLeftStickHorizontal] = &mapping{
Type: mappingTypeAxis,
Index: 0,
Index: SDLControllerAxisLeftX,
AxisScale: 1,
AxisOffset: 0,
}
@ -755,27 +718,23 @@ func addAndroidDefaultMappings(id string) bool {
if axisMask&(1<<SDLControllerAxisLeftY) != 0 {
gamepadAxisMappings[id][StandardAxisLeftStickVertical] = &mapping{
Type: mappingTypeAxis,
Index: 1,
Index: SDLControllerAxisLeftY,
AxisScale: 1,
AxisOffset: 0,
}
}
if axisMask&(1<<SDLControllerAxisRightX) != 0 {
// https://developer.android.com/reference/android/view/MotionEvent#AXIS_Z
// > On game pads with two analog joysticks, this axis is often reinterpreted to report the absolute X position of the second joystick instead.
gamepadAxisMappings[id][StandardAxisRightStickHorizontal] = &mapping{
Type: mappingTypeAxis,
Index: 2,
Index: SDLControllerAxisRightX,
AxisScale: 1,
AxisOffset: 0,
}
}
if axisMask&(1<<SDLControllerAxisRightY) != 0 {
// https://developer.android.com/reference/android/view/MotionEvent#AXIS_RZ
// > On game pads with two analog joysticks, this axis is often reinterpreted to report the absolute Y position of the second joystick instead.
gamepadAxisMappings[id][StandardAxisRightStickVertical] = &mapping{
Type: mappingTypeAxis,
Index: 5,
Index: SDLControllerAxisRightY,
AxisScale: 1,
AxisOffset: 0,
}
@ -783,17 +742,17 @@ func addAndroidDefaultMappings(id string) bool {
if axisMask&(1<<SDLControllerAxisTriggerLeft) != 0 {
gamepadButtonMappings[id][StandardButtonFrontBottomLeft] = &mapping{
Type: mappingTypeAxis,
Index: 6,
AxisScale: 2,
AxisOffset: -1,
Index: SDLControllerAxisTriggerLeft,
AxisScale: 1,
AxisOffset: 0,
}
}
if axisMask&(1<<SDLControllerAxisTriggerRight) != 0 {
gamepadButtonMappings[id][StandardButtonFrontBottomRight] = &mapping{
Type: mappingTypeAxis,
Index: 7,
AxisScale: 2,
AxisOffset: -1,
Index: SDLControllerAxisTriggerRight,
AxisScale: 1,
AxisOffset: 0,
}
}

51
internal/gamepaddb/sdl.go Normal file
View File

@ -0,0 +1,51 @@
// Copyright 2022 The Ebitengine Authors
//
// 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 gamepaddb
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L655-L680
const (
SDLControllerButtonA = 0
SDLControllerButtonB = 1
SDLControllerButtonX = 2
SDLControllerButtonY = 3
SDLControllerButtonBack = 4
SDLControllerButtonGuide = 5
SDLControllerButtonStart = 6
SDLControllerButtonLeftStick = 7
SDLControllerButtonRightStick = 8
SDLControllerButtonLeftShoulder = 9
SDLControllerButtonRightShoulder = 10
SDLControllerButtonDpadUp = 11
SDLControllerButtonDpadDown = 12
SDLControllerButtonDpadLeft = 13
SDLControllerButtonDpadRight = 14
SDLControllerButtonMisc1 = 15
SDLControllerButtonPaddle1 = 16
SDLControllerButtonPaddle2 = 17
SDLControllerButtonPaddle3 = 18
SDLControllerButtonPaddle4 = 19
SDLControllerButtonTouchpad = 20
SDLControllerButtonMax = SDLControllerButtonTouchpad // This is different from the original SDL_CONTROLLER_BUTTON_MAX.
)
// See https://github.com/libsdl-org/SDL/blob/120c76c84bbce4c1bfed4e9eb74e10678bd83120/include/SDL_gamecontroller.h#L550-L560
const (
SDLControllerAxisLeftX = 0
SDLControllerAxisLeftY = 1
SDLControllerAxisRightX = 2
SDLControllerAxisRightY = 3
SDLControllerAxisTriggerLeft = 4
SDLControllerAxisTriggerRight = 5
)

View File

@ -17,10 +17,10 @@ package ebitenmobileview
import (
"encoding/hex"
"hash/crc32"
"math"
"unicode"
"github.com/hajimehoshi/ebiten/v2/internal/gamepad"
"github.com/hajimehoshi/ebiten/v2/internal/gamepaddb"
"github.com/hajimehoshi/ebiten/v2/internal/ui"
)
@ -58,10 +58,14 @@ const (
keycodeButton15 = 0x000000ca
keycodeButton16 = 0x000000cb
keycodeDpadUp = 0x00000013
keycodeDpadDown = 0x00000014
keycodeDpadLeft = 0x00000015
keycodeDpadRight = 0x00000016
keycodeDpadUp = 0x00000013
keycodeDpadDown = 0x00000014
keycodeDpadLeft = 0x00000015
keycodeDpadRight = 0x00000016
keycodeDpadCenter = 0x00000017
keycodeBack = 0x00000004
keycodeMenu = 0x00000052
)
// https://developer.android.com/reference/android/view/InputDevice
@ -71,113 +75,48 @@ const (
sourceJoystick = 0x01000010
)
// TODO: Can we map these values to the standard gamepad buttons?
// 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 androidKeyToGamepadButton = map[int]gamepad.Button{
keycodeButtonA: gamepad.Button0,
keycodeButtonB: gamepad.Button1,
keycodeButtonC: gamepad.Button2,
keycodeButtonX: gamepad.Button3,
keycodeButtonY: gamepad.Button4,
keycodeButtonZ: gamepad.Button5,
keycodeButtonL1: gamepad.Button6,
keycodeButtonR1: gamepad.Button7,
keycodeButtonL2: gamepad.Button8,
keycodeButtonR2: gamepad.Button9,
keycodeButtonThumbl: gamepad.Button10,
keycodeButtonThumbr: gamepad.Button11,
keycodeButtonStart: gamepad.Button12,
keycodeButtonSelect: gamepad.Button13,
keycodeButtonMode: gamepad.Button14,
keycodeButton1: gamepad.Button15,
keycodeButton2: gamepad.Button16,
keycodeButton3: gamepad.Button17,
keycodeButton4: gamepad.Button18,
keycodeButton5: gamepad.Button19,
keycodeButton6: gamepad.Button20,
keycodeButton7: gamepad.Button21,
keycodeButton8: gamepad.Button22,
keycodeButton9: gamepad.Button23,
keycodeButton10: gamepad.Button24,
keycodeButton11: gamepad.Button25,
keycodeButton12: gamepad.Button26,
keycodeButton13: gamepad.Button27,
keycodeButton14: gamepad.Button28,
keycodeButton15: gamepad.Button29,
keycodeButton16: gamepad.Button30,
}
// Axis constant definitions for joysticks only.
// https://developer.android.com/reference/android/view/MotionEvent
const (
axisX = 0x00000000
axisY = 0x00000001
axisZ = 0x0000000b
axisRx = 0x0000000c
axisRy = 0x0000000d
axisRz = 0x0000000e
axisHatX = 0x0000000f
axisHatY = 0x00000010
axisLtrigger = 0x00000011
axisRtrigger = 0x00000012
axisThrottle = 0x00000013
axisRudder = 0x00000014
axisWheel = 0x00000015
axisGas = 0x00000016
axisBrake = 0x00000017
axisGeneric1 = 0x00000020
axisGeneric2 = 0x00000021
axisGeneric3 = 0x00000022
axisGeneric4 = 0x00000023
axisGeneric5 = 0x00000024
axisGeneric6 = 0x00000025
axisGeneric7 = 0x00000026
axisGeneric8 = 0x00000027
axisGeneric9 = 0x00000028
axisGeneric10 = 0x00000029
axisGeneric11 = 0x0000002a
axisGeneric12 = 0x0000002b
axisGeneric13 = 0x0000002c
axisGeneric14 = 0x0000002d
axisGeneric15 = 0x0000002e
axisGeneric16 = 0x0000002f
)
var androidAxisIDToAxisID = map[int]int{
axisX: 0,
axisY: 1,
axisZ: 2,
axisRx: 3,
axisRy: 4,
axisRz: 5,
axisLtrigger: 6,
axisRtrigger: 7,
axisThrottle: 8,
axisRudder: 9,
axisWheel: 10,
axisGas: 11,
axisBrake: 12,
axisGeneric1: 13,
axisGeneric2: 14,
axisGeneric3: 15,
axisGeneric4: 16,
axisGeneric5: 17,
axisGeneric6: 18,
axisGeneric7: 19,
axisGeneric8: 20,
axisGeneric9: 21,
axisGeneric10: 22,
axisGeneric11: 23,
axisGeneric12: 24,
axisGeneric13: 25,
axisGeneric14: 26,
axisGeneric15: 27,
axisGeneric16: 28,
}
var androidAxisIDToHatID2 = map[int]int{
axisHatX: 0,
axisHatY: 1,
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) {
@ -195,8 +134,8 @@ 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 := androidKeyToGamepadButton[keyCode]; ok {
gamepad.UpdateAndroidGamepadButton(deviceID, button, true)
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.
@ -215,8 +154,8 @@ 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 := androidKeyToGamepadButton[keyCode]; ok {
gamepad.UpdateAndroidGamepadButton(deviceID, button, false)
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.
@ -228,30 +167,12 @@ func OnKeyUpOnAndroid(keyCode int, source int, deviceID int) {
}
}
func OnGamepadAxesOrHatsChanged(deviceID int, axisID int, value float32) {
if axis, ok := androidAxisIDToAxisID[axisID]; ok {
gamepad.UpdateAndroidGamepadAxis(deviceID, axis, float64(value))
return
}
func OnGamepadAxisChanged(deviceID int, axisID int, value float32) {
gamepad.UpdateAndroidGamepadAxis(deviceID, axisID, float64(value))
}
if hid2, ok := androidAxisIDToHatID2[axisID]; ok {
const (
hatUp = 1
hatRight = 2
hatDown = 4
hatLeft = 8
)
hatID := hid2 / 2
var dir gamepad.AndroidHatDirection
switch hid2 % 2 {
case 0:
dir = gamepad.AndroidHatDirectionX
case 1:
dir = gamepad.AndroidHatDirectionY
}
gamepad.UpdateAndroidGamepadHat(deviceID, hatID, dir, int(math.Round(float64(value))))
return
}
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) {