mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-18 10:32:43 +00:00
Add device vibration and other fixes
This commit is contained in:
parent
2f7087d6d3
commit
50d45011a8
@ -55,6 +55,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
private final double stickDeadzone;
|
private final double stickDeadzone;
|
||||||
private final InputDeviceContext defaultContext = new InputDeviceContext();
|
private final InputDeviceContext defaultContext = new InputDeviceContext();
|
||||||
private final GameGestures gestures;
|
private final GameGestures gestures;
|
||||||
|
private final Vibrator deviceVibrator;
|
||||||
private boolean hasGameController;
|
private boolean hasGameController;
|
||||||
|
|
||||||
private final PreferenceConfiguration prefConfig;
|
private final PreferenceConfiguration prefConfig;
|
||||||
@ -65,6 +66,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.gestures = gestures;
|
this.gestures = gestures;
|
||||||
this.prefConfig = prefConfig;
|
this.prefConfig = prefConfig;
|
||||||
|
this.deviceVibrator = (Vibrator) activityContext.getSystemService(Context.VIBRATOR_SERVICE);
|
||||||
|
|
||||||
// HACK: For now we're hardcoding a 10% deadzone. Some deadzone
|
// HACK: For now we're hardcoding a 10% deadzone. Some deadzone
|
||||||
// is required for controller batching support to work.
|
// is required for controller batching support to work.
|
||||||
@ -163,6 +165,8 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
deviceContext.vibrator.cancel();
|
deviceContext.vibrator.cancel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceVibrator.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasJoystickAxes(InputDevice device) {
|
private static boolean hasJoystickAxes(InputDevice device) {
|
||||||
@ -220,6 +224,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (PreferenceConfiguration.readPreferences(context).onscreenController) {
|
||||||
|
LimeLog.info("Counting OSC gamepad");
|
||||||
|
mask |= 1;
|
||||||
|
}
|
||||||
|
|
||||||
LimeLog.info("Enumerated "+count+" gamepads");
|
LimeLog.info("Enumerated "+count+" gamepads");
|
||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
@ -633,7 +642,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
private short getActiveControllerMask() {
|
private short getActiveControllerMask() {
|
||||||
if (prefConfig.multiController) {
|
if (prefConfig.multiController) {
|
||||||
return (short)(currentControllers | initialControllers);
|
return (short)(currentControllers | initialControllers | (prefConfig.onscreenController ? 1 : 0));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Only Player 1 is active with multi-controller disabled
|
// Only Player 1 is active with multi-controller disabled
|
||||||
@ -1072,13 +1081,15 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
// Since we can only use a single amplitude value, compute the desired amplitude
|
// Since we can only use a single amplitude value, compute the desired amplitude
|
||||||
// by taking 75% of the big motor and 25% of the small motor.
|
// by taking 75% of the big motor and 25% of the small motor.
|
||||||
// NB: This value is now 0-255 as required by VibrationEffect.
|
// NB: This value is now 0-255 as required by VibrationEffect.
|
||||||
int simulatedAmplitude = (int)(((lowFreqMotor >> 8) * 0.75) + ((highFreqMotor >> 8) * 0.25));
|
short lowFreqMotorMSB = (short)((lowFreqMotor >> 8) & 0xFF);
|
||||||
|
short highFreqMotorMSB = (short)((lowFreqMotor >> 8) & 0xFF);
|
||||||
|
int simulatedAmplitude = (int)((lowFreqMotorMSB * 0.75) + (highFreqMotorMSB * 0.25));
|
||||||
|
|
||||||
// Attempt to use amplitude-based control if we're on Oreo and the device
|
// Attempt to use amplitude-based control if we're on Oreo and the device
|
||||||
// supports amplitude-based vibration control.
|
// supports amplitude-based vibration control.
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
if (vibrator.hasAmplitudeControl()) {
|
if (vibrator.hasAmplitudeControl()) {
|
||||||
VibrationEffect effect = VibrationEffect.createOneShot(Long.MAX_VALUE, simulatedAmplitude);
|
VibrationEffect effect = VibrationEffect.createOneShot(60000, simulatedAmplitude);
|
||||||
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
AudioAttributes audioAttributes = new AudioAttributes.Builder()
|
||||||
.setUsage(AudioAttributes.USAGE_GAME)
|
.setUsage(AudioAttributes.USAGE_GAME)
|
||||||
.build();
|
.build();
|
||||||
@ -1089,18 +1100,26 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
// If we reach this point, we don't have amplitude controls available, so
|
// If we reach this point, we don't have amplitude controls available, so
|
||||||
// we must emulate it by PWMing the vibration. Ick.
|
// we must emulate it by PWMing the vibration. Ick.
|
||||||
long pwmPeriod = 50;
|
long pwmPeriod = 20;
|
||||||
long onTime = (long)((simulatedAmplitude / 255.0) * pwmPeriod);
|
long onTime = (long)((simulatedAmplitude / 255.0) * pwmPeriod);
|
||||||
long offTime = pwmPeriod - onTime;
|
long offTime = pwmPeriod - onTime;
|
||||||
vibrator.vibrate(new long[]{offTime, onTime}, 0);
|
vibrator.vibrate(new long[]{0, onTime, offTime}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
|
public void handleRumble(short controllerNumber, short lowFreqMotor, short highFreqMotor) {
|
||||||
|
boolean foundMatchingDevice = false;
|
||||||
|
boolean vibrated = false;
|
||||||
|
|
||||||
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
for (int i = 0; i < inputDeviceContexts.size(); i++) {
|
||||||
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
InputDeviceContext deviceContext = inputDeviceContexts.valueAt(i);
|
||||||
|
|
||||||
if (deviceContext.controllerNumber == controllerNumber && deviceContext.vibrator != null) {
|
if (deviceContext.controllerNumber == controllerNumber) {
|
||||||
rumbleVibrator(deviceContext.vibrator, lowFreqMotor, highFreqMotor);
|
foundMatchingDevice = true;
|
||||||
|
|
||||||
|
if (deviceContext.vibrator != null) {
|
||||||
|
vibrated = true;
|
||||||
|
rumbleVibrator(deviceContext.vibrator, lowFreqMotor, highFreqMotor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1108,7 +1127,23 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
|
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
|
||||||
|
|
||||||
if (deviceContext.controllerNumber == controllerNumber) {
|
if (deviceContext.controllerNumber == controllerNumber) {
|
||||||
deviceContext.device.rumble(lowFreqMotor, highFreqMotor);
|
foundMatchingDevice = vibrated = true;
|
||||||
|
deviceContext.device.rumble((short)lowFreqMotor, (short)highFreqMotor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We may decide to rumble the device for player 1
|
||||||
|
if (controllerNumber == 0) {
|
||||||
|
// If we didn't find a matching device, it must be the on-screen
|
||||||
|
// controls that triggered the rumble. Vibrate the device if
|
||||||
|
// the user has requested that behavior.
|
||||||
|
if (!foundMatchingDevice && prefConfig.onscreenController && !prefConfig.onlyL3R3 && prefConfig.vibrateOsc) {
|
||||||
|
rumbleVibrator(deviceVibrator, lowFreqMotor, highFreqMotor);
|
||||||
|
}
|
||||||
|
else if (foundMatchingDevice && !vibrated && prefConfig.vibrateFallbackToDevice) {
|
||||||
|
// We found a device to vibrate but it didn't have rumble support. The user
|
||||||
|
// has requested us to vibrate the device in this case.
|
||||||
|
rumbleVibrator(deviceVibrator, lowFreqMotor, highFreqMotor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@ public class PreferenceConfiguration {
|
|||||||
private static final String MOUSE_EMULATION_STRING = "checkbox_mouse_emulation";
|
private static final String MOUSE_EMULATION_STRING = "checkbox_mouse_emulation";
|
||||||
private static final String MOUSE_NAV_BUTTONS_STRING = "checkbox_mouse_nav_buttons";
|
private static final String MOUSE_NAV_BUTTONS_STRING = "checkbox_mouse_nav_buttons";
|
||||||
static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps";
|
static final String UNLOCK_FPS_STRING = "checkbox_unlock_fps";
|
||||||
|
private static final String VIBRATE_OSC_PREF_STRING = "checkbox_vibrate_osc";
|
||||||
|
private static final String VIBRATE_FALLBACK_PREF_STRING = "checkbox_vibrate_fallback";
|
||||||
|
|
||||||
static final String DEFAULT_RESOLUTION = "720p";
|
static final String DEFAULT_RESOLUTION = "720p";
|
||||||
static final String DEFAULT_FPS = "60";
|
static final String DEFAULT_FPS = "60";
|
||||||
@ -58,6 +60,8 @@ public class PreferenceConfiguration {
|
|||||||
private static final boolean DEFAULT_MOUSE_EMULATION = true;
|
private static final boolean DEFAULT_MOUSE_EMULATION = true;
|
||||||
private static final boolean DEFAULT_MOUSE_NAV_BUTTONS = false;
|
private static final boolean DEFAULT_MOUSE_NAV_BUTTONS = false;
|
||||||
private static final boolean DEFAULT_UNLOCK_FPS = false;
|
private static final boolean DEFAULT_UNLOCK_FPS = false;
|
||||||
|
private static final boolean DEFAULT_VIBRATE_OSC = true;
|
||||||
|
private static final boolean DEFAULT_VIBRATE_FALLBACK = false;
|
||||||
|
|
||||||
public static final int FORCE_H265_ON = -1;
|
public static final int FORCE_H265_ON = -1;
|
||||||
public static final int AUTOSELECT_H265 = 0;
|
public static final int AUTOSELECT_H265 = 0;
|
||||||
@ -79,6 +83,8 @@ public class PreferenceConfiguration {
|
|||||||
public boolean mouseEmulation;
|
public boolean mouseEmulation;
|
||||||
public boolean mouseNavButtons;
|
public boolean mouseNavButtons;
|
||||||
public boolean unlockFps;
|
public boolean unlockFps;
|
||||||
|
public boolean vibrateOsc;
|
||||||
|
public boolean vibrateFallbackToDevice;
|
||||||
|
|
||||||
private static int getHeightFromResolutionString(String resString) {
|
private static int getHeightFromResolutionString(String resString) {
|
||||||
if (resString.equalsIgnoreCase("360p")) {
|
if (resString.equalsIgnoreCase("360p")) {
|
||||||
@ -329,6 +335,8 @@ public class PreferenceConfiguration {
|
|||||||
config.mouseEmulation = prefs.getBoolean(MOUSE_EMULATION_STRING, DEFAULT_MOUSE_EMULATION);
|
config.mouseEmulation = prefs.getBoolean(MOUSE_EMULATION_STRING, DEFAULT_MOUSE_EMULATION);
|
||||||
config.mouseNavButtons = prefs.getBoolean(MOUSE_NAV_BUTTONS_STRING, DEFAULT_MOUSE_NAV_BUTTONS);
|
config.mouseNavButtons = prefs.getBoolean(MOUSE_NAV_BUTTONS_STRING, DEFAULT_MOUSE_NAV_BUTTONS);
|
||||||
config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS);
|
config.unlockFps = prefs.getBoolean(UNLOCK_FPS_STRING, DEFAULT_UNLOCK_FPS);
|
||||||
|
config.vibrateOsc = prefs.getBoolean(VIBRATE_OSC_PREF_STRING, DEFAULT_VIBRATE_OSC);
|
||||||
|
config.vibrateFallbackToDevice = prefs.getBoolean(VIBRATE_FALLBACK_PREF_STRING, DEFAULT_VIBRATE_FALLBACK);
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
@ -136,6 +136,8 @@
|
|||||||
<string name="category_input_settings">Input Settings</string>
|
<string name="category_input_settings">Input Settings</string>
|
||||||
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
<string name="title_checkbox_multi_controller">Automatic gamepad presence detection</string>
|
||||||
<string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string>
|
<string name="summary_checkbox_multi_controller">Unchecking this option forces a gamepad to always be present</string>
|
||||||
|
<string name="title_checkbox_vibrate_fallback">Emulate rumble support with vibration</string>
|
||||||
|
<string name="summary_checkbox_vibrate_fallback">Vibrates your device to emulate rumble if your gamepad does not support it</string>
|
||||||
<string name="title_seekbar_deadzone">Adjust analog stick deadzone</string>
|
<string name="title_seekbar_deadzone">Adjust analog stick deadzone</string>
|
||||||
<string name="suffix_seekbar_deadzone">%</string>
|
<string name="suffix_seekbar_deadzone">%</string>
|
||||||
<string name="title_checkbox_xb1_driver">Xbox 360/One controller driver</string>
|
<string name="title_checkbox_xb1_driver">Xbox 360/One controller driver</string>
|
||||||
@ -150,6 +152,8 @@
|
|||||||
<string name="category_on_screen_controls_settings">On-screen Controls Settings</string>
|
<string name="category_on_screen_controls_settings">On-screen Controls Settings</string>
|
||||||
<string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string>
|
<string name="title_checkbox_show_onscreen_controls">Show on-screen controls</string>
|
||||||
<string name="summary_checkbox_show_onscreen_controls">Show virtual controller overlay on touchscreen</string>
|
<string name="summary_checkbox_show_onscreen_controls">Show virtual controller overlay on touchscreen</string>
|
||||||
|
<string name="title_checkbox_vibrate_osc">Enable vibration</string>
|
||||||
|
<string name="summary_checkbox_vibrate_osc">Vibrates your device to emulate rumble for the on-screen controls</string>
|
||||||
<string name="title_only_l3r3">Only show L3 and R3</string>
|
<string name="title_only_l3r3">Only show L3 and R3</string>
|
||||||
<string name="summary_only_l3r3">Hide all virtual buttons except L3 and R3</string>
|
<string name="summary_only_l3r3">Hide all virtual buttons except L3 and R3</string>
|
||||||
<string name="title_reset_osc">Clear saved on-screen controls layout</string>
|
<string name="title_reset_osc">Clear saved on-screen controls layout</string>
|
||||||
|
@ -82,6 +82,11 @@
|
|||||||
android:title="@string/title_checkbox_mouse_emulation"
|
android:title="@string/title_checkbox_mouse_emulation"
|
||||||
android:summary="@string/summary_checkbox_mouse_emulation"
|
android:summary="@string/summary_checkbox_mouse_emulation"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="checkbox_vibrate_fallback"
|
||||||
|
android:title="@string/title_checkbox_vibrate_fallback"
|
||||||
|
android:summary="@string/summary_checkbox_vibrate_fallback"
|
||||||
|
android:defaultValue="false" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/category_on_screen_controls_settings"
|
<PreferenceCategory android:title="@string/category_on_screen_controls_settings"
|
||||||
android:key="category_onscreen_controls">
|
android:key="category_onscreen_controls">
|
||||||
@ -90,6 +95,12 @@
|
|||||||
android:key="checkbox_show_onscreen_controls"
|
android:key="checkbox_show_onscreen_controls"
|
||||||
android:summary="@string/summary_checkbox_show_onscreen_controls"
|
android:summary="@string/summary_checkbox_show_onscreen_controls"
|
||||||
android:title="@string/title_checkbox_show_onscreen_controls" />
|
android:title="@string/title_checkbox_show_onscreen_controls" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="checkbox_vibrate_osc"
|
||||||
|
android:dependency="checkbox_show_onscreen_controls"
|
||||||
|
android:title="@string/title_checkbox_vibrate_osc"
|
||||||
|
android:summary="@string/summary_checkbox_vibrate_osc"
|
||||||
|
android:defaultValue="true" />
|
||||||
<CheckBoxPreference
|
<CheckBoxPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:dependency="checkbox_show_onscreen_controls"
|
android:dependency="checkbox_show_onscreen_controls"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user