forked from MeloNX/MeloNX
refactor virtual pad composition
This commit is contained in:
parent
f0b0f3b796
commit
398fb78a31
@ -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>
|
||||||
|
@ -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) {
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
}
|
}
|
@ -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())
|
||||||
|
Loading…
x
Reference in New Issue
Block a user