mirror of
https://github.com/mauiaaron/apple2.git
synced 2026-01-23 06:16:08 +00:00
Compare commits
24 Commits
android-1.
...
android-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce974177b2 | ||
|
|
b524b5a068 | ||
|
|
646d64cd92 | ||
|
|
6de1cae478 | ||
|
|
4999471ade | ||
|
|
933e99c927 | ||
|
|
9a2044f75e | ||
|
|
3d634004cf | ||
|
|
bf54615198 | ||
|
|
521d1daccf | ||
|
|
cbe8a734d0 | ||
|
|
6c959a030c | ||
|
|
acdc8da64f | ||
|
|
e52f753bf3 | ||
|
|
d4adabda93 | ||
|
|
20f79dc5eb | ||
|
|
84fe0dd479 | ||
|
|
5c4ab06612 | ||
|
|
dcd77a4650 | ||
|
|
916a54fb4b | ||
|
|
616428a03f | ||
|
|
38dce3e212 | ||
|
|
844d8b815b | ||
|
|
997461318f |
@@ -27,8 +27,8 @@ android {
|
||||
applicationId "org.deadc0de.apple2ix.basic"
|
||||
minSdkVersion 10
|
||||
targetSdkVersion 23
|
||||
versionCode 3
|
||||
versionName "1.0.0"
|
||||
versionCode 4
|
||||
versionName "1.0.1"
|
||||
ndk {
|
||||
moduleName "apple2ix"
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public abstract class Apple2AbstractMenu implements Apple2MenuView {
|
||||
setup();
|
||||
}
|
||||
|
||||
public synchronized void show() {
|
||||
public void show() {
|
||||
if (isShowing()) {
|
||||
return;
|
||||
}
|
||||
@@ -59,7 +59,7 @@ public abstract class Apple2AbstractMenu implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mSettingsView.isShown();
|
||||
return mSettingsView.getParent() != null;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
|
||||
@@ -15,7 +15,6 @@ import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Rect;
|
||||
import android.media.AudioManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -25,12 +24,12 @@ import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
@@ -46,14 +45,14 @@ public class Apple2Activity extends Activity {
|
||||
private Apple2View mView = null;
|
||||
private Apple2SplashScreen mSplashScreen = null;
|
||||
private Apple2MainMenu mMainMenu = null;
|
||||
private Apple2SettingsMenu mSettingsMenu = null;
|
||||
private Apple2DisksMenu mDisksMenu = null;
|
||||
|
||||
private ArrayList<Apple2MenuView> mMenuStack = new ArrayList<Apple2MenuView>();
|
||||
private ArrayList<AlertDialog> mAlertDialogs = new ArrayList<AlertDialog>();
|
||||
|
||||
private AtomicBoolean mPausing = new AtomicBoolean(false);
|
||||
|
||||
private int mWidth = 0;
|
||||
private int mHeight = 0;
|
||||
|
||||
private float[] mXCoords = new float[MAX_FINGERS];
|
||||
private float[] mYCoords = new float[MAX_FINGERS];
|
||||
|
||||
@@ -90,17 +89,13 @@ public class Apple2Activity extends Activity {
|
||||
|
||||
private native void nativeOnCreate(String dataDir, int sampleRate, int monoBufferSize, int stereoBufferSize);
|
||||
|
||||
private native void nativeGraphicsInitialized(int width, int height);
|
||||
|
||||
private native void nativeGraphicsChanged(int width, int height);
|
||||
|
||||
private native void nativeOnKeyDown(int keyCode, int metaState);
|
||||
|
||||
private native void nativeOnKeyUp(int keyCode, int metaState);
|
||||
|
||||
private native void nativeOnResume(boolean isSystemResume);
|
||||
public native void nativeEmulationResume();
|
||||
|
||||
public native void nativeOnPause(boolean isSystemPause);
|
||||
public native void nativeEmulationPause();
|
||||
|
||||
public native void nativeOnQuit();
|
||||
|
||||
@@ -108,8 +103,6 @@ public class Apple2Activity extends Activity {
|
||||
|
||||
public native void nativeReboot();
|
||||
|
||||
public native void nativeRender();
|
||||
|
||||
public native void nativeChooseDisk(String path, boolean driveA, boolean readOnly);
|
||||
|
||||
public native void nativeEjectDisk(boolean driveA);
|
||||
@@ -135,22 +128,17 @@ public class Apple2Activity extends Activity {
|
||||
|
||||
Log.e(TAG, "onCreate()");
|
||||
|
||||
// placeholder view on initial launch
|
||||
if (mView == null) {
|
||||
setContentView(new View(this));
|
||||
}
|
||||
|
||||
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
|
||||
if (sNativeBarfed) {
|
||||
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
|
||||
View view = new View(this);
|
||||
setContentView(view);
|
||||
return;
|
||||
}
|
||||
|
||||
// run first-time initializations
|
||||
if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(this)) {
|
||||
Apple2DisksMenu.firstTime(this);
|
||||
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(this);
|
||||
}
|
||||
Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(this, true);
|
||||
|
||||
// get device audio parameters for native OpenSLES
|
||||
int sampleRate = DevicePropertyCalculator.getRecommendedSampleRate(this);
|
||||
int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/false);
|
||||
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
|
||||
@@ -159,31 +147,28 @@ public class Apple2Activity extends Activity {
|
||||
String dataDir = Apple2DisksMenu.getDataDir(this);
|
||||
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
|
||||
|
||||
// NOTE: load preferences after nativeOnCreate ... native CPU thread should still be paused
|
||||
Apple2Preferences.loadPreferences(this);
|
||||
showSplashScreen();
|
||||
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this);
|
||||
|
||||
mView = new Apple2View(this);
|
||||
setContentView(mView);
|
||||
// first-time initializations #1
|
||||
if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(this)) {
|
||||
Apple2DisksMenu.firstTime(this);
|
||||
}
|
||||
|
||||
// Another Android Annoyance ...
|
||||
// Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas),
|
||||
// we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when
|
||||
// the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding...
|
||||
mView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
public void onGlobalLayout() {
|
||||
Rect rect = new Rect();
|
||||
mView.getWindowVisibleDisplayFrame(rect);
|
||||
int h = rect.height();
|
||||
int w = rect.width();
|
||||
if (w < h) {
|
||||
// assure landscape dimensions
|
||||
final int w_ = w;
|
||||
w = h;
|
||||
h = w_;
|
||||
}
|
||||
nativeGraphicsChanged(w, h);
|
||||
mSettingsMenu = new Apple2SettingsMenu(this);
|
||||
mDisksMenu = new Apple2DisksMenu(this);
|
||||
|
||||
Intent intent = getIntent();
|
||||
String path = null;
|
||||
if (intent != null) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
path = data.getPath();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (path != null && Apple2DisksMenu.hasDiskExtension(path)) {
|
||||
handleInsertDiskIntent(path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -195,8 +180,8 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
|
||||
Log.d(TAG, "onResume()");
|
||||
mView.onResume();
|
||||
nativeOnResume(/*isSystemResume:*/true);
|
||||
showSplashScreen();
|
||||
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this); // NOTE : needs to be called again to clean-up
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -212,13 +197,15 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
|
||||
Log.d(TAG, "onPause()");
|
||||
mView.onPause();
|
||||
if (mView != null) {
|
||||
mView.onPause();
|
||||
}
|
||||
|
||||
// Apparently not good to leave popup/dialog windows showing when backgrounding.
|
||||
// Dismiss these popups to avoid android.view.WindowLeaked issues
|
||||
synchronized (this) {
|
||||
dismissAllMenus();
|
||||
nativeOnPause(true);
|
||||
nativeEmulationPause();
|
||||
}
|
||||
|
||||
mPausing.set(false);
|
||||
@@ -260,45 +247,6 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private String actionToString(int action) {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
return "CANCEL:" + action;
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
return "DOWN:" + action;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
return "MOVE:" + action;
|
||||
case MotionEvent.ACTION_UP:
|
||||
return "UP:" + action;
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
return "PDOWN:" + action;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
return "PUP:" + action;
|
||||
default:
|
||||
return "UNK:" + action;
|
||||
}
|
||||
}
|
||||
|
||||
private void printSamples(MotionEvent ev) {
|
||||
final int historySize = ev.getHistorySize();
|
||||
final int pointerCount = ev.getPointerCount();
|
||||
|
||||
for (int h = 0; h < historySize; h++) {
|
||||
Log.d(TAG, "Event "+ev.getAction().toString()+" at historical time "+ev.getHistoricalEventTime(h)+" :");
|
||||
for (int p = 0; p < pointerCount; p++) {
|
||||
Log.d(TAG, " pointer "+ev.getPointerId(p)+": ("+ev.getHistoricalX(p, h)+","+ev.getHistoricalY(p, h)+")");
|
||||
}
|
||||
}
|
||||
int pointerIndex = ev.getActionIndex();
|
||||
|
||||
Log.d(TAG, "Event " + actionToString(ev.getActionMasked()) + " for " + pointerIndex + " at time " + ev.getEventTime() + " :");
|
||||
for (int p = 0; p < pointerCount; p++) {
|
||||
Log.d(TAG, " pointer " + ev.getPointerId(p) + ": (" + ev.getX(p) + "," + ev.getY(p) + ")");
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
do {
|
||||
@@ -394,44 +342,9 @@ public class Apple2Activity extends Activity {
|
||||
return super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
void graphicsInitialized(int w, int h) {
|
||||
if (mMainMenu == null) {
|
||||
mMainMenu = new Apple2MainMenu(this, mView);
|
||||
}
|
||||
|
||||
if (w < h) {
|
||||
// assure landscape dimensions
|
||||
final int w_ = w;
|
||||
w = h;
|
||||
h = w_;
|
||||
}
|
||||
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
|
||||
// tell native about this...
|
||||
nativeGraphicsInitialized(w, h);
|
||||
|
||||
showSplashScreen();
|
||||
|
||||
Intent intent = getIntent();
|
||||
String path = null;
|
||||
if (intent != null) {
|
||||
Uri data = intent.getData();
|
||||
if (data != null) {
|
||||
path = data.getPath();
|
||||
}
|
||||
}
|
||||
if (path != null && Apple2DisksMenu.hasDiskExtension(path)) {
|
||||
handleInsertDiskIntent(path);
|
||||
}
|
||||
}
|
||||
|
||||
public void showMainMenu() {
|
||||
if (mMainMenu != null) {
|
||||
Apple2SettingsMenu settingsMenu = mMainMenu.getSettingsMenu();
|
||||
Apple2DisksMenu disksMenu = mMainMenu.getDisksMenu();
|
||||
if (!(settingsMenu.isShowing() || disksMenu.isShowing())) {
|
||||
if (!(mSettingsMenu.isShowing() || mDisksMenu.isShowing())) {
|
||||
mMainMenu.show();
|
||||
}
|
||||
}
|
||||
@@ -441,6 +354,14 @@ public class Apple2Activity extends Activity {
|
||||
return mMainMenu;
|
||||
}
|
||||
|
||||
public synchronized Apple2DisksMenu getDisksMenu() {
|
||||
return mDisksMenu;
|
||||
}
|
||||
|
||||
public synchronized Apple2SettingsMenu getSettingsMenu() {
|
||||
return mSettingsMenu;
|
||||
}
|
||||
|
||||
private void handleInsertDiskIntent(final String path) {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -464,15 +385,17 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
Apple2Preferences.CURRENT_DISK_A.saveString(Apple2Activity.this, diskPath);
|
||||
|
||||
Apple2DisksMenu disksMenu = mMainMenu.getDisksMenu();
|
||||
while (disksMenu.popPathStack() != null) {
|
||||
while (mDisksMenu.popPathStack() != null) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
String storageDir = Apple2DisksMenu.getExternalStorageDirectory().getAbsolutePath();
|
||||
if (diskPath.contains(storageDir)) {
|
||||
diskPath = diskPath.replace(storageDir + File.separator, "");
|
||||
disksMenu.pushPathStack(storageDir);
|
||||
File storageDir = Apple2DisksMenu.getExternalStorageDirectory();
|
||||
if (storageDir != null) {
|
||||
String storagePath = storageDir.getAbsolutePath();
|
||||
if (diskPath.contains(storagePath)) {
|
||||
diskPath = diskPath.replace(storagePath + File.separator, "");
|
||||
mDisksMenu.pushPathStack(storagePath);
|
||||
}
|
||||
}
|
||||
StringTokenizer tokenizer = new StringTokenizer(diskPath, File.separator);
|
||||
while (tokenizer.hasMoreTokens()) {
|
||||
@@ -483,7 +406,7 @@ public class Apple2Activity extends Activity {
|
||||
if (Apple2DisksMenu.hasDiskExtension(token)) {
|
||||
continue;
|
||||
}
|
||||
disksMenu.pushPathStack(token);
|
||||
mDisksMenu.pushPathStack(token);
|
||||
}
|
||||
|
||||
Toast.makeText(Apple2Activity.this, Apple2Activity.this.getString(R.string.disk_insert_toast), Toast.LENGTH_SHORT).show();
|
||||
@@ -493,21 +416,28 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
|
||||
private void showSplashScreen() {
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (Apple2Activity.this) {
|
||||
if (mSplashScreen == null) {
|
||||
mSplashScreen = new Apple2SplashScreen(Apple2Activity.this);
|
||||
}
|
||||
if (mSplashScreen.isShowing()) {
|
||||
return;
|
||||
}
|
||||
mSplashScreen.show();
|
||||
Apple2CrashHandler.getInstance().checkForCrashes(Apple2Activity.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (mSplashScreen != null) {
|
||||
return;
|
||||
}
|
||||
mSplashScreen = new Apple2SplashScreen(Apple2Activity.this);
|
||||
mSplashScreen.show();
|
||||
}
|
||||
|
||||
private void setupGLView() {
|
||||
|
||||
boolean glViewFirstTime = false;
|
||||
if (mView == null) {
|
||||
glViewFirstTime = true;
|
||||
mView = new Apple2View(this);
|
||||
mMainMenu = new Apple2MainMenu(this, mView);
|
||||
}
|
||||
|
||||
if (glViewFirstTime) {
|
||||
// HACK NOTE : do not blanket setContentView() ... it appears to wedge Gingerbread
|
||||
setContentView(mView);
|
||||
} else {
|
||||
mView.onResume();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAndShowDialog(AlertDialog dialog) {
|
||||
@@ -518,8 +448,8 @@ public class Apple2Activity extends Activity {
|
||||
public synchronized void pushApple2View(Apple2MenuView apple2MenuView) {
|
||||
mMenuStack.add(apple2MenuView);
|
||||
View menuView = apple2MenuView.getView();
|
||||
nativeOnPause(false);
|
||||
addContentView(menuView, new FrameLayout.LayoutParams(getWidth(), getHeight()));
|
||||
nativeEmulationPause();
|
||||
addContentView(menuView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
public synchronized Apple2MenuView popApple2View() {
|
||||
@@ -566,13 +496,11 @@ public class Apple2Activity extends Activity {
|
||||
mAlertDialogs.clear();
|
||||
|
||||
// Get rid of the menu hierarchy
|
||||
Apple2MenuView apple2MenuView;
|
||||
do {
|
||||
apple2MenuView = popApple2View();
|
||||
if (apple2MenuView != null) {
|
||||
apple2MenuView.dismissAll();
|
||||
}
|
||||
} while (apple2MenuView != null);
|
||||
ArrayList<Apple2MenuView> menuHierarchy = new ArrayList<Apple2MenuView>(mMenuStack);
|
||||
Collections.reverse(menuHierarchy);
|
||||
for (Apple2MenuView view : menuHierarchy) {
|
||||
view.dismissAll();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized Apple2MenuView popApple2View(Apple2MenuView apple2MenuView) {
|
||||
@@ -583,43 +511,42 @@ public class Apple2Activity extends Activity {
|
||||
|
||||
private void _disposeApple2View(Apple2MenuView apple2MenuView) {
|
||||
|
||||
boolean dismissedSplashScreen = false;
|
||||
|
||||
// Actually remove View from view hierarchy
|
||||
{
|
||||
View menuView = apple2MenuView.getView();
|
||||
if (menuView.isShown()) {
|
||||
((ViewGroup) menuView.getParent()).removeView(menuView);
|
||||
ViewGroup viewGroup = (ViewGroup) menuView.getParent();
|
||||
if (viewGroup != null) {
|
||||
viewGroup.removeView(menuView);
|
||||
}
|
||||
if (apple2MenuView instanceof Apple2SplashScreen) { // 20151101 HACK NOTE : use instanceof to avoid edge case where joystick calibration occurred (and thus the splash was already dismissed without proper mView initialization)
|
||||
mSplashScreen = null;
|
||||
dismissedSplashScreen = true;
|
||||
}
|
||||
}
|
||||
|
||||
// if no more views on menu stack, resume emulation
|
||||
if (mMenuStack.size() == 0) {
|
||||
dismissAllMenus();
|
||||
dismissAllMenus(); // NOTE : at this point, this should not be re-entrant into mMenuStack, it should just dismiss lingering popups
|
||||
if (!mPausing.get()) {
|
||||
if (dismissedSplashScreen) {
|
||||
setupGLView();
|
||||
} else {
|
||||
nativeEmulationResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeResumeCPU() {
|
||||
if (mMenuStack.size() == 0 && !mPausing.get()) {
|
||||
nativeOnResume(/*isSystemResume:*/false);
|
||||
nativeEmulationResume();
|
||||
}
|
||||
}
|
||||
|
||||
void _mainMenuDismissed() {
|
||||
if (mMenuStack.size() == 0 && !mPausing.get()) {
|
||||
nativeOnResume(/*isSystemResume:*/false);
|
||||
}
|
||||
}
|
||||
|
||||
public Apple2View getView() {
|
||||
return mView;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
public void maybeQuitApp() {
|
||||
nativeOnPause(false);
|
||||
nativeEmulationPause();
|
||||
AlertDialog quitDialog = new AlertDialog.Builder(this).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.quit_really).setMessage(R.string.quit_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
@@ -642,7 +569,7 @@ public class Apple2Activity extends Activity {
|
||||
}
|
||||
|
||||
public void maybeReboot() {
|
||||
nativeOnPause(false);
|
||||
nativeEmulationPause();
|
||||
AlertDialog rebootDialog = new AlertDialog.Builder(this).setIcon(R.drawable.ic_launcher).setCancelable(true).setTitle(R.string.reboot_really).setMessage(R.string.reboot_warning).setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
|
||||
@@ -204,19 +204,22 @@ public class Apple2CrashHandler {
|
||||
final int monoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/false);
|
||||
final int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(activity, /*isStereo:*/true);
|
||||
|
||||
StringBuilder summary = new StringBuilder();
|
||||
StringBuilder allCrashData = new StringBuilder();
|
||||
|
||||
// prepend information about this device
|
||||
allCrashData.append("BRAND: ").append(Build.BRAND).append("\n");
|
||||
allCrashData.append("MODEL: ").append(Build.MODEL).append("\n");
|
||||
allCrashData.append("MANUFACTURER: ").append(Build.MANUFACTURER).append("\n");
|
||||
allCrashData.append("DEVICE: ").append(Build.DEVICE).append("\n");
|
||||
allCrashData.append("SAMPLE RATE: ").append(sampleRate).append("\n");
|
||||
allCrashData.append("MONO BUFSIZE: ").append(monoBufferSize).append("\n");
|
||||
allCrashData.append("STEREO BUFSIZE: ").append(stereoBufferSize).append("\n");
|
||||
allCrashData.append("GPU VENDOR: ").append(Apple2Preferences.GL_VENDOR.stringValue(activity)).append("\n");
|
||||
allCrashData.append("GPU RENDERER: ").append(Apple2Preferences.GL_RENDERER.stringValue(activity)).append("\n");
|
||||
allCrashData.append("GPU VERSION: ").append(Apple2Preferences.GL_VERSION.stringValue(activity)).append("\n");
|
||||
summary.append("BRAND: ").append(Build.BRAND).append("\n");
|
||||
summary.append("MODEL: ").append(Build.MODEL).append("\n");
|
||||
summary.append("MANUFACTURER: ").append(Build.MANUFACTURER).append("\n");
|
||||
summary.append("DEVICE: ").append(Build.DEVICE).append("\n");
|
||||
summary.append("SAMPLE RATE: ").append(sampleRate).append("\n");
|
||||
summary.append("MONO BUFSIZE: ").append(monoBufferSize).append("\n");
|
||||
summary.append("STEREO BUFSIZE: ").append(stereoBufferSize).append("\n");
|
||||
summary.append("GPU VENDOR: ").append(Apple2Preferences.GL_VENDOR.stringValue(activity)).append("\n");
|
||||
summary.append("GPU RENDERER: ").append(Apple2Preferences.GL_RENDERER.stringValue(activity)).append("\n");
|
||||
summary.append("GPU VERSION: ").append(Apple2Preferences.GL_VERSION.stringValue(activity)).append("\n");
|
||||
|
||||
allCrashData.append(summary);
|
||||
|
||||
File[] nativeCrashes = _nativeCrashFiles(activity);
|
||||
if (nativeCrashes == null) {
|
||||
@@ -247,6 +250,8 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
});
|
||||
|
||||
boolean summarizedHeader = false;
|
||||
|
||||
// iteratively process native crashes
|
||||
for (File crash : nativeCrashes) {
|
||||
|
||||
@@ -266,6 +271,48 @@ public class Apple2CrashHandler {
|
||||
}
|
||||
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
|
||||
allCrashData.append(crashData);
|
||||
summary.append("NATIVE CRASH:\n");
|
||||
|
||||
// append succinct information about crashing thread
|
||||
String[] lines = crashData.toString().split("[\\n\\r][\\n\\r]*");
|
||||
for (int i = 0, j = 0; i < lines.length; i++) {
|
||||
|
||||
// 2 lines of minidump summary
|
||||
if (i < 2) {
|
||||
if (!summarizedHeader) {
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// 1 line of crashing thread and reason
|
||||
if (i == 2) {
|
||||
summarizedHeader = true;
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// whole lotta modules
|
||||
if (lines[i].startsWith("Module")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// one apparently empty line
|
||||
if (lines[i].matches("^[ \\t]*$")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// append crashing thread backtrace
|
||||
|
||||
summary.append(lines[i]);
|
||||
summary.append("\n");
|
||||
final int maxSummaryBacktrace = 8;
|
||||
if (j++ >= maxSummaryBacktrace) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
@@ -288,6 +335,10 @@ public class Apple2CrashHandler {
|
||||
|
||||
allCrashData.append(">>>>>>> JAVA CRASH DATA\n");
|
||||
allCrashData.append(javaCrashData);
|
||||
|
||||
summary.append("JAVA CRASH:\n");
|
||||
summary.append(javaCrashData);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -313,7 +364,7 @@ public class Apple2CrashHandler {
|
||||
});
|
||||
|
||||
// send report with all the data
|
||||
_sendEmailToDeveloperWithCrashData(activity, allCrashData);
|
||||
_sendEmailToDeveloperWithCrashData(activity, summary, allCrashData);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
@@ -481,19 +532,25 @@ public class Apple2CrashHandler {
|
||||
return allCrashFile;
|
||||
}
|
||||
|
||||
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder allCrashData) {
|
||||
private void _sendEmailToDeveloperWithCrashData(Apple2Activity activity, StringBuilder summary, StringBuilder allCrashData) {
|
||||
mAlreadySentReport.set(true);
|
||||
|
||||
// <sigh> ... the disaster that is early Android ... there does not appear to be a reliable way to start an
|
||||
// email Intent to send both text and an attachment, but we make a valiant (if futile) effort to do so here.
|
||||
// And the reason to send an attachment is that you trigger an android.os.TransactionTooLargeException with too
|
||||
// much text data in the EXTRA_TEXT ... </sigh>
|
||||
|
||||
Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("mailto", "apple2ix_crash@deadcode.org"/*non-zero variant is correct endpoint at the moment*/, null));
|
||||
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Crasher");
|
||||
|
||||
File allCrashFile = _writeTempLogFile(activity, allCrashData);
|
||||
// Putting all the text data into the EXTRA_TEXT appears to trigger android.os.TransactionTooLargeException ...
|
||||
//emailIntent.putExtra(Intent.EXTRA_TEXT, allCrashData.toString());
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
|
||||
final int maxCharsEmail = 4096;
|
||||
int len = summary.length();
|
||||
len = len < maxCharsEmail ? len : maxCharsEmail;
|
||||
String summaryData = summary.substring(0, len);
|
||||
emailIntent.putExtra(Intent.EXTRA_TEXT, "The app crashed, please help!\n\n"+summaryData);
|
||||
|
||||
// But we can put some text data
|
||||
emailIntent.putExtra(Intent.EXTRA_TEXT, "Greeting Apple2ix developers! The app crashed, please help!");
|
||||
File allCrashFile = _writeTempLogFile(activity, allCrashData);
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
|
||||
|
||||
Log.d(TAG, "STARTING CHOOSER FOR EMAIL ...");
|
||||
activity.startActivity(Intent.createChooser(emailIntent, "Send email"));
|
||||
|
||||
@@ -78,24 +78,36 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public static File getExternalStorageDirectory() {
|
||||
if (sExternalFilesDir == null) {
|
||||
|
||||
do {
|
||||
if (sExternalFilesDir != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
String storageState = Environment.getExternalStorageState();
|
||||
File externalDir = new File(Environment.getExternalStorageDirectory(), "apple2ix"); // /sdcard/apple2ix
|
||||
sExternalFilesDir = null;
|
||||
boolean externalStorageAvailable = storageState.equals(Environment.MEDIA_MOUNTED);
|
||||
if (externalStorageAvailable) {
|
||||
sExternalFilesDir = externalDir;
|
||||
boolean made = sExternalFilesDir.mkdirs();
|
||||
if (!storageState.equals(Environment.MEDIA_MOUNTED)) {
|
||||
// 2015/10/28 : do not expose sExternalFilesDir/sDownloadFilesDir unless they are writable
|
||||
break;
|
||||
}
|
||||
|
||||
File externalStorageDir = Environment.getExternalStorageDirectory();
|
||||
if (externalStorageDir == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
File externalDir = new File(externalStorageDir, "apple2ix"); // /sdcard/apple2ix
|
||||
if (!externalDir.exists()) {
|
||||
boolean made = externalDir.mkdirs();
|
||||
if (!made) {
|
||||
Log.d(TAG, "WARNING: could not make directory : " + sExternalFilesDir);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
sExternalFilesDir = externalDir;
|
||||
}
|
||||
}
|
||||
if (sDownloadFilesDir == null) {
|
||||
|
||||
sExternalFilesDir = externalDir;
|
||||
sDownloadFilesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return sExternalFilesDir;
|
||||
}
|
||||
|
||||
@@ -134,7 +146,9 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath());
|
||||
|
||||
// expose keyboards to modding
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath());
|
||||
if (sExternalFilesDir != null) {
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public static void exposeSymbols(Apple2Activity activity) {
|
||||
@@ -184,7 +198,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mDisksView.isShown();
|
||||
return mDisksView.getParent() != null;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
@@ -437,7 +451,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
|
||||
int idx = 0;
|
||||
if (includeExternalStoragePath) {
|
||||
fileNames[idx] = sExternalFilesDir.getPath();
|
||||
fileNames[idx] = sExternalFilesDir.getAbsolutePath();
|
||||
isDirectory[idx] = true;
|
||||
++idx;
|
||||
}
|
||||
@@ -601,8 +615,6 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
mActivity.dismissAllMenus();
|
||||
boolean isDriveA = diskA.isChecked();
|
||||
boolean diskReadOnly = readOnly.isChecked();
|
||||
if (isDriveA) {
|
||||
@@ -612,6 +624,8 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
||||
Apple2Preferences.CURRENT_DISK_B_RO.saveBoolean(mActivity, diskReadOnly);
|
||||
Apple2Preferences.CURRENT_DISK_B.saveString(mActivity, imageName);
|
||||
}
|
||||
dialog.dismiss();
|
||||
mActivity.dismissAllMenus();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mSettingsView.isShown();
|
||||
return mSettingsView.getParent() != null;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
|
||||
@@ -249,7 +249,10 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
||||
}
|
||||
};
|
||||
|
||||
File[] files = extKeyboardDir.listFiles(kbdJsonFilter);
|
||||
File[] files = null;
|
||||
if (extKeyboardDir != null) {
|
||||
files = extKeyboardDir.listFiles(kbdJsonFilter);
|
||||
}
|
||||
if (files == null) {
|
||||
// read keyboard data from /data/data/...
|
||||
File keyboardDir = new File(Apple2DisksMenu.getDataDir(activity) + File.separator + "keyboards");
|
||||
@@ -270,7 +273,7 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
||||
++idx;
|
||||
}
|
||||
|
||||
final String keyboardDirName = extKeyboardDir.getPath();
|
||||
final String keyboardDirName = extKeyboardDir == null ? "Keyboards" : extKeyboardDir.getPath();
|
||||
|
||||
_alertDialogHandleSelection(activity, keyboardDirName, titles, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
|
||||
@@ -119,7 +119,7 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mSettingsView.isShown();
|
||||
return mSettingsView.getParent() != null;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
|
||||
@@ -36,8 +36,6 @@ public class Apple2MainMenu {
|
||||
private Apple2Activity mActivity = null;
|
||||
private Apple2View mParentView = null;
|
||||
private PopupWindow mMainMenuPopup = null;
|
||||
private Apple2SettingsMenu mSettingsMenu = null;
|
||||
private Apple2DisksMenu mDisksMenu = null;
|
||||
|
||||
public Apple2MainMenu(Apple2Activity activity, Apple2View parent) {
|
||||
mActivity = activity;
|
||||
@@ -164,46 +162,29 @@ public class Apple2MainMenu {
|
||||
mMainMenuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
|
||||
@Override
|
||||
public void onDismiss() {
|
||||
boolean otherMenusShowing = (getSettingsMenu().isShowing() || getDisksMenu().isShowing());
|
||||
if (!otherMenusShowing) {
|
||||
Apple2MainMenu.this.mActivity._mainMenuDismissed();
|
||||
}
|
||||
Apple2MainMenu.this.mActivity.maybeResumeCPU();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void showDisksMenu() {
|
||||
Apple2DisksMenu disksMenu = getDisksMenu();
|
||||
Apple2DisksMenu disksMenu = mActivity.getDisksMenu();
|
||||
disksMenu.show();
|
||||
mMainMenuPopup.dismiss();
|
||||
}
|
||||
|
||||
public void showSettings() {
|
||||
Apple2SettingsMenu settings = getSettingsMenu();
|
||||
Apple2SettingsMenu settings = mActivity.getSettingsMenu();
|
||||
settings.show();
|
||||
mMainMenuPopup.dismiss();
|
||||
}
|
||||
|
||||
public synchronized Apple2DisksMenu getDisksMenu() {
|
||||
if (mDisksMenu == null) {
|
||||
mDisksMenu = new Apple2DisksMenu(mActivity);
|
||||
}
|
||||
return mDisksMenu;
|
||||
}
|
||||
|
||||
public synchronized Apple2SettingsMenu getSettingsMenu() {
|
||||
if (mSettingsMenu == null) {
|
||||
mSettingsMenu = new Apple2SettingsMenu(mActivity);
|
||||
}
|
||||
return mSettingsMenu;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (mMainMenuPopup.isShowing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
mActivity.nativeOnPause(false);
|
||||
mActivity.nativeEmulationPause();
|
||||
|
||||
mMainMenuPopup.showAtLocation(mParentView, Gravity.CENTER, 0, 0);
|
||||
}
|
||||
|
||||
@@ -35,10 +35,7 @@ public enum Apple2Preferences {
|
||||
CURRENT_DISK_PATH {
|
||||
@Override
|
||||
public void load(final Apple2Activity activity) {
|
||||
Apple2MainMenu mainMenu = activity.getMainMenu();
|
||||
if (mainMenu != null) {
|
||||
mainMenu.getDisksMenu().setPathStackJSON(stringValue(activity));
|
||||
}
|
||||
activity.getDisksMenu().setPathStackJSON(stringValue(activity));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -46,9 +46,8 @@ public class Apple2SplashScreen implements Apple2MenuView {
|
||||
prefsButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Apple2SettingsMenu settingsMenu = mActivity.getMainMenu().getSettingsMenu();
|
||||
Apple2SettingsMenu settingsMenu = mActivity.getSettingsMenu();
|
||||
settingsMenu.show();
|
||||
Apple2SplashScreen.this.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -56,9 +55,8 @@ public class Apple2SplashScreen implements Apple2MenuView {
|
||||
disksButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Apple2DisksMenu disksMenu = mActivity.getMainMenu().getDisksMenu();
|
||||
Apple2DisksMenu disksMenu = mActivity.getDisksMenu();
|
||||
disksMenu.show();
|
||||
Apple2SplashScreen.this.dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -71,7 +69,7 @@ public class Apple2SplashScreen implements Apple2MenuView {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
public synchronized void show() {
|
||||
public void show() {
|
||||
if (isShowing()) {
|
||||
return;
|
||||
}
|
||||
@@ -87,7 +85,7 @@ public class Apple2SplashScreen implements Apple2MenuView {
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mSettingsView.isShown();
|
||||
return mSettingsView.getParent() != null;
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
|
||||
@@ -16,9 +16,13 @@
|
||||
package org.deadc0de.apple2ix;
|
||||
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.opengl.GLES20;
|
||||
import android.opengl.GLSurfaceView;
|
||||
import android.util.Log;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import javax.microedition.khronos.egl.EGL10;
|
||||
import javax.microedition.khronos.egl.EGLConfig;
|
||||
@@ -49,14 +53,17 @@ class Apple2View extends GLSurfaceView {
|
||||
private final static boolean DEBUG = false;
|
||||
|
||||
private Apple2Activity mActivity = null;
|
||||
private AtomicBoolean mInitialLaunch = new AtomicBoolean(true);
|
||||
|
||||
private static native void nativeGraphicsInitialized(int width, int height);
|
||||
|
||||
private static native void nativeGraphicsChanged(int width, int height);
|
||||
|
||||
private static native void nativeRender();
|
||||
|
||||
public Apple2View(Apple2Activity activity) {
|
||||
super(activity.getApplication());
|
||||
mActivity = activity;
|
||||
setup(0, 0);
|
||||
}
|
||||
|
||||
private void setup(int depth, int stencil) {
|
||||
|
||||
/* By default, GLSurfaceView() creates a RGB_565 opaque surface.
|
||||
* If we want a translucent one, we should change the surface's
|
||||
@@ -75,14 +82,36 @@ class Apple2View extends GLSurfaceView {
|
||||
* custom config chooser. See ConfigChooser class definition
|
||||
* below.
|
||||
*/
|
||||
setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, depth, stencil));
|
||||
setEGLConfigChooser(new ConfigChooser(8, 8, 8, 8, /*depth:*/0, /*stencil:*/0));
|
||||
|
||||
/* Set the renderer responsible for frame rendering */
|
||||
setRenderer(new Renderer());
|
||||
|
||||
// Another Android Annoyance ...
|
||||
// Even though we no longer use the system soft keyboard (which would definitely trigger width/height changes to our OpenGL canvas),
|
||||
// we still need to listen to dimension changes, because it seems on some janky devices you have an incorrect width/height set when
|
||||
// the initial OpenGL onSurfaceChanged() callback occurs. For now, include this defensive coding...
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
public void onGlobalLayout() {
|
||||
Rect rect = new Rect();
|
||||
Apple2View.this.getWindowVisibleDisplayFrame(rect);
|
||||
int h = rect.height();
|
||||
int w = rect.width();
|
||||
if (w < h) {
|
||||
// assure landscape dimensions
|
||||
final int w_ = w;
|
||||
w = h;
|
||||
h = w_;
|
||||
}
|
||||
nativeGraphicsChanged(w, h);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static class ContextFactory implements GLSurfaceView.EGLContextFactory {
|
||||
private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
|
||||
|
||||
public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {
|
||||
Log.w(TAG, "creating OpenGL ES 2.0 context");
|
||||
checkEglError("Before eglCreateContext", egl);
|
||||
@@ -295,17 +324,45 @@ class Apple2View extends GLSurfaceView {
|
||||
}
|
||||
|
||||
private class Renderer implements GLSurfaceView.Renderer {
|
||||
|
||||
@Override
|
||||
public void onDrawFrame(GL10 gl) {
|
||||
Apple2View.this.mActivity.nativeRender();
|
||||
nativeRender();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceChanged(GL10 gl, int width, int height) {
|
||||
Apple2Preferences.GL_VENDOR.saveString(mActivity, GLES20.glGetString(GLES20.GL_VENDOR));
|
||||
Apple2Preferences.GL_RENDERER.saveString(mActivity, GLES20.glGetString(GLES20.GL_RENDERER));
|
||||
Apple2Preferences.GL_VERSION.saveString(mActivity, GLES20.glGetString(GLES20.GL_VERSION));
|
||||
Apple2View.this.mActivity.graphicsInitialized(width, height);
|
||||
|
||||
Log.v(TAG, "graphicsInitialized(" + width + ", " + height + ")");
|
||||
|
||||
if (width < height) {
|
||||
// assure landscape dimensions
|
||||
final int w_ = width;
|
||||
width = height;
|
||||
height = w_;
|
||||
}
|
||||
|
||||
nativeGraphicsInitialized(width, height);
|
||||
|
||||
// first-time initializations #2
|
||||
if (!Apple2Preferences.FIRST_TIME_CONFIGURED.booleanValue(Apple2View.this.mActivity)) {
|
||||
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(Apple2View.this.mActivity);
|
||||
Apple2Preferences.FIRST_TIME_CONFIGURED.saveBoolean(Apple2View.this.mActivity, true);
|
||||
}
|
||||
|
||||
// load preferences on each new process creation
|
||||
if (mInitialLaunch.get()) {
|
||||
mInitialLaunch.set(false);
|
||||
Apple2Preferences.loadPreferences(Apple2View.this.mActivity);
|
||||
}
|
||||
|
||||
Apple2View.this.mActivity.maybeResumeCPU();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
|
||||
// Do nothing.
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
../../disks
|
||||
../../../apple2-images-pub/disks
|
||||
@@ -96,30 +96,8 @@ static inline int _androidTouchEvent2InterfaceEvent(jint action) {
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// JNI functions
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) {
|
||||
const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0);
|
||||
|
||||
// Android lifecycle can call onCreate() multiple times...
|
||||
if (data_dir) {
|
||||
LOG("IGNORING multiple calls to nativeOnCreate ...");
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not remove this deadc0de ... it forces a runtime load-library/link error on Gingerbread devices if we have
|
||||
// incorrectly compiled the app against a later version of the NDK!!!
|
||||
int pagesize = getpagesize();
|
||||
LOG("PAGESIZE IS : %d", pagesize);
|
||||
|
||||
data_dir = strdup(dataDir);
|
||||
if (crashHandler && crashHandler->init) {
|
||||
crashHandler->init(data_dir);
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, j_dataDir, dataDir);
|
||||
LOG("data_dir : %s", data_dir);
|
||||
static void discover_cpu_family(void) {
|
||||
LOG("Discovering CPU family...");
|
||||
|
||||
AndroidCpuFamily family = android_getCpuFamily();
|
||||
uint64_t features = android_getCpuFeatures();
|
||||
@@ -158,6 +136,34 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
|
||||
//android_arm64Arch = true;
|
||||
android_armArchV7A = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// JNI functions
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobject obj, jstring j_dataDir, jint sampleRate, jint monoBufferSize, jint stereoBufferSize) {
|
||||
const char *dataDir = (*env)->GetStringUTFChars(env, j_dataDir, 0);
|
||||
|
||||
// Android lifecycle can call onCreate() multiple times...
|
||||
if (data_dir) {
|
||||
LOG("IGNORING multiple calls to nativeOnCreate ...");
|
||||
return;
|
||||
}
|
||||
|
||||
discover_cpu_family();
|
||||
|
||||
// Do not remove this deadc0de ... it forces a runtime load-library/link error on Gingerbread devices if we have
|
||||
// incorrectly compiled the app against a later version of the NDK!!!
|
||||
int pagesize = getpagesize();
|
||||
LOG("PAGESIZE IS : %d", pagesize);
|
||||
|
||||
data_dir = strdup(dataDir);
|
||||
if (crashHandler && crashHandler->init) {
|
||||
crashHandler->init(data_dir);
|
||||
}
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, j_dataDir, dataDir);
|
||||
LOG("data_dir : %s", data_dir);
|
||||
|
||||
android_deviceSampleRateHz = (unsigned long)sampleRate;
|
||||
android_monoBufferSubmitSizeSamples = (unsigned long)monoBufferSize;
|
||||
@@ -173,34 +179,33 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jobje
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsChanged(JNIEnv *env, jobject obj, jint width, jint height) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsChanged(JNIEnv *env, jclass cls, jint width, jint height) {
|
||||
// WARNING : this can happen on non-GL thread
|
||||
LOG("...");
|
||||
video_backend->reshape(width, height);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeGraphicsInitialized(JNIEnv *env, jobject obj, jint width, jint height) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsInitialized(JNIEnv *env, jclass cls, jint width, jint height) {
|
||||
// WANRING : this needs to happen on the GL thread only
|
||||
LOG("width:%d height:%d", width, height);
|
||||
|
||||
video_shutdown();
|
||||
video_backend->reshape(width, height);
|
||||
video_backend->init((void *)0);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnResume(JNIEnv *env, jobject obj, jboolean isSystemResume) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationResume(JNIEnv *env, jobject obj) {
|
||||
if (!cpu_isPaused()) {
|
||||
return;
|
||||
}
|
||||
LOG("...");
|
||||
if (!isSystemResume) {
|
||||
#if TESTING
|
||||
// test driver thread is managing CPU
|
||||
// test driver thread is managing CPU
|
||||
#else
|
||||
cpu_resume();
|
||||
cpu_resume();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnPause(JNIEnv *env, jobject obj, jboolean isSystemPause) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env, jobject obj) {
|
||||
if (appState != APP_RUNNING) {
|
||||
return;
|
||||
}
|
||||
@@ -216,7 +221,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnPause(JNIEnv *env, jobjec
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeRender(JNIEnv *env, jobject obj) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeRender(JNIEnv *env, jclass cls) {
|
||||
SCOPE_TRACE_VIDEO("nativeRender");
|
||||
|
||||
if (UNLIKELY(appState != APP_RUNNING)) {
|
||||
@@ -311,6 +316,8 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, job
|
||||
int drive = driveA ? 0 : 1;
|
||||
int ro = readOnly ? 1 : 0;
|
||||
|
||||
assert(cpu_isPaused() && "considered dangerous to insert disk image when CPU thread is running");
|
||||
|
||||
LOG(": (%s, %s, %s)", path, driveA ? "drive A" : "drive B", readOnly ? "read only" : "read/write");
|
||||
if (disk6_insert(drive, path, ro)) {
|
||||
char *gzPath = NULL;
|
||||
|
||||
@@ -23,7 +23,7 @@ Project Goals
|
||||
Android
|
||||
-------
|
||||
|
||||
Currently pre-alpha testing the Google Play Store version...PM me if you would like to be on the Android VIP testers invite =D
|
||||
[Available on Google Play](https://play.google.com/store/apps/details?id=org.deadc0de.apple2ix.basic).
|
||||
|
||||
Running at 30FPS on Gingerbread (Android 2.3.3):
|
||||

|
||||
|
||||
@@ -417,7 +417,7 @@ static long opensl_createSoundBuffer(const AudioContext_s *audio_context, INOUT
|
||||
unsigned long bufferSize = opensles_audio_backend.systemSettings.stereoBufferSizeSamples * opensles_audio_backend.systemSettings.bytesPerSample * NUM_CHANNELS;
|
||||
|
||||
if (ctx->recycledVoices) {
|
||||
LOG("Recycling previously SLVoice ...");
|
||||
LOG("Recycling previous SLVoice ...");
|
||||
voice = ctx->recycledVoices;
|
||||
ctx->recycledVoices = voice->next;
|
||||
uint8_t *prevBuffer = voice->ringBuffer;
|
||||
|
||||
@@ -307,6 +307,13 @@ static unsigned int _submit_samples_buffer(const unsigned long num_channel_sampl
|
||||
break;
|
||||
}
|
||||
|
||||
unsigned long maxSpeakerBytes = channelsSampleRateHz * sizeof(int16_t);
|
||||
|
||||
if (system_buffer_size > maxSpeakerBytes) {
|
||||
RELEASE_LOG("AVOIDING BUFOVER...");
|
||||
system_buffer_size = maxSpeakerBytes;
|
||||
}
|
||||
|
||||
memcpy(system_samples_buffer, &samples_buffer[samples_idx], system_buffer_size);
|
||||
|
||||
err = speakerBuffer->Unlock(speakerBuffer, system_buffer_size);
|
||||
|
||||
@@ -72,24 +72,24 @@ unsigned short video__line_offset[TEXT_ROWS] = {
|
||||
|
||||
uint8_t video__dhires1[256] = {
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x3,0x3,0x5,0x5,0x7,0x7,0x9,0x9,0xb,0xb,0xd,0xd,0xf,0xf,
|
||||
0x0,0x1,0x2,0x3,0x6,0x5,0x6,0x7,0xa,0x9,0xa,0xb,0xe,0xd,0xe,0xf,
|
||||
0x0,0x1,0x3,0x3,0x7,0x5,0x7,0x7,0xb,0x9,0xb,0xb,0xf,0xd,0xf,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x4,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x3,0x3,0x5,0x5,0x7,0x7,0xd,0x9,0xb,0xb,0xd,0xd,0xf,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0xe,0x9,0xa,0xb,0xe,0xd,0xe,0xf,
|
||||
0x0,0x1,0x7,0x3,0x7,0x5,0x7,0x7,0xf,0x9,0xb,0xb,0xf,0xd,0xf,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0xa,0xb,0xc,0xd,0xe,0xf,
|
||||
};
|
||||
|
||||
uint8_t video__dhires2[256] = {
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x8,0x0,0xb,0x8,0xd,0x0,0x0,
|
||||
0x1,0x1,0x1,0x1,0x0,0x5,0x1,0x1,0x0,0x9,0xb,0xb,0x0,0xd,0xf,0xf,
|
||||
0x0,0x1,0x2,0x2,0x2,0x5,0x2,0x2,0x0,0xa,0xa,0xa,0xe,0xd,0x2,0x2,
|
||||
0x3,0x3,0x3,0x3,0x7,0x5,0x7,0x7,0x0,0xb,0xb,0xb,0xf,0xd,0xf,0xf,
|
||||
0x0,0x0,0x4,0x0,0x4,0x4,0x4,0x4,0xc,0x8,0x4,0x8,0xc,0xd,0x4,0x4,
|
||||
0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0xd,0x4,0x4,0x4,0xd,0xd,0x4,0x4,
|
||||
0x6,0x6,0x6,0x2,0xe,0x6,0x6,0x6,0xe,0xe,0xa,0xa,0xe,0x6,0xe,0x6,
|
||||
0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0xf,0xf,0xb,0xb,0xf,0xf,0xf,0xf,
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,
|
||||
0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x9,0x9,0x9,0x9,0x9,0x9,0x9,0x9,
|
||||
0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0xa,0xa,0xa,0xa,0xa,0xa,0xa,0xa,
|
||||
0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0xb,0xb,0xb,0xb,0xb,0xb,0xb,0xb,
|
||||
0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0xc,0xc,0xc,0xc,0xc,0xc,0xc,0xc,
|
||||
0x5,0x5,0x5,0x5,0x5,0x5,0x5,0x5,0xd,0xd,0xd,0xd,0xd,0xd,0xd,0xd,
|
||||
0x6,0x6,0x6,0x6,0x6,0x6,0x6,0x6,0xe,0xe,0xe,0xe,0xe,0xe,0xe,0xe,
|
||||
0x7,0x7,0x7,0x7,0x7,0x7,0x7,0x7,0xf,0xf,0xf,0xf,0xf,0xf,0xf,0xf,
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -506,43 +506,45 @@ static void _gldriver_setup_hackarounds(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
regex_t qualcommRegex = { 0 };
|
||||
regex_t adrenoRegex = { 0 };
|
||||
regex_t twoHundredRegex = { 0 };
|
||||
regex_t twoHundredFiveRegex = { 0 };
|
||||
|
||||
do {
|
||||
// As if we didn't have enough problems with Android ... Bionic's POSIX Regex support for android-10 appears
|
||||
// very basic ... we can't match the word-boundary atomics \> \< \b ... sigh ... hopefully by the time there is
|
||||
// an Adreno 2000 we can remove these hackarounds ;-)
|
||||
|
||||
regex_t qualcommRegex = { 0 };
|
||||
int err = regcomp(&qualcommRegex, "qualcomm", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
||||
if (err) {
|
||||
LOG("Cannot compile regex : %d", err);
|
||||
break;
|
||||
}
|
||||
int nomatch = regexec(&qualcommRegex, vendor, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
||||
regfree(&qualcommRegex);
|
||||
if (nomatch) {
|
||||
LOG("NO MATCH QUALCOMM >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
|
||||
break;
|
||||
}
|
||||
|
||||
regex_t adrenoRegex = { 0 };
|
||||
err = regcomp(&adrenoRegex, "adreno", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
||||
if (err) {
|
||||
LOG("Cannot compile regex : %d", err);
|
||||
break;
|
||||
}
|
||||
nomatch = regexec(&adrenoRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
||||
regfree(&adrenoRegex);
|
||||
if (nomatch) {
|
||||
LOG("NO MATCH ADRENO >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
|
||||
break;
|
||||
}
|
||||
|
||||
regex_t twoHundredRegex = { 0 };
|
||||
err = regcomp(&twoHundredRegex, "200", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
||||
if (err) {
|
||||
LOG("Cannot compile regex : %d", err);
|
||||
break;
|
||||
}
|
||||
|
||||
regex_t twoHundredFiveRegex = { 0 };
|
||||
err = regcomp(&twoHundredFiveRegex, "205", REG_ICASE|REG_NOSUB|REG_EXTENDED);
|
||||
if (err) {
|
||||
LOG("Cannot compile regex : %d", err);
|
||||
@@ -551,6 +553,9 @@ static void _gldriver_setup_hackarounds(void) {
|
||||
|
||||
int found200 = !regexec(&twoHundredRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
||||
int found205 = !regexec(&twoHundredFiveRegex, renderer, /*nmatch:*/0, /*pmatch:*/NULL, /*eflags:*/0);
|
||||
regfree(&twoHundredRegex);
|
||||
regfree(&twoHundredFiveRegex);
|
||||
|
||||
if (found200) {
|
||||
LOG("HACKING AROUND BROKEN ADRENO 200");
|
||||
hackAroundBrokenAdreno200 = true;
|
||||
@@ -563,11 +568,6 @@ static void _gldriver_setup_hackarounds(void) {
|
||||
break;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
regfree(&qualcommRegex);
|
||||
regfree(&adrenoRegex);
|
||||
regfree(&twoHundredRegex);
|
||||
regfree(&twoHundredFiveRegex);
|
||||
}
|
||||
|
||||
static void gldriver_init_common(void) {
|
||||
|
||||
Reference in New Issue
Block a user