From f1bb2f3685a73c68765ada5298bfdc91fdb75e37 Mon Sep 17 00:00:00 2001 From: Narr the Reg Date: Mon, 13 Feb 2023 14:08:06 -0600 Subject: [PATCH] android: Add motion sensor --- .../yuzu/yuzu_emu/overlay/InputOverlay.java | 65 ++++++++++++++++--- .../src/main/jni/emu_window/emu_window.cpp | 20 +++--- src/input_common/drivers/virtual_gamepad.cpp | 16 +++++ src/input_common/drivers/virtual_gamepad.h | 12 +++- 4 files changed, 92 insertions(+), 21 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.java index e6e91aea1a..881c6de91a 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.java +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.java @@ -16,6 +16,10 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -38,20 +42,19 @@ import java.util.Set; * Draws the interactive input overlay on top of the * {@link SurfaceView} that is rendering emulation. */ -public final class InputOverlay extends SurfaceView implements OnTouchListener { +public final class InputOverlay extends SurfaceView implements OnTouchListener, SensorEventListener { private final Set overlayButtons = new HashSet<>(); private final Set overlayDpads = new HashSet<>(); private final Set overlayJoysticks = new HashSet<>(); private boolean mIsInEditMode = false; - private InputOverlayDrawableButton mButtonBeingConfigured; - private InputOverlayDrawableDpad mDpadBeingConfigured; - private InputOverlayDrawableJoystick mJoystickBeingConfigured; private SharedPreferences mPreferences; - // Stores the ID of the pointer that interacted with the 3DS touchscreen. - private int mTouchscreenPointerId = -1; + private float[] gyro = new float[3]; + private float[] accel = new float[3]; + + private long motionTimestamp; /** * Constructor @@ -67,12 +70,12 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener { defaultOverlay(); } - // Reset 3ds touchscreen pointer ID - mTouchscreenPointerId = -1; - // Load the controls. refreshControls(); + // Set the on motion sensor listener. + setMotionSensorListener(context); + // Set the on touch listener. setOnTouchListener(this); @@ -83,6 +86,20 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener { requestFocus(); } + private void setMotionSensorListener(Context context) { + SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + Sensor gyro_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); + Sensor accel_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); + + if (gyro_sensor != null) { + sensorManager.registerListener(this, gyro_sensor, SensorManager.SENSOR_DELAY_GAME); + } + if (accel_sensor != null) { + sensorManager.registerListener(this, accel_sensor, SensorManager.SENSOR_DELAY_GAME); + } + } + + /** * Resizes a {@link Bitmap} by a given scale factor * @@ -427,6 +444,36 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener { return true; } + @Override + public void onSensorChanged(SensorEvent event) { + if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { + accel[0] = -event.values[1] / SensorManager.GRAVITY_EARTH; + accel[1] = event.values[0] / SensorManager.GRAVITY_EARTH; + accel[2] = -event.values[2] / SensorManager.GRAVITY_EARTH; + } + + if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { + // Investigate why sensor value is off by 12x + gyro[0] = event.values[1] / 12.0f; + gyro[1] = -event.values[0] / 12.0f; + gyro[2] = event.values[2] / 12.0f; + } + + // Only update state on accelerometer data + if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) { + return; + } + + long delta_timestamp = (event.timestamp - motionTimestamp) / 1000; + motionTimestamp = event.timestamp; + NativeLibrary.onGamePadMotionEvent(NativeLibrary.Player1Device, delta_timestamp, gyro[0], gyro[1], gyro[2], accel[0], accel[1], accel[2]); + NativeLibrary.onGamePadMotionEvent(NativeLibrary.ConsoleDevice, delta_timestamp, gyro[0], gyro[1], gyro[2], accel[0], accel[1], accel[2]); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int i) { + } + private void addOverlayControls(String orientation) { if (mPreferences.getBoolean("buttonToggle0", true)) { overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.button_a, diff --git a/src/android/app/src/main/jni/emu_window/emu_window.cpp b/src/android/app/src/main/jni/emu_window/emu_window.cpp index 0f6514a61d..2beba6804c 100644 --- a/src/android/app/src/main/jni/emu_window/emu_window.cpp +++ b/src/android/app/src/main/jni/emu_window/emu_window.cpp @@ -11,12 +11,12 @@ void EmuWindow_Android::OnSurfaceChanged(ANativeWindow* surface) { } void EmuWindow_Android::OnTouchPressed(int id, float x, float y) { - const auto [touch_x,touch_y]=MapToTouchScreen(x,y); + const auto [touch_x, touch_y] = MapToTouchScreen(x, y); input_subsystem->GetTouchScreen()->TouchPressed(touch_x, touch_y, id); } void EmuWindow_Android::OnTouchMoved(int id, float x, float y) { - const auto [touch_x,touch_y]=MapToTouchScreen(x,y); + const auto [touch_x, touch_y] = MapToTouchScreen(x, y); input_subsystem->GetTouchScreen()->TouchMoved(touch_x, touch_y, id); } @@ -29,21 +29,19 @@ void EmuWindow_Android::OnGamepadButtonEvent(int player_index, int button_id, bo } void EmuWindow_Android::OnGamepadJoystickEvent(int player_index, int stick_id, float x, float y) { - input_subsystem->GetVirtualGamepad()->SetStickPosition( - player_index, stick_id, x, y); + input_subsystem->GetVirtualGamepad()->SetStickPosition(player_index, stick_id, x, y); } -void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, float gyro_y, - float gyro_z, float accel_x, float accel_y, - float accel_z) { - // TODO: - // input_subsystem->GetVirtualGamepad()->SetMotionState(player_index, delta_timestamp, gyro_x, gyro_y, - // gyro_z, accel_x, accel_y, accel_z); +void EmuWindow_Android::OnGamepadMotionEvent(int player_index, u64 delta_timestamp, float gyro_x, + float gyro_y, float gyro_z, float accel_x, + float accel_y, float accel_z) { + input_subsystem->GetVirtualGamepad()->SetMotionState(player_index, delta_timestamp, gyro_x, + gyro_y, gyro_z, accel_x, accel_y, accel_z); } EmuWindow_Android::EmuWindow_Android(InputCommon::InputSubsystem* input_subsystem_, ANativeWindow* surface_) - : input_subsystem{input_subsystem_} { + : input_subsystem{input_subsystem_} { LOG_INFO(Frontend, "initializing"); if (!surface_) { diff --git a/src/input_common/drivers/virtual_gamepad.cpp b/src/input_common/drivers/virtual_gamepad.cpp index 7db945aa64..c15cbbe58e 100644 --- a/src/input_common/drivers/virtual_gamepad.cpp +++ b/src/input_common/drivers/virtual_gamepad.cpp @@ -39,6 +39,22 @@ void VirtualGamepad::SetStickPosition(std::size_t player_index, VirtualStick axi SetStickPosition(player_index, static_cast(axis_id), x_value, y_value); } +void VirtualGamepad::SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, + float gyro_y, float gyro_z, float accel_x, float accel_y, + float accel_z) { + const auto identifier = GetIdentifier(player_index); + const BasicMotion motion_data{ + .gyro_x = gyro_x, + .gyro_y = gyro_y, + .gyro_z = gyro_z, + .accel_x = accel_x, + .accel_y = accel_y, + .accel_z = accel_z, + .delta_timestamp = delta_timestamp, + }; + SetMotion(identifier, 0, motion_data); +} + void VirtualGamepad::ResetControllers() { for (std::size_t i = 0; i < PlayerIndexCount; i++) { SetStickPosition(i, VirtualStick::Left, 0.0f, 0.0f); diff --git a/src/input_common/drivers/virtual_gamepad.h b/src/input_common/drivers/virtual_gamepad.h index 3df91cc6fa..dfbc45a281 100644 --- a/src/input_common/drivers/virtual_gamepad.h +++ b/src/input_common/drivers/virtual_gamepad.h @@ -52,7 +52,7 @@ public: void SetButtonState(std::size_t player_index, VirtualButton button_id, bool value); /** - * Sets the status of all buttons bound with the key to released + * Sets the status of a stick to a specific player index * @param player_index the player number that will take this action * @param axis_id the id of the axis to move * @param x_value the position of the stick in the x axis @@ -62,6 +62,16 @@ public: void SetStickPosition(std::size_t player_index, VirtualStick axis_id, float x_value, float y_value); + /** + * Sets the status of the motion sensor to a specific player index + * @param player_index the player number that will take this action + * @param delta_timestamp time passed since last reading + * @param gyro_x,gyro_y,gyro_z the gyro sensor readings + * @param accel_x,accel_y,accel_z the acelerometer reading + */ + void SetMotionState(std::size_t player_index, u64 delta_timestamp, float gyro_x, float gyro_y, + float gyro_z, float accel_x, float accel_y, float accel_z); + /// Restores all inputs into the neutral position void ResetControllers();