mirror of
https://gitlab.freedesktop.org/monado/monado.git
synced 2025-01-22 14:41:47 +00:00
aux/android: Factor out a "native counterpart" class.
This commit is contained in:
parent
2082eddb65
commit
5115124bb3
|
@ -8,7 +8,7 @@ android {
|
||||||
buildToolsVersion '30.0.2'
|
buildToolsVersion '30.0.2'
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 20
|
minSdkVersion 24
|
||||||
targetSdkVersion project.sharedTargetSdk
|
targetSdkVersion project.sharedTargetSdk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
package org.freedesktop.monado.auxiliary;
|
package org.freedesktop.monado.auxiliary;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.os.Build;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
|
@ -19,6 +20,7 @@ import android.view.WindowManager;
|
||||||
import androidx.annotation.Keep;
|
import androidx.annotation.Keep;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
@ -43,9 +45,8 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
|
|
||||||
/// Guards currentSurfaceHolder
|
/// Guards currentSurfaceHolder
|
||||||
private final Object currentSurfaceHolderSync = new Object();
|
private final Object currentSurfaceHolderSync = new Object();
|
||||||
/// Guards the usedByNativeCode flag
|
|
||||||
private final Object usedByNativeCodeSync = new Object();
|
|
||||||
private final Method viewSetSysUiVis;
|
private final Method viewSetSysUiVis;
|
||||||
|
private final NativeCounterpart nativeCounterpart;
|
||||||
|
|
||||||
public int width = -1;
|
public int width = -1;
|
||||||
public int height = -1;
|
public int height = -1;
|
||||||
|
@ -53,15 +54,11 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
|
|
||||||
/// Guarded by currentSurfaceHolderSync
|
/// Guarded by currentSurfaceHolderSync
|
||||||
private SurfaceHolder currentSurfaceHolder = null;
|
private SurfaceHolder currentSurfaceHolder = null;
|
||||||
/// Guarded by usedByNativeCodeSync
|
|
||||||
private boolean usedByNativeCode = false;
|
|
||||||
/// Contains the pointer to the native android_custom_surface object.
|
|
||||||
private long nativePointer = 0;
|
|
||||||
|
|
||||||
private MonadoView(Activity activity, long nativePointer) {
|
private MonadoView(Activity activity, long nativePointer) {
|
||||||
super(activity);
|
super(activity);
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.nativePointer = nativePointer;
|
this.nativeCounterpart = new NativeCounterpart(nativePointer);
|
||||||
Method method;
|
Method method;
|
||||||
try {
|
try {
|
||||||
method = activity.getWindow().getDecorView().getClass().getMethod("setSystemUiVisibility", int.class);
|
method = activity.getWindow().getDecorView().getClass().getMethod("setSystemUiVisibility", int.class);
|
||||||
|
@ -133,10 +130,7 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ret != null) {
|
if (ret != null) {
|
||||||
synchronized (usedByNativeCodeSync) {
|
nativeCounterpart.markAsUsedByNativeCode();
|
||||||
usedByNativeCode = true;
|
|
||||||
usedByNativeCodeSync.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -149,16 +143,7 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
*/
|
*/
|
||||||
@Keep
|
@Keep
|
||||||
public void markAsDiscardedByNative() {
|
public void markAsDiscardedByNative() {
|
||||||
|
nativeCounterpart.markAsDiscardedByNative(TAG);
|
||||||
synchronized (usedByNativeCodeSync) {
|
|
||||||
if (!usedByNativeCode) {
|
|
||||||
Log.w(TAG, "This should not have happened: Discarding by native code, but not marked as used!");
|
|
||||||
}
|
|
||||||
usedByNativeCode = false;
|
|
||||||
nativePointer = 0;
|
|
||||||
usedByNativeCodeSync.notifyAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean makeFullscreen() {
|
private boolean makeFullscreen() {
|
||||||
|
@ -184,6 +169,7 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
/**
|
/**
|
||||||
* Add a listener so that if our system UI display state doesn't include all we want, we re-apply.
|
* Add a listener so that if our system UI display state doesn't include all we want, we re-apply.
|
||||||
*/
|
*/
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private void setSystemUiVisChangeListener() {
|
private void setSystemUiVisChangeListener() {
|
||||||
activity.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
|
activity.getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(visibility -> {
|
||||||
|
@ -234,16 +220,9 @@ public class MonadoView extends SurfaceView implements SurfaceHolder.Callback, S
|
||||||
}
|
}
|
||||||
if (lost) {
|
if (lost) {
|
||||||
//! @todo this function should notify native code that the surface is gone.
|
//! @todo this function should notify native code that the surface is gone.
|
||||||
try {
|
if (!nativeCounterpart.blockUntilNativeDiscard(TAG)) {
|
||||||
synchronized (usedByNativeCodeSync) {
|
|
||||||
while (usedByNativeCode) {
|
|
||||||
usedByNativeCodeSync.wait();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Log.i(TAG,
|
Log.i(TAG,
|
||||||
"Interrupted in surfaceDestroyed while waiting for native code to finish up: " + e.toString());
|
"Interrupted in surfaceDestroyed while waiting for native code to finish up.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
// Copyright 2020, Collabora, Ltd.
|
||||||
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
/*!
|
||||||
|
* @file
|
||||||
|
* @brief Utility class to deal with having a native-code counterpart object
|
||||||
|
* @author Ryan Pavlik <ryan.pavlik@collabora.com>
|
||||||
|
* @ingroup aux_android_java
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.freedesktop.monado.auxiliary;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.CheckResult;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import java.security.InvalidParameterException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object that tracks the native counterpart object for a type. Must be initialized on construction,
|
||||||
|
* and may have its native code destroyed/discarded, but may not "re-seat" it to new native code
|
||||||
|
* pointer.
|
||||||
|
* <p>
|
||||||
|
* Use as a member of any type with a native counterpart (a native-allocated-and-owned object that
|
||||||
|
* holds a reference to the owning class). Include the following field and delegating method to use
|
||||||
|
* (note: assumes you have a tag for logging purposes as TAG)
|
||||||
|
* <p>
|
||||||
|
* <pre>
|
||||||
|
* private final NativeCounterpart nativeCounterpart;
|
||||||
|
*
|
||||||
|
* @Keep
|
||||||
|
* public void markAsDiscardedByNative() {
|
||||||
|
* nativeCounterpart.markAsDiscardedByNative(TAG);
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* Then, initialize it in your constructor, call {@code markAsUsedByNativeCode()} where desired
|
||||||
|
* (often in your constructor), and call {@code getNativePointer()} and
|
||||||
|
* {@code blockUntilNativeDiscard()} as needed.
|
||||||
|
* <p>
|
||||||
|
* Your native code can use this to turn a void* into a jlong:
|
||||||
|
* {@code static_cast<long long>(reinterpret_cast<intptr_t>(nativePointer))}
|
||||||
|
*/
|
||||||
|
public final class NativeCounterpart {
|
||||||
|
/**
|
||||||
|
* Guards the usedByNativeCodeSync.
|
||||||
|
*/
|
||||||
|
private final Object usedByNativeCodeSync = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the containing object is in use by native code.
|
||||||
|
* <p>
|
||||||
|
* Guarded by usedByNativeCodeSync.
|
||||||
|
*/
|
||||||
|
private boolean usedByNativeCode = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contains the pointer to the native counterpart object.
|
||||||
|
*/
|
||||||
|
private long nativePointer = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
|
* @param nativePointer The native pointer, cast appropriately. Must be non-zero. Can cast like:
|
||||||
|
* {@code static_cast<long long>(reinterpret_cast<intptr_t>(nativePointer))}
|
||||||
|
*/
|
||||||
|
public NativeCounterpart(long nativePointer) throws InvalidParameterException {
|
||||||
|
if (nativePointer == 0) {
|
||||||
|
throw new InvalidParameterException("nativePointer must not be 0");
|
||||||
|
}
|
||||||
|
this.nativePointer = nativePointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the flag to indicate that native code is using this. Only call this once, probably in
|
||||||
|
* your constructor unless you have a good reason to call it elsewhere.
|
||||||
|
*/
|
||||||
|
public void markAsUsedByNativeCode() {
|
||||||
|
synchronized (usedByNativeCodeSync) {
|
||||||
|
assert nativePointer != 0;
|
||||||
|
usedByNativeCode = true;
|
||||||
|
usedByNativeCodeSync.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Change the flag and notify those waiting on it, to indicate that native code is done with
|
||||||
|
* this object.
|
||||||
|
*
|
||||||
|
* @param TAG Your owning class's logging tag
|
||||||
|
*/
|
||||||
|
public void markAsDiscardedByNative(String TAG) {
|
||||||
|
synchronized (usedByNativeCodeSync) {
|
||||||
|
if (!usedByNativeCode) {
|
||||||
|
Log.w(TAG,
|
||||||
|
"This should not have happened: Discarding by native code, but not marked as used!");
|
||||||
|
}
|
||||||
|
usedByNativeCode = false;
|
||||||
|
nativePointer = 0;
|
||||||
|
usedByNativeCodeSync.notifyAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the native pointer value. Will be 0 if discarded by native code!.
|
||||||
|
*
|
||||||
|
* @return pointer (cast as a long)
|
||||||
|
*/
|
||||||
|
public long getNativePointer() {
|
||||||
|
synchronized (usedByNativeCodeSync) {
|
||||||
|
return nativePointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until {@code markAsDiscardedByNative} has been called indicating that the native code is
|
||||||
|
* done with this. Be sure to check the result!
|
||||||
|
*
|
||||||
|
* @param TAG Your owning class's logging tag
|
||||||
|
* @return true if this class has successfully been discarded by native.
|
||||||
|
*/
|
||||||
|
@CheckResult
|
||||||
|
public boolean blockUntilNativeDiscard(@NonNull String TAG) {
|
||||||
|
try {
|
||||||
|
synchronized (usedByNativeCodeSync) {
|
||||||
|
while (usedByNativeCode) {
|
||||||
|
usedByNativeCodeSync.wait();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Log.i(TAG,
|
||||||
|
"Interrupted while waiting for native code to finish up: " + e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue