forked from MeloNX/MeloNX
UIThread working
This commit is contained in:
parent
877a3b3dd7
commit
552069c8ce
Binary file not shown.
@ -1,31 +1,31 @@
|
||||
package melonx.module
|
||||
|
||||
import skip.lib.*
|
||||
import skip.model.*
|
||||
import skip.foundation.*
|
||||
import skip.ui.*
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Application
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import android.hardware.Sensor
|
||||
import android.os.Process
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveableStateHolder
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.app.ActivityCompat
|
||||
import com.lenta.presentation.utils.Rebugger
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import melo.nxmodel.Ryujinx
|
||||
import org.libsdl.app.SDLActivity
|
||||
import skip.foundation.ProcessInfo
|
||||
import skip.foundation.SkipLogger
|
||||
import skip.ui.ColorScheme
|
||||
import skip.ui.ComposeContext
|
||||
import skip.ui.PresentationRoot
|
||||
import skip.ui.UIApplication
|
||||
|
||||
internal val logger: SkipLogger = SkipLogger(subsystem = "melonx.module", category = "melonx")
|
||||
|
||||
@ -49,6 +49,11 @@ open class MainActivity: SDLActivity {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
}
|
||||
|
||||
private val startGameConfigMutableStateFlow = MutableStateFlow<Ryujinx.Configuration?>(null)
|
||||
|
||||
override fun onCreate(savedInstanceState: android.os.Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
logger.info("starting activity")
|
||||
@ -56,16 +61,15 @@ open class MainActivity: SDLActivity {
|
||||
enableEdgeToEdge()
|
||||
|
||||
setContent {
|
||||
Box {
|
||||
SDLComposeSurface()
|
||||
Box(Modifier.graphicsLayer(alpha = 0.5f)) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
saveableStateHolder.SaveableStateProvider(true) {
|
||||
PresentationRootView(ComposeContext())
|
||||
SideEffect { saveableStateHolder.removeState(true) }
|
||||
}
|
||||
|
||||
val startGameConfig = remember { GameState.shared._startGameConfig.projectedValue }
|
||||
if (startGameConfig?.value != null) {
|
||||
runSimulator(startGameConfig.value!!)
|
||||
GameState.shared.startGameConfig = null
|
||||
}
|
||||
}
|
||||
|
||||
val gameIsRunning = remember { GameState.shared._isGameRunning.projectedValue }
|
||||
RootScreen(gameIsRunning?.value)
|
||||
}
|
||||
|
||||
// Example of requesting permissions on startup.
|
||||
@ -128,7 +132,27 @@ open class MainActivity: SDLActivity {
|
||||
logger.info("onRequestPermissionsResult: ${requestCode}")
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Composable
|
||||
fun RootScreen(gameIsRunning: Boolean?) {
|
||||
Rebugger(
|
||||
trackMap = mapOf(
|
||||
"gameIsRunning" to gameIsRunning
|
||||
)
|
||||
)
|
||||
|
||||
Box {
|
||||
|
||||
SDLComposeSurface()
|
||||
if (gameIsRunning != true) {
|
||||
// Box(Modifier.graphicsLayer(alpha = 0.2f)) {
|
||||
val saveableStateHolder = rememberSaveableStateHolder()
|
||||
saveableStateHolder.SaveableStateProvider(true) {
|
||||
PresentationRootView(ComposeContext())
|
||||
SideEffect { saveableStateHolder.removeState(true) }
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -139,6 +163,27 @@ open class MainActivity: SDLActivity {
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fun runSimulator(config: Ryujinx.Configuration) {
|
||||
class RyujinxMain : Runnable {
|
||||
override fun run() {
|
||||
try {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY)
|
||||
} catch (e: Exception) {
|
||||
Log.v("SDL", "modify thread properties failed $e")
|
||||
}
|
||||
|
||||
Ryujinx.shared.start(config)
|
||||
}
|
||||
}
|
||||
|
||||
// This is the entry point to the C app.
|
||||
// Start up the C app thread and enable sensor input for the first time
|
||||
// FIXME: Why aren't we enabling sensor input at start?
|
||||
mSDLThread = Thread(RyujinxMain(), "SDLThread")
|
||||
mSurface.enableSensor(Sensor.TYPE_ACCELEROMETER, true)
|
||||
mSDLThread.start()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
@ -0,0 +1,84 @@
|
||||
package com.lenta.presentation.utils
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
|
||||
|
||||
private const val TAG = "Rebugger"
|
||||
|
||||
private class Ref<T>(var value: T)
|
||||
|
||||
/**
|
||||
* Fork of Rebugger library, which help catch UI recomposition for composable func
|
||||
* Used an android Log system instead the project Log, because we would't send on
|
||||
*
|
||||
* Example of usage
|
||||
* ```kotlin
|
||||
* @Composable
|
||||
* fun VehicleUi(
|
||||
* car: Car,
|
||||
* bike: Bike,
|
||||
* ) {
|
||||
* var human by remember { mutableStateOf(Human("John")) }
|
||||
* // Call Rebugger and pass the states you want to track.
|
||||
* // It could be a function arg or a state
|
||||
* Rebugger(
|
||||
* trackMap = mapOf(
|
||||
* "car" to car,
|
||||
* "bike" to bike,
|
||||
* "human" to human
|
||||
* ),
|
||||
* )
|
||||
* //...
|
||||
* ```
|
||||
*
|
||||
* @see <a href="https://github.com/theapache64/rebugger">
|
||||
* Rebugger gitHub
|
||||
* </a>
|
||||
*/
|
||||
@Suppress("unused")
|
||||
@Composable
|
||||
fun Rebugger(
|
||||
trackMap: Map<String, Any?>,
|
||||
composableName: String = Thread.currentThread().stackTrace[3].methodName,
|
||||
) {
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
Log.i(TAG, "🐞 Rebugger activated on `$composableName`")
|
||||
}
|
||||
|
||||
val count = remember { Ref(0) }
|
||||
val flag = remember { Ref(false) }
|
||||
SideEffect {
|
||||
count.value++
|
||||
}
|
||||
|
||||
val changeLog = StringBuilder()
|
||||
for ((key, newArg) in trackMap) {
|
||||
var recompositionTrigger by remember { mutableStateOf(false) }
|
||||
val oldArg = remember(recompositionTrigger) { newArg }
|
||||
|
||||
if (oldArg != newArg) {
|
||||
changeLog.append("\n\t `$key` changed from `$oldArg` to `$newArg`, ")
|
||||
flag.value = true
|
||||
|
||||
recompositionTrigger = !recompositionTrigger
|
||||
}
|
||||
}
|
||||
|
||||
if (changeLog.isNotEmpty()) {
|
||||
Log.i(TAG, "🐞$composableName recomposed because $changeLog")
|
||||
} else {
|
||||
if (count.value >= 1 && !flag.value) {
|
||||
Log.i(TAG, "🐞$composableName recomposed not because of param change")
|
||||
} else {
|
||||
flag.value = false
|
||||
}
|
||||
}
|
||||
}
|
@ -214,7 +214,7 @@ private extension Ryujinx {
|
||||
args.append("--enable-debug-logs")
|
||||
}
|
||||
|
||||
args.append("--disable-info-logs")
|
||||
// args.append("--disable-info-logs")
|
||||
|
||||
if config.tracelogs {
|
||||
args.append("--enable-trace-logs")
|
||||
|
@ -6,6 +6,13 @@ enum ContentTab: String, Hashable {
|
||||
case games, home, settings
|
||||
}
|
||||
|
||||
@Observable public class GameState {
|
||||
public static var shared = GameState()
|
||||
public var isGameRunning: Bool = false
|
||||
|
||||
public var startGameConfig: Ryujinx.Configuration?
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@AppStorage("tab") var tab = ContentTab.games
|
||||
@State var viewModel = ViewModel()
|
||||
@ -14,6 +21,7 @@ struct ContentView: View {
|
||||
@State private var config: Ryujinx.Configuration
|
||||
@State private var game: Game?
|
||||
@State private var isLoading = true
|
||||
@State private var startSDL = false
|
||||
|
||||
init() {
|
||||
// let defaultConfig = loadSettings() ?? Ryujinx.Configuration(gamepath: "")
|
||||
@ -33,9 +41,7 @@ struct ContentView: View {
|
||||
} else {
|
||||
TabView(selection: $tab) {
|
||||
NavigationStack {
|
||||
NavigationStack {
|
||||
GamesView(startemu: $game)
|
||||
}
|
||||
GamesView(startemu: $game)
|
||||
}
|
||||
.tabItem { Label("Games", systemImage: "house.fill") }
|
||||
.tag(ContentTab.games)
|
||||
@ -70,7 +76,11 @@ private extension ContentView {
|
||||
var emulationView: some View {
|
||||
Text("Loading...")
|
||||
.onAppear {
|
||||
GameState.shared.isGameRunning = true
|
||||
startSDL = true
|
||||
|
||||
setupEmulation()
|
||||
// SKIP INSERT: // MainActivity.enableSDLLayer(true)
|
||||
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { timer in
|
||||
if Ryujinx.shared.getCurrentFps() != 0 {
|
||||
@ -91,9 +101,9 @@ private extension ContentView {
|
||||
// patchMakeKeyAndVisible()
|
||||
// isVCA = (currentControllers.first(where: { $0 == onscreencontroller }) != nil)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
// DispatchQueue.main.async {
|
||||
start()
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
func start() {
|
||||
@ -117,7 +127,8 @@ private extension ContentView {
|
||||
}
|
||||
|
||||
do {
|
||||
try Ryujinx.shared.start(with: config)
|
||||
GameState.shared.startGameConfig = config
|
||||
// try Ryujinx.shared.start(with: config)
|
||||
} catch {
|
||||
logger.error("Error: \(error.localizedDescription)")
|
||||
}
|
||||
|
@ -315,6 +315,9 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu on Thread name: {Thread.CurrentThread.Name}");
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu on Thread id: {Thread.CurrentThread.ManagedThreadId}");
|
||||
|
||||
// Make process DPI aware for proper window sizing on high-res screens.
|
||||
ForceDpiAware.Windows();
|
||||
|
||||
|
@ -159,6 +159,8 @@ namespace Ryujinx.SDL2.Common
|
||||
|
||||
using ManualResetEventSlim waitHandle = new(false);
|
||||
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu on Thread name 2: {Thread.CurrentThread.Name}");
|
||||
Logger.Info?.Print(LogClass.Application, $"Start Emu on Thread id 2: {Thread.CurrentThread.ManagedThreadId}");
|
||||
while (_isRunning)
|
||||
{
|
||||
MainThreadDispatcher?.Invoke(() =>
|
||||
|
141
src/RyujinxAndroid/.idea/workspace.xml
generated
Normal file
141
src/RyujinxAndroid/.idea/workspace.xml
generated
Normal file
@ -0,0 +1,141 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="NONE" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="b70a156d-88fb-4b09-9b57-c7698aab2d2a" name="Changes" comment="" />
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="ClangdSettings">
|
||||
<option name="formatViaClangd" value="false" />
|
||||
</component>
|
||||
<component name="ExecutionTargetManager" SELECTED_TARGET="device_and_snapshot_combo_box_target[DeviceId(pluginId=LocalEmulator, isTemplate=false, identifier=path=/Users/daniilvinogradov/.android/avd/Pixel_7_Pro_API_34.avd)]" />
|
||||
<component name="ExternalProjectsData">
|
||||
<projectState path="$PROJECT_DIR$">
|
||||
<ProjectState />
|
||||
</projectState>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$/../.." />
|
||||
</component>
|
||||
<component name="ProjectColorInfo"><![CDATA[{
|
||||
"associatedIndex": 4
|
||||
}]]></component>
|
||||
<component name="ProjectId" id="2tjVfEZg0XN8pbpoF5pzqoZcHSH" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent"><![CDATA[{
|
||||
"keyToString": {
|
||||
"Android App.app.executor": "Debug",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.cidr.known.project.marker": "true",
|
||||
"RunOnceActivity.readMode.enableVisualFormatting": "true",
|
||||
"cf.first.check.clang-format": "false",
|
||||
"cidr.known.project.marker": "true",
|
||||
"git-widget-placeholder": "Message-Bridge",
|
||||
"kotlin-language-version-configured": "true",
|
||||
"last_opened_file_path": "/Users/daniilvinogradov/Documents/Dev/MeloNX/src/RyujinxAndroid",
|
||||
"settings.editor.selected.configurable": "AndroidSdkUpdater"
|
||||
}
|
||||
}]]></component>
|
||||
<component name="RunManager">
|
||||
<configuration name="app" type="AndroidRunConfigurationType" factoryName="Android App" activateToolWindowBeforeRun="false">
|
||||
<module name="RyujinxAndroid.app.main" />
|
||||
<option name="DEPLOY" value="true" />
|
||||
<option name="DEPLOY_APK_FROM_BUNDLE" value="false" />
|
||||
<option name="DEPLOY_AS_INSTANT" value="false" />
|
||||
<option name="ARTIFACT_NAME" value="" />
|
||||
<option name="PM_INSTALL_OPTIONS" value="" />
|
||||
<option name="ALL_USERS" value="false" />
|
||||
<option name="ALWAYS_INSTALL_WITH_PM" value="false" />
|
||||
<option name="CLEAR_APP_STORAGE" value="false" />
|
||||
<option name="DYNAMIC_FEATURES_DISABLED_LIST" value="" />
|
||||
<option name="ACTIVITY_EXTRA_FLAGS" value="" />
|
||||
<option name="MODE" value="default_activity" />
|
||||
<option name="RESTORE_ENABLED" value="false" />
|
||||
<option name="RESTORE_FILE" value="" />
|
||||
<option name="CLEAR_LOGCAT" value="false" />
|
||||
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
|
||||
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_CONFIGURATION_ID" value="-1" />
|
||||
<option name="SELECTED_CLOUD_MATRIX_PROJECT_ID" value="" />
|
||||
<option name="DEBUGGER_TYPE" value="Auto" />
|
||||
<Auto>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Auto>
|
||||
<Hybrid>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Hybrid>
|
||||
<Java>
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Java>
|
||||
<Native>
|
||||
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
|
||||
<option name="SHOW_STATIC_VARS" value="true" />
|
||||
<option name="WORKING_DIR" value="" />
|
||||
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
|
||||
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
|
||||
<option name="ATTACH_ON_WAIT_FOR_DEBUGGER" value="false" />
|
||||
<option name="DEBUG_SANDBOX_SDK" value="false" />
|
||||
</Native>
|
||||
<Profilers>
|
||||
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
|
||||
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Java/Kotlin Method Sample (legacy)" />
|
||||
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
|
||||
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
|
||||
</Profilers>
|
||||
<option name="DEEP_LINK" value="" />
|
||||
<option name="ACTIVITY_CLASS" value="" />
|
||||
<option name="SEARCH_ACTIVITY_IN_GLOBAL_SCOPE" value="false" />
|
||||
<option name="SKIP_ACTIVITY_VALIDATION" value="false" />
|
||||
<method v="2">
|
||||
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="b70a156d-88fb-4b09-9b57-c7698aab2d2a" name="Changes" comment="" />
|
||||
<created>1740865571599</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1740865571599</updated>
|
||||
</task>
|
||||
<servers />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<breakpoint enabled="true" type="OCSymbolicBreakpointType">
|
||||
<properties>
|
||||
<option name="moduleName" value="libart.so" />
|
||||
<option name="symbolPattern" value="art_sigbus_fault" />
|
||||
</properties>
|
||||
<option name="timeStamp" value="2" />
|
||||
</breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
</component>
|
||||
</project>
|
Loading…
x
Reference in New Issue
Block a user