summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-03-08 10:41:29 -0500
committerGravatar bunnei2023-06-03 00:05:38 -0700
commit66079923aee1b75bfd14c8a0600a2e5b90a62895 (patch)
tree03077e5456e808c31684067da20f330682e8b5b1
parentandroid: Convert SettingsFile to Kotlin (diff)
downloadyuzu-66079923aee1b75bfd14c8a0600a2e5b90a62895.tar.gz
yuzu-66079923aee1b75bfd14c8a0600a2e5b90a62895.tar.xz
yuzu-66079923aee1b75bfd14c8a0600a2e5b90a62895.zip
android: Convert EmulationFragment to Kotlin
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java375
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt348
2 files changed, 348 insertions, 375 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
deleted file mode 100644
index 2a2b0f68b..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
+++ /dev/null
@@ -1,375 +0,0 @@
1package org.yuzu.yuzu_emu.fragments;
2
3import android.content.Context;
4import android.content.IntentFilter;
5import android.content.SharedPreferences;
6import android.graphics.Color;
7import android.os.Bundle;
8import android.os.Handler;
9import android.preference.PreferenceManager;
10import android.view.Choreographer;
11import android.view.LayoutInflater;
12import android.view.Surface;
13import android.view.SurfaceHolder;
14import android.view.SurfaceView;
15import android.view.View;
16import android.view.ViewGroup;
17import android.widget.Button;
18import android.widget.TextView;
19import android.widget.Toast;
20
21import androidx.annotation.NonNull;
22import androidx.fragment.app.Fragment;
23import androidx.localbroadcastmanager.content.LocalBroadcastManager;
24
25import org.yuzu.yuzu_emu.NativeLibrary;
26import org.yuzu.yuzu_emu.R;
27import org.yuzu.yuzu_emu.activities.EmulationActivity;
28import org.yuzu.yuzu_emu.overlay.InputOverlay;
29import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
30import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState;
31import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver;
32import org.yuzu.yuzu_emu.utils.Log;
33
34public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback {
35 private static final String KEY_GAMEPATH = "gamepath";
36
37 private static final Handler perfStatsUpdateHandler = new Handler();
38
39 private SharedPreferences mPreferences;
40
41 private InputOverlay mInputOverlay;
42
43 private EmulationState mEmulationState;
44
45 private DirectoryStateReceiver directoryStateReceiver;
46
47 private EmulationActivity activity;
48
49 private TextView mPerfStats;
50
51 private Runnable perfStatsUpdater;
52
53 public static EmulationFragment newInstance(String gamePath) {
54 Bundle args = new Bundle();
55 args.putString(KEY_GAMEPATH, gamePath);
56
57 EmulationFragment fragment = new EmulationFragment();
58 fragment.setArguments(args);
59 return fragment;
60 }
61
62 @Override
63 public void onAttach(@NonNull Context context) {
64 super.onAttach(context);
65
66 if (context instanceof EmulationActivity) {
67 activity = (EmulationActivity) context;
68 NativeLibrary.setEmulationActivity((EmulationActivity) context);
69 } else {
70 throw new IllegalStateException("EmulationFragment must have EmulationActivity parent");
71 }
72 }
73
74 /**
75 * Initialize anything that doesn't depend on the layout / views in here.
76 */
77 @Override
78 public void onCreate(Bundle savedInstanceState) {
79 super.onCreate(savedInstanceState);
80
81 // So this fragment doesn't restart on configuration changes; i.e. rotation.
82 setRetainInstance(true);
83
84 mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
85
86 String gamePath = getArguments().getString(KEY_GAMEPATH);
87 mEmulationState = new EmulationState(gamePath);
88 }
89
90 /**
91 * Initialize the UI and start emulation in here.
92 */
93 @Override
94 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
95 View contents = inflater.inflate(R.layout.fragment_emulation, container, false);
96
97 SurfaceView surfaceView = contents.findViewById(R.id.surface_emulation);
98 surfaceView.getHolder().addCallback(this);
99
100 mInputOverlay = contents.findViewById(R.id.surface_input_overlay);
101 mPerfStats = contents.findViewById(R.id.show_fps_text);
102 mPerfStats.setTextColor(Color.YELLOW);
103
104 Button doneButton = contents.findViewById(R.id.done_control_config);
105 if (doneButton != null) {
106 doneButton.setOnClickListener(v -> stopConfiguringControls());
107 }
108
109 // Setup overlay.
110 resetInputOverlay();
111 updateShowFpsOverlay();
112
113 // The new Surface created here will get passed to the native code via onSurfaceChanged.
114 return contents;
115 }
116
117 @Override
118 public void onResume() {
119 super.onResume();
120 Choreographer.getInstance().postFrameCallback(this);
121 if (DirectoryInitialization.areDirectoriesReady()) {
122 mEmulationState.run(activity.isActivityRecreated());
123 } else {
124 setupDirectoriesThenStartEmulation();
125 }
126 }
127
128 @Override
129 public void onPause() {
130 if (directoryStateReceiver != null) {
131 LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(directoryStateReceiver);
132 directoryStateReceiver = null;
133 }
134
135 if (mEmulationState.isRunning()) {
136 mEmulationState.pause();
137 }
138
139 Choreographer.getInstance().removeFrameCallback(this);
140 super.onPause();
141 }
142
143 @Override
144 public void onDetach() {
145 NativeLibrary.clearEmulationActivity();
146 super.onDetach();
147 }
148
149 private void setupDirectoriesThenStartEmulation() {
150 IntentFilter statusIntentFilter = new IntentFilter(
151 DirectoryInitialization.BROADCAST_ACTION);
152
153 directoryStateReceiver =
154 new DirectoryStateReceiver(directoryInitializationState ->
155 {
156 if (directoryInitializationState ==
157 DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED) {
158 mEmulationState.run(activity.isActivityRecreated());
159 } else if (directoryInitializationState ==
160 DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE) {
161 Toast.makeText(getContext(), R.string.external_storage_not_mounted,
162 Toast.LENGTH_SHORT)
163 .show();
164 }
165 });
166
167 // Registers the DirectoryStateReceiver and its intent filters
168 LocalBroadcastManager.getInstance(getActivity()).registerReceiver(
169 directoryStateReceiver,
170 statusIntentFilter);
171 DirectoryInitialization.start(getActivity());
172 }
173
174 public void refreshInputOverlay() {
175 mInputOverlay.refreshControls();
176 }
177
178 public void resetInputOverlay() {
179 // Reset button scale
180 SharedPreferences.Editor editor = mPreferences.edit();
181 editor.putInt("controlScale", 50);
182 editor.apply();
183
184 mInputOverlay.resetButtonPlacement();
185 }
186
187 public void updateShowFpsOverlay() {
188 if (true) {
189 final int SYSTEM_FPS = 0;
190 final int FPS = 1;
191 final int FRAMETIME = 2;
192 final int SPEED = 3;
193
194 perfStatsUpdater = () ->
195 {
196 final double[] perfStats = NativeLibrary.GetPerfStats();
197 if (perfStats[FPS] > 0) {
198 mPerfStats.setText(String.format("FPS: %.1f", perfStats[FPS]));
199 }
200
201 perfStatsUpdateHandler.postDelayed(perfStatsUpdater, 100);
202 };
203 perfStatsUpdateHandler.post(perfStatsUpdater);
204
205 mPerfStats.setVisibility(View.VISIBLE);
206 } else {
207 if (perfStatsUpdater != null) {
208 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater);
209 }
210
211 mPerfStats.setVisibility(View.GONE);
212 }
213 }
214
215 @Override
216 public void surfaceCreated(SurfaceHolder holder) {
217 // We purposely don't do anything here.
218 // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
219 }
220
221 @Override
222 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
223 Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height);
224 mEmulationState.newSurface(holder.getSurface());
225 }
226
227 @Override
228 public void surfaceDestroyed(SurfaceHolder holder) {
229 mEmulationState.clearSurface();
230 }
231
232 @Override
233 public void doFrame(long frameTimeNanos) {
234 Choreographer.getInstance().postFrameCallback(this);
235 NativeLibrary.DoFrame();
236 }
237
238 public void stopEmulation() {
239 mEmulationState.stop();
240 }
241
242 public void startConfiguringControls() {
243 getView().findViewById(R.id.done_control_config).setVisibility(View.VISIBLE);
244 mInputOverlay.setIsInEditMode(true);
245 }
246
247 public void stopConfiguringControls() {
248 getView().findViewById(R.id.done_control_config).setVisibility(View.GONE);
249 mInputOverlay.setIsInEditMode(false);
250 }
251
252 public boolean isConfiguringControls() {
253 return mInputOverlay.isInEditMode();
254 }
255
256 private static class EmulationState {
257 private final String mGamePath;
258 private State state;
259 private Surface mSurface;
260 private boolean mRunWhenSurfaceIsValid;
261
262 EmulationState(String gamePath) {
263 mGamePath = gamePath;
264 // Starting state is stopped.
265 state = State.STOPPED;
266 }
267
268 public synchronized boolean isStopped() {
269 return state == State.STOPPED;
270 }
271
272 // Getters for the current state
273
274 public synchronized boolean isPaused() {
275 return state == State.PAUSED;
276 }
277
278 public synchronized boolean isRunning() {
279 return state == State.RUNNING;
280 }
281
282 public synchronized void stop() {
283 if (state != State.STOPPED) {
284 Log.debug("[EmulationFragment] Stopping emulation.");
285 state = State.STOPPED;
286 NativeLibrary.StopEmulation();
287 } else {
288 Log.warning("[EmulationFragment] Stop called while already stopped.");
289 }
290 }
291
292 // State changing methods
293
294 public synchronized void pause() {
295 if (state != State.PAUSED) {
296 state = State.PAUSED;
297 Log.debug("[EmulationFragment] Pausing emulation.");
298
299 // Release the surface before pausing, since emulation has to be running for that.
300 NativeLibrary.SurfaceDestroyed();
301 NativeLibrary.PauseEmulation();
302 } else {
303 Log.warning("[EmulationFragment] Pause called while already paused.");
304 }
305 }
306
307 public synchronized void run(boolean isActivityRecreated) {
308 if (isActivityRecreated) {
309 if (NativeLibrary.IsRunning()) {
310 state = State.PAUSED;
311 }
312 } else {
313 Log.debug("[EmulationFragment] activity resumed or fresh start");
314 }
315
316 // If the surface is set, run now. Otherwise, wait for it to get set.
317 if (mSurface != null) {
318 runWithValidSurface();
319 } else {
320 mRunWhenSurfaceIsValid = true;
321 }
322 }
323
324 // Surface callbacks
325 public synchronized void newSurface(Surface surface) {
326 mSurface = surface;
327 if (mRunWhenSurfaceIsValid) {
328 runWithValidSurface();
329 }
330 }
331
332 public synchronized void clearSurface() {
333 if (mSurface == null) {
334 Log.warning("[EmulationFragment] clearSurface called, but surface already null.");
335 } else {
336 mSurface = null;
337 Log.debug("[EmulationFragment] Surface destroyed.");
338
339 if (state == State.RUNNING) {
340 NativeLibrary.SurfaceDestroyed();
341 state = State.PAUSED;
342 } else if (state == State.PAUSED) {
343 Log.warning("[EmulationFragment] Surface cleared while emulation paused.");
344 } else {
345 Log.warning("[EmulationFragment] Surface cleared while emulation stopped.");
346 }
347 }
348 }
349
350 private void runWithValidSurface() {
351 mRunWhenSurfaceIsValid = false;
352 if (state == State.STOPPED) {
353 NativeLibrary.SurfaceChanged(mSurface);
354 Thread mEmulationThread = new Thread(() ->
355 {
356 Log.debug("[EmulationFragment] Starting emulation thread.");
357 NativeLibrary.Run(mGamePath);
358 }, "NativeEmulation");
359 mEmulationThread.start();
360
361 } else if (state == State.PAUSED) {
362 Log.debug("[EmulationFragment] Resuming emulation.");
363 NativeLibrary.SurfaceChanged(mSurface);
364 NativeLibrary.UnPauseEmulation();
365 } else {
366 Log.debug("[EmulationFragment] Bug, run called while already running.");
367 }
368 state = State.RUNNING;
369 }
370
371 private enum State {
372 STOPPED, RUNNING, PAUSED
373 }
374 }
375}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
new file mode 100644
index 000000000..77964b88c
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt
@@ -0,0 +1,348 @@
1package org.yuzu.yuzu_emu.fragments
2
3import android.content.Context
4import android.content.IntentFilter
5import android.content.SharedPreferences
6import android.graphics.Color
7import android.os.Bundle
8import android.os.Handler
9import android.view.*
10import android.widget.Button
11import android.widget.TextView
12import android.widget.Toast
13import androidx.fragment.app.Fragment
14import androidx.localbroadcastmanager.content.LocalBroadcastManager
15import androidx.preference.PreferenceManager
16import org.yuzu.yuzu_emu.NativeLibrary
17import org.yuzu.yuzu_emu.R
18import org.yuzu.yuzu_emu.YuzuApplication
19import org.yuzu.yuzu_emu.activities.EmulationActivity
20import org.yuzu.yuzu_emu.features.settings.model.Settings
21import org.yuzu.yuzu_emu.overlay.InputOverlay
22import org.yuzu.yuzu_emu.utils.DirectoryInitialization
23import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState
24import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver
25import org.yuzu.yuzu_emu.utils.Log
26
27class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.FrameCallback {
28 private lateinit var preferences: SharedPreferences
29 private var inputOverlay: InputOverlay? = null
30 private lateinit var emulationState: EmulationState
31 private var directoryStateReceiver: DirectoryStateReceiver? = null
32 private var emulationActivity: EmulationActivity? = null
33 private lateinit var perfStats: TextView
34 private var perfStatsUpdater: (() -> Unit)? = null
35
36 override fun onAttach(context: Context) {
37 super.onAttach(context)
38 if (context is EmulationActivity) {
39 emulationActivity = context
40 NativeLibrary.setEmulationActivity(context)
41 } else {
42 throw IllegalStateException("EmulationFragment must have EmulationActivity parent")
43 }
44 }
45
46 /**
47 * Initialize anything that doesn't depend on the layout / views in here.
48 */
49 override fun onCreate(savedInstanceState: Bundle?) {
50 super.onCreate(savedInstanceState)
51
52 // So this fragment doesn't restart on configuration changes; i.e. rotation.
53 retainInstance = true
54 preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
55 val gamePath = requireArguments().getString(KEY_GAMEPATH)
56 emulationState = EmulationState(gamePath)
57 }
58
59 /**
60 * Initialize the UI and start emulation in here.
61 */
62 override fun onCreateView(
63 inflater: LayoutInflater,
64 container: ViewGroup?,
65 savedInstanceState: Bundle?
66 ): View? {
67 val contents = inflater.inflate(R.layout.fragment_emulation, container, false)
68 val surfaceView = contents.findViewById<SurfaceView>(R.id.surface_emulation)
69 surfaceView.holder.addCallback(this)
70 inputOverlay = contents.findViewById(R.id.surface_input_overlay)
71 perfStats = contents.findViewById(R.id.show_fps_text)
72 perfStats.setTextColor(Color.YELLOW)
73 val doneButton = contents.findViewById<Button>(R.id.done_control_config)
74 doneButton?.setOnClickListener { stopConfiguringControls() }
75
76 // Setup overlay.
77 resetInputOverlay()
78 updateShowFpsOverlay()
79
80 // The new Surface created here will get passed to the native code via onSurfaceChanged.
81 return contents
82 }
83
84 override fun onResume() {
85 super.onResume()
86 Choreographer.getInstance().postFrameCallback(this)
87 if (DirectoryInitialization.areDirectoriesReady()) {
88 emulationState.run(emulationActivity!!.isActivityRecreated)
89 } else {
90 setupDirectoriesThenStartEmulation()
91 }
92 }
93
94 override fun onPause() {
95 if (directoryStateReceiver != null) {
96 LocalBroadcastManager.getInstance(requireActivity()).unregisterReceiver(
97 directoryStateReceiver!!
98 )
99 directoryStateReceiver = null
100 }
101 if (emulationState.isRunning) {
102 emulationState.pause()
103 }
104 Choreographer.getInstance().removeFrameCallback(this)
105 super.onPause()
106 }
107
108 override fun onDetach() {
109 NativeLibrary.clearEmulationActivity()
110 super.onDetach()
111 }
112
113 private fun setupDirectoriesThenStartEmulation() {
114 val statusIntentFilter = IntentFilter(
115 DirectoryInitialization.BROADCAST_ACTION
116 )
117 directoryStateReceiver =
118 DirectoryStateReceiver { directoryInitializationState: DirectoryInitializationState ->
119 if (directoryInitializationState ==
120 DirectoryInitializationState.YUZU_DIRECTORIES_INITIALIZED
121 ) {
122 emulationState.run(emulationActivity!!.isActivityRecreated)
123 } else if (directoryInitializationState ==
124 DirectoryInitializationState.CANT_FIND_EXTERNAL_STORAGE
125 ) {
126 Toast.makeText(
127 context,
128 R.string.external_storage_not_mounted,
129 Toast.LENGTH_SHORT
130 )
131 .show()
132 }
133 }
134
135 // Registers the DirectoryStateReceiver and its intent filters
136 LocalBroadcastManager.getInstance(requireActivity()).registerReceiver(
137 directoryStateReceiver!!,
138 statusIntentFilter
139 )
140 DirectoryInitialization.start(requireContext())
141 }
142
143 fun refreshInputOverlay() {
144 inputOverlay!!.refreshControls()
145 }
146
147 fun resetInputOverlay() {
148 // Reset button scale
149 preferences.edit()
150 .putInt(Settings.PREF_CONTROL_SCALE, 50)
151 .apply()
152 inputOverlay!!.resetButtonPlacement()
153 }
154
155 private fun updateShowFpsOverlay() {
156 // TODO: Create a setting so that this actually works...
157 if (true) {
158 val SYSTEM_FPS = 0
159 val FPS = 1
160 val FRAMETIME = 2
161 val SPEED = 3
162 perfStatsUpdater = {
163 val perfStats = NativeLibrary.GetPerfStats()
164 if (perfStats[FPS] > 0) {
165 this.perfStats.text = String.format("FPS: %.1f", perfStats[FPS])
166 }
167 perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 100)
168 }
169 perfStatsUpdateHandler.post(perfStatsUpdater!!)
170 perfStats.visibility = View.VISIBLE
171 } else {
172 if (perfStatsUpdater != null) {
173 perfStatsUpdateHandler.removeCallbacks(perfStatsUpdater!!)
174 }
175 perfStats.visibility = View.GONE
176 }
177 }
178
179 override fun surfaceCreated(holder: SurfaceHolder) {
180 // We purposely don't do anything here.
181 // All work is done in surfaceChanged, which we are guaranteed to get even for surface creation.
182 }
183
184 override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
185 Log.debug("[EmulationFragment] Surface changed. Resolution: " + width + "x" + height)
186 emulationState.newSurface(holder.surface)
187 }
188
189 override fun surfaceDestroyed(holder: SurfaceHolder) {
190 emulationState.clearSurface()
191 }
192
193 override fun doFrame(frameTimeNanos: Long) {
194 Choreographer.getInstance().postFrameCallback(this)
195 NativeLibrary.DoFrame()
196 }
197
198 fun stopEmulation() {
199 emulationState.stop()
200 }
201
202 fun startConfiguringControls() {
203 requireView().findViewById<View>(R.id.done_control_config).visibility =
204 View.VISIBLE
205 inputOverlay!!.setIsInEditMode(true)
206 }
207
208 fun stopConfiguringControls() {
209 requireView().findViewById<View>(R.id.done_control_config).visibility = View.GONE
210 inputOverlay!!.setIsInEditMode(false)
211 }
212
213 val isConfiguringControls: Boolean
214 get() = inputOverlay!!.isInEditMode
215
216 private class EmulationState(private val mGamePath: String?) {
217 private var state: State
218 private var surface: Surface? = null
219 private var runWhenSurfaceIsValid = false
220
221 init {
222 // Starting state is stopped.
223 state = State.STOPPED
224 }
225
226 @get:Synchronized
227 val isStopped: Boolean
228 get() = state == State.STOPPED
229
230 // Getters for the current state
231 @get:Synchronized
232 val isPaused: Boolean
233 get() = state == State.PAUSED
234
235 @get:Synchronized
236 val isRunning: Boolean
237 get() = state == State.RUNNING
238
239 @Synchronized
240 fun stop() {
241 if (state != State.STOPPED) {
242 Log.debug("[EmulationFragment] Stopping emulation.")
243 state = State.STOPPED
244 NativeLibrary.StopEmulation()
245 } else {
246 Log.warning("[EmulationFragment] Stop called while already stopped.")
247 }
248 }
249
250 // State changing methods
251 @Synchronized
252 fun pause() {
253 if (state != State.PAUSED) {
254 state = State.PAUSED
255 Log.debug("[EmulationFragment] Pausing emulation.")
256
257 // Release the surface before pausing, since emulation has to be running for that.
258 NativeLibrary.SurfaceDestroyed()
259 NativeLibrary.PauseEmulation()
260 } else {
261 Log.warning("[EmulationFragment] Pause called while already paused.")
262 }
263 }
264
265 @Synchronized
266 fun run(isActivityRecreated: Boolean) {
267 if (isActivityRecreated) {
268 if (NativeLibrary.IsRunning()) {
269 state = State.PAUSED
270 }
271 } else {
272 Log.debug("[EmulationFragment] activity resumed or fresh start")
273 }
274
275 // If the surface is set, run now. Otherwise, wait for it to get set.
276 if (surface != null) {
277 runWithValidSurface()
278 } else {
279 runWhenSurfaceIsValid = true
280 }
281 }
282
283 // Surface callbacks
284 @Synchronized
285 fun newSurface(surface: Surface?) {
286 this.surface = surface
287 if (runWhenSurfaceIsValid) {
288 runWithValidSurface()
289 }
290 }
291
292 @Synchronized
293 fun clearSurface() {
294 if (surface == null) {
295 Log.warning("[EmulationFragment] clearSurface called, but surface already null.")
296 } else {
297 surface = null
298 Log.debug("[EmulationFragment] Surface destroyed.")
299 when (state) {
300 State.RUNNING -> {
301 NativeLibrary.SurfaceDestroyed()
302 state = State.PAUSED
303 }
304 State.PAUSED -> Log.warning("[EmulationFragment] Surface cleared while emulation paused.")
305 else -> Log.warning("[EmulationFragment] Surface cleared while emulation stopped.")
306 }
307 }
308 }
309
310 private fun runWithValidSurface() {
311 runWhenSurfaceIsValid = false
312 when (state) {
313 State.STOPPED -> {
314 NativeLibrary.SurfaceChanged(surface)
315 val mEmulationThread = Thread({
316 Log.debug("[EmulationFragment] Starting emulation thread.")
317 NativeLibrary.Run(mGamePath)
318 }, "NativeEmulation")
319 mEmulationThread.start()
320 }
321 State.PAUSED -> {
322 Log.debug("[EmulationFragment] Resuming emulation.")
323 NativeLibrary.SurfaceChanged(surface)
324 NativeLibrary.UnPauseEmulation()
325 }
326 else -> Log.debug("[EmulationFragment] Bug, run called while already running.")
327 }
328 state = State.RUNNING
329 }
330
331 private enum class State {
332 STOPPED, RUNNING, PAUSED
333 }
334 }
335
336 companion object {
337 private const val KEY_GAMEPATH = "gamepath"
338 private val perfStatsUpdateHandler = Handler()
339
340 fun newInstance(gamePath: String?): EmulationFragment {
341 val args = Bundle()
342 args.putString(KEY_GAMEPATH, gamePath)
343 val fragment = EmulationFragment()
344 fragment.arguments = args
345 return fragment
346 }
347 }
348}