skyline/app/src/main/java/emu/skyline/EmulationActivity.kt
◱ PixelyIon c76ef3730b Move to MPL-2.0
We earlier moved to LGPLv3.0 or Later. This was a mistake as what we wanted was being able to link to proprietary libraries but LGPL is the opposite and it allows linking proprietary libraries to libskyline instead. After further consideration, we've moved to MPL-2.0, it allows linking to proprietary libraries and is a standardized license as compared to adding an exception to GPL.
2020-04-23 22:26:27 +05:30

207 lines
6.2 KiB
Kotlin

/*
* SPDX-License-Identifier: MPL-2.0
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.ParcelFileDescriptor
import android.util.Log
import android.view.Surface
import android.view.SurfaceHolder
import android.view.WindowManager
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 file descriptor of the Log file
*/
private lateinit var logFd: 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 logFd The file descriptor of the Log file
*/
private external fun executeApplication(romUri: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
/**
* 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
/**
* This executes the specified ROM, [preferenceFd] and [logFd] are 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, logFd.fd)
if (shouldFinish)
runOnUiThread { finish() }
}
emulationThread.start()
}
/**
* This makes the window fullscreen then sets up [preferenceFd] and [logFd], sets up the performance statistics and finally calls [executeApplication] for executing the application
*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.emu_activity)
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)
val preference = File("${applicationInfo.dataDir}/shared_prefs/${applicationInfo.packageName}_preferences.xml")
preferenceFd = ParcelFileDescriptor.open(preference, ParcelFileDescriptor.MODE_READ_WRITE)
val log = File("${applicationInfo.dataDir}/skyline.log")
logFd = ParcelFileDescriptor.open(log, ParcelFileDescriptor.MODE_CREATE or 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()
logFd.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)
}
}