added abstract class for keyboard and did dimension measuring

This commit is contained in:
amwatson 2024-02-08 22:18:05 -06:00
parent 043f57ec8c
commit f231577afd
7 changed files with 99 additions and 58 deletions

View file

@ -26,3 +26,5 @@
-keep class org.citra.citra_emu.vr.GameSurfaceLayer { *; }
-keep class org.citra.citra_emu.vr.VrActivity { *; }
-keep class org.citra.citra_emu.vr.ui.VrUILayer { *; }
-keep class org.citra.citra_emu.vr.ui.VrKeyboardLayer { *; }
-keepattributes SourceFile,LineNumberTable

View file

@ -0,0 +1,11 @@
package org.citra.citra_emu.vr.ui
import org.citra.citra_emu.R
import org.citra.citra_emu.vr.VrActivity
class VrKeyboardLayer(activity: VrActivity) : VrUILayer(activity, R.layout.vr_keyboard) {
override fun onSurfaceCreated() {
super.onSurfaceCreated()
}
}

View file

@ -17,7 +17,6 @@ import android.view.Surface
import android.view.View
import android.view.Window
import android.view.WindowManager
import org.citra.citra_emu.R
import org.citra.citra_emu.utils.Log
import org.citra.citra_emu.vr.VrActivity
import java.io.File
@ -34,8 +33,8 @@ import kotlin.math.roundToInt
**/
abstract class VrUILayer(
val activity: VrActivity,
densityDpi: Int = DEFAULT_DENSITY.toInt(),
private val layoutId: Int = R.layout.vr_keyboard
private val layoutId: Int,
densityDpi: Int = DEFAULT_DENSITY.toInt()
) {
private val requestedDensity: Float = densityDpi.toFloat()
private var virtualDisplay: VirtualDisplay? = null
@ -46,33 +45,63 @@ abstract class VrUILayer(
/// Called from JNI ////
fun getBoundsForView(handle: Long): Int {
val inflater = activity.getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
val contentView = inflater.inflate(layoutId, null, false)
val contentView = LayoutInflater.from(activity).inflate(layoutId, null, false)
if (contentView == null) {
Log.error( "Failed to inflate content view")
Log.warning("contentView is null")
return -1
}
val (widthPx, heightPx) = calculateDynamicDimensions(activity, 1.0f, 1.0f)
contentView.measure(
View.MeasureSpec.UNSPECIFIED,
View.MeasureSpec.UNSPECIFIED
View.MeasureSpec.makeMeasureSpec(widthPx, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(heightPx, View.MeasureSpec.EXACTLY)
)
val measuredWidthPx = contentView.measuredWidth
val measuredHeightPx = contentView.measuredHeight
val displayMetrics = activity.resources.displayMetrics
val widthDp = contentView.measuredWidth.toFloat() / displayMetrics.density /
(DEFAULT_DENSITY / requestedDensity)
val heightDp = contentView.measuredHeight.toFloat() / displayMetrics.density /
(DEFAULT_DENSITY / requestedDensity)
// roundToInt() matching the rounding Android uses to convert view dimensions to display units
nativeSetBounds(
handle, 0, 0, widthDp.roundToInt(),
heightDp.roundToInt()
)
val measuredWidthDp = measuredWidthPx / displayMetrics.density
val measuredHeightDp = measuredHeightPx / displayMetrics.density
// Call native method with measured dimensions
nativeSetBounds(handle, 0, 0, measuredWidthDp.roundToInt(), measuredHeightDp.roundToInt())
return 0
}
fun sendClickToUI(x: Float, y: Float, motionType: Int): Int {
val action = when (motionType) {
0 -> MotionEvent.ACTION_UP
1 -> MotionEvent.ACTION_DOWN
2 -> MotionEvent.ACTION_MOVE
else -> MotionEvent.ACTION_HOVER_ENTER
}
activity.runOnUiThread { dispatchTouchEvent(x, y, action) }
return 0
}
fun setSurface(
surface: Surface, widthDp: Int,
heightDp: Int
): Int {
activity.runOnUiThread { setSurface_(surface, widthDp, heightDp) }
return 0
}
protected open fun onSurfaceCreated() {}
private fun calculateDynamicDimensions(context: Context, widthPercentage: Float, heightPercentage: Float): Pair<Int, Int> {
val displayMetrics = context.resources.displayMetrics
val screenWidthPx = displayMetrics.widthPixels
val screenHeightPx = displayMetrics.heightPixels
val desiredWidthPx = (screenWidthPx * widthPercentage).toInt()
val desiredHeightPx = (screenHeightPx * heightPercentage).toInt()
return Pair(desiredWidthPx, desiredHeightPx)
}
private fun dispatchTouchEvent(x: Float, y: Float, action: Int) {
val eventTime = SystemClock.uptimeMillis()
MotionEvent.obtain(
@ -100,25 +129,6 @@ abstract class VrUILayer(
}
}
fun sendClickToUI(x: Float, y: Float, motionType: Int): Int {
val action = when (motionType) {
0 -> MotionEvent.ACTION_UP
1 -> MotionEvent.ACTION_DOWN
2 -> MotionEvent.ACTION_MOVE
else -> MotionEvent.ACTION_HOVER_ENTER
}
activity.runOnUiThread { dispatchTouchEvent(x, y, action) }
return 0
}
fun setSurface(
surface: Surface, widthDp: Int,
heightDp: Int
): Int {
activity.runOnUiThread { setSurface_(surface, widthDp, heightDp) }
return 0
}
private fun setSurface_(
surface: Surface, widthDp: Int,
heightDp: Int
@ -140,8 +150,6 @@ abstract class VrUILayer(
onSurfaceCreated()
}
protected fun onSurfaceCreated() {}
fun writeBitmapToDisk(bmp: Bitmap, outName: String?) {
val sdCard = activity.externalCacheDir
if (sdCard != null && outName != null) {

View file

@ -99,7 +99,7 @@ struct BoundsHandle
//-----------------------------------------------------------------------------
// JNI functions
extern "C" JNIEXPORT void JNICALL
Java_org_citra_citra_1emu_vr_ui_UILayer_00024Companion_nativeSetBounds(
Java_org_citra_citra_1emu_vr_ui_VrUILayer_00024Companion_nativeSetBounds(
JNIEnv* env, jobject thiz, jlong handle, jint left, jint top, jint right,
jint bottom)
{
@ -255,7 +255,7 @@ bool UILayer::GetRayIntersectionWithPanel(const XrVector3f& start,
result2d, result3d);
}
// Next error code: -5
// Next error code: -6
int32_t UILayer::Init(const std::string& className,
const jobject activityObject, const XrVector3f& position,
const XrSession& session)
@ -279,7 +279,10 @@ int32_t UILayer::Init(const std::string& className,
BAIL_ON_COND(mGetBoundsMethodID == nullptr,
"could not find getBoundsForView()", -4);
TryCreateSwapchain();
mSetSurfaceMethodId = mEnv->GetMethodID(mVrUILayerClass, "setSurface",
"(Landroid/view/Surface;II)I");
BAIL_ON_COND(mSetSurfaceMethodId == nullptr, "could not find setSurface()",
-5);
return 0;
}
@ -296,11 +299,24 @@ void UILayer::TryCreateSwapchain()
AndroidWindowBounds viewBounds;
{
if (mEnv->ExceptionCheck()) { mEnv->ExceptionClear(); }
const jint ret = mEnv->CallIntMethod(
mVrUILayerObject, mGetBoundsMethodID, BoundsHandle(&viewBounds).l);
// Check for exceptions (and log them).
if (mEnv->ExceptionCheck())
{
mEnv->ExceptionDescribe();
mEnv->ExceptionClear();
}
if (ret < 0)
{
ALOGD("getBoundsForView() returned error %d", ret);
ALOGE("{}() returned error {}", __FUNCTION__, ret);
return;
}
if (viewBounds.Width() == 0 || viewBounds.Height() == 0)
{
ALOGE("{}() returned invalid bounds {} x {}", __FUNCTION__,
viewBounds.Width(), viewBounds.Height());
return;
}
}
@ -338,18 +354,19 @@ void UILayer::TryCreateSwapchain()
mSession, &swapchainCreateInfo, &mSwapchain.mHandle, &mSurface));
mSwapchain.mWidth = viewBounds.Width();
mSwapchain.mHeight = viewBounds.Height();
ALOGD("UILayer: created swapchain {}x{}", mSwapchain.mWidth,
mSwapchain.mHeight);
jmethodID setSurfaceMethodId = mEnv->GetMethodID(
mVrUILayerClass, "setSurface", "(Landroid/view/Surface;II)I");
if (setSurfaceMethodId == nullptr)
{
FAIL("Couldn't find setSurface()");
}
mEnv->CallIntMethod(mVrUILayerObject, setSurfaceMethodId, mSurface,
mIsSwapchainCreated = true;
mEnv->CallIntMethod(mVrUILayerObject, mSetSurfaceMethodId, mSurface,
(int)viewBounds.Width(), (int)viewBounds.Height());
if (mEnv->ExceptionCheck())
{
mEnv->ExceptionDescribe();
mEnv->ExceptionClear();
}
}
mIsSwapchainCreated = true;
}
void UILayer::SendClickToUI(const XrVector2f& pos2d, const int type)

View file

@ -97,4 +97,5 @@ private:
// like window decorations or status bars.
jmethodID mGetBoundsMethodID = nullptr;
jmethodID mSendClickToWindowMethodID = nullptr;
jmethodID mSetSurfaceMethodId = nullptr;
};

View file

@ -278,10 +278,10 @@ public:
jni, mActivityObject, gOpenXr->mSession);
#endif
mUILayer = std::make_unique<UILayer>(
"org/citra/citra_emu/vr/ui/VrUILayer",
XrVector3f{0, 0.0f, -1.5f}, jni, mActivityObject, gOpenXr->mSession);
"org/citra/citra_emu/vr/ui/VrKeyboardLayer",
XrVector3f{0, 0.0f, -1.5f}, jni, mActivityObject,
gOpenXr->mSession);
//////////////////////////////////////////////////
// Intialize JNI methods
@ -523,6 +523,8 @@ private:
}
}
if (!mUILayer->IsSwapchainCreated()) { mUILayer->TryCreateSwapchain(); }
////////////////////////////////
// XrWaitFrame()
////////////////////////////////

View file

@ -13,8 +13,8 @@ app's packagename and returns information for the wrong window.-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/vr_keyboard"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_width="1200dp"
android:layout_height="800dp"
android:layout_gravity="center"
android:layout_marginHorizontal="30dp"
android:gravity="center"