From 398fb78a315ec3081eb571ce9eb45bb497f989fa Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen <emmausssss@gmail.com>
Date: Thu, 27 Jul 2023 20:24:33 +0000
Subject: [PATCH] refactor virtual pad composition

---
 .../app/src/main/AndroidManifest.xml          |  1 +
 .../org/ryujinx/android/GameController.kt     | 71 +++++++++++--------
 .../main/java/org/ryujinx/android/GameHost.kt |  8 +--
 .../android/viewmodels/MainViewModel.kt       |  6 ++
 .../org/ryujinx/android/views/MainView.kt     | 55 ++++++++------
 5 files changed, 83 insertions(+), 58 deletions(-)

diff --git a/src/RyujinxAndroid/app/src/main/AndroidManifest.xml b/src/RyujinxAndroid/app/src/main/AndroidManifest.xml
index 5dc772de1..4501665de 100644
--- a/src/RyujinxAndroid/app/src/main/AndroidManifest.xml
+++ b/src/RyujinxAndroid/app/src/main/AndroidManifest.xml
@@ -28,6 +28,7 @@
         <activity
             android:name=".MainActivity"
             android:exported="true"
+            android:hardwareAccelerated="false"
             android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
             android:theme="@style/Theme.RyujinxAndroid">
             <intent-filter>
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt
index 2266a2eb7..aaf97cd3e 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameController.kt
@@ -13,6 +13,7 @@ import androidx.compose.ui.viewinterop.AndroidView
 import androidx.core.view.isVisible
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleCoroutineScope
+import androidx.lifecycle.lifecycleScope
 import com.swordfish.radialgamepad.library.RadialGamePad
 import com.swordfish.radialgamepad.library.config.ButtonConfig
 import com.swordfish.radialgamepad.library.config.CrossConfig
@@ -27,11 +28,47 @@ import kotlinx.coroutines.flow.catch
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.launch
+import org.ryujinx.android.viewmodels.MainViewModel
 
 typealias GamePad = RadialGamePad
 typealias GamePadConfig = RadialGamePadConfig
 
-class GameController(var activity: Activity, var ryujinxNative: RyujinxNative = RyujinxNative()) {
+class GameController(var activity: Activity) {
+
+    companion object{
+        private fun Create(context: Context, activity: Activity, controller: GameController) : View
+        {
+            val inflator = LayoutInflater.from(context)
+            val view = inflator.inflate(R.layout.game_layout, null)
+            view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(controller.leftGamePad)
+            view.findViewById<FrameLayout>(R.id.rightcontainer)!!.addView(controller.rightGamePad)
+
+            return view
+        }
+        @Composable
+        fun Compose(viewModel: MainViewModel) : Unit
+        {
+            AndroidView(
+                modifier = Modifier.fillMaxSize(), factory = { context ->
+                    val controller = GameController(viewModel.activity)
+                    val c = Create(context, viewModel.activity, controller)
+                        viewModel.activity.lifecycleScope.apply {
+                            viewModel.activity.lifecycleScope.launch {
+                            val events = merge(controller.leftGamePad.events(),controller.rightGamePad.events())
+                                .shareIn(viewModel.activity.lifecycleScope, SharingStarted.Lazily)
+                            events.safeCollect {
+                                controller.handleEvent(it)
+                            }
+                        }
+                    }
+                    controller.controllerView = c
+                    viewModel.setGameController(controller)
+                    c
+                })
+        }
+    }
+
+    private var ryujinxNative: RyujinxNative
     private var controllerView: View? = null
     var leftGamePad: GamePad
     var rightGamePad: GamePad
@@ -56,36 +93,8 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
         leftGamePad.gravityY = 1f
         rightGamePad.gravityX = 1f
         rightGamePad.gravityY = 1f
-    }
 
-    @Composable
-    fun Compose(lifecycleScope: LifecycleCoroutineScope, lifecycle:Lifecycle) : Unit
-    {
-        AndroidView(
-            modifier = Modifier.fillMaxSize(), factory = { context -> Create(context)})
-
-        lifecycleScope.apply {
-            lifecycleScope.launch {
-                val events = merge(leftGamePad.events(),rightGamePad.events())
-                    .shareIn(lifecycleScope, SharingStarted.Lazily)
-
-                events.safeCollect {
-                    handleEvent(it)
-                }
-            }
-        }
-    }
-
-    private fun Create(context: Context) : View
-    {
-        val inflator = LayoutInflater.from(context)
-        val view = inflator.inflate(R.layout.game_layout, null)
-        view.findViewById<FrameLayout>(R.id.leftcontainer)!!.addView(leftGamePad)
-        view.findViewById<FrameLayout>(R.id.rightcontainer)!!.addView(rightGamePad)
-
-        controllerView = view
-
-        return controllerView as View
+        ryujinxNative = RyujinxNative()
     }
 
     fun setVisible(isVisible: Boolean){
@@ -99,7 +108,7 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
 
     fun connect(){
         if(controllerId == -1)
-            controllerId = ryujinxNative.inputConnectGamepad(0)
+            controllerId = RyujinxNative().inputConnectGamepad(0)
     }
 
     private fun handleEvent(ev: Event) {
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
index a66096dbc..2bbb99804 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt
@@ -11,7 +11,7 @@ import org.ryujinx.android.viewmodels.VulkanDriverViewModel
 import java.io.File
 import kotlin.concurrent.thread
 
-class GameHost(context: Context?, val controller: GameController, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
+class GameHost(context: Context?, val mainViewModel: MainViewModel) : SurfaceView(context), SurfaceHolder.Callback {
     private var _renderingThreadWatcher: Thread? = null
     private var _height: Int = 0
     private var _width: Int = 0
@@ -142,16 +142,14 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
         _nativeRyujinx.inputInitialize(width, height)
 
         if(!settings.useVirtualController){
-            controller.setVisible(false)
+            mainViewModel.controller?.setVisible(false)
         }
         else{
-            controller.connect()
+            mainViewModel.controller?.connect()
         }
 
         mainViewModel.activity.physicalControllerManager.connect()
 
-        //
-
         _nativeRyujinx.graphicsRendererSetSize(
             surfaceHolder.surfaceFrame.width(),
             surfaceHolder.surfaceFrame.height()
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
index 1c1e34399..2c1641787 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/viewmodels/MainViewModel.kt
@@ -6,12 +6,14 @@ import android.os.Build
 import android.os.PerformanceHintManager
 import androidx.compose.runtime.MutableState
 import androidx.navigation.NavHostController
+import org.ryujinx.android.GameController
 import org.ryujinx.android.GameHost
 import org.ryujinx.android.MainActivity
 import org.ryujinx.android.PerformanceManager
 
 @SuppressLint("WrongConstant")
 class MainViewModel(val activity: MainActivity) {
+    var controller: GameController? = null
     var performanceManager: PerformanceManager? = null
     var selected: GameModel? = null
     private var gameTimeState: MutableState<Double>? = null
@@ -65,4 +67,8 @@ class MainViewModel(val activity: MainActivity) {
             this.value = gameTime
         }
     }
+
+    fun setGameController(controller: GameController) {
+        this.controller = controller
+    }
 }
\ No newline at end of file
diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt
index 8ec1d5650..752b2c193 100644
--- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt
+++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/views/MainView.kt
@@ -40,35 +40,39 @@ import kotlin.math.roundToInt
 class MainView {
     companion object {
         @Composable
-        fun Main(mainViewModel: MainViewModel){
+        fun Main(mainViewModel: MainViewModel) {
             val navController = rememberNavController()
             mainViewModel.setNavController(navController)
 
             NavHost(navController = navController, startDestination = "home") {
                 composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
                 composable("game") { GameView(mainViewModel) }
-                composable("settings") { SettingViews.Main(SettingsViewModel(navController, mainViewModel.activity)) }
+                composable("settings") {
+                    SettingViews.Main(
+                        SettingsViewModel(
+                            navController,
+                            mainViewModel.activity
+                        )
+                    )
+                }
             }
         }
 
         @Composable
-        fun GameView(mainViewModel: MainViewModel){
+        fun GameView(mainViewModel: MainViewModel) {
             Box(modifier = Modifier.fillMaxSize()) {
-                val controller = remember {
-                    GameController(mainViewModel.activity)
-                }
                 AndroidView(
                     modifier = Modifier.fillMaxSize(),
                     factory = { context ->
-                        GameHost(context, controller, mainViewModel)
+                        GameHost(context, mainViewModel)
                     }
                 )
-                GameOverlay(mainViewModel, controller)
+                GameOverlay(mainViewModel)
             }
         }
 
         @Composable
-        fun GameOverlay(mainViewModel: MainViewModel, controller: GameController){
+        fun GameOverlay(mainViewModel: MainViewModel) {
             Box(modifier = Modifier.fillMaxSize()) {
                 GameStats(mainViewModel)
 
@@ -84,9 +88,6 @@ class MainView {
                                 Thread.sleep(2)
                                 val event = awaitPointerEvent()
 
-                                if(controller.isVisible)
-                                    continue
-
                                 val change = event
                                     .component1()
                                     .firstOrNull()
@@ -100,10 +101,12 @@ class MainView {
                                                 position.y.roundToInt()
                                             )
                                         }
+
                                         PointerEventType.Release -> {
                                             ryujinxNative.inputReleaseTouchPoint()
 
                                         }
+
                                         PointerEventType.Move -> {
                                             ryujinxNative.inputSetTouchPoint(
                                                 position.x.roundToInt(),
@@ -117,18 +120,24 @@ class MainView {
                         }
                     }) {
                 }
-                controller.Compose(mainViewModel.activity.lifecycleScope, mainViewModel.activity.lifecycle)
-                Row(modifier = Modifier
-                    .align(Alignment.BottomCenter)
-                    .padding(8.dp)) {
-                    IconButton(modifier = Modifier.padding(4.dp),onClick = {
-                        controller.setVisible(!controller.isVisible)
+                GameController.Compose(mainViewModel)
+                Row(
+                    modifier = Modifier
+                        .align(Alignment.BottomCenter)
+                        .padding(8.dp)
+                ) {
+                    IconButton(modifier = Modifier.padding(4.dp), onClick = {
+                        mainViewModel.controller?.setVisible(!mainViewModel.controller!!.isVisible)
                     }) {
-                        Icon(imageVector = rememberVideogameAsset(), contentDescription = "Toggle Virtual Pad")
+                        Icon(
+                            imageVector = rememberVideogameAsset(),
+                            contentDescription = "Toggle Virtual Pad"
+                        )
                     }
                 }
             }
         }
+
         @Composable
         fun rememberVideogameAsset(): ImageVector {
             val primaryColor = MaterialTheme.colorScheme.primary
@@ -226,7 +235,7 @@ class MainView {
         }
 
         @Composable
-        fun GameStats(mainViewModel: MainViewModel){
+        fun GameStats(mainViewModel: MainViewModel) {
             val fifo = remember {
                 mutableStateOf(0.0)
             }
@@ -237,8 +246,10 @@ class MainView {
                 mutableStateOf(0.0)
             }
 
-            Surface(modifier = Modifier.padding(16.dp),
-            color = MaterialTheme.colorScheme.surface.copy(0.4f)) {
+            Surface(
+                modifier = Modifier.padding(16.dp),
+                color = MaterialTheme.colorScheme.surface.copy(0.4f)
+            ) {
                 Column {
                     var gameTimeVal = 0.0
                     if (!gameTime.value.isInfinite())