2012-10-16 08:30:02 +00:00
|
|
|
package com.froop.app.kegs;
|
|
|
|
|
|
|
|
import android.graphics.Bitmap;
|
|
|
|
import android.util.AttributeSet;
|
|
|
|
import android.util.Log;
|
2013-07-26 03:19:47 +00:00
|
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger;
|
2012-10-16 08:30:02 +00:00
|
|
|
import java.util.concurrent.locks.ReentrantLock;
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
|
|
|
|
// This is the primary interface into the native KEGS thread, and also
|
|
|
|
// where the native KEGS thread calls back into Java.
|
|
|
|
|
|
|
|
class KegsThread extends Thread {
|
|
|
|
private ConcurrentLinkedQueue<Event.KegsEvent> mEventQueue = new ConcurrentLinkedQueue<Event.KegsEvent>();
|
|
|
|
|
2012-10-21 09:59:37 +00:00
|
|
|
private String mConfigFile; // full path to config_kegs
|
2012-10-16 08:30:02 +00:00
|
|
|
private Bitmap mBitmap;
|
2013-07-26 03:19:47 +00:00
|
|
|
private final AtomicInteger mCurrentSpeed = new AtomicInteger(0);
|
2012-10-16 08:30:02 +00:00
|
|
|
private final ReentrantLock mPauseLock = new ReentrantLock();
|
|
|
|
private final ReentrantLock mPowerWait = new ReentrantLock();
|
|
|
|
|
|
|
|
// Look also at mPauseLock.
|
2013-07-26 03:19:47 +00:00
|
|
|
private final AtomicBoolean mPaused = new AtomicBoolean(false);
|
|
|
|
private final AtomicBoolean mReady = new AtomicBoolean(false);
|
2012-10-16 08:30:02 +00:00
|
|
|
|
|
|
|
interface UpdateScreen {
|
|
|
|
void updateScreen();
|
|
|
|
}
|
|
|
|
private UpdateScreen mUpdateScreen;
|
|
|
|
|
2012-10-21 09:59:37 +00:00
|
|
|
public KegsThread(String configFile, Bitmap bitmap) {
|
|
|
|
mConfigFile = configFile;
|
2012-10-16 08:30:02 +00:00
|
|
|
mBitmap = bitmap;
|
|
|
|
}
|
|
|
|
|
|
|
|
public ConcurrentLinkedQueue getEventQueue() {
|
|
|
|
return mEventQueue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void registerUpdateScreenInterface(UpdateScreen update) {
|
|
|
|
mUpdateScreen = update;
|
|
|
|
}
|
|
|
|
|
2012-10-26 01:38:13 +00:00
|
|
|
// Called by native thread. Sometimes called by UI thread.
|
2012-10-16 08:30:02 +00:00
|
|
|
protected void updateScreen() {
|
|
|
|
if (mUpdateScreen != null) {
|
|
|
|
mUpdateScreen.updateScreen();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-26 01:38:13 +00:00
|
|
|
// Called by native thread.
|
2013-07-26 03:19:47 +00:00
|
|
|
private void checkForPause(int currentEmulationSpeed) {
|
|
|
|
mCurrentSpeed.set(currentEmulationSpeed); // NOTE: This is a hack.
|
|
|
|
if (mPaused.get()) {
|
2012-10-16 08:30:02 +00:00
|
|
|
mPauseLock.lock();
|
|
|
|
// deadlock here until onResume. Maybe not efficient.
|
|
|
|
mPauseLock.unlock();
|
|
|
|
}
|
2012-10-21 09:59:37 +00:00
|
|
|
}
|
|
|
|
|
2012-10-26 01:38:13 +00:00
|
|
|
// Called by native thread.
|
2012-10-21 09:59:37 +00:00
|
|
|
private String getConfigFile() {
|
|
|
|
return mConfigFile;
|
2012-10-16 08:30:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// See jni/android_driver.c:mainLoop()
|
|
|
|
private native void mainLoop(Bitmap b, ConcurrentLinkedQueue q);
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void run() {
|
|
|
|
while(true) {
|
|
|
|
mPowerWait.lock();
|
|
|
|
mPowerWait.unlock();
|
|
|
|
mainLoop(mBitmap, mEventQueue);
|
|
|
|
}
|
|
|
|
// For TESTING:
|
|
|
|
// while (true) {
|
|
|
|
// try {
|
|
|
|
// Thread.sleep(500);
|
|
|
|
// checkForPause();
|
|
|
|
// } catch (InterruptedException e) {}
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onPause() {
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mReady.get()) {
|
2012-10-16 08:30:02 +00:00
|
|
|
return; // bail out, we haven't started doing anything yet
|
|
|
|
}
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mPaused.get()) {
|
|
|
|
mPaused.set(true);
|
2012-10-16 08:30:02 +00:00
|
|
|
mPauseLock.lock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public void onResume() {
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mReady.get()) {
|
2012-10-16 08:30:02 +00:00
|
|
|
return; // bail out, we haven't started doing anything yet
|
|
|
|
}
|
|
|
|
updateScreen();
|
2013-07-26 03:19:47 +00:00
|
|
|
if (mPaused.get()) {
|
|
|
|
mPaused.set(false);
|
2012-10-16 08:30:02 +00:00
|
|
|
mPauseLock.unlock();
|
|
|
|
} else if (!isAlive()) {
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Has the thread itself actually paused waiting to acquire the lock?
|
|
|
|
public boolean nowPaused() {
|
|
|
|
return mPauseLock.hasQueuedThreads();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void doPowerOff() {
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mReady.get()) {
|
2012-11-02 23:28:59 +00:00
|
|
|
return; // bail out, we haven't started doing anything yet
|
|
|
|
}
|
|
|
|
|
2012-10-16 08:30:02 +00:00
|
|
|
// Tell the native thread loop to wait before powering on again.
|
|
|
|
mPowerWait.lock();
|
|
|
|
|
|
|
|
// Special event, see android_driver.c:x_key_special()
|
|
|
|
mEventQueue.add(new Event.KeyKegsEvent(120 + 0x80, true));
|
|
|
|
}
|
|
|
|
|
2012-11-02 23:28:59 +00:00
|
|
|
// Call from the UI thread only.
|
2012-10-16 08:30:02 +00:00
|
|
|
public void allowPowerOn() {
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mReady.get()) {
|
|
|
|
mReady.set(true);
|
2012-11-02 23:28:59 +00:00
|
|
|
onResume(); // Will start the thread if not already started.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-10-16 08:30:02 +00:00
|
|
|
// As the native thread is allowed to loop by default,
|
|
|
|
// this is only useful after using doPowerOff().
|
|
|
|
if (mPowerWait.isHeldByCurrentThread()) {
|
|
|
|
mPowerWait.unlock();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Is native thread loop sitting around waiting for us to allow power on?
|
|
|
|
public boolean nowWaitingForPowerOn() {
|
2013-07-26 03:19:47 +00:00
|
|
|
if (!mReady.get()) {
|
2012-11-02 23:28:59 +00:00
|
|
|
return true; // bail out, we haven't started doing anything yet
|
|
|
|
}
|
|
|
|
|
2012-10-16 08:30:02 +00:00
|
|
|
return mPowerWait.hasQueuedThreads();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEmulationSpeed(int speed) {
|
|
|
|
// Speed matches g_limit_speed inside KEGS.
|
|
|
|
// Instead of a separate "control" event, key ids with bit 8 high are
|
|
|
|
// special events. See android_driver.c:x_key_special()
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(speed + 0x80, true));
|
|
|
|
}
|
|
|
|
|
2013-07-26 03:19:47 +00:00
|
|
|
public int getEmulationSpeed() {
|
|
|
|
// Equal to g_limit_speed inside KEGS.
|
|
|
|
return mCurrentSpeed.get();
|
|
|
|
}
|
|
|
|
|
2012-10-16 08:30:02 +00:00
|
|
|
public void doWarmReset() {
|
|
|
|
// Press keys down.
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_OPEN_APPLE, false));
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_CONTROL, false));
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_RESET, false));
|
|
|
|
// Release reset key first, then the others.
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_RESET, true));
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_CONTROL, true));
|
|
|
|
getEventQueue().add(new Event.KeyKegsEvent(KegsKeyboard.KEY_OPEN_APPLE, true));
|
|
|
|
}
|
|
|
|
}
|