diff --git a/.github/workflows/switch-dev.yml b/.github/workflows/switch-dev.yml
index aabf3174..47777ad0 100644
--- a/.github/workflows/switch-dev.yml
+++ b/.github/workflows/switch-dev.yml
@@ -24,33 +24,19 @@ jobs:
sudo apt -yq update
sudo apt -yq install git build-essential cmake zip
- - name: Update pacman packages
- run: |
- dkp-pacman -Syyu --noconfirm
-
- # TODO: remove when merged
- - name: Install libarchive
- run: |
- dkp-pacman -S --noconfirm dkp-toolchain-vars
- mkdir libarchive && cd libarchive
- wget https://github.com/Cpasjuste/pacman-packages/raw/switch-libarchive/switch/libarchive/PKGBUILD
- wget https://github.com/Cpasjuste/pacman-packages/raw/switch-libarchive/switch/libarchive/libarchive-3.3.2.patch
- chown -R nobody ../libarchive
- sudo -E -u nobody dkp-makepkg
- dkp-pacman -U --noconfirm *.pkg.tar.xz
-
# TODO: remove when merged
- name: Update SDL2
run: |
git clone --depth 1 --branch switch-sdl2-2.0.14-dev https://github.com/devkitPro/SDL.git
cd SDL && mkdir build && cd build
- cmake -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake \
- -DCMAKE_INSTALL_PREFIX=/opt/devkitpro/portlibs/switch ..
+ cmake \
+ -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake \
+ -DCMAKE_INSTALL_PREFIX=/opt/devkitpro/portlibs/switch \
+ -DCMAKE_BUILD_TYPE=Debug ..
make -j $(getconf _NPROCESSORS_ONLN) install
- name: Build pfbneo
run: |
- cd $GITHUB_WORKSPACE
mkdir cmake-build-pfbneo && cd cmake-build-pfbneo
source /etc/profile.d/devkit-env.sh
cmake -G "Unix Makefiles" -DPLATFORM_SWITCH=ON -DOPTION_EMU=pfbneo -DCMAKE_BUILD_TYPE=Debug ..
@@ -59,7 +45,6 @@ jobs:
- name: Build pgen
run: |
- cd $GITHUB_WORKSPACE
mkdir cmake-build-pgen && cd cmake-build-pgen
source /etc/profile.d/devkit-env.sh
cmake -G "Unix Makefiles" -DPLATFORM_SWITCH=ON -DOPTION_EMU=pgen -DCMAKE_BUILD_TYPE=Debug ..
@@ -67,7 +52,6 @@ jobs:
- name: Build pnes
run: |
- cd $GITHUB_WORKSPACE
mkdir cmake-build-pnes && cd cmake-build-pnes
source /etc/profile.d/devkit-env.sh
cmake -G "Unix Makefiles" -DPLATFORM_SWITCH=ON -DOPTION_EMU=pnes -DCMAKE_BUILD_TYPE=Debug ..
@@ -75,7 +59,6 @@ jobs:
- name: Build psnes
run: |
- cd $GITHUB_WORKSPACE
mkdir cmake-build-psnes && cd cmake-build-psnes
source /etc/profile.d/devkit-env.sh
cmake -G "Unix Makefiles" -DPLATFORM_SWITCH=ON -DOPTION_EMU=psnes -DCMAKE_BUILD_TYPE=Debug ..
diff --git a/.github/workflows/switch-release.yml b/.github/workflows/switch-release.yml
index cbc93c5f..6885160f 100644
--- a/.github/workflows/switch-release.yml
+++ b/.github/workflows/switch-release.yml
@@ -24,27 +24,15 @@ jobs:
sudo apt -yq update
sudo apt -yq install git build-essential cmake zip
- - name: Update pacman packages
- run: |
- dkp-pacman -Syyu --noconfirm
-
# TODO: remove when merged
- - name: Install libarchive
- run: |
- dkp-pacman -S --noconfirm dkp-toolchain-vars
- mkdir libarchive && cd libarchive
- wget https://github.com/Cpasjuste/pacman-packages/raw/switch-libarchive/switch/libarchive/PKGBUILD
- wget https://github.com/Cpasjuste/pacman-packages/raw/switch-libarchive/switch/libarchive/libarchive-3.3.2.patch
- chown -R nobody ../libarchive
- sudo -E -u nobody dkp-makepkg
- dkp-pacman -U --noconfirm *.pkg.tar.xz
-
- name: Update SDL2
run: |
git clone --depth 1 --branch switch-sdl2-2.0.14-dev https://github.com/devkitPro/SDL.git
cd SDL && mkdir build && cd build
- cmake -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake \
- -DCMAKE_INSTALL_PREFIX=/opt/devkitpro/portlibs/switch ..
+ cmake \
+ -DCMAKE_TOOLCHAIN_FILE=/opt/devkitpro/cmake/Switch.cmake \
+ -DCMAKE_INSTALL_PREFIX=/opt/devkitpro/portlibs/switch \
+ -DCMAKE_BUILD_TYPE=Release ..
make -j $(getconf _NPROCESSORS_ONLN) install
- name: Build pfbneo
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 623ae4d5..f697928b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -13,6 +13,7 @@ set(OPTION_BUILTIN_LIBCONFIG OFF CACHE BOOL "Build libconfig from sources")
set(OPTION_BUILTIN_MINIZIP OFF CACHE BOOL "Build minizip from sources")
set(OPTION_MPV_PLAYER ON CACHE BOOL "Add support for video snaps (mpv)")
set(OPTION_UPDATE_CONFIG OFF CACHE BOOL "Force config file updating")
+set(OPTION_FTP_SERVER OFF CACHE BOOL "Embedded ftp server")
####################
# BUILD OPTIONS
####################
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
deleted file mode 100644
index 4689c57c..00000000
--- a/android/app/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/android/app/src/main/java/fr/mydedibox/pemu/Main.java b/android/app/src/main/java/fr/mydedibox/pemu/Main.java
deleted file mode 100644
index 1994f107..00000000
--- a/android/app/src/main/java/fr/mydedibox/pemu/Main.java
+++ /dev/null
@@ -1,152 +0,0 @@
-package fr.mydedibox.pemu;
-
-import android.os.Bundle;
-import android.os.Environment;
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Paths;
-
-import org.libsdl.app.SDLActivity;
-
-public class Main extends SDLActivity {
- String dataPath;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // set data path
- if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
- dataPath = getExternalFilesDir(null).getAbsolutePath();
- } else {
- dataPath = getFilesDir().getAbsolutePath();
- }
-
- // extract libcross2d "romfs"
- if (!isDataUpToDate("romfs")) {
- copyAssets("romfs");
- }
-
- // extract libcross2d "data"
- if (!isDataUpToDate("data")) {
- copyAssets("data");
- }
-
- super.onCreate(savedInstanceState);
- }
-
- @Override
- protected String[] getLibraries() {
- return new String[]{
- "psnes"
- };
- }
-
- public boolean isDataUpToDate(String path) {
- String storageVersion, assetVersion;
- try {
- byte[] bytes = Files.readAllBytes(Paths.get(dataPath + "/" + path + "/version"));
- storageVersion = new String(bytes).trim();
- } catch (IOException e) {
- return false;
- }
-
- try {
- InputStream is = getAssets().open(path + "/version");
- BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
- assetVersion = br.readLine().trim();
- br.close();
- } catch (IOException e) {
- return false;
- }
-
- Log.e("C2D", path + ": asset version: "
- + assetVersion + ", storage version: " + storageVersion);
-
- return assetVersion.equals(storageVersion);
- }
-
- public boolean copyAssets(String path) {
- //Log.e("C2D", "getRomFs: " + path);
- try {
- String[] list = getAssets().list(path);
- if (list.length > 0) {
- create(dataPath + "/" + path);
- for (String file : list) {
- if (!copyAssets(path + "/" + file)) {
- return false;
- } else {
- String src = path + "/" + file;
- String dst = dataPath + "/" + src;
- if (isFile(src)) {
- copy(src, dst);
- }
- }
- }
- }
- } catch (IOException e) {
- return false;
- }
-
- return true;
- }
-
- private boolean isFile(String asset) {
- try {
- InputStream in = getAssets().open(asset);
- in.close();
- } catch (IOException e) {
- return false;
- }
-
- return true;
- }
-
- private void create(String path) {
- File dir = new File(path);
- if (!dir.exists()) {
- Log.e("C2D", "create: " + dir.getAbsolutePath());
- dir.mkdirs();
- }
- }
-
- private void copy(String asset, String dst) throws IOException {
- Log.e("C2D", "copy: " + asset + " => " + dst);
- InputStream in = null;
- OutputStream out = null;
-
- try {
- byte[] buffer = new byte[1024 * 4];
- int read;
- in = getAssets().open(asset);
- out = new FileOutputStream(dst);
- while ((read = in.read(buffer)) != -1) {
- out.write(buffer, 0, read);
- }
- } catch (IOException e) {
- Log.e("C2D", "copy failed: " + dst);
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- // NOOP
- }
- }
- if (out != null) {
- try {
- out.close();
- } catch (IOException e) {
- // NOOP
- }
- }
- }
- }
-}
diff --git a/android/app/src/main/java/org/libsdl/app/HIDDevice.java b/android/app/src/main/java/org/libsdl/app/HIDDevice.java
deleted file mode 100644
index 955df5d1..00000000
--- a/android/app/src/main/java/org/libsdl/app/HIDDevice.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package org.libsdl.app;
-
-import android.hardware.usb.UsbDevice;
-
-interface HIDDevice
-{
- public int getId();
- public int getVendorId();
- public int getProductId();
- public String getSerialNumber();
- public int getVersion();
- public String getManufacturerName();
- public String getProductName();
- public UsbDevice getDevice();
- public boolean open();
- public int sendFeatureReport(byte[] report);
- public int sendOutputReport(byte[] report);
- public boolean getFeatureReport(byte[] report);
- public void setFrozen(boolean frozen);
- public void close();
- public void shutdown();
-}
diff --git a/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java b/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
deleted file mode 100644
index 65c5a423..00000000
--- a/android/app/src/main/java/org/libsdl/app/HIDDeviceBLESteamController.java
+++ /dev/null
@@ -1,650 +0,0 @@
-package org.libsdl.app;
-
-import android.content.Context;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothGatt;
-import android.bluetooth.BluetoothGattCallback;
-import android.bluetooth.BluetoothGattCharacteristic;
-import android.bluetooth.BluetoothGattDescriptor;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.bluetooth.BluetoothGattService;
-import android.hardware.usb.UsbDevice;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.os.*;
-
-//import com.android.internal.util.HexDump;
-
-import java.lang.Runnable;
-import java.util.Arrays;
-import java.util.LinkedList;
-import java.util.UUID;
-
-class HIDDeviceBLESteamController extends BluetoothGattCallback implements HIDDevice {
-
- private static final String TAG = "hidapi";
- private HIDDeviceManager mManager;
- private BluetoothDevice mDevice;
- private int mDeviceId;
- private BluetoothGatt mGatt;
- private boolean mIsRegistered = false;
- private boolean mIsConnected = false;
- private boolean mIsChromebook = false;
- private boolean mIsReconnecting = false;
- private boolean mFrozen = false;
- private LinkedList mOperations;
- GattOperation mCurrentOperation = null;
- private Handler mHandler;
-
- private static final int TRANSPORT_AUTO = 0;
- private static final int TRANSPORT_BREDR = 1;
- private static final int TRANSPORT_LE = 2;
-
- private static final int CHROMEBOOK_CONNECTION_CHECK_INTERVAL = 10000;
-
- static public final UUID steamControllerService = UUID.fromString("100F6C32-1735-4313-B402-38567131E5F3");
- static public final UUID inputCharacteristic = UUID.fromString("100F6C33-1735-4313-B402-38567131E5F3");
- static public final UUID reportCharacteristic = UUID.fromString("100F6C34-1735-4313-B402-38567131E5F3");
- static private final byte[] enterValveMode = new byte[] { (byte)0xC0, (byte)0x87, 0x03, 0x08, 0x07, 0x00 };
-
- static class GattOperation {
- private enum Operation {
- CHR_READ,
- CHR_WRITE,
- ENABLE_NOTIFICATION
- }
-
- Operation mOp;
- UUID mUuid;
- byte[] mValue;
- BluetoothGatt mGatt;
- boolean mResult = true;
-
- private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid) {
- mGatt = gatt;
- mOp = operation;
- mUuid = uuid;
- }
-
- private GattOperation(BluetoothGatt gatt, GattOperation.Operation operation, UUID uuid, byte[] value) {
- mGatt = gatt;
- mOp = operation;
- mUuid = uuid;
- mValue = value;
- }
-
- public void run() {
- // This is executed in main thread
- BluetoothGattCharacteristic chr;
-
- switch (mOp) {
- case CHR_READ:
- chr = getCharacteristic(mUuid);
- //Log.v(TAG, "Reading characteristic " + chr.getUuid());
- if (!mGatt.readCharacteristic(chr)) {
- Log.e(TAG, "Unable to read characteristic " + mUuid.toString());
- mResult = false;
- break;
- }
- mResult = true;
- break;
- case CHR_WRITE:
- chr = getCharacteristic(mUuid);
- //Log.v(TAG, "Writing characteristic " + chr.getUuid() + " value=" + HexDump.toHexString(value));
- chr.setValue(mValue);
- if (!mGatt.writeCharacteristic(chr)) {
- Log.e(TAG, "Unable to write characteristic " + mUuid.toString());
- mResult = false;
- break;
- }
- mResult = true;
- break;
- case ENABLE_NOTIFICATION:
- chr = getCharacteristic(mUuid);
- //Log.v(TAG, "Writing descriptor of " + chr.getUuid());
- if (chr != null) {
- BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
- if (cccd != null) {
- int properties = chr.getProperties();
- byte[] value;
- if ((properties & BluetoothGattCharacteristic.PROPERTY_NOTIFY) == BluetoothGattCharacteristic.PROPERTY_NOTIFY) {
- value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
- } else if ((properties & BluetoothGattCharacteristic.PROPERTY_INDICATE) == BluetoothGattCharacteristic.PROPERTY_INDICATE) {
- value = BluetoothGattDescriptor.ENABLE_INDICATION_VALUE;
- } else {
- Log.e(TAG, "Unable to start notifications on input characteristic");
- mResult = false;
- return;
- }
-
- mGatt.setCharacteristicNotification(chr, true);
- cccd.setValue(value);
- if (!mGatt.writeDescriptor(cccd)) {
- Log.e(TAG, "Unable to write descriptor " + mUuid.toString());
- mResult = false;
- return;
- }
- mResult = true;
- }
- }
- }
- }
-
- public boolean finish() {
- return mResult;
- }
-
- private BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
- BluetoothGattService valveService = mGatt.getService(steamControllerService);
- if (valveService == null)
- return null;
- return valveService.getCharacteristic(uuid);
- }
-
- static public GattOperation readCharacteristic(BluetoothGatt gatt, UUID uuid) {
- return new GattOperation(gatt, Operation.CHR_READ, uuid);
- }
-
- static public GattOperation writeCharacteristic(BluetoothGatt gatt, UUID uuid, byte[] value) {
- return new GattOperation(gatt, Operation.CHR_WRITE, uuid, value);
- }
-
- static public GattOperation enableNotification(BluetoothGatt gatt, UUID uuid) {
- return new GattOperation(gatt, Operation.ENABLE_NOTIFICATION, uuid);
- }
- }
-
- public HIDDeviceBLESteamController(HIDDeviceManager manager, BluetoothDevice device) {
- mManager = manager;
- mDevice = device;
- mDeviceId = mManager.getDeviceIDForIdentifier(getIdentifier());
- mIsRegistered = false;
- mIsChromebook = mManager.getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
- mOperations = new LinkedList();
- mHandler = new Handler(Looper.getMainLooper());
-
- mGatt = connectGatt();
- // final HIDDeviceBLESteamController finalThis = this;
- // mHandler.postDelayed(new Runnable() {
- // @Override
- // public void run() {
- // finalThis.checkConnectionForChromebookIssue();
- // }
- // }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
- }
-
- public String getIdentifier() {
- return String.format("SteamController.%s", mDevice.getAddress());
- }
-
- public BluetoothGatt getGatt() {
- return mGatt;
- }
-
- // Because on Chromebooks we show up as a dual-mode device, it will attempt to connect TRANSPORT_AUTO, which will use TRANSPORT_BREDR instead
- // of TRANSPORT_LE. Let's force ourselves to connect low energy.
- private BluetoothGatt connectGatt(boolean managed) {
- if (Build.VERSION.SDK_INT >= 23) {
- try {
- return mDevice.connectGatt(mManager.getContext(), managed, this, TRANSPORT_LE);
- } catch (Exception e) {
- return mDevice.connectGatt(mManager.getContext(), managed, this);
- }
- } else {
- return mDevice.connectGatt(mManager.getContext(), managed, this);
- }
- }
-
- private BluetoothGatt connectGatt() {
- return connectGatt(false);
- }
-
- protected int getConnectionState() {
-
- Context context = mManager.getContext();
- if (context == null) {
- // We are lacking any context to get our Bluetooth information. We'll just assume disconnected.
- return BluetoothProfile.STATE_DISCONNECTED;
- }
-
- BluetoothManager btManager = (BluetoothManager)context.getSystemService(Context.BLUETOOTH_SERVICE);
- if (btManager == null) {
- // This device doesn't support Bluetooth. We should never be here, because how did
- // we instantiate a device to start with?
- return BluetoothProfile.STATE_DISCONNECTED;
- }
-
- return btManager.getConnectionState(mDevice, BluetoothProfile.GATT);
- }
-
- public void reconnect() {
-
- if (getConnectionState() != BluetoothProfile.STATE_CONNECTED) {
- mGatt.disconnect();
- mGatt = connectGatt();
- }
-
- }
-
- protected void checkConnectionForChromebookIssue() {
- if (!mIsChromebook) {
- // We only do this on Chromebooks, because otherwise it's really annoying to just attempt
- // over and over.
- return;
- }
-
- int connectionState = getConnectionState();
-
- switch (connectionState) {
- case BluetoothProfile.STATE_CONNECTED:
- if (!mIsConnected) {
- // We are in the Bad Chromebook Place. We can force a disconnect
- // to try to recover.
- Log.v(TAG, "Chromebook: We are in a very bad state; the controller shows as connected in the underlying Bluetooth layer, but we never received a callback. Forcing a reconnect.");
- mIsReconnecting = true;
- mGatt.disconnect();
- mGatt = connectGatt(false);
- break;
- }
- else if (!isRegistered()) {
- if (mGatt.getServices().size() > 0) {
- Log.v(TAG, "Chromebook: We are connected to a controller, but never got our registration. Trying to recover.");
- probeService(this);
- }
- else {
- Log.v(TAG, "Chromebook: We are connected to a controller, but never discovered services. Trying to recover.");
- mIsReconnecting = true;
- mGatt.disconnect();
- mGatt = connectGatt(false);
- break;
- }
- }
- else {
- Log.v(TAG, "Chromebook: We are connected, and registered. Everything's good!");
- return;
- }
- break;
-
- case BluetoothProfile.STATE_DISCONNECTED:
- Log.v(TAG, "Chromebook: We have either been disconnected, or the Chromebook BtGatt.ContextMap bug has bitten us. Attempting a disconnect/reconnect, but we may not be able to recover.");
-
- mIsReconnecting = true;
- mGatt.disconnect();
- mGatt = connectGatt(false);
- break;
-
- case BluetoothProfile.STATE_CONNECTING:
- Log.v(TAG, "Chromebook: We're still trying to connect. Waiting a bit longer.");
- break;
- }
-
- final HIDDeviceBLESteamController finalThis = this;
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- finalThis.checkConnectionForChromebookIssue();
- }
- }, CHROMEBOOK_CONNECTION_CHECK_INTERVAL);
- }
-
- private boolean isRegistered() {
- return mIsRegistered;
- }
-
- private void setRegistered() {
- mIsRegistered = true;
- }
-
- private boolean probeService(HIDDeviceBLESteamController controller) {
-
- if (isRegistered()) {
- return true;
- }
-
- if (!mIsConnected) {
- return false;
- }
-
- Log.v(TAG, "probeService controller=" + controller);
-
- for (BluetoothGattService service : mGatt.getServices()) {
- if (service.getUuid().equals(steamControllerService)) {
- Log.v(TAG, "Found Valve steam controller service " + service.getUuid());
-
- for (BluetoothGattCharacteristic chr : service.getCharacteristics()) {
- if (chr.getUuid().equals(inputCharacteristic)) {
- Log.v(TAG, "Found input characteristic");
- // Start notifications
- BluetoothGattDescriptor cccd = chr.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"));
- if (cccd != null) {
- enableNotification(chr.getUuid());
- }
- }
- }
- return true;
- }
- }
-
- if ((mGatt.getServices().size() == 0) && mIsChromebook && !mIsReconnecting) {
- Log.e(TAG, "Chromebook: Discovered services were empty; this almost certainly means the BtGatt.ContextMap bug has bitten us.");
- mIsConnected = false;
- mIsReconnecting = true;
- mGatt.disconnect();
- mGatt = connectGatt(false);
- }
-
- return false;
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- private void finishCurrentGattOperation() {
- GattOperation op = null;
- synchronized (mOperations) {
- if (mCurrentOperation != null) {
- op = mCurrentOperation;
- mCurrentOperation = null;
- }
- }
- if (op != null) {
- boolean result = op.finish(); // TODO: Maybe in main thread as well?
-
- // Our operation failed, let's add it back to the beginning of our queue.
- if (!result) {
- mOperations.addFirst(op);
- }
- }
- executeNextGattOperation();
- }
-
- private void executeNextGattOperation() {
- synchronized (mOperations) {
- if (mCurrentOperation != null)
- return;
-
- if (mOperations.isEmpty())
- return;
-
- mCurrentOperation = mOperations.removeFirst();
- }
-
- // Run in main thread
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (mOperations) {
- if (mCurrentOperation == null) {
- Log.e(TAG, "Current operation null in executor?");
- return;
- }
-
- mCurrentOperation.run();
- // now wait for the GATT callback and when it comes, finish this operation
- }
- }
- });
- }
-
- private void queueGattOperation(GattOperation op) {
- synchronized (mOperations) {
- mOperations.add(op);
- }
- executeNextGattOperation();
- }
-
- private void enableNotification(UUID chrUuid) {
- GattOperation op = HIDDeviceBLESteamController.GattOperation.enableNotification(mGatt, chrUuid);
- queueGattOperation(op);
- }
-
- public void writeCharacteristic(UUID uuid, byte[] value) {
- GattOperation op = HIDDeviceBLESteamController.GattOperation.writeCharacteristic(mGatt, uuid, value);
- queueGattOperation(op);
- }
-
- public void readCharacteristic(UUID uuid) {
- GattOperation op = HIDDeviceBLESteamController.GattOperation.readCharacteristic(mGatt, uuid);
- queueGattOperation(op);
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////////// BluetoothGattCallback overridden methods
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- public void onConnectionStateChange(BluetoothGatt g, int status, int newState) {
- //Log.v(TAG, "onConnectionStateChange status=" + status + " newState=" + newState);
- mIsReconnecting = false;
- if (newState == 2) {
- mIsConnected = true;
- // Run directly, without GattOperation
- if (!isRegistered()) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mGatt.discoverServices();
- }
- });
- }
- }
- else if (newState == 0) {
- mIsConnected = false;
- }
-
- // Disconnection is handled in SteamLink using the ACTION_ACL_DISCONNECTED Intent.
- }
-
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
- //Log.v(TAG, "onServicesDiscovered status=" + status);
- if (status == 0) {
- if (gatt.getServices().size() == 0) {
- Log.v(TAG, "onServicesDiscovered returned zero services; something has gone horribly wrong down in Android's Bluetooth stack.");
- mIsReconnecting = true;
- mIsConnected = false;
- gatt.disconnect();
- mGatt = connectGatt(false);
- }
- else {
- probeService(this);
- }
- }
- }
-
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
- //Log.v(TAG, "onCharacteristicRead status=" + status + " uuid=" + characteristic.getUuid());
-
- if (characteristic.getUuid().equals(reportCharacteristic) && !mFrozen) {
- mManager.HIDDeviceFeatureReport(getId(), characteristic.getValue());
- }
-
- finishCurrentGattOperation();
- }
-
- public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
- //Log.v(TAG, "onCharacteristicWrite status=" + status + " uuid=" + characteristic.getUuid());
-
- if (characteristic.getUuid().equals(reportCharacteristic)) {
- // Only register controller with the native side once it has been fully configured
- if (!isRegistered()) {
- Log.v(TAG, "Registering Steam Controller with ID: " + getId());
- mManager.HIDDeviceConnected(getId(), getIdentifier(), getVendorId(), getProductId(), getSerialNumber(), getVersion(), getManufacturerName(), getProductName(), 0, 0, 0, 0);
- setRegistered();
- }
- }
-
- finishCurrentGattOperation();
- }
-
- public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
- // Enable this for verbose logging of controller input reports
- //Log.v(TAG, "onCharacteristicChanged uuid=" + characteristic.getUuid() + " data=" + HexDump.dumpHexString(characteristic.getValue()));
-
- if (characteristic.getUuid().equals(inputCharacteristic) && !mFrozen) {
- mManager.HIDDeviceInputReport(getId(), characteristic.getValue());
- }
- }
-
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
- //Log.v(TAG, "onDescriptorRead status=" + status);
- }
-
- public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
- BluetoothGattCharacteristic chr = descriptor.getCharacteristic();
- //Log.v(TAG, "onDescriptorWrite status=" + status + " uuid=" + chr.getUuid() + " descriptor=" + descriptor.getUuid());
-
- if (chr.getUuid().equals(inputCharacteristic)) {
- boolean hasWrittenInputDescriptor = true;
- BluetoothGattCharacteristic reportChr = chr.getService().getCharacteristic(reportCharacteristic);
- if (reportChr != null) {
- Log.v(TAG, "Writing report characteristic to enter valve mode");
- reportChr.setValue(enterValveMode);
- gatt.writeCharacteristic(reportChr);
- }
- }
-
- finishCurrentGattOperation();
- }
-
- public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
- //Log.v(TAG, "onReliableWriteCompleted status=" + status);
- }
-
- public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
- //Log.v(TAG, "onReadRemoteRssi status=" + status);
- }
-
- public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
- //Log.v(TAG, "onMtuChanged status=" + status);
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //////// Public API
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- @Override
- public int getId() {
- return mDeviceId;
- }
-
- @Override
- public int getVendorId() {
- // Valve Corporation
- final int VALVE_USB_VID = 0x28DE;
- return VALVE_USB_VID;
- }
-
- @Override
- public int getProductId() {
- // We don't have an easy way to query from the Bluetooth device, but we know what it is
- final int D0G_BLE2_PID = 0x1106;
- return D0G_BLE2_PID;
- }
-
- @Override
- public String getSerialNumber() {
- // This will be read later via feature report by Steam
- return "12345";
- }
-
- @Override
- public int getVersion() {
- return 0;
- }
-
- @Override
- public String getManufacturerName() {
- return "Valve Corporation";
- }
-
- @Override
- public String getProductName() {
- return "Steam Controller";
- }
-
- @Override
- public UsbDevice getDevice() {
- return null;
- }
-
- @Override
- public boolean open() {
- return true;
- }
-
- @Override
- public int sendFeatureReport(byte[] report) {
- if (!isRegistered()) {
- Log.e(TAG, "Attempted sendFeatureReport before Steam Controller is registered!");
- if (mIsConnected) {
- probeService(this);
- }
- return -1;
- }
-
- // We need to skip the first byte, as that doesn't go over the air
- byte[] actual_report = Arrays.copyOfRange(report, 1, report.length - 1);
- //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(actual_report));
- writeCharacteristic(reportCharacteristic, actual_report);
- return report.length;
- }
-
- @Override
- public int sendOutputReport(byte[] report) {
- if (!isRegistered()) {
- Log.e(TAG, "Attempted sendOutputReport before Steam Controller is registered!");
- if (mIsConnected) {
- probeService(this);
- }
- return -1;
- }
-
- //Log.v(TAG, "sendFeatureReport " + HexDump.dumpHexString(report));
- writeCharacteristic(reportCharacteristic, report);
- return report.length;
- }
-
- @Override
- public boolean getFeatureReport(byte[] report) {
- if (!isRegistered()) {
- Log.e(TAG, "Attempted getFeatureReport before Steam Controller is registered!");
- if (mIsConnected) {
- probeService(this);
- }
- return false;
- }
-
- //Log.v(TAG, "getFeatureReport");
- readCharacteristic(reportCharacteristic);
- return true;
- }
-
- @Override
- public void close() {
- }
-
- @Override
- public void setFrozen(boolean frozen) {
- mFrozen = frozen;
- }
-
- @Override
- public void shutdown() {
- close();
-
- BluetoothGatt g = mGatt;
- if (g != null) {
- g.disconnect();
- g.close();
- mGatt = null;
- }
- mManager = null;
- mIsRegistered = false;
- mIsConnected = false;
- mOperations.clear();
- }
-
-}
-
diff --git a/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java b/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
deleted file mode 100644
index 802c7254..00000000
--- a/android/app/src/main/java/org/libsdl/app/HIDDeviceManager.java
+++ /dev/null
@@ -1,675 +0,0 @@
-package org.libsdl.app;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.PendingIntent;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.BluetoothProfile;
-import android.os.Build;
-import android.util.Log;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
-import android.hardware.usb.*;
-import android.os.Handler;
-import android.os.Looper;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-public class HIDDeviceManager {
- private static final String TAG = "hidapi";
- private static final String ACTION_USB_PERMISSION = "org.libsdl.app.USB_PERMISSION";
-
- private static HIDDeviceManager sManager;
- private static int sManagerRefCount = 0;
-
- public static HIDDeviceManager acquire(Context context) {
- if (sManagerRefCount == 0) {
- sManager = new HIDDeviceManager(context);
- }
- ++sManagerRefCount;
- return sManager;
- }
-
- public static void release(HIDDeviceManager manager) {
- if (manager == sManager) {
- --sManagerRefCount;
- if (sManagerRefCount == 0) {
- sManager.close();
- sManager = null;
- }
- }
- }
-
- private Context mContext;
- private HashMap mDevicesById = new HashMap();
- private HashMap mBluetoothDevices = new HashMap();
- private int mNextDeviceId = 0;
- private SharedPreferences mSharedPreferences = null;
- private boolean mIsChromebook = false;
- private UsbManager mUsbManager;
- private Handler mHandler;
- private BluetoothManager mBluetoothManager;
- private List mLastBluetoothDevices;
-
- private final BroadcastReceiver mUsbBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
- UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- handleUsbDeviceAttached(usbDevice);
- } else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
- UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- handleUsbDeviceDetached(usbDevice);
- } else if (action.equals(HIDDeviceManager.ACTION_USB_PERMISSION)) {
- UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
- handleUsbDevicePermission(usbDevice, intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false));
- }
- }
- };
-
- private final BroadcastReceiver mBluetoothBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- // Bluetooth device was connected. If it was a Steam Controller, handle it
- if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- Log.d(TAG, "Bluetooth device connected: " + device);
-
- if (isSteamController(device)) {
- connectBluetoothDevice(device);
- }
- }
-
- // Bluetooth device was disconnected, remove from controller manager (if any)
- if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) {
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- Log.d(TAG, "Bluetooth device disconnected: " + device);
-
- disconnectBluetoothDevice(device);
- }
- }
- };
-
- private HIDDeviceManager(final Context context) {
- mContext = context;
-
- HIDDeviceRegisterCallback();
-
- mSharedPreferences = mContext.getSharedPreferences("hidapi", Context.MODE_PRIVATE);
- mIsChromebook = mContext.getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
-
-// if (shouldClear) {
-// SharedPreferences.Editor spedit = mSharedPreferences.edit();
-// spedit.clear();
-// spedit.commit();
-// }
-// else
- {
- mNextDeviceId = mSharedPreferences.getInt("next_device_id", 0);
- }
- }
-
- public Context getContext() {
- return mContext;
- }
-
- public int getDeviceIDForIdentifier(String identifier) {
- SharedPreferences.Editor spedit = mSharedPreferences.edit();
-
- int result = mSharedPreferences.getInt(identifier, 0);
- if (result == 0) {
- result = mNextDeviceId++;
- spedit.putInt("next_device_id", mNextDeviceId);
- }
-
- spedit.putInt(identifier, result);
- spedit.commit();
- return result;
- }
-
- private void initializeUSB() {
- mUsbManager = (UsbManager)mContext.getSystemService(Context.USB_SERVICE);
- if (mUsbManager == null) {
- return;
- }
-
- /*
- // Logging
- for (UsbDevice device : mUsbManager.getDeviceList().values()) {
- Log.i(TAG,"Path: " + device.getDeviceName());
- Log.i(TAG,"Manufacturer: " + device.getManufacturerName());
- Log.i(TAG,"Product: " + device.getProductName());
- Log.i(TAG,"ID: " + device.getDeviceId());
- Log.i(TAG,"Class: " + device.getDeviceClass());
- Log.i(TAG,"Protocol: " + device.getDeviceProtocol());
- Log.i(TAG,"Vendor ID " + device.getVendorId());
- Log.i(TAG,"Product ID: " + device.getProductId());
- Log.i(TAG,"Interface count: " + device.getInterfaceCount());
- Log.i(TAG,"---------------------------------------");
-
- // Get interface details
- for (int index = 0; index < device.getInterfaceCount(); index++) {
- UsbInterface mUsbInterface = device.getInterface(index);
- Log.i(TAG," ***** *****");
- Log.i(TAG," Interface index: " + index);
- Log.i(TAG," Interface ID: " + mUsbInterface.getId());
- Log.i(TAG," Interface class: " + mUsbInterface.getInterfaceClass());
- Log.i(TAG," Interface subclass: " + mUsbInterface.getInterfaceSubclass());
- Log.i(TAG," Interface protocol: " + mUsbInterface.getInterfaceProtocol());
- Log.i(TAG," Endpoint count: " + mUsbInterface.getEndpointCount());
-
- // Get endpoint details
- for (int epi = 0; epi < mUsbInterface.getEndpointCount(); epi++)
- {
- UsbEndpoint mEndpoint = mUsbInterface.getEndpoint(epi);
- Log.i(TAG," ++++ ++++ ++++");
- Log.i(TAG," Endpoint index: " + epi);
- Log.i(TAG," Attributes: " + mEndpoint.getAttributes());
- Log.i(TAG," Direction: " + mEndpoint.getDirection());
- Log.i(TAG," Number: " + mEndpoint.getEndpointNumber());
- Log.i(TAG," Interval: " + mEndpoint.getInterval());
- Log.i(TAG," Packet size: " + mEndpoint.getMaxPacketSize());
- Log.i(TAG," Type: " + mEndpoint.getType());
- }
- }
- }
- Log.i(TAG," No more devices connected.");
- */
-
- // Register for USB broadcasts and permission completions
- IntentFilter filter = new IntentFilter();
- filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
- filter.addAction(HIDDeviceManager.ACTION_USB_PERMISSION);
- mContext.registerReceiver(mUsbBroadcast, filter);
-
- for (UsbDevice usbDevice : mUsbManager.getDeviceList().values()) {
- handleUsbDeviceAttached(usbDevice);
- }
- }
-
- UsbManager getUSBManager() {
- return mUsbManager;
- }
-
- private void shutdownUSB() {
- try {
- mContext.unregisterReceiver(mUsbBroadcast);
- } catch (Exception e) {
- // We may not have registered, that's okay
- }
- }
-
- private boolean isHIDDeviceInterface(UsbDevice usbDevice, UsbInterface usbInterface) {
- if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_HID) {
- return true;
- }
- if (isXbox360Controller(usbDevice, usbInterface) || isXboxOneController(usbDevice, usbInterface)) {
- return true;
- }
- return false;
- }
-
- private boolean isXbox360Controller(UsbDevice usbDevice, UsbInterface usbInterface) {
- final int XB360_IFACE_SUBCLASS = 93;
- final int XB360_IFACE_PROTOCOL = 1; // Wired
- final int XB360W_IFACE_PROTOCOL = 129; // Wireless
- final int[] SUPPORTED_VENDORS = {
- 0x0079, // GPD Win 2
- 0x044f, // Thrustmaster
- 0x045e, // Microsoft
- 0x046d, // Logitech
- 0x056e, // Elecom
- 0x06a3, // Saitek
- 0x0738, // Mad Catz
- 0x07ff, // Mad Catz
- 0x0e6f, // PDP
- 0x0f0d, // Hori
- 0x1038, // SteelSeries
- 0x11c9, // Nacon
- 0x12ab, // Unknown
- 0x1430, // RedOctane
- 0x146b, // BigBen
- 0x1532, // Razer Sabertooth
- 0x15e4, // Numark
- 0x162e, // Joytech
- 0x1689, // Razer Onza
- 0x1949, // Lab126, Inc.
- 0x1bad, // Harmonix
- 0x24c6, // PowerA
- };
-
- if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
- usbInterface.getInterfaceSubclass() == XB360_IFACE_SUBCLASS &&
- (usbInterface.getInterfaceProtocol() == XB360_IFACE_PROTOCOL ||
- usbInterface.getInterfaceProtocol() == XB360W_IFACE_PROTOCOL)) {
- int vendor_id = usbDevice.getVendorId();
- for (int supportedVid : SUPPORTED_VENDORS) {
- if (vendor_id == supportedVid) {
- return true;
- }
- }
- }
- return false;
- }
-
- private boolean isXboxOneController(UsbDevice usbDevice, UsbInterface usbInterface) {
- final int XB1_IFACE_SUBCLASS = 71;
- final int XB1_IFACE_PROTOCOL = 208;
- final int[] SUPPORTED_VENDORS = {
- 0x045e, // Microsoft
- 0x0738, // Mad Catz
- 0x0e6f, // PDP
- 0x0f0d, // Hori
- 0x1532, // Razer Wildcat
- 0x24c6, // PowerA
- 0x2e24, // Hyperkin
- };
-
- if (usbInterface.getId() == 0 &&
- usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_VENDOR_SPEC &&
- usbInterface.getInterfaceSubclass() == XB1_IFACE_SUBCLASS &&
- usbInterface.getInterfaceProtocol() == XB1_IFACE_PROTOCOL) {
- int vendor_id = usbDevice.getVendorId();
- for (int supportedVid : SUPPORTED_VENDORS) {
- if (vendor_id == supportedVid) {
- return true;
- }
- }
- }
- return false;
- }
-
- private void handleUsbDeviceAttached(UsbDevice usbDevice) {
- connectHIDDeviceUSB(usbDevice);
- }
-
- private void handleUsbDeviceDetached(UsbDevice usbDevice) {
- List devices = new ArrayList();
- for (HIDDevice device : mDevicesById.values()) {
- if (usbDevice.equals(device.getDevice())) {
- devices.add(device.getId());
- }
- }
- for (int id : devices) {
- HIDDevice device = mDevicesById.get(id);
- mDevicesById.remove(id);
- device.shutdown();
- HIDDeviceDisconnected(id);
- }
- }
-
- private void handleUsbDevicePermission(UsbDevice usbDevice, boolean permission_granted) {
- for (HIDDevice device : mDevicesById.values()) {
- if (usbDevice.equals(device.getDevice())) {
- boolean opened = false;
- if (permission_granted) {
- opened = device.open();
- }
- HIDDeviceOpenResult(device.getId(), opened);
- }
- }
- }
-
- private void connectHIDDeviceUSB(UsbDevice usbDevice) {
- synchronized (this) {
- int interface_mask = 0;
- for (int interface_index = 0; interface_index < usbDevice.getInterfaceCount(); interface_index++) {
- UsbInterface usbInterface = usbDevice.getInterface(interface_index);
- if (isHIDDeviceInterface(usbDevice, usbInterface)) {
- // Check to see if we've already added this interface
- // This happens with the Xbox Series X controller which has a duplicate interface 0, which is inactive
- int interface_id = usbInterface.getId();
- if ((interface_mask & (1 << interface_id)) != 0) {
- continue;
- }
- interface_mask |= (1 << interface_id);
-
- HIDDeviceUSB device = new HIDDeviceUSB(this, usbDevice, interface_index);
- int id = device.getId();
- mDevicesById.put(id, device);
- HIDDeviceConnected(id, device.getIdentifier(), device.getVendorId(), device.getProductId(), device.getSerialNumber(), device.getVersion(), device.getManufacturerName(), device.getProductName(), usbInterface.getId(), usbInterface.getInterfaceClass(), usbInterface.getInterfaceSubclass(), usbInterface.getInterfaceProtocol());
- }
- }
- }
- }
-
- private void initializeBluetooth() {
- Log.d(TAG, "Initializing Bluetooth");
-
- if (Build.VERSION.SDK_INT <= 30 &&
- mContext.getPackageManager().checkPermission(android.Manifest.permission.BLUETOOTH, mContext.getPackageName()) != PackageManager.PERMISSION_GRANTED) {
- Log.d(TAG, "Couldn't initialize Bluetooth, missing android.permission.BLUETOOTH");
- return;
- }
-
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE) || (Build.VERSION.SDK_INT < 18)) {
- Log.d(TAG, "Couldn't initialize Bluetooth, this version of Android does not support Bluetooth LE");
- return;
- }
-
- // Find bonded bluetooth controllers and create SteamControllers for them
- mBluetoothManager = (BluetoothManager)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
- if (mBluetoothManager == null) {
- // This device doesn't support Bluetooth.
- return;
- }
-
- BluetoothAdapter btAdapter = mBluetoothManager.getAdapter();
- if (btAdapter == null) {
- // This device has Bluetooth support in the codebase, but has no available adapters.
- return;
- }
-
- // Get our bonded devices.
- for (BluetoothDevice device : btAdapter.getBondedDevices()) {
-
- Log.d(TAG, "Bluetooth device available: " + device);
- if (isSteamController(device)) {
- connectBluetoothDevice(device);
- }
-
- }
-
- // NOTE: These don't work on Chromebooks, to my undying dismay.
- IntentFilter filter = new IntentFilter();
- filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
- filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
- mContext.registerReceiver(mBluetoothBroadcast, filter);
-
- if (mIsChromebook) {
- mHandler = new Handler(Looper.getMainLooper());
- mLastBluetoothDevices = new ArrayList();
-
- // final HIDDeviceManager finalThis = this;
- // mHandler.postDelayed(new Runnable() {
- // @Override
- // public void run() {
- // finalThis.chromebookConnectionHandler();
- // }
- // }, 5000);
- }
- }
-
- private void shutdownBluetooth() {
- try {
- mContext.unregisterReceiver(mBluetoothBroadcast);
- } catch (Exception e) {
- // We may not have registered, that's okay
- }
- }
-
- // Chromebooks do not pass along ACTION_ACL_CONNECTED / ACTION_ACL_DISCONNECTED properly.
- // This function provides a sort of dummy version of that, watching for changes in the
- // connected devices and attempting to add controllers as things change.
- public void chromebookConnectionHandler() {
- if (!mIsChromebook) {
- return;
- }
-
- ArrayList disconnected = new ArrayList();
- ArrayList connected = new ArrayList();
-
- List currentConnected = mBluetoothManager.getConnectedDevices(BluetoothProfile.GATT);
-
- for (BluetoothDevice bluetoothDevice : currentConnected) {
- if (!mLastBluetoothDevices.contains(bluetoothDevice)) {
- connected.add(bluetoothDevice);
- }
- }
- for (BluetoothDevice bluetoothDevice : mLastBluetoothDevices) {
- if (!currentConnected.contains(bluetoothDevice)) {
- disconnected.add(bluetoothDevice);
- }
- }
-
- mLastBluetoothDevices = currentConnected;
-
- for (BluetoothDevice bluetoothDevice : disconnected) {
- disconnectBluetoothDevice(bluetoothDevice);
- }
- for (BluetoothDevice bluetoothDevice : connected) {
- connectBluetoothDevice(bluetoothDevice);
- }
-
- final HIDDeviceManager finalThis = this;
- mHandler.postDelayed(new Runnable() {
- @Override
- public void run() {
- finalThis.chromebookConnectionHandler();
- }
- }, 10000);
- }
-
- public boolean connectBluetoothDevice(BluetoothDevice bluetoothDevice) {
- Log.v(TAG, "connectBluetoothDevice device=" + bluetoothDevice);
- synchronized (this) {
- if (mBluetoothDevices.containsKey(bluetoothDevice)) {
- Log.v(TAG, "Steam controller with address " + bluetoothDevice + " already exists, attempting reconnect");
-
- HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
- device.reconnect();
-
- return false;
- }
- HIDDeviceBLESteamController device = new HIDDeviceBLESteamController(this, bluetoothDevice);
- int id = device.getId();
- mBluetoothDevices.put(bluetoothDevice, device);
- mDevicesById.put(id, device);
-
- // The Steam Controller will mark itself connected once initialization is complete
- }
- return true;
- }
-
- public void disconnectBluetoothDevice(BluetoothDevice bluetoothDevice) {
- synchronized (this) {
- HIDDeviceBLESteamController device = mBluetoothDevices.get(bluetoothDevice);
- if (device == null)
- return;
-
- int id = device.getId();
- mBluetoothDevices.remove(bluetoothDevice);
- mDevicesById.remove(id);
- device.shutdown();
- HIDDeviceDisconnected(id);
- }
- }
-
- public boolean isSteamController(BluetoothDevice bluetoothDevice) {
- // Sanity check. If you pass in a null device, by definition it is never a Steam Controller.
- if (bluetoothDevice == null) {
- return false;
- }
-
- // If the device has no local name, we really don't want to try an equality check against it.
- if (bluetoothDevice.getName() == null) {
- return false;
- }
-
- return bluetoothDevice.getName().equals("SteamController") && ((bluetoothDevice.getType() & BluetoothDevice.DEVICE_TYPE_LE) != 0);
- }
-
- private void close() {
- shutdownUSB();
- shutdownBluetooth();
- synchronized (this) {
- for (HIDDevice device : mDevicesById.values()) {
- device.shutdown();
- }
- mDevicesById.clear();
- mBluetoothDevices.clear();
- HIDDeviceReleaseCallback();
- }
- }
-
- public void setFrozen(boolean frozen) {
- synchronized (this) {
- for (HIDDevice device : mDevicesById.values()) {
- device.setFrozen(frozen);
- }
- }
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- private HIDDevice getDevice(int id) {
- synchronized (this) {
- HIDDevice result = mDevicesById.get(id);
- if (result == null) {
- Log.v(TAG, "No device for id: " + id);
- Log.v(TAG, "Available devices: " + mDevicesById.keySet());
- }
- return result;
- }
- }
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- ////////// JNI interface functions
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- public boolean initialize(boolean usb, boolean bluetooth) {
- Log.v(TAG, "initialize(" + usb + ", " + bluetooth + ")");
-
- if (usb) {
- initializeUSB();
- }
- if (bluetooth) {
- initializeBluetooth();
- }
- return true;
- }
-
- public boolean openDevice(int deviceID) {
- Log.v(TAG, "openDevice deviceID=" + deviceID);
- HIDDevice device = getDevice(deviceID);
- if (device == null) {
- HIDDeviceDisconnected(deviceID);
- return false;
- }
-
- // Look to see if this is a USB device and we have permission to access it
- UsbDevice usbDevice = device.getDevice();
- if (usbDevice != null && !mUsbManager.hasPermission(usbDevice)) {
- HIDDeviceOpenPending(deviceID);
- try {
- final int FLAG_MUTABLE = 0x02000000; // PendingIntent.FLAG_MUTABLE, but don't require SDK 31
- int flags;
- if (Build.VERSION.SDK_INT >= 31) {
- flags = FLAG_MUTABLE;
- } else {
- flags = 0;
- }
- mUsbManager.requestPermission(usbDevice, PendingIntent.getBroadcast(mContext, 0, new Intent(HIDDeviceManager.ACTION_USB_PERMISSION), flags));
- } catch (Exception e) {
- Log.v(TAG, "Couldn't request permission for USB device " + usbDevice);
- HIDDeviceOpenResult(deviceID, false);
- }
- return false;
- }
-
- try {
- return device.open();
- } catch (Exception e) {
- Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
- }
- return false;
- }
-
- public int sendOutputReport(int deviceID, byte[] report) {
- try {
- //Log.v(TAG, "sendOutputReport deviceID=" + deviceID + " length=" + report.length);
- HIDDevice device;
- device = getDevice(deviceID);
- if (device == null) {
- HIDDeviceDisconnected(deviceID);
- return -1;
- }
-
- return device.sendOutputReport(report);
- } catch (Exception e) {
- Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
- }
- return -1;
- }
-
- public int sendFeatureReport(int deviceID, byte[] report) {
- try {
- //Log.v(TAG, "sendFeatureReport deviceID=" + deviceID + " length=" + report.length);
- HIDDevice device;
- device = getDevice(deviceID);
- if (device == null) {
- HIDDeviceDisconnected(deviceID);
- return -1;
- }
-
- return device.sendFeatureReport(report);
- } catch (Exception e) {
- Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
- }
- return -1;
- }
-
- public boolean getFeatureReport(int deviceID, byte[] report) {
- try {
- //Log.v(TAG, "getFeatureReport deviceID=" + deviceID);
- HIDDevice device;
- device = getDevice(deviceID);
- if (device == null) {
- HIDDeviceDisconnected(deviceID);
- return false;
- }
-
- return device.getFeatureReport(report);
- } catch (Exception e) {
- Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
- }
- return false;
- }
-
- public void closeDevice(int deviceID) {
- try {
- Log.v(TAG, "closeDevice deviceID=" + deviceID);
- HIDDevice device;
- device = getDevice(deviceID);
- if (device == null) {
- HIDDeviceDisconnected(deviceID);
- return;
- }
-
- device.close();
- } catch (Exception e) {
- Log.e(TAG, "Got exception: " + Log.getStackTraceString(e));
- }
- }
-
-
- //////////////////////////////////////////////////////////////////////////////////////////////////////
- /////////////// Native methods
- //////////////////////////////////////////////////////////////////////////////////////////////////////
-
- private native void HIDDeviceRegisterCallback();
- private native void HIDDeviceReleaseCallback();
-
- native void HIDDeviceConnected(int deviceID, String identifier, int vendorId, int productId, String serial_number, int release_number, String manufacturer_string, String product_string, int interface_number, int interface_class, int interface_subclass, int interface_protocol);
- native void HIDDeviceOpenPending(int deviceID);
- native void HIDDeviceOpenResult(int deviceID, boolean opened);
- native void HIDDeviceDisconnected(int deviceID);
-
- native void HIDDeviceInputReport(int deviceID, byte[] report);
- native void HIDDeviceFeatureReport(int deviceID, byte[] report);
-}
diff --git a/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java b/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
deleted file mode 100644
index d20fe80b..00000000
--- a/android/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
+++ /dev/null
@@ -1,309 +0,0 @@
-package org.libsdl.app;
-
-import android.hardware.usb.*;
-import android.os.Build;
-import android.util.Log;
-import java.util.Arrays;
-
-class HIDDeviceUSB implements HIDDevice {
-
- private static final String TAG = "hidapi";
-
- protected HIDDeviceManager mManager;
- protected UsbDevice mDevice;
- protected int mInterfaceIndex;
- protected int mInterface;
- protected int mDeviceId;
- protected UsbDeviceConnection mConnection;
- protected UsbEndpoint mInputEndpoint;
- protected UsbEndpoint mOutputEndpoint;
- protected InputThread mInputThread;
- protected boolean mRunning;
- protected boolean mFrozen;
-
- public HIDDeviceUSB(HIDDeviceManager manager, UsbDevice usbDevice, int interface_index) {
- mManager = manager;
- mDevice = usbDevice;
- mInterfaceIndex = interface_index;
- mInterface = mDevice.getInterface(mInterfaceIndex).getId();
- mDeviceId = manager.getDeviceIDForIdentifier(getIdentifier());
- mRunning = false;
- }
-
- public String getIdentifier() {
- return String.format("%s/%x/%x/%d", mDevice.getDeviceName(), mDevice.getVendorId(), mDevice.getProductId(), mInterfaceIndex);
- }
-
- @Override
- public int getId() {
- return mDeviceId;
- }
-
- @Override
- public int getVendorId() {
- return mDevice.getVendorId();
- }
-
- @Override
- public int getProductId() {
- return mDevice.getProductId();
- }
-
- @Override
- public String getSerialNumber() {
- String result = null;
- if (Build.VERSION.SDK_INT >= 21) {
- try {
- result = mDevice.getSerialNumber();
- }
- catch (SecurityException exception) {
- //Log.w(TAG, "App permissions mean we cannot get serial number for device " + getDeviceName() + " message: " + exception.getMessage());
- }
- }
- if (result == null) {
- result = "";
- }
- return result;
- }
-
- @Override
- public int getVersion() {
- return 0;
- }
-
- @Override
- public String getManufacturerName() {
- String result = null;
- if (Build.VERSION.SDK_INT >= 21) {
- result = mDevice.getManufacturerName();
- }
- if (result == null) {
- result = String.format("%x", getVendorId());
- }
- return result;
- }
-
- @Override
- public String getProductName() {
- String result = null;
- if (Build.VERSION.SDK_INT >= 21) {
- result = mDevice.getProductName();
- }
- if (result == null) {
- result = String.format("%x", getProductId());
- }
- return result;
- }
-
- @Override
- public UsbDevice getDevice() {
- return mDevice;
- }
-
- public String getDeviceName() {
- return getManufacturerName() + " " + getProductName() + "(0x" + String.format("%x", getVendorId()) + "/0x" + String.format("%x", getProductId()) + ")";
- }
-
- @Override
- public boolean open() {
- mConnection = mManager.getUSBManager().openDevice(mDevice);
- if (mConnection == null) {
- Log.w(TAG, "Unable to open USB device " + getDeviceName());
- return false;
- }
-
- // Force claim our interface
- UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
- if (!mConnection.claimInterface(iface, true)) {
- Log.w(TAG, "Failed to claim interfaces on USB device " + getDeviceName());
- close();
- return false;
- }
-
- // Find the endpoints
- for (int j = 0; j < iface.getEndpointCount(); j++) {
- UsbEndpoint endpt = iface.getEndpoint(j);
- switch (endpt.getDirection()) {
- case UsbConstants.USB_DIR_IN:
- if (mInputEndpoint == null) {
- mInputEndpoint = endpt;
- }
- break;
- case UsbConstants.USB_DIR_OUT:
- if (mOutputEndpoint == null) {
- mOutputEndpoint = endpt;
- }
- break;
- }
- }
-
- // Make sure the required endpoints were present
- if (mInputEndpoint == null || mOutputEndpoint == null) {
- Log.w(TAG, "Missing required endpoint on USB device " + getDeviceName());
- close();
- return false;
- }
-
- // Start listening for input
- mRunning = true;
- mInputThread = new InputThread();
- mInputThread.start();
-
- return true;
- }
-
- @Override
- public int sendFeatureReport(byte[] report) {
- int res = -1;
- int offset = 0;
- int length = report.length;
- boolean skipped_report_id = false;
- byte report_number = report[0];
-
- if (report_number == 0x0) {
- ++offset;
- --length;
- skipped_report_id = true;
- }
-
- res = mConnection.controlTransfer(
- UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_OUT,
- 0x09/*HID set_report*/,
- (3/*HID feature*/ << 8) | report_number,
- mInterface,
- report, offset, length,
- 1000/*timeout millis*/);
-
- if (res < 0) {
- Log.w(TAG, "sendFeatureReport() returned " + res + " on device " + getDeviceName());
- return -1;
- }
-
- if (skipped_report_id) {
- ++length;
- }
- return length;
- }
-
- @Override
- public int sendOutputReport(byte[] report) {
- int r = mConnection.bulkTransfer(mOutputEndpoint, report, report.length, 1000);
- if (r != report.length) {
- Log.w(TAG, "sendOutputReport() returned " + r + " on device " + getDeviceName());
- }
- return r;
- }
-
- @Override
- public boolean getFeatureReport(byte[] report) {
- int res = -1;
- int offset = 0;
- int length = report.length;
- boolean skipped_report_id = false;
- byte report_number = report[0];
-
- if (report_number == 0x0) {
- /* Offset the return buffer by 1, so that the report ID
- will remain in byte 0. */
- ++offset;
- --length;
- skipped_report_id = true;
- }
-
- res = mConnection.controlTransfer(
- UsbConstants.USB_TYPE_CLASS | 0x01 /*RECIPIENT_INTERFACE*/ | UsbConstants.USB_DIR_IN,
- 0x01/*HID get_report*/,
- (3/*HID feature*/ << 8) | report_number,
- mInterface,
- report, offset, length,
- 1000/*timeout millis*/);
-
- if (res < 0) {
- Log.w(TAG, "getFeatureReport() returned " + res + " on device " + getDeviceName());
- return false;
- }
-
- if (skipped_report_id) {
- ++res;
- ++length;
- }
-
- byte[] data;
- if (res == length) {
- data = report;
- } else {
- data = Arrays.copyOfRange(report, 0, res);
- }
- mManager.HIDDeviceFeatureReport(mDeviceId, data);
-
- return true;
- }
-
- @Override
- public void close() {
- mRunning = false;
- if (mInputThread != null) {
- while (mInputThread.isAlive()) {
- mInputThread.interrupt();
- try {
- mInputThread.join();
- } catch (InterruptedException e) {
- // Keep trying until we're done
- }
- }
- mInputThread = null;
- }
- if (mConnection != null) {
- UsbInterface iface = mDevice.getInterface(mInterfaceIndex);
- mConnection.releaseInterface(iface);
- mConnection.close();
- mConnection = null;
- }
- }
-
- @Override
- public void shutdown() {
- close();
- mManager = null;
- }
-
- @Override
- public void setFrozen(boolean frozen) {
- mFrozen = frozen;
- }
-
- protected class InputThread extends Thread {
- @Override
- public void run() {
- int packetSize = mInputEndpoint.getMaxPacketSize();
- byte[] packet = new byte[packetSize];
- while (mRunning) {
- int r;
- try
- {
- r = mConnection.bulkTransfer(mInputEndpoint, packet, packetSize, 1000);
- }
- catch (Exception e)
- {
- Log.v(TAG, "Exception in UsbDeviceConnection bulktransfer: " + e);
- break;
- }
- if (r < 0) {
- // Could be a timeout or an I/O error
- }
- if (r > 0) {
- byte[] data;
- if (r == packetSize) {
- data = packet;
- } else {
- data = Arrays.copyOfRange(packet, 0, r);
- }
-
- if (!mFrozen) {
- mManager.HIDDeviceInputReport(mDeviceId, data);
- }
- }
- }
- }
- }
-}
diff --git a/android/app/src/main/java/org/libsdl/app/SDL.java b/android/app/src/main/java/org/libsdl/app/SDL.java
deleted file mode 100644
index dafc0cb8..00000000
--- a/android/app/src/main/java/org/libsdl/app/SDL.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package org.libsdl.app;
-
-import android.content.Context;
-
-import java.lang.Class;
-import java.lang.reflect.Method;
-
-/**
- SDL library initialization
-*/
-public class SDL {
-
- // This function should be called first and sets up the native code
- // so it can call into the Java classes
- public static void setupJNI() {
- SDLActivity.nativeSetupJNI();
- SDLAudioManager.nativeSetupJNI();
- SDLControllerManager.nativeSetupJNI();
- }
-
- // This function should be called each time the activity is started
- public static void initialize() {
- setContext(null);
-
- SDLActivity.initialize();
- SDLAudioManager.initialize();
- SDLControllerManager.initialize();
- }
-
- // This function stores the current activity (SDL or not)
- public static void setContext(Context context) {
- mContext = context;
- }
-
- public static Context getContext() {
- return mContext;
- }
-
- public static void loadLibrary(String libraryName) throws UnsatisfiedLinkError, SecurityException, NullPointerException {
-
- if (libraryName == null) {
- throw new NullPointerException("No library name provided.");
- }
-
- try {
- // Let's see if we have ReLinker available in the project. This is necessary for
- // some projects that have huge numbers of local libraries bundled, and thus may
- // trip a bug in Android's native library loader which ReLinker works around. (If
- // loadLibrary works properly, ReLinker will simply use the normal Android method
- // internally.)
- //
- // To use ReLinker, just add it as a dependency. For more information, see
- // https://github.com/KeepSafe/ReLinker for ReLinker's repository.
- //
- Class> relinkClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker");
- Class> relinkListenerClass = mContext.getClassLoader().loadClass("com.getkeepsafe.relinker.ReLinker$LoadListener");
- Class> contextClass = mContext.getClassLoader().loadClass("android.content.Context");
- Class> stringClass = mContext.getClassLoader().loadClass("java.lang.String");
-
- // Get a 'force' instance of the ReLinker, so we can ensure libraries are reinstalled if
- // they've changed during updates.
- Method forceMethod = relinkClass.getDeclaredMethod("force");
- Object relinkInstance = forceMethod.invoke(null);
- Class> relinkInstanceClass = relinkInstance.getClass();
-
- // Actually load the library!
- Method loadMethod = relinkInstanceClass.getDeclaredMethod("loadLibrary", contextClass, stringClass, stringClass, relinkListenerClass);
- loadMethod.invoke(relinkInstance, mContext, libraryName, null, null);
- }
- catch (final Throwable e) {
- // Fall back
- try {
- System.loadLibrary(libraryName);
- }
- catch (final UnsatisfiedLinkError ule) {
- throw ule;
- }
- catch (final SecurityException se) {
- throw se;
- }
- }
- }
-
- protected static Context mContext;
-}
diff --git a/android/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/app/src/main/java/org/libsdl/app/SDLActivity.java
deleted file mode 100644
index ed0159e5..00000000
--- a/android/app/src/main/java/org/libsdl/app/SDLActivity.java
+++ /dev/null
@@ -1,2448 +0,0 @@
-package org.libsdl.app;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.app.Dialog;
-import android.app.UiModeManager;
-import android.content.ClipboardManager;
-import android.content.ClipData;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.PorterDuff;
-import android.graphics.drawable.Drawable;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.text.InputType;
-import android.util.DisplayMetrics;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.PointerIcon;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Window;
-import android.view.WindowManager;
-import android.view.inputmethod.BaseInputConnection;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.Hashtable;
-import java.util.Locale;
-
-
-/**
- SDL Activity
- */
-public class SDLActivity extends Activity implements View.OnSystemUiVisibilityChangeListener {
- private static final String TAG = "SDL";
-/*
- // Display InputType.SOURCE/CLASS of events and devices
- //
- // SDLActivity.debugSource(device.getSources(), "device[" + device.getName() + "]");
- // SDLActivity.debugSource(event.getSource(), "event");
- public static void debugSource(int sources, String prefix) {
- int s = sources;
- int s_copy = sources;
- String cls = "";
- String src = "";
- int tst = 0;
- int FLAG_TAINTED = 0x80000000;
-
- if ((s & InputDevice.SOURCE_CLASS_BUTTON) != 0) cls += " BUTTON";
- if ((s & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) cls += " JOYSTICK";
- if ((s & InputDevice.SOURCE_CLASS_POINTER) != 0) cls += " POINTER";
- if ((s & InputDevice.SOURCE_CLASS_POSITION) != 0) cls += " POSITION";
- if ((s & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) cls += " TRACKBALL";
-
-
- int s2 = s_copy & ~InputDevice.SOURCE_ANY; // keep class bits
- s2 &= ~( InputDevice.SOURCE_CLASS_BUTTON
- | InputDevice.SOURCE_CLASS_JOYSTICK
- | InputDevice.SOURCE_CLASS_POINTER
- | InputDevice.SOURCE_CLASS_POSITION
- | InputDevice.SOURCE_CLASS_TRACKBALL);
-
- if (s2 != 0) cls += "Some_Unkown";
-
- s2 = s_copy & InputDevice.SOURCE_ANY; // keep source only, no class;
-
- tst = InputDevice.SOURCE_BLUETOOTH_STYLUS;
- if ((s & tst) == tst) src += " BLUETOOTH_STYLUS";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_DPAD;
- if ((s & tst) == tst) src += " DPAD";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_GAMEPAD;
- if ((s & tst) == tst) src += " GAMEPAD";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_HDMI;
- if ((s & tst) == tst) src += " HDMI";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_JOYSTICK;
- if ((s & tst) == tst) src += " JOYSTICK";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_KEYBOARD;
- if ((s & tst) == tst) src += " KEYBOARD";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_MOUSE;
- if ((s & tst) == tst) src += " MOUSE";
- s2 &= ~tst;
-
- if (Build.VERSION.SDK_INT >= 26) {
- tst = InputDevice.SOURCE_MOUSE_RELATIVE;
- if ((s & tst) == tst) src += " MOUSE_RELATIVE";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_ROTARY_ENCODER;
- if ((s & tst) == tst) src += " ROTARY_ENCODER";
- s2 &= ~tst;
- }
- tst = InputDevice.SOURCE_STYLUS;
- if ((s & tst) == tst) src += " STYLUS";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_TOUCHPAD;
- if ((s & tst) == tst) src += " TOUCHPAD";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_TOUCHSCREEN;
- if ((s & tst) == tst) src += " TOUCHSCREEN";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_TOUCH_NAVIGATION;
- if ((s & tst) == tst) src += " TOUCH_NAVIGATION";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_TRACKBALL;
- if ((s & tst) == tst) src += " TRACKBALL";
- s2 &= ~tst;
-
- tst = InputDevice.SOURCE_ANY;
- if ((s & tst) == tst) src += " ANY";
- s2 &= ~tst;
-
- if (s == FLAG_TAINTED) src += " FLAG_TAINTED";
- s2 &= ~FLAG_TAINTED;
-
- if (s2 != 0) src += " Some_Unkown";
-
- Log.v(TAG, prefix + "int=" + s_copy + " CLASS={" + cls + " } source(s):" + src);
- }
-*/
-
- public static boolean mIsResumedCalled, mHasFocus;
- public static final boolean mHasMultiWindow = (Build.VERSION.SDK_INT >= 24);
-
- // Cursor types
- // private static final int SDL_SYSTEM_CURSOR_NONE = -1;
- private static final int SDL_SYSTEM_CURSOR_ARROW = 0;
- private static final int SDL_SYSTEM_CURSOR_IBEAM = 1;
- private static final int SDL_SYSTEM_CURSOR_WAIT = 2;
- private static final int SDL_SYSTEM_CURSOR_CROSSHAIR = 3;
- private static final int SDL_SYSTEM_CURSOR_WAITARROW = 4;
- private static final int SDL_SYSTEM_CURSOR_SIZENWSE = 5;
- private static final int SDL_SYSTEM_CURSOR_SIZENESW = 6;
- private static final int SDL_SYSTEM_CURSOR_SIZEWE = 7;
- private static final int SDL_SYSTEM_CURSOR_SIZENS = 8;
- private static final int SDL_SYSTEM_CURSOR_SIZEALL = 9;
- private static final int SDL_SYSTEM_CURSOR_NO = 10;
- private static final int SDL_SYSTEM_CURSOR_HAND = 11;
-
- protected static final int SDL_ORIENTATION_UNKNOWN = 0;
- protected static final int SDL_ORIENTATION_LANDSCAPE = 1;
- protected static final int SDL_ORIENTATION_LANDSCAPE_FLIPPED = 2;
- protected static final int SDL_ORIENTATION_PORTRAIT = 3;
- protected static final int SDL_ORIENTATION_PORTRAIT_FLIPPED = 4;
-
- protected static int mCurrentOrientation;
- protected static Locale mCurrentLocale;
-
- // Handle the state of the native layer
- public enum NativeState {
- INIT, RESUMED, PAUSED
- }
-
- public static NativeState mNextNativeState;
- public static NativeState mCurrentNativeState;
-
- /** If shared libraries (e.g. SDL or the native application) could not be loaded. */
- public static boolean mBrokenLibraries = true;
-
- // Main components
- protected static SDLActivity mSingleton;
- protected static SDLSurface mSurface;
- protected static View mTextEdit;
- protected static boolean mScreenKeyboardShown;
- protected static ViewGroup mLayout;
- protected static SDLClipboardHandler mClipboardHandler;
- protected static Hashtable mCursors;
- protected static int mLastCursorID;
- protected static SDLGenericMotionListener_API12 mMotionListener;
- protected static HIDDeviceManager mHIDDeviceManager;
-
- // This is what SDL runs in. It invokes SDL_main(), eventually
- protected static Thread mSDLThread;
-
- protected static SDLGenericMotionListener_API12 getMotionListener() {
- if (mMotionListener == null) {
- if (Build.VERSION.SDK_INT >= 26) {
- mMotionListener = new SDLGenericMotionListener_API26();
- } else if (Build.VERSION.SDK_INT >= 24) {
- mMotionListener = new SDLGenericMotionListener_API24();
- } else {
- mMotionListener = new SDLGenericMotionListener_API12();
- }
- }
-
- return mMotionListener;
- }
-
- /**
- * This method returns the name of the shared object with the application entry point
- * It can be overridden by derived classes.
- */
- protected String getMainSharedObject() {
- String library;
- String[] libraries = SDLActivity.mSingleton.getLibraries();
- if (libraries.length > 0) {
- library = "lib" + libraries[libraries.length - 1] + ".so";
- } else {
- library = "libmain.so";
- }
- return getContext().getApplicationInfo().nativeLibraryDir + "/" + library;
- }
-
- /**
- * This method returns the name of the application entry point
- * It can be overridden by derived classes.
- */
- protected String getMainFunction() {
- return "SDL_main";
- }
-
- /**
- * This method is called by SDL before loading the native shared libraries.
- * It can be overridden to provide names of shared libraries to be loaded.
- * The default implementation returns the defaults. It never returns null.
- * An array returned by a new implementation must at least contain "SDL2".
- * Also keep in mind that the order the libraries are loaded may matter.
- * @return names of shared libraries to be loaded (e.g. "SDL2", "main").
- */
- protected String[] getLibraries() {
- return new String[] {
- "SDL2",
- // "SDL2_image",
- // "SDL2_mixer",
- // "SDL2_net",
- // "SDL2_ttf",
- "main"
- };
- }
-
- // Load the .so
- public void loadLibraries() {
- for (String lib : getLibraries()) {
- SDL.loadLibrary(lib);
- }
- }
-
- /**
- * This method is called by SDL before starting the native application thread.
- * It can be overridden to provide the arguments after the application name.
- * The default implementation returns an empty array. It never returns null.
- * @return arguments for the native application.
- */
- protected String[] getArguments() {
- return new String[0];
- }
-
- public static void initialize() {
- // The static nature of the singleton and Android quirkyness force us to initialize everything here
- // Otherwise, when exiting the app and returning to it, these variables *keep* their pre exit values
- mSingleton = null;
- mSurface = null;
- mTextEdit = null;
- mLayout = null;
- mClipboardHandler = null;
- mCursors = new Hashtable();
- mLastCursorID = 0;
- mSDLThread = null;
- mIsResumedCalled = false;
- mHasFocus = true;
- mNextNativeState = NativeState.INIT;
- mCurrentNativeState = NativeState.INIT;
- }
-
- // Setup
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- Log.v(TAG, "Device: " + Build.DEVICE);
- Log.v(TAG, "Model: " + Build.MODEL);
- Log.v(TAG, "onCreate()");
- super.onCreate(savedInstanceState);
-
- try {
- Thread.currentThread().setName("SDLActivity");
- } catch (Exception e) {
- Log.v(TAG, "modify thread properties failed " + e.toString());
- }
-
- // Load shared libraries
- String errorMsgBrokenLib = "";
- try {
- loadLibraries();
- mBrokenLibraries = false; /* success */
- } catch(UnsatisfiedLinkError e) {
- System.err.println(e.getMessage());
- mBrokenLibraries = true;
- errorMsgBrokenLib = e.getMessage();
- } catch(Exception e) {
- System.err.println(e.getMessage());
- mBrokenLibraries = true;
- errorMsgBrokenLib = e.getMessage();
- }
-
- if (mBrokenLibraries)
- {
- mSingleton = this;
- AlertDialog.Builder dlgAlert = new AlertDialog.Builder(this);
- dlgAlert.setMessage("An error occurred while trying to start the application. Please try again and/or reinstall."
- + System.getProperty("line.separator")
- + System.getProperty("line.separator")
- + "Error: " + errorMsgBrokenLib);
- dlgAlert.setTitle("SDL Error");
- dlgAlert.setPositiveButton("Exit",
- new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog,int id) {
- // if this button is clicked, close current activity
- SDLActivity.mSingleton.finish();
- }
- });
- dlgAlert.setCancelable(false);
- dlgAlert.create().show();
-
- return;
- }
-
- // Set up JNI
- SDL.setupJNI();
-
- // Initialize state
- SDL.initialize();
-
- // So we can call stuff from static callbacks
- mSingleton = this;
- SDL.setContext(this);
-
- mClipboardHandler = new SDLClipboardHandler();
-
- mHIDDeviceManager = HIDDeviceManager.acquire(this);
-
- // Set up the surface
- mSurface = new SDLSurface(getApplication());
-
- mLayout = new RelativeLayout(this);
- mLayout.addView(mSurface);
-
- // Get our current screen orientation and pass it down.
- mCurrentOrientation = SDLActivity.getCurrentOrientation();
- // Only record current orientation
- SDLActivity.onNativeOrientationChanged(mCurrentOrientation);
-
- try {
- if (Build.VERSION.SDK_INT < 24) {
- mCurrentLocale = getContext().getResources().getConfiguration().locale;
- } else {
- mCurrentLocale = getContext().getResources().getConfiguration().getLocales().get(0);
- }
- } catch(Exception ignored) {
- }
-
- setContentView(mLayout);
-
- setWindowStyle(true);
-
- getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this);
-
- // Get filename from "Open with" of another application
- Intent intent = getIntent();
- if (intent != null && intent.getData() != null) {
- String filename = intent.getData().getPath();
- if (filename != null) {
- Log.v(TAG, "Got filename: " + filename);
- SDLActivity.onNativeDropFile(filename);
- }
- }
- }
-
- protected void pauseNativeThread() {
- mNextNativeState = NativeState.PAUSED;
- mIsResumedCalled = false;
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- SDLActivity.handleNativeState();
- }
-
- protected void resumeNativeThread() {
- mNextNativeState = NativeState.RESUMED;
- mIsResumedCalled = true;
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- SDLActivity.handleNativeState();
- }
-
- // Events
- @Override
- protected void onPause() {
- Log.v(TAG, "onPause()");
- super.onPause();
-
- if (mHIDDeviceManager != null) {
- mHIDDeviceManager.setFrozen(true);
- }
- if (!mHasMultiWindow) {
- pauseNativeThread();
- }
- }
-
- @Override
- protected void onResume() {
- Log.v(TAG, "onResume()");
- super.onResume();
-
- if (mHIDDeviceManager != null) {
- mHIDDeviceManager.setFrozen(false);
- }
- if (!mHasMultiWindow) {
- resumeNativeThread();
- }
- }
-
- @Override
- protected void onStop() {
- Log.v(TAG, "onStop()");
- super.onStop();
- if (mHasMultiWindow) {
- pauseNativeThread();
- }
- }
-
- @Override
- protected void onStart() {
- Log.v(TAG, "onStart()");
- super.onStart();
- if (mHasMultiWindow) {
- resumeNativeThread();
- }
- }
-
- public static int getCurrentOrientation() {
- int result = SDL_ORIENTATION_UNKNOWN;
-
- Activity activity = (Activity)getContext();
- if (activity == null) {
- return result;
- }
- Display display = activity.getWindowManager().getDefaultDisplay();
-
- switch (display.getRotation()) {
- case Surface.ROTATION_0:
- result = SDL_ORIENTATION_PORTRAIT;
- break;
-
- case Surface.ROTATION_90:
- result = SDL_ORIENTATION_LANDSCAPE;
- break;
-
- case Surface.ROTATION_180:
- result = SDL_ORIENTATION_PORTRAIT_FLIPPED;
- break;
-
- case Surface.ROTATION_270:
- result = SDL_ORIENTATION_LANDSCAPE_FLIPPED;
- break;
- }
-
- return result;
- }
-
- @Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
- Log.v(TAG, "onWindowFocusChanged(): " + hasFocus);
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- mHasFocus = hasFocus;
- if (hasFocus) {
- mNextNativeState = NativeState.RESUMED;
- SDLActivity.getMotionListener().reclaimRelativeMouseModeIfNeeded();
-
- SDLActivity.handleNativeState();
- nativeFocusChanged(true);
-
- } else {
- nativeFocusChanged(false);
- if (!mHasMultiWindow) {
- mNextNativeState = NativeState.PAUSED;
- SDLActivity.handleNativeState();
- }
- }
- }
-
- @Override
- public void onLowMemory() {
- Log.v(TAG, "onLowMemory()");
- super.onLowMemory();
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- SDLActivity.nativeLowMemory();
- }
-
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- Log.v(TAG, "onConfigurationChanged()");
- super.onConfigurationChanged(newConfig);
-
- if (SDLActivity.mBrokenLibraries) {
- return;
- }
-
- if (mCurrentLocale == null || !mCurrentLocale.equals(newConfig.locale)) {
- mCurrentLocale = newConfig.locale;
- SDLActivity.onNativeLocaleChanged();
- }
- }
-
- @Override
- protected void onDestroy() {
- Log.v(TAG, "onDestroy()");
-
- if (mHIDDeviceManager != null) {
- HIDDeviceManager.release(mHIDDeviceManager);
- mHIDDeviceManager = null;
- }
-
- if (SDLActivity.mBrokenLibraries) {
- super.onDestroy();
- return;
- }
-
- if (SDLActivity.mSDLThread != null) {
-
- // Send Quit event to "SDLThread" thread
- SDLActivity.nativeSendQuit();
-
- // Wait for "SDLThread" thread to end
- try {
- SDLActivity.mSDLThread.join();
- } catch(Exception e) {
- Log.v(TAG, "Problem stopping SDLThread: " + e);
- }
- }
-
- SDLActivity.nativeQuit();
-
- super.onDestroy();
- }
-
- @Override
- public void onBackPressed() {
- // Check if we want to block the back button in case of mouse right click.
- //
- // If we do, the normal hardware back button will no longer work and people have to use home,
- // but the mouse right click will work.
- //
- boolean trapBack = SDLActivity.nativeGetHintBoolean("SDL_ANDROID_TRAP_BACK_BUTTON", false);
- if (trapBack) {
- // Exit and let the mouse handler handle this button (if appropriate)
- return;
- }
-
- // Default system back button behavior.
- if (!isFinishing()) {
- super.onBackPressed();
- }
- }
-
- // Called by JNI from SDL.
- public static void manualBackButton() {
- mSingleton.pressBackButton();
- }
-
- // Used to get us onto the activity's main thread
- public void pressBackButton() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (!SDLActivity.this.isFinishing()) {
- SDLActivity.this.superOnBackPressed();
- }
- }
- });
- }
-
- // Used to access the system back behavior.
- public void superOnBackPressed() {
- super.onBackPressed();
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
-
- if (SDLActivity.mBrokenLibraries) {
- return false;
- }
-
- int keyCode = event.getKeyCode();
- // Ignore certain special keys so they're handled by Android
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN ||
- keyCode == KeyEvent.KEYCODE_VOLUME_UP ||
- keyCode == KeyEvent.KEYCODE_CAMERA ||
- keyCode == KeyEvent.KEYCODE_ZOOM_IN || /* API 11 */
- keyCode == KeyEvent.KEYCODE_ZOOM_OUT /* API 11 */
- ) {
- return false;
- }
- return super.dispatchKeyEvent(event);
- }
-
- /* Transition to next state */
- public static void handleNativeState() {
-
- if (mNextNativeState == mCurrentNativeState) {
- // Already in same state, discard.
- return;
- }
-
- // Try a transition to init state
- if (mNextNativeState == NativeState.INIT) {
-
- mCurrentNativeState = mNextNativeState;
- return;
- }
-
- // Try a transition to paused state
- if (mNextNativeState == NativeState.PAUSED) {
- if (mSDLThread != null) {
- nativePause();
- }
- if (mSurface != null) {
- mSurface.handlePause();
- }
- mCurrentNativeState = mNextNativeState;
- return;
- }
-
- // Try a transition to resumed state
- if (mNextNativeState == NativeState.RESUMED) {
- if (mSurface.mIsSurfaceReady && mHasFocus && mIsResumedCalled) {
- if (mSDLThread == null) {
- // This is the entry point to the C app.
- // Start up the C app thread and enable sensor input for the first time
- // FIXME: Why aren't we enabling sensor input at start?
-
- mSDLThread = new Thread(new SDLMain(), "SDLThread");
- mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true);
- mSDLThread.start();
-
- // No nativeResume(), don't signal Android_ResumeSem
- } else {
- nativeResume();
- }
- mSurface.handleResume();
-
- mCurrentNativeState = mNextNativeState;
- }
- }
- }
-
- // Messages from the SDLMain thread
- static final int COMMAND_CHANGE_TITLE = 1;
- static final int COMMAND_CHANGE_WINDOW_STYLE = 2;
- static final int COMMAND_TEXTEDIT_HIDE = 3;
- static final int COMMAND_SET_KEEP_SCREEN_ON = 5;
-
- protected static final int COMMAND_USER = 0x8000;
-
- protected static boolean mFullscreenModeActive;
-
- /**
- * This method is called by SDL if SDL did not handle a message itself.
- * This happens if a received message contains an unsupported command.
- * Method can be overwritten to handle Messages in a different class.
- * @param command the command of the message.
- * @param param the parameter of the message. May be null.
- * @return if the message was handled in overridden method.
- */
- protected boolean onUnhandledMessage(int command, Object param) {
- return false;
- }
-
- /**
- * A Handler class for Messages from native SDL applications.
- * It uses current Activities as target (e.g. for the title).
- * static to prevent implicit references to enclosing object.
- */
- protected static class SDLCommandHandler extends Handler {
- @Override
- public void handleMessage(Message msg) {
- Context context = SDL.getContext();
- if (context == null) {
- Log.e(TAG, "error handling message, getContext() returned null");
- return;
- }
- switch (msg.arg1) {
- case COMMAND_CHANGE_TITLE:
- if (context instanceof Activity) {
- ((Activity) context).setTitle((String)msg.obj);
- } else {
- Log.e(TAG, "error handling message, getContext() returned no Activity");
- }
- break;
- case COMMAND_CHANGE_WINDOW_STYLE:
- if (Build.VERSION.SDK_INT >= 19) {
- if (context instanceof Activity) {
- Window window = ((Activity) context).getWindow();
- if (window != null) {
- if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) {
- int flags = View.SYSTEM_UI_FLAG_FULLSCREEN |
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.INVISIBLE;
- window.getDecorView().setSystemUiVisibility(flags);
- window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- window.clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- SDLActivity.mFullscreenModeActive = true;
- } else {
- int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE;
- window.getDecorView().setSystemUiVisibility(flags);
- window.addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
- window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
- SDLActivity.mFullscreenModeActive = false;
- }
- }
- } else {
- Log.e(TAG, "error handling message, getContext() returned no Activity");
- }
- }
- break;
- case COMMAND_TEXTEDIT_HIDE:
- if (mTextEdit != null) {
- // Note: On some devices setting view to GONE creates a flicker in landscape.
- // Setting the View's sizes to 0 is similar to GONE but without the flicker.
- // The sizes will be set to useful values when the keyboard is shown again.
- mTextEdit.setLayoutParams(new RelativeLayout.LayoutParams(0, 0));
-
- InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.hideSoftInputFromWindow(mTextEdit.getWindowToken(), 0);
-
- mScreenKeyboardShown = false;
-
- mSurface.requestFocus();
- }
- break;
- case COMMAND_SET_KEEP_SCREEN_ON:
- {
- if (context instanceof Activity) {
- Window window = ((Activity) context).getWindow();
- if (window != null) {
- if ((msg.obj instanceof Integer) && ((Integer) msg.obj != 0)) {
- window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- } else {
- window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
- }
- }
- break;
- }
- default:
- if ((context instanceof SDLActivity) && !((SDLActivity) context).onUnhandledMessage(msg.arg1, msg.obj)) {
- Log.e(TAG, "error handling message, command is " + msg.arg1);
- }
- }
- }
- }
-
- // Handler for the messages
- Handler commandHandler = new SDLCommandHandler();
-
- // Send a message from the SDLMain thread
- boolean sendCommand(int command, Object data) {
- Message msg = commandHandler.obtainMessage();
- msg.arg1 = command;
- msg.obj = data;
- boolean result = commandHandler.sendMessage(msg);
-
- if (Build.VERSION.SDK_INT >= 19) {
- if (command == COMMAND_CHANGE_WINDOW_STYLE) {
- // Ensure we don't return until the resize has actually happened,
- // or 500ms have passed.
-
- boolean bShouldWait = false;
-
- if (data instanceof Integer) {
- // Let's figure out if we're already laid out fullscreen or not.
- Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
- DisplayMetrics realMetrics = new DisplayMetrics();
- display.getRealMetrics(realMetrics);
-
- boolean bFullscreenLayout = ((realMetrics.widthPixels == mSurface.getWidth()) &&
- (realMetrics.heightPixels == mSurface.getHeight()));
-
- if ((Integer) data == 1) {
- // If we aren't laid out fullscreen or actively in fullscreen mode already, we're going
- // to change size and should wait for surfaceChanged() before we return, so the size
- // is right back in native code. If we're already laid out fullscreen, though, we're
- // not going to change size even if we change decor modes, so we shouldn't wait for
- // surfaceChanged() -- which may not even happen -- and should return immediately.
- bShouldWait = !bFullscreenLayout;
- } else {
- // If we're laid out fullscreen (even if the status bar and nav bar are present),
- // or are actively in fullscreen, we're going to change size and should wait for
- // surfaceChanged before we return, so the size is right back in native code.
- bShouldWait = bFullscreenLayout;
- }
- }
-
- if (bShouldWait && (SDLActivity.getContext() != null)) {
- // We'll wait for the surfaceChanged() method, which will notify us
- // when called. That way, we know our current size is really the
- // size we need, instead of grabbing a size that's still got
- // the navigation and/or status bars before they're hidden.
- //
- // We'll wait for up to half a second, because some devices
- // take a surprisingly long time for the surface resize, but
- // then we'll just give up and return.
- //
- synchronized (SDLActivity.getContext()) {
- try {
- SDLActivity.getContext().wait(500);
- } catch (InterruptedException ie) {
- ie.printStackTrace();
- }
- }
- }
- }
- }
-
- return result;
- }
-
- // C functions we call
- public static native int nativeSetupJNI();
- public static native int nativeRunMain(String library, String function, Object arguments);
- public static native void nativeLowMemory();
- public static native void nativeSendQuit();
- public static native void nativeQuit();
- public static native void nativePause();
- public static native void nativeResume();
- public static native void nativeFocusChanged(boolean hasFocus);
- public static native void onNativeDropFile(String filename);
- public static native void nativeSetScreenResolution(int surfaceWidth, int surfaceHeight, int deviceWidth, int deviceHeight, float rate);
- public static native void onNativeResize();
- public static native void onNativeKeyDown(int keycode);
- public static native void onNativeKeyUp(int keycode);
- public static native boolean onNativeSoftReturnKey();
- public static native void onNativeKeyboardFocusLost();
- public static native void onNativeMouse(int button, int action, float x, float y, boolean relative);
- public static native void onNativeTouch(int touchDevId, int pointerFingerId,
- int action, float x,
- float y, float p);
- public static native void onNativeAccel(float x, float y, float z);
- public static native void onNativeClipboardChanged();
- public static native void onNativeSurfaceCreated();
- public static native void onNativeSurfaceChanged();
- public static native void onNativeSurfaceDestroyed();
- public static native String nativeGetHint(String name);
- public static native boolean nativeGetHintBoolean(String name, boolean default_value);
- public static native void nativeSetenv(String name, String value);
- public static native void onNativeOrientationChanged(int orientation);
- public static native void nativeAddTouch(int touchId, String name);
- public static native void nativePermissionResult(int requestCode, boolean result);
- public static native void onNativeLocaleChanged();
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean setActivityTitle(String title) {
- // Called from SDLMain() thread and can't directly affect the view
- return mSingleton.sendCommand(COMMAND_CHANGE_TITLE, title);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static void setWindowStyle(boolean fullscreen) {
- // Called from SDLMain() thread and can't directly affect the view
- mSingleton.sendCommand(COMMAND_CHANGE_WINDOW_STYLE, fullscreen ? 1 : 0);
- }
-
- /**
- * This method is called by SDL using JNI.
- * This is a static method for JNI convenience, it calls a non-static method
- * so that is can be overridden
- */
- public static void setOrientation(int w, int h, boolean resizable, String hint)
- {
- if (mSingleton != null) {
- mSingleton.setOrientationBis(w, h, resizable, hint);
- }
- }
-
- /**
- * This can be overridden
- */
- public void setOrientationBis(int w, int h, boolean resizable, String hint)
- {
- int orientation_landscape = -1;
- int orientation_portrait = -1;
-
- /* If set, hint "explicitly controls which UI orientations are allowed". */
- if (hint.contains("LandscapeRight") && hint.contains("LandscapeLeft")) {
- orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE;
- } else if (hint.contains("LandscapeRight")) {
- orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- } else if (hint.contains("LandscapeLeft")) {
- orientation_landscape = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
- }
-
- if (hint.contains("Portrait") && hint.contains("PortraitUpsideDown")) {
- orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT;
- } else if (hint.contains("Portrait")) {
- orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- } else if (hint.contains("PortraitUpsideDown")) {
- orientation_portrait = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
- }
-
- boolean is_landscape_allowed = (orientation_landscape != -1);
- boolean is_portrait_allowed = (orientation_portrait != -1);
- int req; /* Requested orientation */
-
- /* No valid hint, nothing is explicitly allowed */
- if (!is_portrait_allowed && !is_landscape_allowed) {
- if (resizable) {
- /* All orientations are allowed */
- req = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
- } else {
- /* Fixed window and nothing specified. Get orientation from w/h of created window */
- req = (w > h ? ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE : ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
- }
- } else {
- /* At least one orientation is allowed */
- if (resizable) {
- if (is_portrait_allowed && is_landscape_allowed) {
- /* hint allows both landscape and portrait, promote to full sensor */
- req = ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR;
- } else {
- /* Use the only one allowed "orientation" */
- req = (is_landscape_allowed ? orientation_landscape : orientation_portrait);
- }
- } else {
- /* Fixed window and both orientations are allowed. Choose one. */
- if (is_portrait_allowed && is_landscape_allowed) {
- req = (w > h ? orientation_landscape : orientation_portrait);
- } else {
- /* Use the only one allowed "orientation" */
- req = (is_landscape_allowed ? orientation_landscape : orientation_portrait);
- }
- }
- }
-
- Log.v(TAG, "setOrientation() requestedOrientation=" + req + " width=" + w +" height="+ h +" resizable=" + resizable + " hint=" + hint);
- mSingleton.setRequestedOrientation(req);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static void minimizeWindow() {
-
- if (mSingleton == null) {
- return;
- }
-
- Intent startMain = new Intent(Intent.ACTION_MAIN);
- startMain.addCategory(Intent.CATEGORY_HOME);
- startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mSingleton.startActivity(startMain);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean shouldMinimizeOnFocusLoss() {
-/*
- if (Build.VERSION.SDK_INT >= 24) {
- if (mSingleton == null) {
- return true;
- }
-
- if (mSingleton.isInMultiWindowMode()) {
- return false;
- }
-
- if (mSingleton.isInPictureInPictureMode()) {
- return false;
- }
- }
-
- return true;
-*/
- return false;
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean isScreenKeyboardShown()
- {
- if (mTextEdit == null) {
- return false;
- }
-
- if (!mScreenKeyboardShown) {
- return false;
- }
-
- InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- return imm.isAcceptingText();
-
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean supportsRelativeMouse()
- {
- // DeX mode in Samsung Experience 9.0 and earlier doesn't support relative mice properly under
- // Android 7 APIs, and simply returns no data under Android 8 APIs.
- //
- // This is fixed in Samsung Experience 9.5, which corresponds to Android 8.1.0, and
- // thus SDK version 27. If we are in DeX mode and not API 27 or higher, as a result,
- // we should stick to relative mode.
- //
- if ((Build.VERSION.SDK_INT < 27) && isDeXMode()) {
- return false;
- }
-
- return SDLActivity.getMotionListener().supportsRelativeMouse();
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean setRelativeMouseEnabled(boolean enabled)
- {
- if (enabled && !supportsRelativeMouse()) {
- return false;
- }
-
- return SDLActivity.getMotionListener().setRelativeMouseEnabled(enabled);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean sendMessage(int command, int param) {
- if (mSingleton == null) {
- return false;
- }
- return mSingleton.sendCommand(command, param);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static Context getContext() {
- return SDL.getContext();
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean isAndroidTV() {
- UiModeManager uiModeManager = (UiModeManager) getContext().getSystemService(UI_MODE_SERVICE);
- if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
- return true;
- }
- if (Build.MANUFACTURER.equals("MINIX") && Build.MODEL.equals("NEO-U1")) {
- return true;
- }
- if (Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.equals("X96-W")) {
- return true;
- }
- return Build.MANUFACTURER.equals("Amlogic") && Build.MODEL.startsWith("TV");
- }
-
- public static double getDiagonal()
- {
- DisplayMetrics metrics = new DisplayMetrics();
- Activity activity = (Activity)getContext();
- if (activity == null) {
- return 0.0;
- }
- activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- double dWidthInches = metrics.widthPixels / (double)metrics.xdpi;
- double dHeightInches = metrics.heightPixels / (double)metrics.ydpi;
-
- return Math.sqrt((dWidthInches * dWidthInches) + (dHeightInches * dHeightInches));
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean isTablet() {
- // If our diagonal size is seven inches or greater, we consider ourselves a tablet.
- return (getDiagonal() >= 7.0);
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean isChromebook() {
- if (getContext() == null) {
- return false;
- }
- return getContext().getPackageManager().hasSystemFeature("org.chromium.arc.device_management");
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean isDeXMode() {
- if (Build.VERSION.SDK_INT < 24) {
- return false;
- }
- try {
- final Configuration config = getContext().getResources().getConfiguration();
- final Class> configClass = config.getClass();
- return configClass.getField("SEM_DESKTOP_MODE_ENABLED").getInt(configClass)
- == configClass.getField("semDesktopModeEnabled").getInt(config);
- } catch(Exception ignored) {
- return false;
- }
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static DisplayMetrics getDisplayDPI() {
- return getContext().getResources().getDisplayMetrics();
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean getManifestEnvironmentVariables() {
- try {
- if (getContext() == null) {
- return false;
- }
-
- ApplicationInfo applicationInfo = getContext().getPackageManager().getApplicationInfo(getContext().getPackageName(), PackageManager.GET_META_DATA);
- Bundle bundle = applicationInfo.metaData;
- if (bundle == null) {
- return false;
- }
- String prefix = "SDL_ENV.";
- final int trimLength = prefix.length();
- for (String key : bundle.keySet()) {
- if (key.startsWith(prefix)) {
- String name = key.substring(trimLength);
- String value = bundle.get(key).toString();
- nativeSetenv(name, value);
- }
- }
- /* environment variables set! */
- return true;
- } catch (Exception e) {
- Log.v(TAG, "exception " + e.toString());
- }
- return false;
- }
-
- // This method is called by SDLControllerManager's API 26 Generic Motion Handler.
- public static View getContentView()
- {
- return mLayout;
- }
-
- static class ShowTextInputTask implements Runnable {
- /*
- * This is used to regulate the pan&scan method to have some offset from
- * the bottom edge of the input region and the top edge of an input
- * method (soft keyboard)
- */
- static final int HEIGHT_PADDING = 15;
-
- public int x, y, w, h;
-
- public ShowTextInputTask(int x, int y, int w, int h) {
- this.x = x;
- this.y = y;
- this.w = w;
- this.h = h;
-
- /* Minimum size of 1 pixel, so it takes focus. */
- if (this.w <= 0) {
- this.w = 1;
- }
- if (this.h + HEIGHT_PADDING <= 0) {
- this.h = 1 - HEIGHT_PADDING;
- }
- }
-
- @Override
- public void run() {
- RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(w, h + HEIGHT_PADDING);
- params.leftMargin = x;
- params.topMargin = y;
-
- if (mTextEdit == null) {
- mTextEdit = new DummyEdit(SDL.getContext());
-
- mLayout.addView(mTextEdit, params);
- } else {
- mTextEdit.setLayoutParams(params);
- }
-
- mTextEdit.setVisibility(View.VISIBLE);
- mTextEdit.requestFocus();
-
- InputMethodManager imm = (InputMethodManager) SDL.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- imm.showSoftInput(mTextEdit, 0);
-
- mScreenKeyboardShown = true;
- }
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static boolean showTextInput(int x, int y, int w, int h) {
- // Transfer the task to the main thread as a Runnable
- return mSingleton.commandHandler.post(new ShowTextInputTask(x, y, w, h));
- }
-
- public static boolean isTextInputEvent(KeyEvent event) {
-
- // Key pressed with Ctrl should be sent as SDL_KEYDOWN/SDL_KEYUP and not SDL_TEXTINPUT
- if (event.isCtrlPressed()) {
- return false;
- }
-
- return event.isPrintingKey() || event.getKeyCode() == KeyEvent.KEYCODE_SPACE;
- }
-
- /**
- * This method is called by SDL using JNI.
- */
- public static Surface getNativeSurface() {
- if (SDLActivity.mSurface == null) {
- return null;
- }
- return SDLActivity.mSurface.getNativeSurface();
- }
-
- // Input
-
- /**
- * This method is called by SDL using JNI.
- */
- public static void initTouch() {
- int[] ids = InputDevice.getDeviceIds();
-
- for (int id : ids) {
- InputDevice device = InputDevice.getDevice(id);
- /* Allow SOURCE_TOUCHSCREEN and also Virtual InputDevices because they can send TOUCHSCREEN events */
- if (device != null && ((device.getSources() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN
- || device.isVirtual())) {
-
- int touchDevId = device.getId();
- /*
- * Prevent id to be -1, since it's used in SDL internal for synthetic events
- * Appears when using Android emulator, eg:
- * adb shell input mouse tap 100 100
- * adb shell input touchscreen tap 100 100
- */
- if (touchDevId < 0) {
- touchDevId -= 1;
- }
- nativeAddTouch(touchDevId, device.getName());
- }
- }
- }
-
- // Messagebox
-
- /** Result of current messagebox. Also used for blocking the calling thread. */
- protected final int[] messageboxSelection = new int[1];
-
- /**
- * This method is called by SDL using JNI.
- * Shows the messagebox from UI thread and block calling thread.
- * buttonFlags, buttonIds and buttonTexts must have same length.
- * @param buttonFlags array containing flags for every button.
- * @param buttonIds array containing id for every button.
- * @param buttonTexts array containing text for every button.
- * @param colors null for default or array of length 5 containing colors.
- * @return button id or -1.
- */
- public int messageboxShowMessageBox(
- final int flags,
- final String title,
- final String message,
- final int[] buttonFlags,
- final int[] buttonIds,
- final String[] buttonTexts,
- final int[] colors) {
-
- messageboxSelection[0] = -1;
-
- // sanity checks
-
- if ((buttonFlags.length != buttonIds.length) && (buttonIds.length != buttonTexts.length)) {
- return -1; // implementation broken
- }
-
- // collect arguments for Dialog
-
- final Bundle args = new Bundle();
- args.putInt("flags", flags);
- args.putString("title", title);
- args.putString("message", message);
- args.putIntArray("buttonFlags", buttonFlags);
- args.putIntArray("buttonIds", buttonIds);
- args.putStringArray("buttonTexts", buttonTexts);
- args.putIntArray("colors", colors);
-
- // trigger Dialog creation on UI thread
-
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- messageboxCreateAndShow(args);
- }
- });
-
- // block the calling thread
-
- synchronized (messageboxSelection) {
- try {
- messageboxSelection.wait();
- } catch (InterruptedException ex) {
- ex.printStackTrace();
- return -1;
- }
- }
-
- // return selected value
-
- return messageboxSelection[0];
- }
-
- protected void messageboxCreateAndShow(Bundle args) {
-
- // TODO set values from "flags" to messagebox dialog
-
- // get colors
-
- int[] colors = args.getIntArray("colors");
- int backgroundColor;
- int textColor;
- int buttonBorderColor;
- int buttonBackgroundColor;
- int buttonSelectedColor;
- if (colors != null) {
- int i = -1;
- backgroundColor = colors[++i];
- textColor = colors[++i];
- buttonBorderColor = colors[++i];
- buttonBackgroundColor = colors[++i];
- buttonSelectedColor = colors[++i];
- } else {
- backgroundColor = Color.TRANSPARENT;
- textColor = Color.TRANSPARENT;
- buttonBorderColor = Color.TRANSPARENT;
- buttonBackgroundColor = Color.TRANSPARENT;
- buttonSelectedColor = Color.TRANSPARENT;
- }
-
- // create dialog with title and a listener to wake up calling thread
-
- final AlertDialog dialog = new AlertDialog.Builder(this).create();
- dialog.setTitle(args.getString("title"));
- dialog.setCancelable(false);
- dialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
- @Override
- public void onDismiss(DialogInterface unused) {
- synchronized (messageboxSelection) {
- messageboxSelection.notify();
- }
- }
- });
-
- // create text
-
- TextView message = new TextView(this);
- message.setGravity(Gravity.CENTER);
- message.setText(args.getString("message"));
- if (textColor != Color.TRANSPARENT) {
- message.setTextColor(textColor);
- }
-
- // create buttons
-
- int[] buttonFlags = args.getIntArray("buttonFlags");
- int[] buttonIds = args.getIntArray("buttonIds");
- String[] buttonTexts = args.getStringArray("buttonTexts");
-
- final SparseArray