mirror of
https://github.com/jamessanford/kegs.git
synced 2024-06-03 05:29:42 +00:00
Moving KegsThread out of KegsView.
This commit is contained in:
parent
589a28af6a
commit
1d82d39eb2
|
@ -283,8 +283,9 @@ x_update_color(int col_num, int red, int green, int blue, word32 rgb)
|
|||
|
||||
|
||||
// Instead of 'KegsView$KegsThread', the $ is encoded as _00024.
|
||||
// (not any more, but it was KegsView_00024KegsThread_mainLoop)
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_froop_app_kegs_KegsView_00024KegsThread_mainLoop( JNIEnv* env, jobject thiz, jobject bitmap, jobject eventqueue )
|
||||
Java_com_froop_app_kegs_KegsThread_mainLoop( JNIEnv* env, jobject thiz, jobject bitmap, jobject eventqueue )
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
|
|
@ -30,6 +30,9 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
private static final String FRAGMENT_DOWNLOAD = "download";
|
||||
private static final String FRAGMENT_ERROR = "error";
|
||||
private static final String FRAGMENT_SPEED = "speed";
|
||||
private static final String FRAGMENT_DISKIMAGE = "diskimage";
|
||||
|
||||
private KegsThread mKegsThread;
|
||||
|
||||
protected KegsView mKegsView;
|
||||
private KegsTouch mKegsTouch;
|
||||
|
@ -106,11 +109,11 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
new SpeedFragment().show(getFragmentManager(), FRAGMENT_SPEED);
|
||||
return true;
|
||||
} else if (item_id == R.id.warm_reset) {
|
||||
mKegsView.doWarmReset();
|
||||
getThread().doWarmReset();
|
||||
return true;
|
||||
} else if (item_id == R.id.power_cycle) {
|
||||
mKegsView.doPowerOff();
|
||||
mKegsView.allowPowerOn();
|
||||
getThread().doPowerOff();
|
||||
getThread().allowPowerOn();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -168,7 +171,7 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
}
|
||||
} else {
|
||||
Config.checkConfig(mRomfile);
|
||||
mKegsView.setReady(true);
|
||||
getThread().setReady(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,6 +205,10 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
}
|
||||
}
|
||||
|
||||
public KegsThread getThread() {
|
||||
return mKegsThread;
|
||||
}
|
||||
|
||||
private void updateActionBar(boolean showActionBar) {
|
||||
final ActionBar actionBar = getActionBar();
|
||||
if (showActionBar) {
|
||||
|
@ -246,7 +253,7 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
}
|
||||
|
||||
// Force another redraw of the bitmap into the canvas. Bug workaround.
|
||||
mKegsView.getThread().updateScreen();
|
||||
getThread().updateScreen();
|
||||
}
|
||||
|
||||
private void workaroundScreenSize() {
|
||||
|
@ -322,7 +329,8 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
// drop down menu for special keys...?
|
||||
return true;
|
||||
} else if (item_id == R.id.action_diskimage) {
|
||||
// start fragment for disk images
|
||||
// FIXME
|
||||
// new DiskImageFragment().show(getFragmentManager(), FRAGMENT_DISKIMAGE);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -335,12 +343,15 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
|
||||
mKegsView = (KegsView)findViewById(R.id.kegsview);
|
||||
|
||||
mKegsThread = new KegsThread(mKegsView.getBitmap());
|
||||
mKegsThread.registerUpdateScreenInterface(mKegsView);
|
||||
|
||||
workaroundScreenSize();
|
||||
|
||||
mKegsTouch = new KegsTouch(mKegsView.getEventQueue());
|
||||
mKegsTouch = new KegsTouch(getThread().getEventQueue());
|
||||
final GestureDetector inputDetect = new GestureDetector(this, mKegsTouch);
|
||||
|
||||
mJoystick = new TouchJoystick(mKegsView.getEventQueue());
|
||||
mJoystick = new TouchJoystick(getThread().getEventQueue());
|
||||
|
||||
final View mainView = findViewById(R.id.mainview);
|
||||
mainView.setClickable(true);
|
||||
|
@ -356,7 +367,7 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
}
|
||||
});
|
||||
|
||||
mKegsKeyboard = new KegsKeyboard(mKegsView.getEventQueue());
|
||||
mKegsKeyboard = new KegsKeyboard(getThread().getEventQueue());
|
||||
mKegsKeyboard.setOnStickyReset(this);
|
||||
|
||||
mSettingsMenu = new PopupMenu(this, findViewById(R.id.key_settings));
|
||||
|
@ -380,20 +391,20 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
|
|||
chooseRom.show(getFragmentManager(), FRAGMENT_ROM);
|
||||
} else {
|
||||
Config.checkConfig(romfile);
|
||||
mKegsView.setReady(true);
|
||||
getThread().setReady(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
mKegsView.getThread().onPause();
|
||||
getThread().onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
mKegsView.getThread().onResume();
|
||||
getThread().onResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
157
src/com/froop/app/kegs/KegsThread.java
Normal file
157
src/com/froop/app/kegs/KegsThread.java
Normal file
|
@ -0,0 +1,157 @@
|
|||
package com.froop.app.kegs;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
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>();
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private final ReentrantLock mPauseLock = new ReentrantLock();
|
||||
private final ReentrantLock mPowerWait = new ReentrantLock();
|
||||
|
||||
// Look also at mPauseLock.
|
||||
private boolean mPaused = false;
|
||||
private boolean mReady = false; // 'true' will begin the native thread.
|
||||
|
||||
interface UpdateScreen {
|
||||
void updateScreen();
|
||||
}
|
||||
private UpdateScreen mUpdateScreen;
|
||||
|
||||
public KegsThread(Bitmap bitmap) {
|
||||
mBitmap = bitmap;
|
||||
}
|
||||
|
||||
public ConcurrentLinkedQueue getEventQueue() {
|
||||
return mEventQueue;
|
||||
}
|
||||
|
||||
public void registerUpdateScreenInterface(UpdateScreen update) {
|
||||
mUpdateScreen = update;
|
||||
}
|
||||
|
||||
protected void updateScreen() {
|
||||
if (mUpdateScreen != null) {
|
||||
mUpdateScreen.updateScreen();
|
||||
}
|
||||
}
|
||||
|
||||
private void checkForPause() {
|
||||
if (mPaused) {
|
||||
mPauseLock.lock();
|
||||
// deadlock here until onResume. Maybe not efficient.
|
||||
mPauseLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if (!mReady) {
|
||||
return; // bail out, we haven't started doing anything yet
|
||||
}
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
mPauseLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
if (!mReady) {
|
||||
return; // bail out, we haven't started doing anything yet
|
||||
}
|
||||
updateScreen();
|
||||
if (mPaused) {
|
||||
mPaused = false;
|
||||
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() {
|
||||
// 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));
|
||||
}
|
||||
|
||||
public void allowPowerOn() {
|
||||
// Intended to be called from the UI thread.
|
||||
// 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() {
|
||||
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));
|
||||
}
|
||||
|
||||
public void setStartupSlot(int slot) {
|
||||
if (slot >= 0 && slot <= 9) {
|
||||
// Special event, see android_driver.c:x_key_special()
|
||||
getEventQueue().add(new Event.KeyKegsEvent(slot + 10 + 0x80, true));
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
public void setReady(boolean ready) {
|
||||
final boolean wasReady = mReady;
|
||||
mReady = ready;
|
||||
|
||||
if (ready && !wasReady) {
|
||||
// Will start the thread if not already started.
|
||||
onResume();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,158 +1,22 @@
|
|||
package com.froop.app.kegs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
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 KegsView extends SurfaceView implements SurfaceHolder.Callback {
|
||||
class KegsView extends SurfaceView implements KegsThread.UpdateScreen, SurfaceHolder.Callback {
|
||||
// Reported area of this view, see updateScreenSize()
|
||||
private int mWidth = 0;
|
||||
private int mHeight = 0;
|
||||
|
||||
// Look also at mPauseLock.
|
||||
private boolean mPaused = false;
|
||||
private boolean mReady = false; // 'true' will begin the native thread.
|
||||
|
||||
protected ConcurrentLinkedQueue<Event.KegsEvent> mEventQueue = new ConcurrentLinkedQueue<Event.KegsEvent>();
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private final BitmapThread mBitmapThread = new BitmapThread();
|
||||
|
||||
class KegsThread extends Thread {
|
||||
private Handler mHandler;
|
||||
private Bitmap mBitmap;
|
||||
private Canvas mCanvas;
|
||||
private SurfaceHolder mSurfaceHolder;
|
||||
private Context mContext;
|
||||
private final ReentrantLock mPauseLock = new ReentrantLock();
|
||||
private final ReentrantLock mPowerWait = new ReentrantLock();
|
||||
|
||||
public KegsThread(SurfaceHolder surfaceHolder, Context context) {
|
||||
mSurfaceHolder = surfaceHolder;
|
||||
mContext = context;
|
||||
|
||||
mBitmap = Bitmap.createBitmap(BitmapSize.Const.A2Width,
|
||||
BitmapSize.Const.A2Height,
|
||||
Bitmap.Config.RGB_565);
|
||||
mBitmap.setHasAlpha(false);
|
||||
|
||||
mBitmapThread.setBitmap(surfaceHolder, mBitmap);
|
||||
mHandler = mBitmapThread.getHandler();
|
||||
}
|
||||
|
||||
private FpsCounter fpsCount = new FpsCounter("kegs", "native");
|
||||
|
||||
// Typically updateScreen is called by the native thread,
|
||||
// but it may also be run on the UI thread.
|
||||
//
|
||||
// We use a Handler to tell the bitmap thread to actually draw
|
||||
// on the canvas. No locking is involved, so it is possible for
|
||||
// the canvas to get a bitmap that is in the process of being updated
|
||||
// by the native thread. This should be relatively uncommon.
|
||||
//
|
||||
// If you wish to draw to the canvas in the native thread, it should
|
||||
// be safe to bypass the Handler and call mBitmapThread.updateScreen()
|
||||
// here instead.
|
||||
protected void updateScreen() {
|
||||
// Empty the queue first in case bitmap thread is lagging behind.
|
||||
mHandler.removeMessages(0);
|
||||
mHandler.sendEmptyMessage(0);
|
||||
fpsCount.fps();
|
||||
}
|
||||
|
||||
private void checkForPause() {
|
||||
if (mPaused) {
|
||||
mPauseLock.lock();
|
||||
// deadlock here until onResume. Maybe not efficient.
|
||||
mPauseLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
// 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() {
|
||||
if (!mReady) {
|
||||
return; // bail out, we haven't started doing anything yet
|
||||
}
|
||||
if (!mPaused) {
|
||||
mPaused = true;
|
||||
mPauseLock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
public void onResume() {
|
||||
if (!mReady) {
|
||||
return; // bail out, we haven't started doing anything yet
|
||||
}
|
||||
updateScreen();
|
||||
if (mPaused) {
|
||||
mPaused = false;
|
||||
mPauseLock.unlock();
|
||||
} else if (!thread.isAlive()) {
|
||||
thread.start();
|
||||
mBitmapThread.start();
|
||||
}
|
||||
}
|
||||
|
||||
// Has the thread itself actually paused waiting to acquire the lock?
|
||||
public boolean nowPaused() {
|
||||
return mPauseLock.hasQueuedThreads();
|
||||
}
|
||||
|
||||
public void doPowerOff() {
|
||||
// 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));
|
||||
}
|
||||
|
||||
public void allowPowerOn() {
|
||||
// Intended to be called from the UI thread.
|
||||
// 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() {
|
||||
return mPowerWait.hasQueuedThreads();
|
||||
}
|
||||
}
|
||||
|
||||
private KegsThread thread;
|
||||
private Handler mHandler;
|
||||
|
||||
public KegsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
@ -160,69 +24,47 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
|
|||
SurfaceHolder holder = getHolder();
|
||||
holder.addCallback(this);
|
||||
|
||||
thread = new KegsThread(holder, context);
|
||||
Bitmap bitmap = Bitmap.createBitmap(BitmapSize.Const.A2Width,
|
||||
BitmapSize.Const.A2Height,
|
||||
Bitmap.Config.RGB_565);
|
||||
mBitmap = bitmap;
|
||||
|
||||
mBitmapThread.setBitmap(holder, bitmap);
|
||||
mHandler = mBitmapThread.getHandler();
|
||||
mBitmapThread.start();
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
requestFocus();
|
||||
}
|
||||
|
||||
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()
|
||||
mEventQueue.add(new Event.KeyKegsEvent(speed + 0x80, true));
|
||||
}
|
||||
|
||||
public void setStartupSlot(int slot) {
|
||||
if (slot >= 0 && slot <= 9) {
|
||||
// Special event, see android_driver.c:x_key_special()
|
||||
mEventQueue.add(new Event.KeyKegsEvent(slot + 10 + 0x80, true));
|
||||
}
|
||||
}
|
||||
|
||||
public void doWarmReset() {
|
||||
// Press keys down.
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_OPEN_APPLE, false));
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_CONTROL, false));
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_RESET, false));
|
||||
// Release reset key first, then the others.
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_RESET, true));
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_CONTROL, true));
|
||||
mEventQueue.add(new Event.KeyKegsEvent(KegsKeyboard.KEY_OPEN_APPLE, true));
|
||||
}
|
||||
|
||||
public void doPowerOff() {
|
||||
thread.doPowerOff();
|
||||
}
|
||||
|
||||
public void allowPowerOn() {
|
||||
thread.allowPowerOn();
|
||||
}
|
||||
|
||||
public KegsThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
public ConcurrentLinkedQueue getEventQueue() {
|
||||
return mEventQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(mWidth, mHeight);
|
||||
}
|
||||
|
||||
public native String stringFromJNI();
|
||||
private FpsCounter fpsCount = new FpsCounter("kegs", "native");
|
||||
|
||||
public void setReady(boolean ready) {
|
||||
final boolean wasReady = mReady;
|
||||
mReady = ready;
|
||||
public Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
if (ready && !wasReady) {
|
||||
// Will start the thread if not already started.
|
||||
thread.onResume();
|
||||
}
|
||||
// Typically updateScreen is called by the native thread,
|
||||
// but it may also be run on the UI thread.
|
||||
//
|
||||
// We use a Handler to tell the bitmap thread to actually draw
|
||||
// on the canvas. No locking is involved, so it is possible for
|
||||
// the canvas to get a bitmap that is in the process of being updated
|
||||
// by the native thread. This should be relatively uncommon.
|
||||
//
|
||||
// If you wish to draw to the canvas in the native thread, it should
|
||||
// be safe to bypass the Handler and call mBitmapThread.updateScreen()
|
||||
// here instead.
|
||||
public void updateScreen() {
|
||||
// Empty the queue first in case bitmap thread is lagging behind.
|
||||
mHandler.removeMessages(0);
|
||||
mHandler.sendEmptyMessage(0);
|
||||
fpsCount.fps();
|
||||
}
|
||||
|
||||
public void updateScreenSize(BitmapSize bitmapSize) {
|
||||
|
|
|
@ -22,7 +22,7 @@ public class SpeedFragment extends DialogFragment {
|
|||
if (item > 3) {
|
||||
item = 0;
|
||||
}
|
||||
((KegsMain)getActivity()).mKegsView.setEmulationSpeed(item);
|
||||
((KegsMain)getActivity()).getThread().setEmulationSpeed(item);
|
||||
}
|
||||
});
|
||||
return builder.create();
|
||||
|
|
Loading…
Reference in New Issue
Block a user