refactor virtual pad composition

This commit is contained in:
Emmanuel Hansen 2023-07-27 20:24:33 +00:00
parent f0b0f3b796
commit 398fb78a31
5 changed files with 83 additions and 58 deletions

View File

@ -28,6 +28,7 @@
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"
android:hardwareAccelerated="false"
android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode" android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode"
android:theme="@style/Theme.RyujinxAndroid"> android:theme="@style/Theme.RyujinxAndroid">
<intent-filter> <intent-filter>

View File

@ -13,6 +13,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleCoroutineScope import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.lifecycleScope
import com.swordfish.radialgamepad.library.RadialGamePad import com.swordfish.radialgamepad.library.RadialGamePad
import com.swordfish.radialgamepad.library.config.ButtonConfig import com.swordfish.radialgamepad.library.config.ButtonConfig
import com.swordfish.radialgamepad.library.config.CrossConfig 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.merge
import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.ryujinx.android.viewmodels.MainViewModel
typealias GamePad = RadialGamePad typealias GamePad = RadialGamePad
typealias GamePadConfig = RadialGamePadConfig 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 private var controllerView: View? = null
var leftGamePad: GamePad var leftGamePad: GamePad
var rightGamePad: GamePad var rightGamePad: GamePad
@ -56,36 +93,8 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
leftGamePad.gravityY = 1f leftGamePad.gravityY = 1f
rightGamePad.gravityX = 1f rightGamePad.gravityX = 1f
rightGamePad.gravityY = 1f rightGamePad.gravityY = 1f
}
@Composable ryujinxNative = RyujinxNative()
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
} }
fun setVisible(isVisible: Boolean){ fun setVisible(isVisible: Boolean){
@ -99,7 +108,7 @@ class GameController(var activity: Activity, var ryujinxNative: RyujinxNative =
fun connect(){ fun connect(){
if(controllerId == -1) if(controllerId == -1)
controllerId = ryujinxNative.inputConnectGamepad(0) controllerId = RyujinxNative().inputConnectGamepad(0)
} }
private fun handleEvent(ev: Event) { private fun handleEvent(ev: Event) {

View File

@ -11,7 +11,7 @@ import org.ryujinx.android.viewmodels.VulkanDriverViewModel
import java.io.File import java.io.File
import kotlin.concurrent.thread 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 _renderingThreadWatcher: Thread? = null
private var _height: Int = 0 private var _height: Int = 0
private var _width: Int = 0 private var _width: Int = 0
@ -142,16 +142,14 @@ class GameHost(context: Context?, val controller: GameController, val mainViewMo
_nativeRyujinx.inputInitialize(width, height) _nativeRyujinx.inputInitialize(width, height)
if(!settings.useVirtualController){ if(!settings.useVirtualController){
controller.setVisible(false) mainViewModel.controller?.setVisible(false)
} }
else{ else{
controller.connect() mainViewModel.controller?.connect()
} }
mainViewModel.activity.physicalControllerManager.connect() mainViewModel.activity.physicalControllerManager.connect()
//
_nativeRyujinx.graphicsRendererSetSize( _nativeRyujinx.graphicsRendererSetSize(
surfaceHolder.surfaceFrame.width(), surfaceHolder.surfaceFrame.width(),
surfaceHolder.surfaceFrame.height() surfaceHolder.surfaceFrame.height()

View File

@ -6,12 +6,14 @@ import android.os.Build
import android.os.PerformanceHintManager import android.os.PerformanceHintManager
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import org.ryujinx.android.GameController
import org.ryujinx.android.GameHost import org.ryujinx.android.GameHost
import org.ryujinx.android.MainActivity import org.ryujinx.android.MainActivity
import org.ryujinx.android.PerformanceManager import org.ryujinx.android.PerformanceManager
@SuppressLint("WrongConstant") @SuppressLint("WrongConstant")
class MainViewModel(val activity: MainActivity) { class MainViewModel(val activity: MainActivity) {
var controller: GameController? = null
var performanceManager: PerformanceManager? = null var performanceManager: PerformanceManager? = null
var selected: GameModel? = null var selected: GameModel? = null
private var gameTimeState: MutableState<Double>? = null private var gameTimeState: MutableState<Double>? = null
@ -65,4 +67,8 @@ class MainViewModel(val activity: MainActivity) {
this.value = gameTime this.value = gameTime
} }
} }
fun setGameController(controller: GameController) {
this.controller = controller
}
} }

View File

@ -40,35 +40,39 @@ import kotlin.math.roundToInt
class MainView { class MainView {
companion object { companion object {
@Composable @Composable
fun Main(mainViewModel: MainViewModel){ fun Main(mainViewModel: MainViewModel) {
val navController = rememberNavController() val navController = rememberNavController()
mainViewModel.setNavController(navController) mainViewModel.setNavController(navController)
NavHost(navController = navController, startDestination = "home") { NavHost(navController = navController, startDestination = "home") {
composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) } composable("home") { HomeViews.Home(mainViewModel.homeViewModel, navController) }
composable("game") { GameView(mainViewModel) } composable("game") { GameView(mainViewModel) }
composable("settings") { SettingViews.Main(SettingsViewModel(navController, mainViewModel.activity)) } composable("settings") {
SettingViews.Main(
SettingsViewModel(
navController,
mainViewModel.activity
)
)
}
} }
} }
@Composable @Composable
fun GameView(mainViewModel: MainViewModel){ fun GameView(mainViewModel: MainViewModel) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
val controller = remember {
GameController(mainViewModel.activity)
}
AndroidView( AndroidView(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
factory = { context -> factory = { context ->
GameHost(context, controller, mainViewModel) GameHost(context, mainViewModel)
} }
) )
GameOverlay(mainViewModel, controller) GameOverlay(mainViewModel)
} }
} }
@Composable @Composable
fun GameOverlay(mainViewModel: MainViewModel, controller: GameController){ fun GameOverlay(mainViewModel: MainViewModel) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
GameStats(mainViewModel) GameStats(mainViewModel)
@ -84,9 +88,6 @@ class MainView {
Thread.sleep(2) Thread.sleep(2)
val event = awaitPointerEvent() val event = awaitPointerEvent()
if(controller.isVisible)
continue
val change = event val change = event
.component1() .component1()
.firstOrNull() .firstOrNull()
@ -100,10 +101,12 @@ class MainView {
position.y.roundToInt() position.y.roundToInt()
) )
} }
PointerEventType.Release -> { PointerEventType.Release -> {
ryujinxNative.inputReleaseTouchPoint() ryujinxNative.inputReleaseTouchPoint()
} }
PointerEventType.Move -> { PointerEventType.Move -> {
ryujinxNative.inputSetTouchPoint( ryujinxNative.inputSetTouchPoint(
position.x.roundToInt(), position.x.roundToInt(),
@ -117,18 +120,24 @@ class MainView {
} }
}) { }) {
} }
controller.Compose(mainViewModel.activity.lifecycleScope, mainViewModel.activity.lifecycle) GameController.Compose(mainViewModel)
Row(modifier = Modifier Row(
.align(Alignment.BottomCenter) modifier = Modifier
.padding(8.dp)) { .align(Alignment.BottomCenter)
IconButton(modifier = Modifier.padding(4.dp),onClick = { .padding(8.dp)
controller.setVisible(!controller.isVisible) ) {
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 @Composable
fun rememberVideogameAsset(): ImageVector { fun rememberVideogameAsset(): ImageVector {
val primaryColor = MaterialTheme.colorScheme.primary val primaryColor = MaterialTheme.colorScheme.primary
@ -226,7 +235,7 @@ class MainView {
} }
@Composable @Composable
fun GameStats(mainViewModel: MainViewModel){ fun GameStats(mainViewModel: MainViewModel) {
val fifo = remember { val fifo = remember {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
@ -237,8 +246,10 @@ class MainView {
mutableStateOf(0.0) mutableStateOf(0.0)
} }
Surface(modifier = Modifier.padding(16.dp), Surface(
color = MaterialTheme.colorScheme.surface.copy(0.4f)) { modifier = Modifier.padding(16.dp),
color = MaterialTheme.colorScheme.surface.copy(0.4f)
) {
Column { Column {
var gameTimeVal = 0.0 var gameTimeVal = 0.0
if (!gameTime.value.isInfinite()) if (!gameTime.value.isInfinite())