mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-10-20 23:36:53 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7231f5468b | ||
|
|
4dfb0d7220 | ||
|
|
2f4f53b048 | ||
|
|
b6e8389544 | ||
|
|
d113878613 | ||
|
|
f7ed7e06db | ||
|
|
977a1d4a3c | ||
|
|
eefc08db47 | ||
|
|
ab2b1663d3 | ||
|
|
04b8a718e3 | ||
|
|
37cf260ba6 | ||
|
|
8f91fe4cd1 | ||
|
|
9246ad412f | ||
|
|
1ccbbdd4fb | ||
|
|
16cf37994d | ||
|
|
01e84624c2 | ||
|
|
939cd7cf70 | ||
|
|
4b11603035 | ||
|
|
ca18b6b052 | ||
|
|
3d0d19e561 | ||
|
|
ae463a8735 | ||
|
|
7e797829ae | ||
|
|
431ed6bc5d | ||
|
|
e9bb711c42 | ||
|
|
623bc5c156 | ||
|
|
cfefef4619 | ||
|
|
4a9a881c1f | ||
|
|
13a06d585c | ||
|
|
1c8ad64da0 | ||
|
|
1d8925de57 | ||
|
|
0eb7e779b8 | ||
|
|
a4b86eefe2 | ||
|
|
902a58bc70 | ||
|
|
a34a44f29a | ||
|
|
454fe80172 | ||
|
|
81b6a8a311 | ||
|
|
3011a5bad7 | ||
|
|
dcb7be3acd | ||
|
|
68a6b510b1 | ||
|
|
dca3e89303 | ||
|
|
bae6fef588 | ||
|
|
37f65e43a5 | ||
|
|
8c910101c7 | ||
|
|
112d9c41eb | ||
|
|
c91d1097f6 |
No files matched your search
+5
-2
@@ -8,8 +8,11 @@ android:
|
|||||||
components:
|
components:
|
||||||
- tools
|
- tools
|
||||||
- platform-tools
|
- platform-tools
|
||||||
- build-tools-29.0.1
|
- build-tools-29.0.3
|
||||||
- android-29
|
- android-29
|
||||||
|
|
||||||
|
before_install:
|
||||||
|
- sdkmanager --list
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- yes | sdkmanager "ndk-bundle"
|
- yes | sdkmanager "ndk;20.0.5594570"
|
||||||
+6
-6
@@ -7,8 +7,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
|
|
||||||
versionName "8.9"
|
versionName "8.11"
|
||||||
versionCode = 211
|
versionCode = 216
|
||||||
}
|
}
|
||||||
|
|
||||||
flavorDimensions "root"
|
flavorDimensions "root"
|
||||||
@@ -114,10 +114,10 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.bouncycastle:bcprov-jdk15on:1.62'
|
implementation 'org.bouncycastle:bcprov-jdk15on:1.64'
|
||||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.62'
|
implementation 'org.bouncycastle:bcpkix-jdk15on:1.64'
|
||||||
implementation 'org.jcodec:jcodec:0.2.3'
|
implementation 'org.jcodec:jcodec:0.2.3'
|
||||||
implementation 'com.squareup.okhttp3:okhttp:3.12.3'
|
implementation 'com.squareup.okhttp3:okhttp:3.12.10'
|
||||||
implementation 'com.squareup.okio:okio:1.17.4'
|
implementation 'com.squareup.okio:okio:1.17.5'
|
||||||
implementation 'org.jmdns:jmdns:3.5.5'
|
implementation 'org.jmdns:jmdns:3.5.5'
|
||||||
}
|
}
|
||||||
@@ -517,6 +517,12 @@ public class AppView extends Activity implements AdapterFragmentCallbacks {
|
|||||||
if (!foundExistingApp) {
|
if (!foundExistingApp) {
|
||||||
// This app must be new
|
// This app must be new
|
||||||
appGridAdapter.addApp(new AppObject(app));
|
appGridAdapter.addApp(new AppObject(app));
|
||||||
|
|
||||||
|
// We could have a leftover shortcut from last time this PC was paired
|
||||||
|
// or if this app was removed then added again. Enable those shortcuts
|
||||||
|
// again if present.
|
||||||
|
shortcutHelper.enableAppShortcut(computer, app);
|
||||||
|
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ import android.content.Intent;
|
|||||||
import android.content.ServiceConnection;
|
import android.content.ServiceConnection;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
@@ -73,6 +74,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
@@ -398,6 +400,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Hopefully, we can get rid of this once someone comes up with a better way
|
// Hopefully, we can get rid of this once someone comes up with a better way
|
||||||
// to track the state of the pipeline and time frames.
|
// to track the state of the pipeline and time frames.
|
||||||
int roundedRefreshRate = Math.round(displayRefreshRate);
|
int roundedRefreshRate = Math.round(displayRefreshRate);
|
||||||
|
int chosenFrameRate = prefConfig.fps;
|
||||||
if (!prefConfig.disableFrameDrop || prefConfig.unlockFps) {
|
if (!prefConfig.disableFrameDrop || prefConfig.unlockFps) {
|
||||||
if (Build.DEVICE.equals("coral") || Build.DEVICE.equals("flame")) {
|
if (Build.DEVICE.equals("coral") || Build.DEVICE.equals("flame")) {
|
||||||
// HACK: Pixel 4 (XL) ignores the preferred display mode and lowers refresh rate,
|
// HACK: Pixel 4 (XL) ignores the preferred display mode and lowers refresh rate,
|
||||||
@@ -423,8 +426,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Use the old rendering strategy on these broken devices
|
// Use the old rendering strategy on these broken devices
|
||||||
decoderRenderer.enableLegacyFrameDropRendering();
|
decoderRenderer.enableLegacyFrameDropRendering();
|
||||||
} else {
|
} else {
|
||||||
prefConfig.fps = roundedRefreshRate - 1;
|
chosenFrameRate = roundedRefreshRate - 1;
|
||||||
LimeLog.info("Adjusting FPS target for screen to " + prefConfig.fps);
|
LimeLog.info("Adjusting FPS target for screen to " + chosenFrameRate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,7 +439,8 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
StreamConfiguration config = new StreamConfiguration.Builder()
|
StreamConfiguration config = new StreamConfiguration.Builder()
|
||||||
.setResolution(prefConfig.width, prefConfig.height)
|
.setResolution(prefConfig.width, prefConfig.height)
|
||||||
.setRefreshRate(prefConfig.fps)
|
.setLaunchRefreshRate(prefConfig.fps)
|
||||||
|
.setRefreshRate(chosenFrameRate)
|
||||||
.setApp(new NvApp(appName != null ? appName : "app", appId, willStreamHdr))
|
.setApp(new NvApp(appName != null ? appName : "app", appId, willStreamHdr))
|
||||||
.setBitrate(prefConfig.bitrate)
|
.setBitrate(prefConfig.bitrate)
|
||||||
.setEnableSops(prefConfig.enableSops)
|
.setEnableSops(prefConfig.enableSops)
|
||||||
@@ -592,9 +596,33 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Remove when Android R SDK is finalized
|
||||||
|
private static void setPreferMinimalPostProcessingWithReflection(WindowManager.LayoutParams windowLayoutParams, boolean isPreferred) {
|
||||||
|
// Build.VERSION.PREVIEW_SDK_INT was added in M
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q && Build.VERSION.PREVIEW_SDK_INT == 0) {
|
||||||
|
// Don't attempt this reflection unless on Android R Developer Preview
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Field field = windowLayoutParams.getClass().getDeclaredField("preferMinimalPostProcessing");
|
||||||
|
field.set(windowLayoutParams, isPreferred);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private float prepareDisplayForRendering() {
|
private float prepareDisplayForRendering() {
|
||||||
Display display = getWindowManager().getDefaultDisplay();
|
Display display = getWindowManager().getDefaultDisplay();
|
||||||
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
WindowManager.LayoutParams windowLayoutParams = getWindow().getAttributes();
|
||||||
|
float displayRefreshRate;
|
||||||
|
|
||||||
// On M, we can explicitly set the optimal display mode
|
// On M, we can explicitly set the optimal display mode
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@@ -638,6 +666,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
LimeLog.info("Selected display mode: "+bestMode.getPhysicalWidth()+"x"+
|
||||||
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
bestMode.getPhysicalHeight()+"x"+bestMode.getRefreshRate());
|
||||||
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
windowLayoutParams.preferredDisplayModeId = bestMode.getModeId();
|
||||||
|
displayRefreshRate = bestMode.getRefreshRate();
|
||||||
}
|
}
|
||||||
// On L, we can at least tell the OS that we want a refresh rate
|
// On L, we can at least tell the OS that we want a refresh rate
|
||||||
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
@@ -658,12 +687,17 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
LimeLog.info("Selected refresh rate: "+bestRefreshRate);
|
||||||
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
windowLayoutParams.preferredRefreshRate = bestRefreshRate;
|
||||||
|
displayRefreshRate = bestRefreshRate;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Otherwise, the active display refresh rate is just
|
// Otherwise, the active display refresh rate is just
|
||||||
// whatever is currently in use.
|
// whatever is currently in use.
|
||||||
|
displayRefreshRate = display.getRefreshRate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable HDMI ALLM (game mode) on Android R
|
||||||
|
setPreferMinimalPostProcessingWithReflection(windowLayoutParams, true);
|
||||||
|
|
||||||
// Apply the display mode change
|
// Apply the display mode change
|
||||||
getWindow().setAttributes(windowLayoutParams);
|
getWindow().setAttributes(windowLayoutParams);
|
||||||
|
|
||||||
@@ -698,9 +732,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
streamView.setDesiredAspectRatio((double)prefConfig.width / (double)prefConfig.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION) ||
|
||||||
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
|
||||||
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
// TVs may take a few moments to switch refresh rates, and we can probably assume
|
||||||
|
// it will be eventually activated.
|
||||||
|
// TODO: Improve this
|
||||||
|
return displayRefreshRate;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use the actual refresh rate of the display, since the preferred refresh rate or mode
|
||||||
|
// may not actually be applied (ex: Pixel 4 with Smooth Display disabled).
|
||||||
|
return getWindowManager().getDefaultDisplay().getRefreshRate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
@@ -959,11 +1002,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
// Handle a synthetic back button event that some Android OS versions
|
// Handle a synthetic back button event that some Android OS versions
|
||||||
// create as a result of a right-click. This event WILL repeat if
|
// create as a result of a right-click. This event WILL repeat if
|
||||||
// the right mouse button is held down, so we ignore those.
|
// the right mouse button is held down, so we ignore those.
|
||||||
if (!prefConfig.mouseNavButtons &&
|
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
|
||||||
|
// Send the right mouse button event if mouse back and forward
|
||||||
|
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||||
|
// care of this.
|
||||||
|
if (!prefConfig.mouseNavButtons) {
|
||||||
|
conn.sendMouseButtonDown(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return true, otherwise the back press will be propagated
|
||||||
|
// up to the parent and finish the activity.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -987,6 +1038,11 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Eat repeat down events
|
||||||
|
if (event.getRepeatCount() > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Pass through keyboard input if we're not grabbing
|
// Pass through keyboard input if we're not grabbing
|
||||||
if (!grabbedInput) {
|
if (!grabbedInput) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1017,11 +1073,19 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// Handle a synthetic back button event that some Android OS versions
|
// Handle a synthetic back button event that some Android OS versions
|
||||||
// create as a result of a right-click.
|
// create as a result of a right-click.
|
||||||
if (!prefConfig.mouseNavButtons &&
|
if ((event.getSource() == InputDevice.SOURCE_MOUSE ||
|
||||||
(event.getSource() == InputDevice.SOURCE_MOUSE ||
|
|
||||||
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
event.getSource() == InputDevice.SOURCE_MOUSE_RELATIVE) &&
|
||||||
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
|
||||||
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
|
||||||
|
// Send the right mouse button event if mouse back and forward
|
||||||
|
// are disabled. If they are enabled, handleMotionEvent() will take
|
||||||
|
// care of this.
|
||||||
|
if (!prefConfig.mouseNavButtons) {
|
||||||
|
conn.sendMouseButtonUp(MouseButtonPacket.BUTTON_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always return true, otherwise the back press will be propagated
|
||||||
|
// up to the parent and finish the activity.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1103,7 +1167,9 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// Ignore mouse input if we're not capturing from our input source
|
// Ignore mouse input if we're not capturing from our input source
|
||||||
if (!inputCaptureProvider.isCapturingActive()) {
|
if (!inputCaptureProvider.isCapturingActive()) {
|
||||||
return false;
|
// We return true here because otherwise the events may end up causing
|
||||||
|
// Android to synthesize d-pad events.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
if (event.getActionMasked() == MotionEvent.ACTION_SCROLL) {
|
||||||
@@ -1373,7 +1439,7 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stageFailed(final String stage, final long errorCode) {
|
public void stageFailed(final String stage, final int errorCode) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -1388,18 +1454,18 @@ public class Game extends Activity implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
// If video initialization failed and the surface is still valid, display extra information for the user
|
// If video initialization failed and the surface is still valid, display extra information for the user
|
||||||
if (stage.contains("video") && streamView.getHolder().getSurface().isValid()) {
|
if (stage.contains("video") && streamView.getHolder().getSurface().isValid()) {
|
||||||
Toast.makeText(Game.this, "Video decoder failed to initialize. Your device may not support the selected resolution.", Toast.LENGTH_LONG).show();
|
Toast.makeText(Game.this, getResources().getText(R.string.video_decoder_init_failed), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_error_title),
|
Dialog.displayDialog(Game.this, getResources().getString(R.string.conn_error_title),
|
||||||
getResources().getString(R.string.conn_error_msg) + " " + stage, true);
|
getResources().getString(R.string.conn_error_msg) + " " + stage +" (error "+errorCode+")", true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connectionTerminated(final long errorCode) {
|
public void connectionTerminated(final int errorCode) {
|
||||||
runOnUiThread(new Runnable() {
|
runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.limelight.binding.input;
|
package com.limelight.binding.input;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.hardware.input.InputManager;
|
import android.hardware.input.InputManager;
|
||||||
import android.hardware.usb.UsbDevice;
|
import android.hardware.usb.UsbDevice;
|
||||||
@@ -51,7 +52,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
private final SparseArray<UsbDeviceContext> usbDeviceContexts = new SparseArray<>();
|
private final SparseArray<UsbDeviceContext> usbDeviceContexts = new SparseArray<>();
|
||||||
|
|
||||||
private final NvConnection conn;
|
private final NvConnection conn;
|
||||||
private final Context activityContext;
|
private final Activity activityContext;
|
||||||
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;
|
||||||
@@ -61,7 +62,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
private final PreferenceConfiguration prefConfig;
|
private final PreferenceConfiguration prefConfig;
|
||||||
private short currentControllers, initialControllers;
|
private short currentControllers, initialControllers;
|
||||||
|
|
||||||
public ControllerHandler(Context activityContext, NvConnection conn, GameGestures gestures, PreferenceConfiguration prefConfig) {
|
public ControllerHandler(Activity activityContext, NvConnection conn, GameGestures gestures, PreferenceConfiguration prefConfig) {
|
||||||
this.activityContext = activityContext;
|
this.activityContext = activityContext;
|
||||||
this.conn = conn;
|
this.conn = conn;
|
||||||
this.gestures = gestures;
|
this.gestures = gestures;
|
||||||
@@ -108,6 +109,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
defaultContext.rightTriggerAxis = MotionEvent.AXIS_GAS;
|
||||||
defaultContext.controllerNumber = (short) 0;
|
defaultContext.controllerNumber = (short) 0;
|
||||||
defaultContext.assignedControllerNumber = true;
|
defaultContext.assignedControllerNumber = true;
|
||||||
|
defaultContext.external = false;
|
||||||
|
|
||||||
// Some devices (GPD XD) have a back button which sends input events
|
// Some devices (GPD XD) have a back button which sends input events
|
||||||
// with device ID == 0. This hits the default context which would normally
|
// with device ID == 0. This hits the default context which would normally
|
||||||
@@ -146,6 +148,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
if (context != null) {
|
if (context != null) {
|
||||||
LimeLog.info("Removed controller: "+context.name+" ("+deviceId+")");
|
LimeLog.info("Removed controller: "+context.name+" ("+deviceId+")");
|
||||||
releaseControllerNumber(context);
|
releaseControllerNumber(context);
|
||||||
|
context.destroy();
|
||||||
inputDeviceContexts.remove(deviceId);
|
inputDeviceContexts.remove(deviceId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -160,10 +163,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
public void stop() {
|
public void stop() {
|
||||||
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);
|
||||||
|
deviceContext.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
if (deviceContext.vibrator != null) {
|
for (int i = 0; i < usbDeviceContexts.size(); i++) {
|
||||||
deviceContext.vibrator.cancel();
|
UsbDeviceContext deviceContext = usbDeviceContexts.valueAt(i);
|
||||||
}
|
deviceContext.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceVibrator.cancel();
|
deviceVibrator.cancel();
|
||||||
@@ -264,9 +269,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
InputDeviceContext devContext = (InputDeviceContext) context;
|
InputDeviceContext devContext = (InputDeviceContext) context;
|
||||||
|
|
||||||
LimeLog.info(devContext.name+" ("+context.id+") needs a controller number assigned");
|
LimeLog.info(devContext.name+" ("+context.id+") needs a controller number assigned");
|
||||||
if (devContext.name != null &&
|
if (!devContext.external) {
|
||||||
(devContext.name.contains("gpio-keys") || // This is the back button on Shield portable consoles
|
|
||||||
devContext.name.contains("joy_key"))) { // These are the gamepad buttons on the Archos Gamepad 2
|
|
||||||
LimeLog.info("Built-in buttons hardcoded as controller 0");
|
LimeLog.info("Built-in buttons hardcoded as controller 0");
|
||||||
context.controllerNumber = 0;
|
context.controllerNumber = 0;
|
||||||
}
|
}
|
||||||
@@ -327,6 +330,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
context.id = device.getControllerId();
|
context.id = device.getControllerId();
|
||||||
context.device = device;
|
context.device = device;
|
||||||
|
context.external = true;
|
||||||
|
|
||||||
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
context.leftStickDeadzoneRadius = (float) stickDeadzone;
|
||||||
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
context.rightStickDeadzoneRadius = (float) stickDeadzone;
|
||||||
@@ -343,6 +347,17 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String deviceName = dev.getName();
|
||||||
|
if (deviceName.contains("gpio") || // This is the back button on Shield portable consoles
|
||||||
|
deviceName.contains("joy_key") || // These are the gamepad buttons on the Archos Gamepad 2
|
||||||
|
deviceName.contains("keypad") || // These are gamepad buttons on the XPERIA Play
|
||||||
|
deviceName.equalsIgnoreCase("NVIDIA Corporation NVIDIA Controller v01.01") || // Gamepad on Shield Portable
|
||||||
|
deviceName.equalsIgnoreCase("NVIDIA Corporation NVIDIA Controller v01.02")) // Gamepad on Shield Portable (?)
|
||||||
|
{
|
||||||
|
LimeLog.info(dev.getName()+" is internal by hardcoded mapping");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
// Landroid/view/InputDevice;->isExternal()Z is officially public on Android Q
|
// Landroid/view/InputDevice;->isExternal()Z is officially public on Android Q
|
||||||
return dev.isExternal();
|
return dev.isExternal();
|
||||||
@@ -376,9 +391,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Classify this device as a remote by name if it has no joystick axes
|
// Classify this device as a remote by name if it has no joystick axes
|
||||||
if (getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_X) == null &&
|
if (!hasJoystickAxes(dev) && devName.toLowerCase().contains("remote")) {
|
||||||
getMotionRangeForJoystickAxis(dev, MotionEvent.AXIS_Y) == null &&
|
|
||||||
devName.toLowerCase().contains("remote")) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,11 +414,14 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
// Note that we are explicitly NOT excluding the current device we're examining here,
|
// Note that we are explicitly NOT excluding the current device we're examining here,
|
||||||
// since the other gamepad buttons may be on our current device and that's fine.
|
// since the other gamepad buttons may be on our current device and that's fine.
|
||||||
boolean[] keys = currentDev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_BUTTON_A);
|
if (currentDev.hasKeys(KeyEvent.KEYCODE_BUTTON_SELECT)[0]) {
|
||||||
if (keys[0]) {
|
|
||||||
foundInternalSelect = true;
|
foundInternalSelect = true;
|
||||||
}
|
}
|
||||||
if (keys[1]) {
|
|
||||||
|
// We don't check KEYCODE_BUTTON_A here, since the Shield Android TV has a
|
||||||
|
// virtual mouse device that claims to have KEYCODE_BUTTON_A. Instead, we rely
|
||||||
|
// on the SOURCE_GAMEPAD flag to be set on gamepad devices.
|
||||||
|
if (hasGamepadButtons(currentDev)) {
|
||||||
foundInternalGamepad = true;
|
foundInternalGamepad = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -417,8 +433,11 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
// c) have an internal gamepad but no internal select button (NVIDIA SHIELD Portable)
|
// c) have an internal gamepad but no internal select button (NVIDIA SHIELD Portable)
|
||||||
return !foundInternalGamepad || foundInternalSelect;
|
return !foundInternalGamepad || foundInternalSelect;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
return false;
|
// For external devices, we want to pass through the back button if the device
|
||||||
|
// has no gamepad axes or gamepad buttons.
|
||||||
|
return !hasJoystickAxes(dev) && !hasGamepadButtons(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputDeviceContext createInputDeviceContextForDevice(InputDevice dev) {
|
private InputDeviceContext createInputDeviceContextForDevice(InputDevice dev) {
|
||||||
@@ -434,6 +453,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
context.name = devName;
|
context.name = devName;
|
||||||
context.id = dev.getId();
|
context.id = dev.getId();
|
||||||
|
context.external = isExternal(dev);
|
||||||
|
|
||||||
if (dev.getVibrator().hasVibrator()) {
|
if (dev.getVibrator().hasVibrator()) {
|
||||||
context.vibrator = dev.getVibrator();
|
context.vibrator = dev.getVibrator();
|
||||||
@@ -888,18 +908,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.hatXAxis != -1 && context.hatYAxis != -1) {
|
if (context.hatXAxis == -1 &&
|
||||||
switch (event.getKeyCode()) {
|
|
||||||
// These are duplicate dpad events for hat input
|
|
||||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
|
||||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
|
||||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
|
||||||
case KeyEvent.KEYCODE_DPAD_UP:
|
|
||||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (context.hatXAxis == -1 &&
|
|
||||||
context.hatYAxis == -1 &&
|
context.hatYAxis == -1 &&
|
||||||
/* FIXME: There's no good way to know for sure if xpad is bound
|
/* FIXME: There's no good way to know for sure if xpad is bound
|
||||||
to this device, so we won't use the name to validate if these
|
to this device, so we won't use the name to validate if these
|
||||||
@@ -1030,17 +1039,21 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
context.inputMap &= ~(ControllerPacket.LEFT_FLAG | ControllerPacket.RIGHT_FLAG);
|
||||||
if (hatX < -0.5) {
|
if (hatX < -0.5) {
|
||||||
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
||||||
|
context.hatXAxisUsed = true;
|
||||||
}
|
}
|
||||||
else if (hatX > 0.5) {
|
else if (hatX > 0.5) {
|
||||||
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||||
|
context.hatXAxisUsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
context.inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
context.inputMap &= ~(ControllerPacket.UP_FLAG | ControllerPacket.DOWN_FLAG);
|
||||||
if (hatY < -0.5) {
|
if (hatY < -0.5) {
|
||||||
context.inputMap |= ControllerPacket.UP_FLAG;
|
context.inputMap |= ControllerPacket.UP_FLAG;
|
||||||
|
context.hatYAxisUsed = true;
|
||||||
}
|
}
|
||||||
else if (hatY > 0.5) {
|
else if (hatY > 0.5) {
|
||||||
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
||||||
|
context.hatYAxisUsed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1255,15 +1268,31 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
context.inputMap &= ~ControllerPacket.BACK_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
|
if (context.hatXAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap &= ~ControllerPacket.LEFT_FLAG;
|
context.inputMap &= ~ControllerPacket.LEFT_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
|
if (context.hatXAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
context.inputMap &= ~ControllerPacket.RIGHT_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_UP:
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
if (context.hatYAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap &= ~ControllerPacket.UP_FLAG;
|
context.inputMap &= ~ControllerPacket.UP_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
if (context.hatYAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap &= ~ControllerPacket.DOWN_FLAG;
|
context.inputMap &= ~ControllerPacket.DOWN_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_BUTTON_B:
|
case KeyEvent.KEYCODE_BUTTON_B:
|
||||||
@@ -1347,6 +1376,12 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
sendControllerInputPacket(context);
|
sendControllerInputPacket(context);
|
||||||
|
|
||||||
|
if (context.pendingExit && context.inputMap == 0) {
|
||||||
|
// All buttons from the quit combo are lifted. Finish the activity now.
|
||||||
|
activityContext.finish();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1377,15 +1412,31 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.inputMap |= ControllerPacket.BACK_FLAG;
|
context.inputMap |= ControllerPacket.BACK_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
|
if (context.hatXAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
context.inputMap |= ControllerPacket.LEFT_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
|
if (context.hatXAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
context.inputMap |= ControllerPacket.RIGHT_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_UP:
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
if (context.hatYAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap |= ControllerPacket.UP_FLAG;
|
context.inputMap |= ControllerPacket.UP_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
if (context.hatYAxisUsed) {
|
||||||
|
// Suppress this duplicate event if we have a hat
|
||||||
|
return true;
|
||||||
|
}
|
||||||
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
context.inputMap |= ControllerPacket.DOWN_FLAG;
|
||||||
break;
|
break;
|
||||||
case KeyEvent.KEYCODE_BUTTON_B:
|
case KeyEvent.KEYCODE_BUTTON_B:
|
||||||
@@ -1431,9 +1482,16 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start+Back+LB+RB is the quit combo
|
||||||
|
if (context.inputMap == (ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG |
|
||||||
|
ControllerPacket.LB_FLAG | ControllerPacket.RB_FLAG)) {
|
||||||
|
// Wait for the combo to lift and then finish the activity
|
||||||
|
context.pendingExit = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Start+LB acts like select for controllers with one button
|
// Start+LB acts like select for controllers with one button
|
||||||
if ((context.inputMap & ControllerPacket.PLAY_FLAG) != 0 &&
|
if (context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG) ||
|
||||||
((context.inputMap & ControllerPacket.LB_FLAG) != 0 ||
|
(context.inputMap == ControllerPacket.PLAY_FLAG &&
|
||||||
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
SystemClock.uptimeMillis() - context.lastLbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||||
{
|
{
|
||||||
context.inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
context.inputMap &= ~(ControllerPacket.PLAY_FLAG | ControllerPacket.LB_FLAG);
|
||||||
@@ -1443,10 +1501,10 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We detect select+start or start+RB as the special button combo
|
// We detect select+start or start+RB as the special button combo
|
||||||
if (((context.inputMap & ControllerPacket.RB_FLAG) != 0 ||
|
if (context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.BACK_FLAG) ||
|
||||||
(SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS) ||
|
context.inputMap == (ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG) ||
|
||||||
(context.inputMap & ControllerPacket.BACK_FLAG) != 0) &&
|
(context.inputMap == ControllerPacket.PLAY_FLAG &&
|
||||||
(context.inputMap & ControllerPacket.PLAY_FLAG) != 0)
|
SystemClock.uptimeMillis() - context.lastRbUpTime <= MAXIMUM_BUMPER_UP_DELAY_MS))
|
||||||
{
|
{
|
||||||
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
context.inputMap &= ~(ControllerPacket.BACK_FLAG | ControllerPacket.PLAY_FLAG | ControllerPacket.RB_FLAG);
|
||||||
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
context.inputMap |= ControllerPacket.SPECIAL_BUTTON_FLAG;
|
||||||
@@ -1454,7 +1512,6 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
context.emulatingButtonFlags |= ControllerHandler.EMULATING_SPECIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// We don't need to send repeat key down events, but the platform
|
// We don't need to send repeat key down events, but the platform
|
||||||
// sends us events that claim to be repeats but they're from different
|
// sends us events that claim to be repeats but they're from different
|
||||||
// devices, so we just send them all and deal with some duplicates.
|
// devices, so we just send them all and deal with some duplicates.
|
||||||
@@ -1525,6 +1582,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
if (context != null) {
|
if (context != null) {
|
||||||
LimeLog.info("Removed controller: "+controller.getControllerId());
|
LimeLog.info("Removed controller: "+controller.getControllerId());
|
||||||
releaseControllerNumber(context);
|
releaseControllerNumber(context);
|
||||||
|
context.destroy();
|
||||||
usbDeviceContexts.remove(controller.getControllerId());
|
usbDeviceContexts.remove(controller.getControllerId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1537,6 +1595,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
class GenericControllerContext {
|
class GenericControllerContext {
|
||||||
public int id;
|
public int id;
|
||||||
|
public boolean external;
|
||||||
|
|
||||||
public float leftStickDeadzoneRadius;
|
public float leftStickDeadzoneRadius;
|
||||||
public float rightStickDeadzoneRadius;
|
public float rightStickDeadzoneRadius;
|
||||||
@@ -1557,6 +1616,13 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
public boolean mouseEmulationActive;
|
public boolean mouseEmulationActive;
|
||||||
public Timer mouseEmulationTimer;
|
public Timer mouseEmulationTimer;
|
||||||
public short mouseEmulationLastInputMap;
|
public short mouseEmulationLastInputMap;
|
||||||
|
|
||||||
|
public void destroy() {
|
||||||
|
if (mouseEmulationTimer != null) {
|
||||||
|
mouseEmulationTimer.cancel();
|
||||||
|
mouseEmulationTimer = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InputDeviceContext extends GenericControllerContext {
|
class InputDeviceContext extends GenericControllerContext {
|
||||||
@@ -1576,6 +1642,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
|
|
||||||
public int hatXAxis = -1;
|
public int hatXAxis = -1;
|
||||||
public int hatYAxis = -1;
|
public int hatYAxis = -1;
|
||||||
|
public boolean hatXAxisUsed, hatYAxisUsed;
|
||||||
|
|
||||||
public boolean isNonStandardDualShock4;
|
public boolean isNonStandardDualShock4;
|
||||||
public boolean usesLinuxGamepadStandardFaceButtons;
|
public boolean usesLinuxGamepadStandardFaceButtons;
|
||||||
@@ -1585,6 +1652,7 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
public boolean modeIsSelect;
|
public boolean modeIsSelect;
|
||||||
public boolean ignoreBack;
|
public boolean ignoreBack;
|
||||||
public boolean hasJoystickAxes;
|
public boolean hasJoystickAxes;
|
||||||
|
public boolean pendingExit;
|
||||||
|
|
||||||
public int emulatingButtonFlags = 0;
|
public int emulatingButtonFlags = 0;
|
||||||
|
|
||||||
@@ -1597,9 +1665,25 @@ public class ControllerHandler implements InputManager.InputDeviceListener, UsbD
|
|||||||
public long lastRbUpTime = 0;
|
public long lastRbUpTime = 0;
|
||||||
|
|
||||||
public long startDownTime = 0;
|
public long startDownTime = 0;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
|
if (vibrator != null) {
|
||||||
|
vibrator.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UsbDeviceContext extends GenericControllerContext {
|
class UsbDeviceContext extends GenericControllerContext {
|
||||||
public AbstractController device;
|
public AbstractController device;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroy() {
|
||||||
|
super.destroy();
|
||||||
|
|
||||||
|
// Nothing for now
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
@@ -125,6 +125,13 @@ public class VirtualController {
|
|||||||
elements.clear();
|
elements.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setOpacity(int opacity) {
|
||||||
|
for (VirtualControllerElement element : elements) {
|
||||||
|
element.setOpacity(opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void addElement(VirtualControllerElement element, int x, int y, int width, int height) {
|
public void addElement(VirtualControllerElement element, int x, int y, int width, int height) {
|
||||||
elements.add(element);
|
elements.add(element);
|
||||||
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
|
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(width, height);
|
||||||
|
|||||||
+4
-2
@@ -240,7 +240,7 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
controller.addElement(createLeftTrigger(
|
controller.addElement(createLeftTrigger(
|
||||||
0, "LT", -1, controller, context),
|
1, "LT", -1, controller, context),
|
||||||
screenScale(TRIGGER_L_BASE_X, height),
|
screenScale(TRIGGER_L_BASE_X, height),
|
||||||
screenScale(TRIGGER_BASE_Y, height),
|
screenScale(TRIGGER_BASE_Y, height),
|
||||||
screenScale(TRIGGER_WIDTH, height),
|
screenScale(TRIGGER_WIDTH, height),
|
||||||
@@ -248,7 +248,7 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
);
|
);
|
||||||
|
|
||||||
controller.addElement(createRightTrigger(
|
controller.addElement(createRightTrigger(
|
||||||
0, "RT", -1, controller, context),
|
1, "RT", -1, controller, context),
|
||||||
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
screenScale(TRIGGER_R_BASE_X + TRIGGER_DISTANCE, height) + rightDisplacement,
|
||||||
screenScale(TRIGGER_BASE_Y, height),
|
screenScale(TRIGGER_BASE_Y, height),
|
||||||
screenScale(TRIGGER_WIDTH, height),
|
screenScale(TRIGGER_WIDTH, height),
|
||||||
@@ -324,6 +324,8 @@ public class VirtualControllerConfigurationLoader {
|
|||||||
screenScale(TRIGGER_HEIGHT, height)
|
screenScale(TRIGGER_HEIGHT, height)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller.setOpacity(config.oscOpacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void saveProfile(final VirtualController controller,
|
public static void saveProfile(final VirtualController controller,
|
||||||
|
|||||||
+9
@@ -295,6 +295,15 @@ public abstract class VirtualControllerElement extends View {
|
|||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void setOpacity(int opacity) {
|
||||||
|
int hexOpacity = opacity * 255 / 100;
|
||||||
|
this.normalColor = (hexOpacity << 24) | (normalColor & 0x00FFFFFF);
|
||||||
|
this.pressedColor = (hexOpacity << 24) | (pressedColor & 0x00FFFFFF);
|
||||||
|
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
protected final float getPercent(float value, float percent) {
|
protected final float getPercent(float value, float percent) {
|
||||||
return value / 100 * percent;
|
return value / 100 * percent;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private Thread rendererThread;
|
private Thread rendererThread;
|
||||||
private boolean needsSpsBitstreamFixup, isExynos4;
|
private boolean needsSpsBitstreamFixup, isExynos4;
|
||||||
private boolean adaptivePlayback, directSubmit;
|
private boolean adaptivePlayback, directSubmit;
|
||||||
|
private boolean lowLatency;
|
||||||
private boolean constrainedHighProfile;
|
private boolean constrainedHighProfile;
|
||||||
private boolean refFrameInvalidationAvc, refFrameInvalidationHevc;
|
private boolean refFrameInvalidationAvc, refFrameInvalidationHevc;
|
||||||
private boolean refFrameInvalidationActive;
|
private boolean refFrameInvalidationActive;
|
||||||
@@ -60,6 +61,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
private boolean legacyFrameDropRendering = false;
|
private boolean legacyFrameDropRendering = false;
|
||||||
private PerfOverlayListener perfListener;
|
private PerfOverlayListener perfListener;
|
||||||
|
|
||||||
|
private MediaFormat inputFormat;
|
||||||
|
private MediaFormat outputFormat;
|
||||||
|
private MediaFormat configuredFormat;
|
||||||
|
|
||||||
private boolean needsBaselineSpsHack;
|
private boolean needsBaselineSpsHack;
|
||||||
private SeqParameterSet savedSps;
|
private SeqParameterSet savedSps;
|
||||||
|
|
||||||
@@ -161,7 +166,6 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
// shared between AVC and HEVC decoders on the same device.
|
// shared between AVC and HEVC decoders on the same device.
|
||||||
if (avcDecoder != null) {
|
if (avcDecoder != null) {
|
||||||
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
directSubmit = MediaCodecHelper.decoderCanDirectSubmit(avcDecoder.getName());
|
||||||
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder);
|
|
||||||
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.height);
|
refFrameInvalidationAvc = MediaCodecHelper.decoderSupportsRefFrameInvalidationAvc(avcDecoder.getName(), prefs.height);
|
||||||
refFrameInvalidationHevc = MediaCodecHelper.decoderSupportsRefFrameInvalidationHevc(avcDecoder.getName());
|
refFrameInvalidationHevc = MediaCodecHelper.decoderSupportsRefFrameInvalidationHevc(avcDecoder.getName());
|
||||||
|
|
||||||
@@ -264,6 +268,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refFrameInvalidationActive = refFrameInvalidationAvc;
|
refFrameInvalidationActive = refFrameInvalidationAvc;
|
||||||
|
|
||||||
|
lowLatency = MediaCodecHelper.decoderSupportsLowLatency(avcDecoder, mimeType);
|
||||||
|
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(avcDecoder, mimeType);
|
||||||
}
|
}
|
||||||
else if ((videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H265) != 0) {
|
else if ((videoFormat & MoonBridge.VIDEO_FORMAT_MASK_H265) != 0) {
|
||||||
mimeType = "video/hevc";
|
mimeType = "video/hevc";
|
||||||
@@ -275,6 +282,9 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refFrameInvalidationActive = refFrameInvalidationHevc;
|
refFrameInvalidationActive = refFrameInvalidationHevc;
|
||||||
|
|
||||||
|
lowLatency = MediaCodecHelper.decoderSupportsLowLatency(hevcDecoder, mimeType);
|
||||||
|
adaptivePlayback = MediaCodecHelper.decoderSupportsAdaptivePlayback(hevcDecoder, mimeType);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Unknown format
|
// Unknown format
|
||||||
@@ -293,6 +303,14 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
|
|
||||||
MediaFormat videoFormat = MediaFormat.createVideoFormat(mimeType, width, height);
|
MediaFormat videoFormat = MediaFormat.createVideoFormat(mimeType, width, height);
|
||||||
|
|
||||||
|
// Avoid setting KEY_FRAME_RATE on Lollipop and earlier to reduce compatibility risk
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
// We use prefs.fps instead of redrawRate here because the low latency hack in Game.java
|
||||||
|
// may leave us with an odd redrawRate value like 59 or 49 which might cause the decoder
|
||||||
|
// to puke. To be safe, we'll use the unmodified value.
|
||||||
|
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, prefs.fps);
|
||||||
|
}
|
||||||
|
|
||||||
// Adaptive playback can also be enabled by the whitelist on pre-KitKat devices
|
// Adaptive playback can also be enabled by the whitelist on pre-KitKat devices
|
||||||
// so we don't fill these pre-KitKat
|
// so we don't fill these pre-KitKat
|
||||||
if (adaptivePlayback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (adaptivePlayback && Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
@@ -300,7 +318,22 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height);
|
videoFormat.setInteger(MediaFormat.KEY_MAX_HEIGHT, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (lowLatency) {
|
||||||
|
videoFormat.setInteger(MediaCodecHelper.KEY_LOW_LATENCY, 1);
|
||||||
|
}
|
||||||
|
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
// Set the Qualcomm vendor low latency extension if the Android R option is unavailable
|
||||||
|
if (MediaCodecHelper.decoderSupportsQcomVendorLowLatency(selectedDecoderName)) {
|
||||||
|
// MediaCodec supports vendor-defined format keys using the "vendor.<extension name>.<parameter name>" syntax.
|
||||||
|
// These allow access to functionality that is not exposed through documented MediaFormat.KEY_* values.
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:hardware/qcom/sdm845/media/mm-video-v4l2/vidc/common/inc/vidc_vendor_extensions.h;l=67
|
||||||
|
//
|
||||||
|
// Examples of Qualcomm's vendor extensions for Snapdragon 845:
|
||||||
|
// https://cs.android.com/android/platform/superproject/+/master:hardware/qcom/sdm845/media/mm-video-v4l2/vidc/vdec/src/omx_vdec_extensions.hpp
|
||||||
|
// https://cs.android.com/android/_/android/platform/hardware/qcom/sm8150/media/+/0621ceb1c1b19564999db8293574a0e12952ff6c
|
||||||
|
videoFormat.setInteger("vendor.qti-ext-dec-low-latency.enable", 1);
|
||||||
|
}
|
||||||
|
|
||||||
// Operate at maximum rate to lower latency as much as possible on
|
// Operate at maximum rate to lower latency as much as possible on
|
||||||
// some Qualcomm platforms. We could also set KEY_PRIORITY to 0 (realtime)
|
// some Qualcomm platforms. We could also set KEY_PRIORITY to 0 (realtime)
|
||||||
// but that will actually result in the decoder crashing if it can't satisfy
|
// but that will actually result in the decoder crashing if it can't satisfy
|
||||||
@@ -308,8 +341,18 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
videoFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Short.MAX_VALUE);
|
videoFormat.setInteger(MediaFormat.KEY_OPERATING_RATE, Short.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configuredFormat = videoFormat;
|
||||||
|
LimeLog.info("Configuring with format: "+configuredFormat);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
videoDecoder.configure(videoFormat, renderTarget.getSurface(), null, 0);
|
videoDecoder.configure(videoFormat, renderTarget.getSurface(), null, 0);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
// This will contain the actual accepted input format attributes
|
||||||
|
inputFormat = videoDecoder.getInputFormat();
|
||||||
|
LimeLog.info("Input format: "+inputFormat);
|
||||||
|
}
|
||||||
|
|
||||||
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
videoDecoder.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
|
||||||
|
|
||||||
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (USE_FRAME_RENDER_TIME && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
@@ -334,6 +377,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
legacyInputBuffers = videoDecoder.getInputBuffers();
|
legacyInputBuffers = videoDecoder.getInputBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
return -5;
|
return -5;
|
||||||
@@ -445,7 +489,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
break;
|
break;
|
||||||
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
|
||||||
LimeLog.info("Output format changed");
|
LimeLog.info("Output format changed");
|
||||||
LimeLog.info("New output Format: " + videoDecoder.getOutputFormat());
|
outputFormat = videoDecoder.getOutputFormat();
|
||||||
|
LimeLog.info("New output format: " + outputFormat);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -1018,12 +1063,31 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
str += "HEVC Decoder: "+((renderer.hevcDecoder != null) ? renderer.hevcDecoder.getName():"(none)")+"\n";
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.avcDecoder != null) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.avcDecoder != null) {
|
||||||
Range<Integer> avcWidthRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
Range<Integer> avcWidthRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getSupportedWidths();
|
||||||
str += "AVC supported width range: "+avcWidthRange.getLower()+" - "+avcWidthRange.getUpper()+"\n";
|
str += "AVC supported width range: "+avcWidthRange+"\n";
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
try {
|
||||||
|
Range<Double> avcFpsRange = renderer.avcDecoder.getCapabilitiesForType("video/avc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||||
|
str += "AVC achievable FPS range: "+avcFpsRange+"\n";
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
str += "AVC achievable FPS range: UNSUPPORTED!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.hevcDecoder != null) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && renderer.hevcDecoder != null) {
|
||||||
Range<Integer> hevcWidthRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
Range<Integer> hevcWidthRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getSupportedWidths();
|
||||||
str += "HEVC supported width range: "+hevcWidthRange.getLower()+" - "+hevcWidthRange.getUpper()+"\n";
|
str += "HEVC supported width range: "+hevcWidthRange+"\n";
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
|
try {
|
||||||
|
Range<Double> hevcFpsRange = renderer.hevcDecoder.getCapabilitiesForType("video/hevc").getVideoCapabilities().getAchievableFrameRatesFor(renderer.initialWidth, renderer.initialHeight);
|
||||||
|
str += "HEVC achievable FPS range: " + hevcFpsRange + "\n";
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
str += "HEVC achievable FPS range: UNSUPPORTED!\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
str += "Configured format: "+renderer.configuredFormat+"\n";
|
||||||
|
str += "Input format: "+renderer.inputFormat+"\n";
|
||||||
|
str += "Output format: "+renderer.outputFormat+"\n";
|
||||||
str += "Adaptive playback: "+renderer.adaptivePlayback+"\n";
|
str += "Adaptive playback: "+renderer.adaptivePlayback+"\n";
|
||||||
str += "GL Renderer: "+renderer.glRenderer+"\n";
|
str += "GL Renderer: "+renderer.glRenderer+"\n";
|
||||||
str += "Build fingerprint: "+Build.FINGERPRINT+"\n";
|
str += "Build fingerprint: "+Build.FINGERPRINT+"\n";
|
||||||
@@ -1031,6 +1095,7 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer {
|
|||||||
str += "Consecutive crashes: "+renderer.consecutiveCrashCount+"\n";
|
str += "Consecutive crashes: "+renderer.consecutiveCrashCount+"\n";
|
||||||
str += "RFI active: "+renderer.refFrameInvalidationActive+"\n";
|
str += "RFI active: "+renderer.refFrameInvalidationActive+"\n";
|
||||||
str += "Using modern SPS patching: "+(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)+"\n";
|
str += "Using modern SPS patching: "+(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)+"\n";
|
||||||
|
str += "Low latency mode: "+renderer.lowLatency+"\n";
|
||||||
str += "Video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
str += "Video dimensions: "+renderer.initialWidth+"x"+renderer.initialHeight+"\n";
|
||||||
str += "FPS target: "+renderer.refreshRate+"\n";
|
str += "FPS target: "+renderer.refreshRate+"\n";
|
||||||
str += "Bitrate: "+renderer.prefs.bitrate+" Kbps \n";
|
str += "Bitrate: "+renderer.prefs.bitrate+" Kbps \n";
|
||||||
|
|||||||
@@ -38,6 +38,11 @@ public class MediaCodecHelper {
|
|||||||
private static final List<String> refFrameInvalidationHevcPrefixes;
|
private static final List<String> refFrameInvalidationHevcPrefixes;
|
||||||
private static final List<String> blacklisted49FpsDecoderPrefixes;
|
private static final List<String> blacklisted49FpsDecoderPrefixes;
|
||||||
private static final List<String> blacklisted59FpsDecoderPrefixes;
|
private static final List<String> blacklisted59FpsDecoderPrefixes;
|
||||||
|
private static final List<String> qualcommDecoderPrefixes;
|
||||||
|
|
||||||
|
// FIXME: Remove when Android R SDK is finalized
|
||||||
|
public static final String FEATURE_LowLatency = "low-latency";
|
||||||
|
public static final String KEY_LOW_LATENCY = "low-latency";
|
||||||
|
|
||||||
private static boolean isLowEndSnapdragon = false;
|
private static boolean isLowEndSnapdragon = false;
|
||||||
private static boolean initialized = false;
|
private static boolean initialized = false;
|
||||||
@@ -189,6 +194,13 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
qualcommDecoderPrefixes = new LinkedList<>();
|
||||||
|
|
||||||
|
qualcommDecoderPrefixes.add("omx.qcom");
|
||||||
|
qualcommDecoderPrefixes.add("c2.qti");
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean isPowerVR(String glRenderer) {
|
private static boolean isPowerVR(String glRenderer) {
|
||||||
return glRenderer.toLowerCase().contains("powervr");
|
return glRenderer.toLowerCase().contains("powervr");
|
||||||
}
|
}
|
||||||
@@ -330,7 +342,24 @@ public class MediaCodecHelper {
|
|||||||
return System.nanoTime() / 1000000L;
|
return System.nanoTime() / 1000000L;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean decoderSupportsAdaptivePlayback(MediaCodecInfo decoderInfo) {
|
public static boolean decoderSupportsLowLatency(MediaCodecInfo decoderInfo, String mimeType) {
|
||||||
|
// KitKat added CodecCapabilities.isFeatureSupported()
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
|
try {
|
||||||
|
if (decoderInfo.getCapabilitiesForType(mimeType).isFeatureSupported(FEATURE_LowLatency)) {
|
||||||
|
LimeLog.info("Low latency decoding mode supported (FEATURE_LowLatency)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Tolerate buggy codecs
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean decoderSupportsAdaptivePlayback(MediaCodecInfo decoderInfo, String mimeType) {
|
||||||
// Possibly enable adaptive playback on KitKat and above
|
// Possibly enable adaptive playback on KitKat and above
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
|
if (isDecoderInList(blacklistedAdaptivePlaybackPrefixes, decoderInfo.getName())) {
|
||||||
@@ -339,7 +368,7 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (decoderInfo.getCapabilitiesForType("video/avc").
|
if (decoderInfo.getCapabilitiesForType(mimeType).
|
||||||
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback))
|
||||||
{
|
{
|
||||||
// This will make getCapabilities() return that adaptive playback is supported
|
// This will make getCapabilities() return that adaptive playback is supported
|
||||||
@@ -348,12 +377,20 @@ public class MediaCodecHelper {
|
|||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Tolerate buggy codecs
|
// Tolerate buggy codecs
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean decoderSupportsQcomVendorLowLatency(String decoderName) {
|
||||||
|
// MediaCodec vendor extension support was introduced in Android 8.0:
|
||||||
|
// https://cs.android.com/android/_/android/platform/frameworks/av/+/01c10f8cdcd58d1e7025f426a72e6e75ba5d7fc2
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
|
||||||
|
isDecoderInList(qualcommDecoderPrefixes, decoderName);
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean decoderNeedsConstrainedHighProfile(String decoderName) {
|
public static boolean decoderNeedsConstrainedHighProfile(String decoderName) {
|
||||||
return isDecoderInList(constrainedHighProfilePrefixes, decoderName);
|
return isDecoderInList(constrainedHighProfilePrefixes, decoderName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ public class ConnectionContext {
|
|||||||
public String serverGfeVersion;
|
public String serverGfeVersion;
|
||||||
|
|
||||||
public int negotiatedWidth, negotiatedHeight;
|
public int negotiatedWidth, negotiatedHeight;
|
||||||
public int negotiatedFps;
|
|
||||||
public boolean negotiatedHdr;
|
public boolean negotiatedHdr;
|
||||||
|
|
||||||
public int videoCapabilities;
|
public int videoCapabilities;
|
||||||
|
|||||||
@@ -121,13 +121,11 @@ public class NvConnection {
|
|||||||
// Lower resolution to 1080p
|
// Lower resolution to 1080p
|
||||||
context.negotiatedWidth = 1920;
|
context.negotiatedWidth = 1920;
|
||||||
context.negotiatedHeight = 1080;
|
context.negotiatedHeight = 1080;
|
||||||
context.negotiatedFps = context.streamConfig.getRefreshRate();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Take what the client wanted
|
// Take what the client wanted
|
||||||
context.negotiatedWidth = context.streamConfig.getWidth();
|
context.negotiatedWidth = context.streamConfig.getWidth();
|
||||||
context.negotiatedHeight = context.streamConfig.getHeight();
|
context.negotiatedHeight = context.streamConfig.getHeight();
|
||||||
context.negotiatedFps = context.streamConfig.getRefreshRate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -263,7 +261,7 @@ public class NvConnection {
|
|||||||
int ret = MoonBridge.startConnection(context.serverAddress,
|
int ret = MoonBridge.startConnection(context.serverAddress,
|
||||||
context.serverAppVersion, context.serverGfeVersion,
|
context.serverAppVersion, context.serverGfeVersion,
|
||||||
context.negotiatedWidth, context.negotiatedHeight,
|
context.negotiatedWidth, context.negotiatedHeight,
|
||||||
context.negotiatedFps, context.streamConfig.getBitrate(),
|
context.streamConfig.getRefreshRate(), context.streamConfig.getBitrate(),
|
||||||
context.streamConfig.getMaxPacketSize(),
|
context.streamConfig.getMaxPacketSize(),
|
||||||
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration(),
|
context.streamConfig.getRemote(), context.streamConfig.getAudioConfiguration(),
|
||||||
context.streamConfig.getHevcSupported(),
|
context.streamConfig.getHevcSupported(),
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package com.limelight.nvstream;
|
|||||||
public interface NvConnectionListener {
|
public interface NvConnectionListener {
|
||||||
void stageStarting(String stage);
|
void stageStarting(String stage);
|
||||||
void stageComplete(String stage);
|
void stageComplete(String stage);
|
||||||
void stageFailed(String stage, long errorCode);
|
void stageFailed(String stage, int errorCode);
|
||||||
|
|
||||||
void connectionStarted();
|
void connectionStarted();
|
||||||
void connectionTerminated(long errorCode);
|
void connectionTerminated(int errorCode);
|
||||||
void connectionStatusUpdate(int connectionStatus);
|
void connectionStatusUpdate(int connectionStatus);
|
||||||
|
|
||||||
void displayMessage(String message);
|
void displayMessage(String message);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class StreamConfiguration {
|
|||||||
private NvApp app;
|
private NvApp app;
|
||||||
private int width, height;
|
private int width, height;
|
||||||
private int refreshRate;
|
private int refreshRate;
|
||||||
|
private int launchRefreshRate;
|
||||||
private int clientRefreshRateX100;
|
private int clientRefreshRateX100;
|
||||||
private int bitrate;
|
private int bitrate;
|
||||||
private boolean sops;
|
private boolean sops;
|
||||||
@@ -58,6 +59,11 @@ public class StreamConfiguration {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StreamConfiguration.Builder setLaunchRefreshRate(int refreshRate) {
|
||||||
|
config.launchRefreshRate = refreshRate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public StreamConfiguration.Builder setBitrate(int bitrate) {
|
public StreamConfiguration.Builder setBitrate(int bitrate) {
|
||||||
config.bitrate = bitrate;
|
config.bitrate = bitrate;
|
||||||
return this;
|
return this;
|
||||||
@@ -147,6 +153,7 @@ public class StreamConfiguration {
|
|||||||
this.width = 1280;
|
this.width = 1280;
|
||||||
this.height = 720;
|
this.height = 720;
|
||||||
this.refreshRate = 60;
|
this.refreshRate = 60;
|
||||||
|
this.launchRefreshRate = 60;
|
||||||
this.bitrate = 10000;
|
this.bitrate = 10000;
|
||||||
this.maxPacketSize = 1024;
|
this.maxPacketSize = 1024;
|
||||||
this.remote = STREAM_CFG_AUTO;
|
this.remote = STREAM_CFG_AUTO;
|
||||||
@@ -171,6 +178,10 @@ public class StreamConfiguration {
|
|||||||
return refreshRate;
|
return refreshRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getLaunchRefreshRate() {
|
||||||
|
return launchRefreshRate;
|
||||||
|
}
|
||||||
|
|
||||||
public int getBitrate() {
|
public int getBitrate() {
|
||||||
return bitrate;
|
return bitrate;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ public class NvHTTP {
|
|||||||
// Using an FPS value over 60 causes SOPS to default to 720p60,
|
// Using an FPS value over 60 causes SOPS to default to 720p60,
|
||||||
// so force it to 60 when starting. This won't impact our ability
|
// so force it to 60 when starting. This won't impact our ability
|
||||||
// to get > 60 FPS while actually streaming though.
|
// to get > 60 FPS while actually streaming though.
|
||||||
int fps = context.negotiatedFps > 60 ? 60 : context.negotiatedFps;
|
int fps = context.streamConfig.getLaunchRefreshRate() > 60 ? 60 : context.streamConfig.getLaunchRefreshRate();
|
||||||
|
|
||||||
// Using an unsupported resolution (not 720p, 1080p, or 4K) causes
|
// Using an unsupported resolution (not 720p, 1080p, or 4K) causes
|
||||||
// GFE to force SOPS to 720p60. This is fine for < 720p resolutions like
|
// GFE to force SOPS to 720p60. This is fine for < 720p resolutions like
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class MoonBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void bridgeClStageFailed(int stage, long errorCode) {
|
public static void bridgeClStageFailed(int stage, int errorCode) {
|
||||||
if (connectionListener != null) {
|
if (connectionListener != null) {
|
||||||
connectionListener.stageFailed(getStageName(stage), errorCode);
|
connectionListener.stageFailed(getStageName(stage), errorCode);
|
||||||
}
|
}
|
||||||
@@ -141,7 +141,7 @@ public class MoonBridge {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void bridgeClConnectionTerminated(long errorCode) {
|
public static void bridgeClConnectionTerminated(int errorCode) {
|
||||||
if (connectionListener != null) {
|
if (connectionListener != null) {
|
||||||
connectionListener.connectionTerminated(errorCode);
|
connectionListener.connectionTerminated(errorCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ public class PreferenceConfiguration {
|
|||||||
private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings";
|
private static final String DISABLE_TOASTS_PREF_STRING = "checkbox_disable_warnings";
|
||||||
private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio";
|
private static final String HOST_AUDIO_PREF_STRING = "checkbox_host_audio";
|
||||||
private static final String DEADZONE_PREF_STRING = "seekbar_deadzone";
|
private static final String DEADZONE_PREF_STRING = "seekbar_deadzone";
|
||||||
|
private static final String OSC_OPACITY_PREF_STRING = "seekbar_osc_opacity";
|
||||||
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
private static final String LANGUAGE_PREF_STRING = "list_languages";
|
||||||
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
private static final String LIST_MODE_PREF_STRING = "checkbox_list_mode";
|
||||||
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
private static final String SMALL_ICONS_PREF_STRING = "checkbox_small_icon_mode";
|
||||||
@@ -46,6 +47,7 @@ public class PreferenceConfiguration {
|
|||||||
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
private static final boolean DEFAULT_DISABLE_TOASTS = false;
|
||||||
private static final boolean DEFAULT_HOST_AUDIO = false;
|
private static final boolean DEFAULT_HOST_AUDIO = false;
|
||||||
private static final int DEFAULT_DEADZONE = 15;
|
private static final int DEFAULT_DEADZONE = 15;
|
||||||
|
private static final int DEFAULT_OPACITY = 90;
|
||||||
public static final String DEFAULT_LANGUAGE = "default";
|
public static final String DEFAULT_LANGUAGE = "default";
|
||||||
private static final boolean DEFAULT_LIST_MODE = false;
|
private static final boolean DEFAULT_LIST_MODE = false;
|
||||||
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
private static final boolean DEFAULT_MULTI_CONTROLLER = true;
|
||||||
@@ -73,6 +75,7 @@ public class PreferenceConfiguration {
|
|||||||
public int bitrate;
|
public int bitrate;
|
||||||
public int videoFormat;
|
public int videoFormat;
|
||||||
public int deadzonePercentage;
|
public int deadzonePercentage;
|
||||||
|
public int oscOpacity;
|
||||||
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
public boolean stretchVideo, enableSops, playHostAudio, disableWarnings;
|
||||||
public String language;
|
public String language;
|
||||||
public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver;
|
public boolean listMode, smallIconMode, multiController, enable51Surround, usbDriver;
|
||||||
@@ -317,6 +320,8 @@ public class PreferenceConfiguration {
|
|||||||
|
|
||||||
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
config.deadzonePercentage = prefs.getInt(DEADZONE_PREF_STRING, DEFAULT_DEADZONE);
|
||||||
|
|
||||||
|
config.oscOpacity = prefs.getInt(OSC_OPACITY_PREF_STRING, DEFAULT_OPACITY);
|
||||||
|
|
||||||
config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE);
|
config.language = prefs.getString(LANGUAGE_PREF_STRING, DEFAULT_LANGUAGE);
|
||||||
|
|
||||||
// Checkbox preferences
|
// Checkbox preferences
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.DialogPreference;
|
import android.preference.DialogPreference;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.OnClickListener;
|
import android.view.View.OnClickListener;
|
||||||
@@ -78,6 +79,8 @@ public class SeekBarPreference extends DialogPreference
|
|||||||
valueText = new TextView(context);
|
valueText = new TextView(context);
|
||||||
valueText.setGravity(Gravity.CENTER_HORIZONTAL);
|
valueText.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||||
valueText.setTextSize(32);
|
valueText.setTextSize(32);
|
||||||
|
// Default text for value; hides bug where OnSeekBarChangeListener isn't called when opacity is 0%
|
||||||
|
valueText.setText("0%");
|
||||||
params = new LinearLayout.LayoutParams(
|
params = new LinearLayout.LayoutParams(
|
||||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||||
LinearLayout.LayoutParams.WRAP_CONTENT);
|
LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||||
|
|||||||
@@ -192,4 +192,13 @@ public class ShortcutHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void enableAppShortcut(ComputerDetails computer, NvApp app) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
|
String id = getShortcutIdForGame(computer, app);
|
||||||
|
if (getInfoForId(id) != null) {
|
||||||
|
sm.enableShortcuts(Collections.singletonList(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -87,9 +87,9 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jclass clazz) {
|
|||||||
BridgeArPlaySampleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArPlaySample", "([S)V");
|
BridgeArPlaySampleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArPlaySample", "([S)V");
|
||||||
BridgeClStageStartingMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageStarting", "(I)V");
|
BridgeClStageStartingMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageStarting", "(I)V");
|
||||||
BridgeClStageCompleteMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageComplete", "(I)V");
|
BridgeClStageCompleteMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageComplete", "(I)V");
|
||||||
BridgeClStageFailedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageFailed", "(IJ)V");
|
BridgeClStageFailedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClStageFailed", "(II)V");
|
||||||
BridgeClConnectionStartedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStarted", "()V");
|
BridgeClConnectionStartedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStarted", "()V");
|
||||||
BridgeClConnectionTerminatedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionTerminated", "(J)V");
|
BridgeClConnectionTerminatedMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionTerminated", "(I)V");
|
||||||
BridgeClRumbleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClRumble", "(SSS)V");
|
BridgeClRumbleMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClRumble", "(SSS)V");
|
||||||
BridgeClConnectionStatusUpdateMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStatusUpdate", "(I)V");
|
BridgeClConnectionStatusUpdateMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeClConnectionStatusUpdate", "(I)V");
|
||||||
}
|
}
|
||||||
@@ -98,12 +98,9 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
|||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
|
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSetupMethod, videoFormat, width, height, redrawRate);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// This is called on a Java thread, so it's safe to return
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
else if (err != 0) {
|
else if (err != 0) {
|
||||||
@@ -119,20 +116,12 @@ int BridgeDrSetup(int videoFormat, int width, int height, int redrawRate, void*
|
|||||||
void BridgeDrStart(void) {
|
void BridgeDrStart(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStartMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStartMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeDrStop(void) {
|
void BridgeDrStop(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStopMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrStopMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,10 +130,6 @@ void BridgeDrCleanup(void) {
|
|||||||
|
|
||||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrCleanupMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeDrCleanupMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,10 +137,6 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return DR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Increase the size of our frame data buffer if our frame won't fit
|
// Increase the size of our frame data buffer if our frame won't fit
|
||||||
if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
|
if ((*env)->GetArrayLength(env, DecodedFrameBuffer) < decodeUnit->fullLength) {
|
||||||
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedFrameBuffer);
|
||||||
@@ -178,6 +159,8 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
|
||||||
decodeUnit->frameNumber, decodeUnit->receiveTimeMs);
|
decodeUnit->frameNumber, decodeUnit->receiveTimeMs);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
return DR_OK;
|
return DR_OK;
|
||||||
}
|
}
|
||||||
else if (ret != DR_OK) {
|
else if (ret != DR_OK) {
|
||||||
@@ -192,22 +175,27 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
|
|||||||
currentEntry = currentEntry->next;
|
currentEntry = currentEntry->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
|
||||||
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
|
||||||
decodeUnit->frameNumber,
|
decodeUnit->frameNumber,
|
||||||
decodeUnit->receiveTimeMs);
|
decodeUnit->receiveTimeMs);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
return DR_OK;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
|
int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeArInitMethod, audioConfiguration, opusConfig->sampleRate, opusConfig->samplesPerFrame);
|
err = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeArInitMethod, audioConfiguration, opusConfig->sampleRate, opusConfig->samplesPerFrame);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// This is called on a Java thread, so it's safe to return
|
||||||
err = -1;
|
err = -1;
|
||||||
}
|
}
|
||||||
if (err == 0) {
|
if (err == 0) {
|
||||||
@@ -233,20 +221,12 @@ int BridgeArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusCon
|
|||||||
void BridgeArStart(void) {
|
void BridgeArStart(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStartMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStartMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeArStop(void) {
|
void BridgeArStop(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStopMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArStopMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,20 +237,12 @@ void BridgeArCleanup() {
|
|||||||
|
|
||||||
(*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
|
(*env)->DeleteGlobalRef(env, DecodedAudioBuffer);
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArCleanupMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
jshort* decodedData = (*env)->GetShortArrayElements(env, DecodedAudioBuffer, 0);
|
jshort* decodedData = (*env)->GetShortArrayElements(env, DecodedAudioBuffer, 0);
|
||||||
|
|
||||||
int decodeLen = opus_multistream_decode(Decoder,
|
int decodeLen = opus_multistream_decode(Decoder,
|
||||||
@@ -284,6 +256,10 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
|||||||
(*env)->ReleaseShortArrayElements(env, DecodedAudioBuffer, decodedData, 0);
|
(*env)->ReleaseShortArrayElements(env, DecodedAudioBuffer, decodedData, 0);
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeArPlaySampleMethod, DecodedAudioBuffer);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// We can abort here to avoid the copy back since no data was modified
|
// We can abort here to avoid the copy back since no data was modified
|
||||||
@@ -294,71 +270,56 @@ void BridgeArDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
|||||||
void BridgeClStageStarting(int stage) {
|
void BridgeClStageStarting(int stage) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageStartingMethod, stage);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageStartingMethod, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClStageComplete(int stage) {
|
void BridgeClStageComplete(int stage) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageCompleteMethod, stage);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageCompleteMethod, stage);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClStageFailed(int stage, long errorCode) {
|
void BridgeClStageFailed(int stage, int errorCode) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageFailedMethod, stage, errorCode);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClStageFailedMethod, stage, errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionStarted(void) {
|
void BridgeClConnectionStarted(void) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStartedMethod, NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionTerminated(long errorCode) {
|
void BridgeClConnectionTerminated(int errorCode) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionTerminatedMethod, errorCode);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionTerminatedMethod, errorCode);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) {
|
void BridgeClRumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, lowFreqMotor, highFreqMotor);
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClRumbleMethod, controllerNumber, lowFreqMotor, highFreqMotor);
|
||||||
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClConnectionStatusUpdate(int connectionStatus) {
|
void BridgeClConnectionStatusUpdate(int connectionStatus) {
|
||||||
JNIEnv* env = GetThreadEnv();
|
JNIEnv* env = GetThreadEnv();
|
||||||
|
|
||||||
|
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
||||||
if ((*env)->ExceptionCheck(env)) {
|
if ((*env)->ExceptionCheck(env)) {
|
||||||
|
// We will crash here
|
||||||
|
(*JVM)->DetachCurrentThread(JVM);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
(*env)->CallStaticVoidMethod(env, GlobalBridgeClass, BridgeClConnectionStatusUpdateMethod, connectionStatus);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BridgeClLogMessage(const char* format, ...) {
|
void BridgeClLogMessage(const char* format, ...) {
|
||||||
|
|||||||
Submodule app/src/main/jni/moonlight-core/moonlight-common-c updated: f5ae5df5d0...c1b8aa266f.
@@ -186,5 +186,7 @@
|
|||||||
<string name="summary_enable_hdr">HDR-Streaming sofern dies von der PC GPU unterstützt wird. HDR erfordert eine GPU der GTX 1000 Serie oder neuer.</string>
|
<string name="summary_enable_hdr">HDR-Streaming sofern dies von der PC GPU unterstützt wird. HDR erfordert eine GPU der GTX 1000 Serie oder neuer.</string>
|
||||||
<string name="title_enable_perf_overlay">Performance Overlay aktivieren</string>
|
<string name="title_enable_perf_overlay">Performance Overlay aktivieren</string>
|
||||||
<string name="summary_enable_perf_overlay">Leistungsmerkmale während des Streamens in Echtzeit einblenden.</string>
|
<string name="summary_enable_perf_overlay">Leistungsmerkmale während des Streamens in Echtzeit einblenden.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Transparenz</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -187,5 +187,6 @@
|
|||||||
<string name="summary_enable_hdr">Diffuser du HDR lorsque le jeu et le processeur graphique du PC le prennent en charge. HDR nécessite un GPU série GTX 1000 ou une version ultérieure.</string>
|
<string name="summary_enable_hdr">Diffuser du HDR lorsque le jeu et le processeur graphique du PC le prennent en charge. HDR nécessite un GPU série GTX 1000 ou une version ultérieure.</string>
|
||||||
<string name="title_enable_perf_overlay">Activer la superposition de performance</string>
|
<string name="title_enable_perf_overlay">Activer la superposition de performance</string>
|
||||||
<string name="summary_enable_perf_overlay">Afficher une superposition à l\'écran avec des informations de performance en temps réel pendant la lecture en continu</string>
|
<string name="summary_enable_perf_overlay">Afficher une superposition à l\'écran avec des informations de performance en temps réel pendant la lecture en continu</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -159,5 +159,6 @@
|
|||||||
<string name="summary_video_format">H.265 riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string>
|
<string name="summary_video_format">H.265 riduce i requisiti di larghezza di banda video ma richiede un dispositivo molto recente</string>
|
||||||
<string name="title_enable_hdr">Abilita HDR (sperimentale)</string>
|
<string name="title_enable_hdr">Abilita HDR (sperimentale)</string>
|
||||||
<string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video serie GTX 1000 o sucessive.</string>
|
<string name="summary_enable_hdr">Utilizza l\'HDR quando il gioco e la scheda video del PC lo supportano. L\'HDR richiede una scheda video serie GTX 1000 o sucessive.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -121,5 +121,18 @@
|
|||||||
<string name="category_advanced_settings">高度な設定</string>
|
<string name="category_advanced_settings">高度な設定</string>
|
||||||
<string name="title_video_format">H.265</string>
|
<string name="title_video_format">H.265</string>
|
||||||
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
|
<string name="summary_video_format">H.265は動画に必要な帯域幅を圧縮します。この機能にはなるべく新しいデバイスが必要です</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透過率</string>
|
||||||
|
<string name="title_osc_opacity">透過率</string>
|
||||||
|
<string name="summary_osc_opacity">オンスクリーンコントローラの透過率を調整します</string>
|
||||||
|
<string name="title_only_l3r3">L3 と R3 のみ表示します</string>
|
||||||
|
<string name="summary_only_l3r3">L3 と R3 以外のボタンを表示しない</string>
|
||||||
|
<string name="title_reset_osc">オンスクリーンコントローラをデフォルトに戻します</string>
|
||||||
|
<string name="summary_reset_osc">サイズやレイアウトを戻します</string>
|
||||||
|
<string name="dialog_title_reset_osc">デフォルトに戻します</string>
|
||||||
|
<string name="dialog_text_reset_osc">本当にデフォルトに戻しますか?</string>
|
||||||
|
<string name="toast_reset_osc_success">オンスクリーンコントローラをデフォルトに戻しました</string>
|
||||||
|
<string name="title_checkbox_vibrate_osc">振動</string>
|
||||||
|
<string name="summary_checkbox_vibrate_osc">コントローラの振動を真似します</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -136,5 +136,6 @@
|
|||||||
<string name="category_advanced_settings">고급 설정</string>
|
<string name="category_advanced_settings">고급 설정</string>
|
||||||
<string name="title_video_format">H.265 설정 변경</string>
|
<string name="title_video_format">H.265 설정 변경</string>
|
||||||
<string name="summary_video_format">H.265는 비디오 대역폭 요구사항을 낮춰주지만 최신 장치가 필요합니다.</string>
|
<string name="summary_video_format">H.265는 비디오 대역폭 요구사항을 낮춰주지만 최신 장치가 필요합니다.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -125,5 +125,6 @@
|
|||||||
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
|
<string name="category_advanced_settings">Geavanceerde Instellingen</string>
|
||||||
<string name="title_video_format">Verander H.265 instellingen</string>
|
<string name="title_video_format">Verander H.265 instellingen</string>
|
||||||
<string name="summary_video_format">H.265 verlaagt video bandbreedte vereisten maar benodigdt een recent apparaat.</string>
|
<string name="summary_video_format">H.265 verlaagt video bandbreedte vereisten maar benodigdt een recent apparaat.</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -183,4 +183,5 @@
|
|||||||
<string name="summary_fps_list">Увеличение для более плавного видео потока. Уменьшите для лучшей производительности на более слабых устройствах.</string>
|
<string name="summary_fps_list">Увеличение для более плавного видео потока. Уменьшите для лучшей производительности на более слабых устройствах.</string>
|
||||||
<string name="scut_invalid_uuid">Указанный PC недействителен</string>
|
<string name="scut_invalid_uuid">Указанный PC недействителен</string>
|
||||||
<string name="scut_invalid_app_id">Указанное приложение недействительно</string>
|
<string name="scut_invalid_app_id">Указанное приложение недействительно</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -205,6 +205,10 @@
|
|||||||
|
|
||||||
<string name="title_enable_perf_overlay"> 启用性能信息 </string>
|
<string name="title_enable_perf_overlay"> 启用性能信息 </string>
|
||||||
<string name="summary_enable_perf_overlay"> 在串流中显示实时性能信息 </string>
|
<string name="summary_enable_perf_overlay"> 在串流中显示实时性能信息 </string>
|
||||||
|
<string name="title_osc_opacity">更改屏幕按钮透明度</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透明度</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="summary_osc_opacity">令屏幕按钮变得更透明/更不透明</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -205,6 +205,10 @@
|
|||||||
|
|
||||||
<string name="title_enable_perf_overlay"> 啟用性能資訊 </string>
|
<string name="title_enable_perf_overlay"> 啟用性能資訊 </string>
|
||||||
<string name="summary_enable_perf_overlay"> 在串流中顯示即時性能資訊 </string>
|
<string name="summary_enable_perf_overlay"> 在串流中顯示即時性能資訊 </string>
|
||||||
|
<string name="title_osc_opacity">更改屏幕按鈕透明度</string>
|
||||||
|
<string name="dialog_title_osc_opacity">透明度</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
<string name="summary_osc_opacity">令屏幕按钮變得更透明/更不透明</string>
|
||||||
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
@@ -60,6 +60,7 @@
|
|||||||
<string name="message_decoding_reset">Your device\'s video decoder continues to crash at your selected streaming settings. Your streaming settings have been reset to default.</string>
|
<string name="message_decoding_reset">Your device\'s video decoder continues to crash at your selected streaming settings. Your streaming settings have been reset to default.</string>
|
||||||
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
|
<string name="error_usb_prohibited">USB access is prohibited by your device administrator. Check your Knox or MDM settings.</string>
|
||||||
<string name="unable_to_pin_shortcut">Your current launcher does not allow for creating pinned shortcuts.</string>
|
<string name="unable_to_pin_shortcut">Your current launcher does not allow for creating pinned shortcuts.</string>
|
||||||
|
<string name="video_decoder_init_failed">Video decoder failed to initialize. Your device may not support the selected resolution or frame rate.</string>
|
||||||
|
|
||||||
<!-- Start application messages -->
|
<!-- Start application messages -->
|
||||||
<string name="conn_establishing_title">Establishing Connection</string>
|
<string name="conn_establishing_title">Establishing Connection</string>
|
||||||
@@ -165,6 +166,10 @@
|
|||||||
<string name="dialog_title_reset_osc">Reset Layout</string>
|
<string name="dialog_title_reset_osc">Reset Layout</string>
|
||||||
<string name="dialog_text_reset_osc">Are you sure you want to delete your saved on-screen controls layout?</string>
|
<string name="dialog_text_reset_osc">Are you sure you want to delete your saved on-screen controls layout?</string>
|
||||||
<string name="toast_reset_osc_success">On-screen controls reset to default</string>
|
<string name="toast_reset_osc_success">On-screen controls reset to default</string>
|
||||||
|
<string name="title_osc_opacity">Change opacity of on-screen controls</string>
|
||||||
|
<string name="summary_osc_opacity">Make the on-screen controls more/less transparent</string>
|
||||||
|
<string name="dialog_title_osc_opacity">Change opacity</string>
|
||||||
|
<string name="suffix_osc_opacity">%</string>
|
||||||
|
|
||||||
<string name="category_ui_settings">UI Settings</string>
|
<string name="category_ui_settings">UI Settings</string>
|
||||||
<string name="title_language_list">Language</string>
|
<string name="title_language_list">Language</string>
|
||||||
|
|||||||
@@ -107,6 +107,17 @@
|
|||||||
android:key="checkbox_only_show_L3R3"
|
android:key="checkbox_only_show_L3R3"
|
||||||
android:summary="@string/summary_only_l3r3"
|
android:summary="@string/summary_only_l3r3"
|
||||||
android:title="@string/title_only_l3r3" />
|
android:title="@string/title_only_l3r3" />
|
||||||
|
<com.limelight.preferences.SeekBarPreference
|
||||||
|
android:key="seekbar_osc_opacity"
|
||||||
|
android:dependency="checkbox_show_onscreen_controls"
|
||||||
|
android:dialogMessage="@string/summary_osc_opacity"
|
||||||
|
seekbar:min="0"
|
||||||
|
seekbar:step="1"
|
||||||
|
android:max="100"
|
||||||
|
android:defaultValue="90"
|
||||||
|
android:summary="@string/summary_osc_opacity"
|
||||||
|
android:text="@string/suffix_osc_opacity"
|
||||||
|
android:title="@string/dialog_title_osc_opacity" />
|
||||||
<com.limelight.preferences.ConfirmDeleteOscPreference
|
<com.limelight.preferences.ConfirmDeleteOscPreference
|
||||||
android:title="@string/title_reset_osc"
|
android:title="@string/title_reset_osc"
|
||||||
android:summary="@string/summary_reset_osc"
|
android:summary="@string/summary_reset_osc"
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ buildscript {
|
|||||||
google()
|
google()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
- Latency optimizations for Android R DP1
|
||||||
|
- Video decoder optimizations
|
||||||
|
- Added Start+Back+LB+RB combo to disconnect the stream
|
||||||
|
- Fixed back button on some Android TV remotes
|
||||||
|
- Fixed d-pad on gamepads that expose non-functional hat axes
|
||||||
|
- Fixed unexpected mouse input after using gamepad mouse emulation mode
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
- Configurable transparency for on-screen controls
|
||||||
|
- Fixed a crash when pinning an app shortcut to the home screen
|
||||||
|
- Fixed right click unexpectedly stopping the stream on some devices
|
||||||
|
- Improved key repeating behavior while streaming
|
||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
#Tue Aug 20 11:37:45 PDT 2019
|
#Mon Feb 24 12:32:24 PST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
|
||||||
+1
-1
@@ -1 +1 @@
|
|||||||
include ':app', ':moonlight-common'
|
include ':app'
|
||||||
Reference in new issue
Block a user