skyline/app/src/main/java/emu/skyline/EmulationActivity.kt
Billy Laws 0219eda2db Initial C++ Input Implementation
This commit contains the C++ side of the initial Input implementation, this is based on the work done in the `hid` branch in `bylaws/skyline`.
Co-authored-by: ◱ PixelyIon <pixelyion@protonmail.com>
2020-08-21 11:48:29 +00:00

214 lines
6.8 KiB
Kotlin

/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.util.Log
import android.view.*
import androidx.appcompat.app.AppCompatActivity
import androidx.preference.PreferenceManager
import emu.skyline.loader.getRomFormat
import kotlinx.android.synthetic.main.emu_activity.*
import java.io.File
class EmulationActivity : AppCompatActivity(), SurfaceHolder.Callback {
init {
System.loadLibrary("skyline") // libskyline.so
}
/**
* The file descriptor of the ROM
*/
private lateinit var romFd : ParcelFileDescriptor
/**
* The file descriptor of the application Preference XML
*/
private lateinit var preferenceFd : ParcelFileDescriptor
/**
* The surface object used for displaying frames
*/
private var surface : Surface? = null
/**
* A boolean flag denoting if the emulation thread should call finish() or not
*/
private var shouldFinish : Boolean = true
/**
* The Kotlin thread on which emulation code executes
*/
private lateinit var emulationThread : Thread
/**
* This is the entry point into the emulation code for libskyline
*
* @param romUri The URI of the ROM as a string, used to print out in the logs
* @param romType The type of the ROM as an enum value
* @param romFd The file descriptor of the ROM object
* @param preferenceFd The file descriptor of the Preference XML
* @param appFilesPath The full path to the app files directory
*/
private external fun executeApplication(romUri : String, romType : Int, romFd : Int, preferenceFd : Int, appFilesPath : String)
/**
* This sets the halt flag in libskyline to the provided value, if set to true it causes libskyline to halt emulation
*
* @param halt The value to set halt to
*/
private external fun setHalt(halt : Boolean)
/**
* This sets the surface object in libskyline to the provided value, emulation is halted if set to null
*
* @param surface The value to set surface to
*/
private external fun setSurface(surface : Surface?)
/**
* This returns the current FPS of the application
*/
private external fun getFps() : Int
/**
* This returns the current frame-time of the application
*/
private external fun getFrametime() : Float
private external fun setButtonState(id : Long, state : Int)
private external fun setAxisValue(id : Int, value : Int)
/**
* This executes the specified ROM, [preferenceFd] is assumed to be valid beforehand
*
* @param rom The URI of the ROM to execute
*/
private fun executeApplication(rom : Uri) {
val romType = getRomFormat(rom, contentResolver).ordinal
romFd = contentResolver.openFileDescriptor(rom, "r")!!
emulationThread = Thread {
while ((surface == null))
Thread.yield()
executeApplication(Uri.decode(rom.toString()), romType, romFd.fd, preferenceFd.fd, applicationContext.filesDir.canonicalPath + "/")
if (shouldFinish)
runOnUiThread { finish() }
}
emulationThread.start()
}
/**
* This makes the window fullscreen then sets up [preferenceFd], sets up the performance statistics and finally calls [executeApplication] for executing the application
*/
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState : Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.emu_activity)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.insetsController?.hide(WindowInsets.Type.navigationBars() or WindowInsets.Type.systemBars() or WindowInsets.Type.systemGestures() or WindowInsets.Type.statusBars())
window.insetsController?.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
} else {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_FULLSCREEN)
}
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
game_view.holder.addCallback(this)
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
if (sharedPreferences.getBoolean("perf_stats", false)) {
lateinit var perfRunnable : Runnable
perfRunnable = Runnable {
perf_stats.text = "${getFps()} FPS\n${getFrametime()}ms"
perf_stats.postDelayed(perfRunnable, 250)
}
perf_stats.postDelayed(perfRunnable, 250)
}
executeApplication(intent.data!!)
}
/**
* This is used to stop the currently executing ROM and replace it with the one specified in the new intent
*/
override fun onNewIntent(intent : Intent?) {
shouldFinish = false
setHalt(true)
emulationThread.join()
shouldFinish = true
romFd.close()
executeApplication(intent?.data!!)
super.onNewIntent(intent)
}
/**
* This is used to halt emulation entirely
*/
override fun onDestroy() {
shouldFinish = false
setHalt(true)
emulationThread.join()
romFd.close()
preferenceFd.close()
super.onDestroy()
}
/**
* This sets [surface] to [holder].surface and passes it into libskyline
*/
override fun surfaceCreated(holder : SurfaceHolder) {
Log.d("surfaceCreated", "Holder: ${holder.toString()}")
surface = holder.surface
setSurface(surface)
}
/**
* This is purely used for debugging surface changes
*/
override fun surfaceChanged(holder : SurfaceHolder, format : Int, width : Int, height : Int) {
Log.d("surfaceChanged", "Holder: ${holder.toString()}, Format: $format, Width: $width, Height: $height")
}
/**
* This sets [surface] to null and passes it into libskyline
*/
override fun surfaceDestroyed(holder : SurfaceHolder) {
Log.d("surfaceDestroyed", "Holder: ${holder.toString()}")
surface = null
setSurface(surface)
}
}