mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 852935 - Add Android gamepad backend. r=snorp, rs=smaug
This commit is contained in:
parent
5656eb9940
commit
dff9bd8e4c
@ -5949,6 +5949,11 @@ case "$OS_TARGET" in
|
||||
Darwin|WINNT|Linux)
|
||||
MOZ_GAMEPAD=1
|
||||
;;
|
||||
Android)
|
||||
if test "$MOZ_WIDGET_TOOLKIT" != "gonk"; then
|
||||
MOZ_GAMEPAD=1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
@ -5973,6 +5978,9 @@ if test "$MOZ_GAMEPAD"; then
|
||||
fi
|
||||
MOZ_GAMEPAD_BACKEND=linux
|
||||
;;
|
||||
Android)
|
||||
MOZ_GAMEPAD_BACKEND=android
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
@ -368,15 +368,15 @@ var interfaceNamesInGlobalScope =
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"GainNode",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "Gamepad", desktop: true},
|
||||
{name: "Gamepad", b2g: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "GamepadAxisMoveEvent", desktop: true},
|
||||
{name: "GamepadAxisMoveEvent", b2g: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "GamepadButtonEvent", desktop: true},
|
||||
{name: "GamepadButtonEvent", b2g: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "GamepadButton", desktop: true},
|
||||
{name: "GamepadButton", b2g: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
{name: "GamepadEvent", desktop: true},
|
||||
{name: "GamepadEvent", b2g: false},
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
"HashChangeEvent",
|
||||
// IMPORTANT: Do not change this list without review from a DOM peer!
|
||||
|
27
hal/android/AndroidGamepad.cpp
Normal file
27
hal/android/AndroidGamepad.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "Hal.h"
|
||||
#include "AndroidBridge.h"
|
||||
|
||||
using namespace mozilla::hal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace hal_impl {
|
||||
|
||||
void
|
||||
StartMonitoringGamepadStatus()
|
||||
{
|
||||
mozilla::widget::android::GeckoAppShell::StartMonitoringGamepad();
|
||||
}
|
||||
|
||||
void
|
||||
StopMonitoringGamepadStatus()
|
||||
{
|
||||
mozilla::widget::android::GeckoAppShell::StopMonitoringGamepad();
|
||||
}
|
||||
|
||||
} // hal_impl
|
||||
} // mozilla
|
@ -48,6 +48,10 @@ elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'linux':
|
||||
UNIFIED_SOURCES += [
|
||||
'linux/LinuxGamepad.cpp'
|
||||
]
|
||||
elif CONFIG['MOZ_GAMEPAD_BACKEND'] == 'android':
|
||||
UNIFIED_SOURCES += [
|
||||
'android/AndroidGamepad.cpp'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
UNIFIED_SOURCES += [
|
||||
|
395
mobile/android/base/AndroidGamepadManager.java
Normal file
395
mobile/android/base/AndroidGamepadManager.java
Normal file
@ -0,0 +1,395 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.util.GamepadUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Build;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import java.lang.Math;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
|
||||
public class AndroidGamepadManager {
|
||||
// This is completely arbitrary.
|
||||
private static final float TRIGGER_PRESSED_THRESHOLD = 0.25f;
|
||||
private static final long POLL_TIMER_PERIOD = 1000; // milliseconds
|
||||
|
||||
private static enum Axis {
|
||||
X(MotionEvent.AXIS_X),
|
||||
Y(MotionEvent.AXIS_Y),
|
||||
Z(MotionEvent.AXIS_Z),
|
||||
RZ(MotionEvent.AXIS_RZ);
|
||||
|
||||
public final int axis;
|
||||
|
||||
private Axis(int axis) {
|
||||
this.axis = axis;
|
||||
}
|
||||
};
|
||||
|
||||
// A list of gamepad button mappings. Axes are determined at
|
||||
// runtime, as they vary by Android version.
|
||||
private static enum Trigger {
|
||||
Left(6),
|
||||
Right(7);
|
||||
|
||||
public final int button;
|
||||
|
||||
private Trigger(int button) {
|
||||
this.button = button;
|
||||
}
|
||||
};
|
||||
|
||||
private static final int FIRST_DPAD_BUTTON = 12;
|
||||
// A list of axis number, gamepad button mappings for negative, positive.
|
||||
// Button mappings are added to FIRST_DPAD_BUTTON.
|
||||
private static enum DpadAxis {
|
||||
UpDown(MotionEvent.AXIS_HAT_Y, 0, 1),
|
||||
LeftRight(MotionEvent.AXIS_HAT_X, 2, 3);
|
||||
|
||||
public final int axis;
|
||||
public final int negativeButton;
|
||||
public final int positiveButton;
|
||||
|
||||
private DpadAxis(int axis, int negativeButton, int positiveButton) {
|
||||
this.axis = axis;
|
||||
this.negativeButton = negativeButton;
|
||||
this.positiveButton = positiveButton;
|
||||
}
|
||||
};
|
||||
|
||||
private static enum Button {
|
||||
A(KeyEvent.KEYCODE_BUTTON_A),
|
||||
B(KeyEvent.KEYCODE_BUTTON_B),
|
||||
X(KeyEvent.KEYCODE_BUTTON_X),
|
||||
Y(KeyEvent.KEYCODE_BUTTON_Y),
|
||||
L1(KeyEvent.KEYCODE_BUTTON_L1),
|
||||
R1(KeyEvent.KEYCODE_BUTTON_R1),
|
||||
L2(KeyEvent.KEYCODE_BUTTON_L2),
|
||||
R2(KeyEvent.KEYCODE_BUTTON_R2),
|
||||
SELECT(KeyEvent.KEYCODE_BUTTON_SELECT),
|
||||
START(KeyEvent.KEYCODE_BUTTON_START),
|
||||
THUMBL(KeyEvent.KEYCODE_BUTTON_THUMBL),
|
||||
THUMBR(KeyEvent.KEYCODE_BUTTON_THUMBR),
|
||||
DPAD_UP(KeyEvent.KEYCODE_DPAD_UP),
|
||||
DPAD_DOWN(KeyEvent.KEYCODE_DPAD_DOWN),
|
||||
DPAD_LEFT(KeyEvent.KEYCODE_DPAD_LEFT),
|
||||
DPAD_RIGHT(KeyEvent.KEYCODE_DPAD_RIGHT);
|
||||
|
||||
public final int button;
|
||||
|
||||
private Button(int button) {
|
||||
this.button = button;
|
||||
}
|
||||
};
|
||||
|
||||
private static class Gamepad {
|
||||
// ID from GamepadService
|
||||
public int id;
|
||||
// Retain axis state so we can determine changes.
|
||||
public float axes[];
|
||||
public boolean dpad[];
|
||||
public int triggerAxes[];
|
||||
public float triggers[];
|
||||
|
||||
public Gamepad(int serviceId, int deviceId) {
|
||||
id = serviceId;
|
||||
axes = new float[Axis.values().length];
|
||||
dpad = new boolean[4];
|
||||
triggers = new float[2];
|
||||
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device != null) {
|
||||
// LTRIGGER/RTRIGGER don't seem to be exposed on older
|
||||
// versions of Android.
|
||||
if (device.getMotionRange(MotionEvent.AXIS_LTRIGGER) != null && device.getMotionRange(MotionEvent.AXIS_RTRIGGER) != null) {
|
||||
triggerAxes = new int[]{MotionEvent.AXIS_LTRIGGER,
|
||||
MotionEvent.AXIS_RTRIGGER};
|
||||
} else if (device.getMotionRange(MotionEvent.AXIS_BRAKE) != null && device.getMotionRange(MotionEvent.AXIS_GAS) != null) {
|
||||
triggerAxes = new int[]{MotionEvent.AXIS_BRAKE,
|
||||
MotionEvent.AXIS_GAS};
|
||||
} else {
|
||||
triggerAxes = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean sStarted = false;
|
||||
private static HashMap<Integer, Gamepad> sGamepads = null;
|
||||
private static HashMap<Integer, List<KeyEvent>> sPendingGamepads = null;
|
||||
private static InputManager.InputDeviceListener sListener = null;
|
||||
private static Timer sPollTimer = null;
|
||||
|
||||
private AndroidGamepadManager() {
|
||||
}
|
||||
|
||||
public static void startup() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!sStarted) {
|
||||
sGamepads = new HashMap<Integer, Gamepad>();
|
||||
sPendingGamepads = new HashMap<Integer, List<KeyEvent>>();
|
||||
scanForGamepads();
|
||||
addDeviceListener();
|
||||
sStarted = true;
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (sStarted) {
|
||||
removeDeviceListener();
|
||||
sPendingGamepads = null;
|
||||
sGamepads = null;
|
||||
sStarted = false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void gamepadAdded(int deviceId, int serviceId) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!sStarted) {
|
||||
return;
|
||||
}
|
||||
if (!sPendingGamepads.containsKey(deviceId)) {
|
||||
removeGamepad(deviceId);
|
||||
return;
|
||||
}
|
||||
|
||||
List<KeyEvent> pending = sPendingGamepads.get(deviceId);
|
||||
sPendingGamepads.remove(deviceId);
|
||||
sGamepads.put(deviceId, new Gamepad(serviceId, deviceId));
|
||||
// Handle queued KeyEvents
|
||||
for (KeyEvent ev : pending) {
|
||||
handleKeyEvent(ev);
|
||||
}
|
||||
}
|
||||
|
||||
private static float deadZone(MotionEvent ev, int axis) {
|
||||
if (GamepadUtils.isValueInDeadZone(ev, axis)) {
|
||||
return 0.0f;
|
||||
}
|
||||
return ev.getAxisValue(axis);
|
||||
}
|
||||
|
||||
private static void mapDpadAxis(Gamepad gamepad,
|
||||
boolean pressed,
|
||||
float value,
|
||||
int which) {
|
||||
if (pressed != gamepad.dpad[which]) {
|
||||
gamepad.dpad[which] = pressed;
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, FIRST_DPAD_BUTTON + which, pressed, Math.abs(value)));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean handleMotionEvent(MotionEvent ev) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!sStarted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sGamepads.containsKey(ev.getDeviceId())) {
|
||||
// Not a device we care about.
|
||||
return false;
|
||||
}
|
||||
|
||||
Gamepad gamepad = sGamepads.get(ev.getDeviceId());
|
||||
// First check the analog stick axes
|
||||
boolean[] valid = new boolean[Axis.values().length];
|
||||
float[] axes = new float[Axis.values().length];
|
||||
boolean anyValidAxes = false;
|
||||
for (Axis axis : Axis.values()) {
|
||||
float value = deadZone(ev, axis.axis);
|
||||
int i = axis.ordinal();
|
||||
if (value != gamepad.axes[i]) {
|
||||
axes[i] = value;
|
||||
gamepad.axes[i] = value;
|
||||
valid[i] = true;
|
||||
anyValidAxes = true;
|
||||
}
|
||||
}
|
||||
if (anyValidAxes) {
|
||||
// Send an axismove event.
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAxisEvent(gamepad.id, valid, axes));
|
||||
}
|
||||
|
||||
// Map triggers to buttons.
|
||||
if (gamepad.triggerAxes != null) {
|
||||
for (Trigger trigger : Trigger.values()) {
|
||||
int i = trigger.ordinal();
|
||||
int axis = gamepad.triggerAxes[i];
|
||||
float value = deadZone(ev, axis);
|
||||
if (value != gamepad.triggers[i]) {
|
||||
gamepad.triggers[i] = value;
|
||||
boolean pressed = value > TRIGGER_PRESSED_THRESHOLD;
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, trigger.button, pressed, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Map d-pad to buttons.
|
||||
for (DpadAxis dpadaxis : DpadAxis.values()) {
|
||||
float value = deadZone(ev, dpadaxis.axis);
|
||||
mapDpadAxis(gamepad, value < 0.0f, value, dpadaxis.negativeButton);
|
||||
mapDpadAxis(gamepad, value > 0.0f, value, dpadaxis.positiveButton);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean handleKeyEvent(KeyEvent ev) {
|
||||
ThreadUtils.assertOnUiThread();
|
||||
if (!sStarted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int deviceId = ev.getDeviceId();
|
||||
if (sPendingGamepads.containsKey(deviceId)) {
|
||||
// Queue up key events for pending devices.
|
||||
sPendingGamepads.get(deviceId).add(ev);
|
||||
return true;
|
||||
} else if (!sGamepads.containsKey(deviceId)) {
|
||||
InputDevice device = ev.getDevice();
|
||||
if (device != null &&
|
||||
(device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||
// This is a gamepad we haven't seen yet.
|
||||
addGamepad(device);
|
||||
sPendingGamepads.get(deviceId).add(ev);
|
||||
return true;
|
||||
}
|
||||
// Not a device we care about.
|
||||
return false;
|
||||
}
|
||||
|
||||
int key = -1;
|
||||
for (Button button : Button.values()) {
|
||||
if (button.button == ev.getKeyCode()) {
|
||||
key = button.ordinal();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (key == -1) {
|
||||
// Not a key we know how to handle.
|
||||
return false;
|
||||
}
|
||||
if (ev.getRepeatCount() > 0) {
|
||||
// We would handle this key, but we're not interested in
|
||||
// repeats. Eat it.
|
||||
return true;
|
||||
}
|
||||
|
||||
Gamepad gamepad = sGamepads.get(deviceId);
|
||||
boolean pressed = ev.getAction() == KeyEvent.ACTION_DOWN;
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadButtonEvent(gamepad.id, key, pressed, pressed ? 1.0f : 0.0f));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void scanForGamepads() {
|
||||
if (Build.VERSION.SDK_INT < 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
int[] deviceIds = InputDevice.getDeviceIds();
|
||||
if (deviceIds == null) {
|
||||
return;
|
||||
}
|
||||
for (int i=0; i < deviceIds.length; i++) {
|
||||
InputDevice device = InputDevice.getDevice(deviceIds[i]);
|
||||
if (device == null) {
|
||||
continue;
|
||||
}
|
||||
if ((device.getSources() & InputDevice.SOURCE_GAMEPAD) != InputDevice.SOURCE_GAMEPAD) {
|
||||
continue;
|
||||
}
|
||||
addGamepad(device);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addGamepad(InputDevice device) {
|
||||
//TODO: when we're using a newer SDK version, use these.
|
||||
//if (Build.VERSION.SDK_INT >= 12) {
|
||||
//int vid = device.getVendorId();
|
||||
//int pid = device.getProductId();
|
||||
//}
|
||||
sPendingGamepads.put(device.getId(), new ArrayList<KeyEvent>());
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAddRemoveEvent(device.getId(), true));
|
||||
}
|
||||
|
||||
private static void removeGamepad(int deviceId) {
|
||||
Gamepad gamepad = sGamepads.get(deviceId);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createGamepadAddRemoveEvent(gamepad.id, false));
|
||||
sGamepads.remove(deviceId);
|
||||
}
|
||||
|
||||
private static void addDeviceListener() {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
// Poll known gamepads to see if they've disappeared.
|
||||
sPollTimer = new Timer();
|
||||
sPollTimer.scheduleAtFixedRate(new TimerTask() {
|
||||
public void run() {
|
||||
for (Integer deviceId : sGamepads.keySet()) {
|
||||
if (InputDevice.getDevice(deviceId) == null) {
|
||||
removeGamepad(deviceId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, POLL_TIMER_PERIOD, POLL_TIMER_PERIOD);
|
||||
return;
|
||||
}
|
||||
sListener = new InputManager.InputDeviceListener() {
|
||||
public void onInputDeviceAdded(int deviceId) {
|
||||
InputDevice device = InputDevice.getDevice(deviceId);
|
||||
if (device == null) {
|
||||
return;
|
||||
}
|
||||
if ((device.getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD) {
|
||||
addGamepad(device);
|
||||
}
|
||||
}
|
||||
|
||||
public void onInputDeviceRemoved(int deviceId) {
|
||||
if (sPendingGamepads.containsKey(deviceId)) {
|
||||
// Got removed before Gecko's ack reached us.
|
||||
// gamepadAdded will deal with it.
|
||||
sPendingGamepads.remove(deviceId);
|
||||
return;
|
||||
}
|
||||
if (sGamepads.containsKey(deviceId)) {
|
||||
removeGamepad(deviceId);
|
||||
}
|
||||
}
|
||||
|
||||
public void onInputDeviceChanged(int deviceId) {
|
||||
}
|
||||
};
|
||||
((InputManager)GeckoAppShell.getContext().getSystemService(Context.INPUT_SERVICE)).registerInputDeviceListener(sListener, ThreadUtils.getUiHandler());
|
||||
}
|
||||
|
||||
private static void removeDeviceListener() {
|
||||
if (Build.VERSION.SDK_INT < 16) {
|
||||
if (sPollTimer != null) {
|
||||
sPollTimer.cancel();
|
||||
sPollTimer = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
((InputManager)GeckoAppShell.getContext().getSystemService(Context.INPUT_SERVICE)).unregisterInputDeviceListener(sListener);
|
||||
sListener = null;
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ import java.util.Vector;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AndroidGamepadManager;
|
||||
import org.mozilla.gecko.DynamicToolbar.PinReason;
|
||||
import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
|
||||
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
|
||||
@ -268,6 +269,10 @@ abstract public class BrowserApp extends GeckoApp
|
||||
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
if (AndroidGamepadManager.handleKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Global onKey handler. This is called if the focused UI doesn't
|
||||
// handle the key event, and before Gecko swallows the events.
|
||||
if (event.getAction() != KeyEvent.ACTION_DOWN) {
|
||||
@ -357,6 +362,14 @@ abstract public class BrowserApp extends GeckoApp
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (AndroidGamepadManager.handleKeyEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
return super.onKeyUp(keyCode, event);
|
||||
}
|
||||
|
||||
void handleReaderListStatusRequest(final String url) {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
|
@ -698,6 +698,43 @@ public class GeckoAppShell
|
||||
}
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void startMonitoringGamepad() {
|
||||
if (Build.VERSION.SDK_INT < 9) {
|
||||
return;
|
||||
}
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AndroidGamepadManager.startup();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void stopMonitoringGamepad() {
|
||||
if (Build.VERSION.SDK_INT < 9) {
|
||||
return;
|
||||
}
|
||||
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AndroidGamepadManager.shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void gamepadAdded(final int device_id, final int service_id) {
|
||||
ThreadUtils.postToUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
AndroidGamepadManager.gamepadAdded(device_id, service_id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static void moveTaskToBack() {
|
||||
if (getGeckoInterface() != null)
|
||||
|
@ -109,7 +109,9 @@ public class GeckoEvent {
|
||||
PREFERENCES_REMOVE_OBSERVERS(41),
|
||||
TELEMETRY_UI_SESSION_START(42),
|
||||
TELEMETRY_UI_SESSION_STOP(43),
|
||||
TELEMETRY_UI_EVENT(44);
|
||||
TELEMETRY_UI_EVENT(44),
|
||||
GAMEPAD_ADDREMOVE(45),
|
||||
GAMEPAD_DATA(46);
|
||||
|
||||
public final int value;
|
||||
|
||||
@ -179,6 +181,12 @@ public class GeckoEvent {
|
||||
public static final int ACTION_MAGNIFY = 12;
|
||||
public static final int ACTION_MAGNIFY_END = 13;
|
||||
|
||||
public static final int ACTION_GAMEPAD_ADDED = 1;
|
||||
public static final int ACTION_GAMEPAD_REMOVED = 2;
|
||||
|
||||
public static final int ACTION_GAMEPAD_BUTTON = 1;
|
||||
public static final int ACTION_GAMEPAD_AXES = 2;
|
||||
|
||||
private final int mType;
|
||||
private int mAction;
|
||||
private boolean mAckNeeded;
|
||||
@ -231,6 +239,12 @@ public class GeckoEvent {
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
private int mID;
|
||||
private int mGamepadButton;
|
||||
private boolean mGamepadButtonPressed;
|
||||
private float mGamepadButtonValue;
|
||||
private float[] mGamepadValues;
|
||||
|
||||
private String[] mPrefNames;
|
||||
|
||||
private GeckoEvent(NativeGeckoEvent event) {
|
||||
@ -813,6 +827,47 @@ public class GeckoEvent {
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createGamepadAddRemoveEvent(int id, boolean added) {
|
||||
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.GAMEPAD_ADDREMOVE);
|
||||
event.mID = id;
|
||||
event.mAction = added ? ACTION_GAMEPAD_ADDED : ACTION_GAMEPAD_REMOVED;
|
||||
return event;
|
||||
}
|
||||
|
||||
private static int boolArrayToBitfield(boolean[] array) {
|
||||
int bits = 0;
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i]) {
|
||||
bits |= 1<<i;
|
||||
}
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
public static GeckoEvent createGamepadButtonEvent(int id,
|
||||
int which,
|
||||
boolean pressed,
|
||||
float value) {
|
||||
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.GAMEPAD_DATA);
|
||||
event.mID = id;
|
||||
event.mAction = ACTION_GAMEPAD_BUTTON;
|
||||
event.mGamepadButton = which;
|
||||
event.mGamepadButtonPressed = pressed;
|
||||
event.mGamepadButtonValue = value;
|
||||
return event;
|
||||
}
|
||||
|
||||
public static GeckoEvent createGamepadAxisEvent(int id, boolean[] valid,
|
||||
float[] values) {
|
||||
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.GAMEPAD_DATA);
|
||||
event.mID = id;
|
||||
event.mAction = ACTION_GAMEPAD_AXES;
|
||||
event.mFlags = boolArrayToBitfield(valid);
|
||||
event.mCount = values.length;
|
||||
event.mGamepadValues = values;
|
||||
return event;
|
||||
}
|
||||
|
||||
public void setAckNeeded(boolean ackNeeded) {
|
||||
mAckNeeded = ackNeeded;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
package org.mozilla.gecko.gfx;
|
||||
|
||||
import org.mozilla.gecko.AndroidGamepadManager;
|
||||
import org.mozilla.gecko.GeckoAccessibility;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
@ -291,6 +292,9 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (AndroidGamepadManager.handleMotionEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
if (mPanZoomController != null && mPanZoomController.onMotionEvent(event)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -109,6 +109,7 @@ gbjar.sources += [
|
||||
'ActionModeCompatView.java',
|
||||
'ActivityHandlerHelper.java',
|
||||
'AlertNotification.java',
|
||||
'AndroidGamepadManager.java',
|
||||
'animation/AnimationUtils.java',
|
||||
'animation/AnimatorProxy.java',
|
||||
'animation/HeightChangeAnimation.java',
|
||||
|
@ -169,6 +169,9 @@
|
||||
@BINPATH@/components/dom_xbl.xpt
|
||||
@BINPATH@/components/dom_xpath.xpt
|
||||
@BINPATH@/components/dom_xul.xpt
|
||||
#ifdef MOZ_GAMEPAD
|
||||
@BINPATH@/components/dom_gamepad.xpt
|
||||
#endif
|
||||
@BINPATH@/components/downloads.xpt
|
||||
@BINPATH@/components/editor.xpt
|
||||
@BINPATH@/components/embed_base.xpt
|
||||
|
@ -62,6 +62,11 @@ jfieldID AndroidGeckoEvent::jScreenOrientationField = 0;
|
||||
jfieldID AndroidGeckoEvent::jByteBufferField = 0;
|
||||
jfieldID AndroidGeckoEvent::jWidthField = 0;
|
||||
jfieldID AndroidGeckoEvent::jHeightField = 0;
|
||||
jfieldID AndroidGeckoEvent::jIDField = 0;
|
||||
jfieldID AndroidGeckoEvent::jGamepadButtonField = 0;
|
||||
jfieldID AndroidGeckoEvent::jGamepadButtonPressedField = 0;
|
||||
jfieldID AndroidGeckoEvent::jGamepadButtonValueField = 0;
|
||||
jfieldID AndroidGeckoEvent::jGamepadValuesField = 0;
|
||||
jfieldID AndroidGeckoEvent::jPrefNamesField = 0;
|
||||
|
||||
jclass AndroidGeckoEvent::jDomKeyLocationClass = 0;
|
||||
@ -167,6 +172,11 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv)
|
||||
jByteBufferField = getField("mBuffer", "Ljava/nio/ByteBuffer;");
|
||||
jWidthField = getField("mWidth", "I");
|
||||
jHeightField = getField("mHeight", "I");
|
||||
jIDField = getField("mID", "I");
|
||||
jGamepadButtonField = getField("mGamepadButton", "I");
|
||||
jGamepadButtonPressedField = getField("mGamepadButtonPressed", "Z");
|
||||
jGamepadButtonValueField = getField("mGamepadButtonValue", "F");
|
||||
jGamepadValuesField = getField("mGamepadValues", "[F");
|
||||
jPrefNamesField = getField("mPrefNames", "[Ljava/lang/String;");
|
||||
|
||||
// Init GeckoEvent.DomKeyLocation enum
|
||||
@ -586,6 +596,26 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
||||
break;
|
||||
}
|
||||
|
||||
case GAMEPAD_ADDREMOVE: {
|
||||
mID = jenv->GetIntField(jobj, jIDField);
|
||||
break;
|
||||
}
|
||||
|
||||
case GAMEPAD_DATA: {
|
||||
mID = jenv->GetIntField(jobj, jIDField);
|
||||
if (mAction == ACTION_GAMEPAD_BUTTON) {
|
||||
mGamepadButton = jenv->GetIntField(jobj, jGamepadButtonField);
|
||||
mGamepadButtonPressed = jenv->GetBooleanField(jobj, jGamepadButtonPressedField);
|
||||
mGamepadButtonValue = jenv->GetFloatField(jobj, jGamepadButtonValueField);
|
||||
} else if (mAction == ACTION_GAMEPAD_AXES) {
|
||||
// Flags is a bitfield of valid entries in gamepadvalues
|
||||
mFlags = jenv->GetIntField(jobj, jFlagsField);
|
||||
mCount = jenv->GetIntField(jobj, jCountField);
|
||||
ReadFloatArray(mGamepadValues, jenv, jGamepadValuesField, mCount);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case PREFERENCES_OBSERVE:
|
||||
case PREFERENCES_GET: {
|
||||
ReadStringArray(mPrefNames, jenv, jPrefNamesField);
|
||||
|
@ -537,6 +537,11 @@ public:
|
||||
RefCountedJavaObject* ByteBuffer() { return mByteBuffer; }
|
||||
int Width() { return mWidth; }
|
||||
int Height() { return mHeight; }
|
||||
int ID() { return mID; }
|
||||
int GamepadButton() { return mGamepadButton; }
|
||||
bool GamepadButtonPressed() { return mGamepadButtonPressed; }
|
||||
float GamepadButtonValue() { return mGamepadButtonValue; }
|
||||
const nsTArray<float>& GamepadValues() { return mGamepadValues; }
|
||||
int RequestId() { return mCount; } // for convenience
|
||||
WidgetTouchEvent MakeTouchEvent(nsIWidget* widget);
|
||||
MultiTouchInput MakeMultiTouchInput(nsIWidget* widget);
|
||||
@ -574,6 +579,11 @@ protected:
|
||||
short mScreenOrientation;
|
||||
nsRefPtr<RefCountedJavaObject> mByteBuffer;
|
||||
int mWidth, mHeight;
|
||||
int mID;
|
||||
int mGamepadButton;
|
||||
bool mGamepadButtonPressed;
|
||||
float mGamepadButtonValue;
|
||||
nsTArray<float> mGamepadValues;
|
||||
nsCOMPtr<nsIObserver> mObserver;
|
||||
nsTArray<nsString> mPrefNames;
|
||||
|
||||
@ -652,6 +662,12 @@ protected:
|
||||
static jfieldID jWidthField;
|
||||
static jfieldID jHeightField;
|
||||
|
||||
static jfieldID jIDField;
|
||||
static jfieldID jGamepadButtonField;
|
||||
static jfieldID jGamepadButtonPressedField;
|
||||
static jfieldID jGamepadButtonValueField;
|
||||
static jfieldID jGamepadValuesField;
|
||||
|
||||
static jclass jDomKeyLocationClass;
|
||||
static jfieldID jDomKeyLocationValueField;
|
||||
|
||||
@ -693,6 +709,8 @@ public:
|
||||
TELEMETRY_UI_SESSION_START = 42,
|
||||
TELEMETRY_UI_SESSION_STOP = 43,
|
||||
TELEMETRY_UI_EVENT = 44,
|
||||
GAMEPAD_ADDREMOVE = 45,
|
||||
GAMEPAD_DATA = 46,
|
||||
dummy_java_enum_list_end
|
||||
};
|
||||
|
||||
@ -719,6 +737,16 @@ public:
|
||||
IME_ACKNOWLEDGE_FOCUS = 6,
|
||||
dummy_ime_enum_list_end
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTION_GAMEPAD_ADDED = 1,
|
||||
ACTION_GAMEPAD_REMOVED = 2
|
||||
};
|
||||
|
||||
enum {
|
||||
ACTION_GAMEPAD_BUTTON = 1,
|
||||
ACTION_GAMEPAD_AXES = 2
|
||||
};
|
||||
};
|
||||
|
||||
class nsJNIString : public nsString
|
||||
|
@ -33,6 +33,7 @@ jmethodID GeckoAppShell::jEnableLocationHighAccuracy = 0;
|
||||
jmethodID GeckoAppShell::jEnableNetworkNotifications = 0;
|
||||
jmethodID GeckoAppShell::jEnableScreenOrientationNotifications = 0;
|
||||
jmethodID GeckoAppShell::jEnableSensor = 0;
|
||||
jmethodID GeckoAppShell::jGamepadAdded = 0;
|
||||
jmethodID GeckoAppShell::jGetContext = 0;
|
||||
jmethodID GeckoAppShell::jGetCurrentBatteryInformationWrapper = 0;
|
||||
jmethodID GeckoAppShell::jGetCurrentNetworkInformationWrapper = 0;
|
||||
@ -82,6 +83,8 @@ jmethodID GeckoAppShell::jSetKeepScreenOn = 0;
|
||||
jmethodID GeckoAppShell::jSetURITitle = 0;
|
||||
jmethodID GeckoAppShell::jShowAlertNotificationWrapper = 0;
|
||||
jmethodID GeckoAppShell::jShowInputMethodPicker = 0;
|
||||
jmethodID GeckoAppShell::jStartMonitoringGamepad = 0;
|
||||
jmethodID GeckoAppShell::jStopMonitoringGamepad = 0;
|
||||
jmethodID GeckoAppShell::jUnlockProfile = 0;
|
||||
jmethodID GeckoAppShell::jUnlockScreenOrientation = 0;
|
||||
jmethodID GeckoAppShell::jUnregisterSurfaceTextureFrameListener = 0;
|
||||
@ -112,6 +115,7 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) {
|
||||
jEnableNetworkNotifications = getStaticMethod("enableNetworkNotifications", "()V");
|
||||
jEnableScreenOrientationNotifications = getStaticMethod("enableScreenOrientationNotifications", "()V");
|
||||
jEnableSensor = getStaticMethod("enableSensor", "(I)V");
|
||||
jGamepadAdded = getStaticMethod("gamepadAdded", "(II)V");
|
||||
jGetContext = getStaticMethod("getContext", "()Landroid/content/Context;");
|
||||
jGetCurrentBatteryInformationWrapper = getStaticMethod("getCurrentBatteryInformation", "()[D");
|
||||
jGetCurrentNetworkInformationWrapper = getStaticMethod("getCurrentNetworkInformation", "()[D");
|
||||
@ -161,6 +165,8 @@ void GeckoAppShell::InitStubs(JNIEnv *jEnv) {
|
||||
jSetURITitle = getStaticMethod("setUriTitle", "(Ljava/lang/String;Ljava/lang/String;)V");
|
||||
jShowAlertNotificationWrapper = getStaticMethod("showAlertNotification", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||
jShowInputMethodPicker = getStaticMethod("showInputMethodPicker", "()V");
|
||||
jStartMonitoringGamepad = getStaticMethod("startMonitoringGamepad", "()V");
|
||||
jStopMonitoringGamepad = getStaticMethod("stopMonitoringGamepad", "()V");
|
||||
jUnlockProfile = getStaticMethod("unlockProfile", "()Z");
|
||||
jUnlockScreenOrientation = getStaticMethod("unlockScreenOrientation", "()V");
|
||||
jUnregisterSurfaceTextureFrameListener = getStaticMethod("unregisterSurfaceTextureFrameListener", "(Ljava/lang/Object;)V");
|
||||
@ -460,6 +466,18 @@ void GeckoAppShell::EnableSensor(int32_t a0) {
|
||||
env->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
void GeckoAppShell::GamepadAdded(int32_t a0, int32_t a1) {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (env->PushLocalFrame(0) != 0) {
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
MOZ_CRASH("Exception should have caused crash.");
|
||||
}
|
||||
|
||||
env->CallStaticVoidMethod(mGeckoAppShellClass, jGamepadAdded, a0, a1);
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
env->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
jobject GeckoAppShell::GetContext() {
|
||||
JNIEnv *env = GetJNIForThread();
|
||||
if (env->PushLocalFrame(1) != 0) {
|
||||
@ -1139,6 +1157,30 @@ void GeckoAppShell::ShowInputMethodPicker() {
|
||||
env->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
void GeckoAppShell::StartMonitoringGamepad() {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (env->PushLocalFrame(0) != 0) {
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
MOZ_CRASH("Exception should have caused crash.");
|
||||
}
|
||||
|
||||
env->CallStaticVoidMethod(mGeckoAppShellClass, jStartMonitoringGamepad);
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
env->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
void GeckoAppShell::StopMonitoringGamepad() {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (env->PushLocalFrame(0) != 0) {
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
MOZ_CRASH("Exception should have caused crash.");
|
||||
}
|
||||
|
||||
env->CallStaticVoidMethod(mGeckoAppShellClass, jStopMonitoringGamepad);
|
||||
AndroidBridge::HandleUncaughtException(env);
|
||||
env->PopLocalFrame(nullptr);
|
||||
}
|
||||
|
||||
bool GeckoAppShell::UnlockProfile() {
|
||||
JNIEnv *env = AndroidBridge::GetJNIEnv();
|
||||
if (env->PushLocalFrame(0) != 0) {
|
||||
|
@ -40,6 +40,7 @@ public:
|
||||
static void EnableNetworkNotifications();
|
||||
static void EnableScreenOrientationNotifications();
|
||||
static void EnableSensor(int32_t a0);
|
||||
static void GamepadAdded(int32_t a0, int32_t a1);
|
||||
static jobject GetContext();
|
||||
static jdoubleArray GetCurrentBatteryInformationWrapper();
|
||||
static jdoubleArray GetCurrentNetworkInformationWrapper();
|
||||
@ -89,6 +90,8 @@ public:
|
||||
static void SetURITitle(const nsAString& a0, const nsAString& a1);
|
||||
static void ShowAlertNotificationWrapper(const nsAString& a0, const nsAString& a1, const nsAString& a2, const nsAString& a3, const nsAString& a4);
|
||||
static void ShowInputMethodPicker();
|
||||
static void StartMonitoringGamepad();
|
||||
static void StopMonitoringGamepad();
|
||||
static bool UnlockProfile();
|
||||
static void UnlockScreenOrientation();
|
||||
static void UnregisterSurfaceTextureFrameListener(jobject a0);
|
||||
@ -118,6 +121,7 @@ protected:
|
||||
static jmethodID jEnableNetworkNotifications;
|
||||
static jmethodID jEnableScreenOrientationNotifications;
|
||||
static jmethodID jEnableSensor;
|
||||
static jmethodID jGamepadAdded;
|
||||
static jmethodID jGetContext;
|
||||
static jmethodID jGetCurrentBatteryInformationWrapper;
|
||||
static jmethodID jGetCurrentNetworkInformationWrapper;
|
||||
@ -167,6 +171,8 @@ protected:
|
||||
static jmethodID jSetURITitle;
|
||||
static jmethodID jShowAlertNotificationWrapper;
|
||||
static jmethodID jShowInputMethodPicker;
|
||||
static jmethodID jStartMonitoringGamepad;
|
||||
static jmethodID jStopMonitoringGamepad;
|
||||
static jmethodID jUnlockProfile;
|
||||
static jmethodID jUnlockScreenOrientation;
|
||||
static jmethodID jUnregisterSurfaceTextureFrameListener;
|
||||
|
@ -49,6 +49,7 @@ FINAL_LIBRARY = 'xul'
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/docshell/base',
|
||||
'/dom/base',
|
||||
'/dom/system/android',
|
||||
'/netwerk/cache',
|
||||
'/widget/android/android',
|
||||
|
@ -39,6 +39,9 @@
|
||||
#include <wchar.h>
|
||||
|
||||
#include "mozilla/dom/ScreenOrientation.h"
|
||||
#ifdef MOZ_GAMEPAD
|
||||
#include "mozilla/dom/GamepadService.h"
|
||||
#endif
|
||||
|
||||
#include "GeckoProfiler.h"
|
||||
#ifdef MOZ_ANDROID_HISTORY
|
||||
@ -590,6 +593,49 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait)
|
||||
curEvent->Count());
|
||||
break;
|
||||
|
||||
case AndroidGeckoEvent::GAMEPAD_ADDREMOVE: {
|
||||
#ifdef MOZ_GAMEPAD
|
||||
nsRefPtr<mozilla::dom::GamepadService> svc =
|
||||
mozilla::dom::GamepadService::GetService();
|
||||
if (svc) {
|
||||
if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_ADDED) {
|
||||
int svc_id = svc->AddGamepad("android",
|
||||
mozilla::dom::StandardMapping,
|
||||
mozilla::dom::kStandardGamepadButtons,
|
||||
mozilla::dom::kStandardGamepadAxes);
|
||||
mozilla::widget::android::GeckoAppShell::GamepadAdded(curEvent->ID(),
|
||||
svc_id);
|
||||
} else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_REMOVED) {
|
||||
svc->RemoveGamepad(curEvent->ID());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
case AndroidGeckoEvent::GAMEPAD_DATA: {
|
||||
#ifdef MOZ_GAMEPAD
|
||||
nsRefPtr<mozilla::dom::GamepadService> svc =
|
||||
mozilla::dom::GamepadService::GetService();
|
||||
if (svc) {
|
||||
int id = curEvent->ID();
|
||||
if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_BUTTON) {
|
||||
svc->NewButtonEvent(id, curEvent->GamepadButton(),
|
||||
curEvent->GamepadButtonPressed(),
|
||||
curEvent->GamepadButtonValue());
|
||||
} else if (curEvent->Action() == AndroidGeckoEvent::ACTION_GAMEPAD_AXES) {
|
||||
int valid = curEvent->Flags();
|
||||
const nsTArray<float>& values = curEvent->GamepadValues();
|
||||
for (unsigned i = 0; i < values.Length(); i++) {
|
||||
if (valid & (1<<i)) {
|
||||
svc->NewAxisMoveEvent(id, i, values[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case AndroidGeckoEvent::NOOP:
|
||||
break;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user