mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-24 11:29:33 +00:00
Refactor Android variant to use common shared JSON preferences
- Eliminates a whole bunch of boilerplate setter/getter code in various places - Accesses preferences directly from JSON data through custom json_parse.h API - Improves code locality for Java menus/settings
This commit is contained in:
parent
b59e1ca7ab
commit
db816ad502
|
@ -82,6 +82,12 @@ public abstract class Apple2AbstractMenu implements Apple2MenuView {
|
|||
|
||||
public String getSummary(final Apple2Activity activity);
|
||||
|
||||
public String getPrefDomain();
|
||||
|
||||
public String getPrefKey();
|
||||
|
||||
public Object getPrefDefault();
|
||||
|
||||
public View getView(final Apple2Activity activity, View convertView);
|
||||
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked);
|
||||
|
|
|
@ -42,7 +42,6 @@ public class Apple2Activity extends Activity {
|
|||
private static volatile boolean DEBUG_STRICT = false;
|
||||
|
||||
private Apple2View mView = null;
|
||||
private Runnable mGraphicsInitializedRunnable = null;
|
||||
private Apple2SplashScreen mSplashScreen = null;
|
||||
private Apple2MainMenu mMainMenu = null;
|
||||
private Apple2SettingsMenu mSettingsMenu = null;
|
||||
|
@ -78,18 +77,14 @@ public class Apple2Activity extends Activity {
|
|||
|
||||
private static native String nativeLoadState(String path);
|
||||
|
||||
private static native void nativeEmulationResume();
|
||||
private static native boolean nativeEmulationResume();
|
||||
|
||||
private static native void nativeEmulationPause();
|
||||
private static native boolean nativeEmulationPause();
|
||||
|
||||
private static native void nativeOnQuit();
|
||||
|
||||
private static native void nativeReboot();
|
||||
|
||||
private static native void nativeChooseDisk(String path, boolean driveA, boolean readOnly);
|
||||
|
||||
private static native void nativeEjectDisk(boolean driveA);
|
||||
|
||||
public final static boolean isNativeBarfed() {
|
||||
return sNativeBarfed;
|
||||
}
|
||||
|
@ -119,8 +114,6 @@ public class Apple2Activity extends Activity {
|
|||
setContentView(new View(this));
|
||||
}
|
||||
|
||||
Apple2Preferences.LANDSCAPE_MODE.load(this);
|
||||
|
||||
Apple2CrashHandler.getInstance().initializeAndSetCustomExceptionHandler(this);
|
||||
if (sNativeBarfed) {
|
||||
Log.e(TAG, "NATIVE BARFED...", sNativeBarfedThrowable);
|
||||
|
@ -132,20 +125,17 @@ public class Apple2Activity extends Activity {
|
|||
int stereoBufferSize = DevicePropertyCalculator.getRecommendedBufferSize(this, /*isStereo:*/true);
|
||||
Log.d(TAG, "Device sampleRate:" + sampleRate + " mono bufferSize:" + monoBufferSize + " stereo bufferSize:" + stereoBufferSize);
|
||||
|
||||
String dataDir = Apple2DisksMenu.getDataDir(this);
|
||||
String dataDir = Apple2Utils.getDataDir(this);
|
||||
nativeOnCreate(dataDir, sampleRate, monoBufferSize, stereoBufferSize);
|
||||
|
||||
final boolean firstTime = (Apple2Preferences.EMULATOR_VERSION.intValue(this) != BuildConfig.VERSION_CODE);
|
||||
if (firstTime) {
|
||||
// allow for primitive migrations as needed
|
||||
Apple2Preferences.EMULATOR_VERSION.saveInt(this, BuildConfig.VERSION_CODE);
|
||||
Log.v(TAG, "Triggering migration to Apple2ix version : " + BuildConfig.VERSION_NAME);
|
||||
// NOTE: ordering here is important!
|
||||
Apple2Preferences.load(this);
|
||||
final boolean firstTime = Apple2Preferences.migrate(this);
|
||||
Apple2VideoSettingsMenu.SETTINGS.applyLandscapeMode(this);
|
||||
Apple2Preferences.sync(this, null);
|
||||
|
||||
// HACK FIXME TODO 2016/01/24 REMOVE AFTER version 16 ships ... force enables Mockingboard
|
||||
if (BuildConfig.VERSION_CODE == 16) {
|
||||
Apple2Preferences.MOCKINGBOARD_ENABLED.saveBoolean(this, true);
|
||||
}
|
||||
}
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A), /*driveA:*/true, (boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO));
|
||||
Apple2DisksMenu.insertDisk((String) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B), /*driveA:*/false, (boolean) Apple2Preferences.getJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_RO));
|
||||
|
||||
showSplashScreen(!firstTime);
|
||||
Apple2CrashHandler.getInstance().checkForCrashes(this);
|
||||
|
@ -171,25 +161,15 @@ public class Apple2Activity extends Activity {
|
|||
}
|
||||
}
|
||||
|
||||
mGraphicsInitializedRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (firstTime) {
|
||||
Apple2Preferences.KeypadPreset.IJKM_SPACE.apply(Apple2Activity.this);
|
||||
}
|
||||
Apple2Preferences.loadPreferences(Apple2Activity.this);
|
||||
}
|
||||
};
|
||||
|
||||
// first-time initializations
|
||||
final boolean externalStoragePermission = extperm;
|
||||
if (firstTime) {
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Apple2DisksMenu.exposeAPKAssets(Apple2Activity.this);
|
||||
Apple2Utils.exposeAPKAssets(Apple2Activity.this);
|
||||
if (externalStoragePermission) {
|
||||
Apple2DisksMenu.exposeAPKAssetsToExternal(Apple2Activity.this);
|
||||
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
|
||||
}
|
||||
mSplashScreen.setDismissable(true);
|
||||
Log.d(TAG, "Finished first time copying...");
|
||||
|
@ -226,7 +206,7 @@ public class Apple2Activity extends Activity {
|
|||
}
|
||||
if (grantedPermissions) {
|
||||
// this will force copying APK files (now that we have permission
|
||||
Apple2DisksMenu.exposeAPKAssetsToExternal(Apple2Activity.this);
|
||||
Apple2Utils.exposeAPKAssetsToExternal(Apple2Activity.this);
|
||||
} // else ... we keep nagging on app startup ...
|
||||
} else {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
|
@ -258,6 +238,12 @@ public class Apple2Activity extends Activity {
|
|||
return;
|
||||
}
|
||||
|
||||
if (isEmulationPaused()) {
|
||||
Apple2Preferences.save(this);
|
||||
} else {
|
||||
Log.d(TAG, "Letting native save preferences...");
|
||||
}
|
||||
|
||||
Log.d(TAG, "onPause()");
|
||||
if (mView != null) {
|
||||
mView.onPause();
|
||||
|
@ -347,19 +333,20 @@ public class Apple2Activity extends Activity {
|
|||
return;
|
||||
}
|
||||
|
||||
Apple2Preferences.CURRENT_DISK_A_RO.saveBoolean(Apple2Activity.this, true);
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO, true);
|
||||
final int len = diskPath.length();
|
||||
final String suffix = diskPath.substring(len - 3, len);
|
||||
if (suffix.equalsIgnoreCase(".gz")) { // HACK FIXME TODO : small amount of code duplication of Apple2DisksMenu
|
||||
diskPath = diskPath.substring(0, len - 3);
|
||||
}
|
||||
Apple2Preferences.CURRENT_DISK_A.saveString(Apple2Activity.this, diskPath);
|
||||
|
||||
Apple2DisksMenu.insertDisk(diskPath, /*driveA:*/true, /*readOnly:*/true);
|
||||
|
||||
while (mDisksMenu.popPathStack() != null) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
File storageDir = Apple2DisksMenu.getExternalStorageDirectory(Apple2Activity.this);
|
||||
File storageDir = Apple2Utils.getExternalStorageDirectory(Apple2Activity.this);
|
||||
if (storageDir != null) {
|
||||
String storagePath = storageDir.getAbsolutePath();
|
||||
if (diskPath.contains(storagePath)) {
|
||||
|
@ -402,8 +389,7 @@ public class Apple2Activity extends Activity {
|
|||
boolean glViewFirstTime = false;
|
||||
if (mView == null) {
|
||||
glViewFirstTime = true;
|
||||
mView = new Apple2View(this, mGraphicsInitializedRunnable);
|
||||
mGraphicsInitializedRunnable = null;
|
||||
mView = new Apple2View(this);
|
||||
mMainMenu = new Apple2MainMenu(this, mView);
|
||||
}
|
||||
|
||||
|
@ -424,7 +410,7 @@ public class Apple2Activity extends Activity {
|
|||
//
|
||||
mMenuStack.add(apple2MenuView);
|
||||
View menuView = apple2MenuView.getView();
|
||||
nativeEmulationPause();
|
||||
pauseEmulation();
|
||||
addContentView(menuView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
|
@ -498,7 +484,7 @@ public class Apple2Activity extends Activity {
|
|||
if (dismissedSplashScreen) {
|
||||
setupGLView();
|
||||
} else {
|
||||
nativeEmulationResume();
|
||||
maybeResumeEmulation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -512,12 +498,19 @@ public class Apple2Activity extends Activity {
|
|||
|
||||
public void maybeResumeEmulation() {
|
||||
if (mMenuStack.size() == 0 && !mPausing.get()) {
|
||||
nativeEmulationResume();
|
||||
Apple2Preferences.sync(this, null);
|
||||
boolean previouslyPaused = nativeEmulationResume();
|
||||
if (BuildConfig.DEBUG && !previouslyPaused) {
|
||||
throw new RuntimeException("expecting native CPU thread to have been paused");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void pauseEmulation() {
|
||||
nativeEmulationPause();
|
||||
boolean previouslyRunning = nativeEmulationPause();
|
||||
if (previouslyRunning) {
|
||||
Apple2Preferences.load(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void rebootEmulation() {
|
||||
|
@ -532,14 +525,6 @@ public class Apple2Activity extends Activity {
|
|||
return Apple2Activity.nativeLoadState(stateFile);
|
||||
}
|
||||
|
||||
public void chooseDisk(String path, boolean driveA, boolean readOnly) {
|
||||
nativeChooseDisk(path, driveA, readOnly);
|
||||
}
|
||||
|
||||
public void ejectDisk(boolean driveA) {
|
||||
nativeEjectDisk(driveA);
|
||||
}
|
||||
|
||||
public void quitEmulator() {
|
||||
nativeOnQuit();
|
||||
finish();
|
||||
|
|
|
@ -17,13 +17,19 @@ import android.widget.CompoundButton;
|
|||
import android.widget.TextView;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
||||
|
||||
private final static String TAG = "Apple2AudioSettingsMenu";
|
||||
|
||||
private final static int AUDIO_LATENCY_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
|
||||
|
||||
private static int sSampleRateCanary = 0;
|
||||
|
||||
public Apple2AudioSettingsMenu(Apple2Activity activity) {
|
||||
super(activity);
|
||||
sSampleRateCanary = DevicePropertyCalculator.getRecommendedSampleRate(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,6 +55,27 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
return position == SETTINGS.MOCKINGBOARD_ENABLED.ordinal();
|
||||
}
|
||||
|
||||
public enum Volume {
|
||||
OFF(0),
|
||||
ONE(1),
|
||||
TWO(2),
|
||||
THREE(3),
|
||||
FOUR(4),
|
||||
MEDIUM(5),
|
||||
FIVE(5),
|
||||
SIX(6),
|
||||
SEVEN(7),
|
||||
EIGHT(8),
|
||||
NINE(9),
|
||||
MAX(10),
|
||||
ELEVEN(11); // Ours goes to eleven...
|
||||
private int vol;
|
||||
|
||||
Volume(int vol) {
|
||||
this.vol = vol;
|
||||
}
|
||||
}
|
||||
|
||||
enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
SPEAKER_VOLUME {
|
||||
@Override
|
||||
|
@ -61,17 +88,28 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.speaker_volume_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "speakerVolume";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return Volume.MEDIUM.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.Volume.MAX.ordinal() - 1, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, Volume.MAX.ordinal() - 1, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.SPEAKER_VOLUME.saveVolume(activity, Apple2Preferences.Volume.values()[progress]);
|
||||
Apple2Preferences.setJSONPref(self, Volume.values()[progress].ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.SPEAKER_VOLUME.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -92,14 +130,25 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.mockingboard_enable_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "mbEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.MOCKINGBOARD_ENABLED.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.MOCKINGBOARD_ENABLED.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -116,17 +165,28 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.mockingboard_volume_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "mbVolume";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return Volume.MEDIUM.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.Volume.MAX.ordinal() - 1, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, Volume.MAX.ordinal() - 1, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.MOCKINGBOARD_VOLUME.saveVolume(activity, Apple2Preferences.Volume.values()[progress]);
|
||||
Apple2Preferences.setJSONPref(self, Volume.values()[progress].ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.MOCKINGBOARD_VOLUME.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -158,26 +218,46 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.audio_latency_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "audioLatency";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
int defaultLatency;
|
||||
if (Apple2AudioSettingsMenu.sSampleRateCanary == DevicePropertyCalculator.defaultSampleRate) {
|
||||
// quite possibly an audio-challenged device
|
||||
defaultLatency = 8; // /AUDIO_LATENCY_NUM_CHOICES -> 0.4f
|
||||
} else {
|
||||
// reasonable default for high-end devices
|
||||
defaultLatency = 5; // /AUDIO_LATENCY_NUM_CHOICES -> 0.25f
|
||||
}
|
||||
return ((float) defaultLatency) / AUDIO_LATENCY_NUM_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.AUDIO_LATENCY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, AUDIO_LATENCY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
if (progress == 0) {
|
||||
// disallow 0-length buffer ...
|
||||
progress = 1;
|
||||
}
|
||||
Apple2Preferences.AUDIO_LATENCY.saveInt(activity, progress);
|
||||
Apple2Preferences.setJSONPref(self, ((float) progress) / AUDIO_LATENCY_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.AUDIO_LATENCY.intValue(activity);
|
||||
float pref = Apple2Preferences.getFloatJSONPref(self);
|
||||
return (int) (pref * AUDIO_LATENCY_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showValue(int progress, final TextView seekBarValue) {
|
||||
seekBarValue.setText("" + ((float) progress / Apple2Preferences.AUDIO_LATENCY_NUM_CHOICES));
|
||||
seekBarValue.setText("" + ((float) progress / AUDIO_LATENCY_NUM_CHOICES));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -185,9 +265,23 @@ public class Apple2AudioSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_AUDIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -21,16 +21,13 @@ import android.os.Build;
|
|||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.BuildConfig;
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
|
@ -45,6 +42,67 @@ public class Apple2CrashHandler {
|
|||
return sCrashHandler;
|
||||
}
|
||||
|
||||
public enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
GL_VENDOR {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "glVendor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return "unknown";
|
||||
}
|
||||
},
|
||||
GL_RENDERER {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "glRenderer";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return "unknown";
|
||||
}
|
||||
},
|
||||
GL_VERSION {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "glVersion";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return "unknown";
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public final String getTitle(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getSummary(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_INTERFACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CrashType {
|
||||
JAVA_CRASH {
|
||||
@Override
|
||||
|
@ -89,7 +147,7 @@ public class Apple2CrashHandler {
|
|||
public synchronized void initializeAndSetCustomExceptionHandler(Apple2Activity activity) {
|
||||
synchronized (this) {
|
||||
if (homeDir == null) {
|
||||
homeDir = Apple2DisksMenu.getDataDir(activity);
|
||||
homeDir = Apple2Utils.getDataDir(activity);
|
||||
}
|
||||
}
|
||||
if (mDefaultExceptionHandler != null) {
|
||||
|
@ -137,7 +195,7 @@ public class Apple2CrashHandler {
|
|||
return;
|
||||
}
|
||||
|
||||
if (!Apple2Preferences.CRASH_CHECK.booleanValue(activity)) {
|
||||
if (!(boolean) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CRASH)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -169,7 +227,9 @@ public class Apple2CrashHandler {
|
|||
}
|
||||
|
||||
// remove previous log file
|
||||
_writeTempLogFile(activity, new StringBuilder());
|
||||
File allCrashFile = _getCrashFile(activity);
|
||||
Apple2Utils.writeFile(new StringBuilder(), allCrashFile);
|
||||
allCrashFile.delete();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -216,9 +276,9 @@ public class Apple2CrashHandler {
|
|||
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");
|
||||
summary.append("GPU VENDOR: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VENDOR)).append("\n");
|
||||
summary.append("GPU RENDERER: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_RENDERER)).append("\n");
|
||||
summary.append("GPU VERSION: ").append(Apple2Preferences.getJSONPref(SETTINGS.GL_VERSION)).append("\n");
|
||||
|
||||
try {
|
||||
PackageInfo pInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), 0);
|
||||
|
@ -246,7 +306,7 @@ public class Apple2CrashHandler {
|
|||
});
|
||||
|
||||
if (len > 0) {
|
||||
Apple2DisksMenu.exposeSymbols(activity);
|
||||
Apple2Utils.exposeSymbols(activity);
|
||||
}
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
|
@ -274,7 +334,7 @@ public class Apple2CrashHandler {
|
|||
}
|
||||
|
||||
StringBuilder crashData = new StringBuilder();
|
||||
if (!_readFile(new File(processedPath), crashData)) {
|
||||
if (!Apple2Utils.readEntireFile(new File(processedPath), crashData)) {
|
||||
Log.e(TAG, "Error processing crash : " + crashPath);
|
||||
}
|
||||
allCrashData.append(">>>>>>> NATIVE CRASH [").append(crashPath).append("]\n");
|
||||
|
@ -336,7 +396,7 @@ public class Apple2CrashHandler {
|
|||
File javaCrashFile = _javaCrashFile(activity);
|
||||
if (javaCrashFile.exists()) {
|
||||
Log.d(TAG, "Reading java crashes file");
|
||||
if (!_readFile(javaCrashFile, javaCrashData)) {
|
||||
if (!Apple2Utils.readEntireFile(javaCrashFile, javaCrashData)) {
|
||||
Log.e(TAG, "Error processing java crash : " + javaCrashFileName);
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +416,7 @@ public class Apple2CrashHandler {
|
|||
}
|
||||
});
|
||||
|
||||
Apple2DisksMenu.unexposeSymbols(activity);
|
||||
Apple2Utils.unexposeSymbols(activity);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -465,76 +525,15 @@ public class Apple2CrashHandler {
|
|||
return crashPath.substring(0, crashPath.length() - 4) + ".txt";
|
||||
}
|
||||
|
||||
private boolean _readFile(File file, StringBuilder fileData) {
|
||||
final int maxAttempts = 5;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
char[] buf = new char[1024];
|
||||
int numRead = 0;
|
||||
while ((numRead = reader.read(buf)) != -1) {
|
||||
String readData = String.valueOf(buf, 0, numRead);
|
||||
fileData.append(readData);
|
||||
}
|
||||
reader.close();
|
||||
break;
|
||||
} catch (InterruptedIOException ie) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Error reading file at path : " + file.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException e) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
return attempts < maxAttempts;
|
||||
}
|
||||
|
||||
private File _writeTempLogFile(Apple2Activity activity, StringBuilder allCrashData) {
|
||||
|
||||
File allCrashFile = null;
|
||||
|
||||
private File _getCrashFile(Apple2Activity activity) {
|
||||
File allCrashFile;
|
||||
String storageState = Environment.getExternalStorageState();
|
||||
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
|
||||
allCrashFile = new File(Environment.getExternalStorageDirectory(), "apple2ix_crash.txt");
|
||||
} else {
|
||||
allCrashFile = new File(Apple2DisksMenu.getDataDir(activity), "apple2ix_crash.txt");
|
||||
allCrashFile = new File(Apple2Utils.getDataDir(activity), "apple2ix_crash.txt");
|
||||
}
|
||||
|
||||
Log.d(TAG, "Writing all crashes to temp file : " + allCrashFile.getAbsolutePath());
|
||||
final int maxAttempts = 5;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(allCrashFile));
|
||||
writer.append(allCrashData);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
break;
|
||||
} catch (InterruptedIOException ie) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception attempting to write data : " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException e) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) {
|
||||
Log.d(TAG, "Oops, could not set all crash data readable!");
|
||||
}
|
||||
|
||||
return allCrashFile;
|
||||
}
|
||||
|
||||
|
@ -553,9 +552,14 @@ public class Apple2CrashHandler {
|
|||
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);
|
||||
emailIntent.putExtra(Intent.EXTRA_TEXT, "The app crashed, please help!\n\n" + summaryData);
|
||||
|
||||
File allCrashFile = _getCrashFile(activity);
|
||||
Apple2Utils.writeFile(allCrashData, allCrashFile);
|
||||
if (!allCrashFile.setReadable(true, /*ownerOnly:*/false)) {
|
||||
Log.d(TAG, "Oops, could not set file data readable!");
|
||||
}
|
||||
|
||||
File allCrashFile = _writeTempLogFile(activity, allCrashData);
|
||||
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(allCrashFile));
|
||||
|
||||
Log.d(TAG, "STARTING CHOOSER FOR EMAIL ...");
|
||||
|
|
|
@ -14,11 +14,7 @@ package org.deadc0de.apple2ix;
|
|||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -30,39 +26,138 @@ import android.widget.CompoundButton;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
|
||||
public class Apple2DisksMenu implements Apple2MenuView {
|
||||
|
||||
private final static String TAG = "Apple2DisksMenu";
|
||||
private static String sDataDir = null;
|
||||
|
||||
private Apple2Activity mActivity = null;
|
||||
private View mDisksView = null;
|
||||
|
||||
private final ArrayList<String> mPathStack = new ArrayList<String>();
|
||||
|
||||
private static File sExternalFilesDir = null;
|
||||
private static File sDownloadFilesDir = null;
|
||||
private static boolean sInitializedPath = false;
|
||||
|
||||
private static native void nativeChooseDisk(String path, boolean driveA, boolean readOnly);
|
||||
|
||||
private static native void nativeEjectDisk(boolean driveA);
|
||||
|
||||
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
CURRENT_DISK_SEARCH_PATH {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "diskPathStack";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return new JSONArray();
|
||||
}
|
||||
},
|
||||
CURRENT_DRIVE_A {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveACurrent";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_RO_BUTTON {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveACurrent";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_A {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveAInsertedDisk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_A_RO {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveAInsertedDiskRO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_B {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveBInsertedDisk";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return "";
|
||||
}
|
||||
},
|
||||
CURRENT_DISK_PATH_B_RO {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "driveBInsertedDiskRO";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public final String getTitle(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getSummary(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_VM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
public Apple2DisksMenu(Apple2Activity activity) {
|
||||
mActivity = activity;
|
||||
|
||||
|
@ -77,150 +172,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
}
|
||||
});
|
||||
|
||||
getExternalStorageDirectory(activity);
|
||||
}
|
||||
|
||||
public static File getExternalStorageDirectory(Apple2Activity activity) {
|
||||
|
||||
do {
|
||||
if (sExternalFilesDir != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
String storageState = Environment.getExternalStorageState();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sExternalFilesDir = externalDir;
|
||||
sDownloadFilesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
} while (false);
|
||||
|
||||
return sExternalFilesDir;
|
||||
}
|
||||
|
||||
// HACK NOTE 2015/02/22 : Apparently native code cannot easily access stuff in the APK ... so copy various resources
|
||||
// out of the APK and into the /data/data/... for ease of access. Because this is FOSS software we don't care about
|
||||
// security or DRM for these assets =)
|
||||
public static String getDataDir(Apple2Activity activity) {
|
||||
|
||||
if (sDataDir != null) {
|
||||
return sDataDir;
|
||||
}
|
||||
|
||||
try {
|
||||
PackageManager pm = activity.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(activity.getPackageName(), 0);
|
||||
sDataDir = pi.applicationInfo.dataDir;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "" + e);
|
||||
if (sDataDir == null) {
|
||||
sDataDir = "/data/local/tmp";
|
||||
}
|
||||
}
|
||||
|
||||
return sDataDir;
|
||||
}
|
||||
|
||||
public static void exposeAPKAssetsToExternal(Apple2Activity activity) {
|
||||
getExternalStorageDirectory(activity);
|
||||
if (sExternalFilesDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.VISIBLE);
|
||||
bar.setIndeterminate(true);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Log.v(TAG, "Overwriting system files in /sdcard/apple2ix/ (external storage) ...");
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath(), false);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.INVISIBLE);
|
||||
bar.setIndeterminate(false);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #2");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void exposeAPKAssets(Apple2Activity activity) {
|
||||
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.VISIBLE);
|
||||
bar.setIndeterminate(true);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getDataDir(activity);
|
||||
|
||||
// FIXME TODO : Heavy-handed migration to 1.1.3 ...
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "blanks"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "demo"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "eamon"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame"));
|
||||
|
||||
Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
|
||||
|
||||
getExternalStorageDirectory(activity);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/new File(sDataDir, "keyboards").getAbsolutePath(), false);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath(), false);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.INVISIBLE);
|
||||
bar.setIndeterminate(false);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void exposeSymbols(Apple2Activity activity) {
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"symbols", /*to location:*/new File(sDataDir, "symbols").getAbsolutePath(), false);
|
||||
}
|
||||
|
||||
public static void unexposeSymbols(Apple2Activity activity) {
|
||||
recursivelyDelete(new File(sDataDir, "symbols"));
|
||||
Apple2Utils.getExternalStorageDirectory(activity);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
@ -240,7 +192,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
}
|
||||
if (!sInitializedPath) {
|
||||
sInitializedPath = true;
|
||||
Apple2Preferences.CURRENT_DISK_PATH.load(mActivity);
|
||||
setPathStackJSON((JSONArray) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_SEARCH_PATH));
|
||||
}
|
||||
dynamicSetup();
|
||||
mActivity.pushApple2View(this);
|
||||
|
@ -272,15 +224,13 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
// ------------------------------------------------------------------------
|
||||
// path stack methods
|
||||
|
||||
public String getPathStackJSON() {
|
||||
JSONArray jsonArray = new JSONArray(Arrays.asList(mPathStack.toArray()));
|
||||
return jsonArray.toString();
|
||||
public JSONArray getPathStackJSON() {
|
||||
return new JSONArray(Arrays.asList(mPathStack.toArray()));
|
||||
}
|
||||
|
||||
public void setPathStackJSON(String pathStackJSON) {
|
||||
public void setPathStackJSON(JSONArray jsonArray) {
|
||||
mPathStack.clear();
|
||||
try {
|
||||
JSONArray jsonArray = new JSONArray(pathStackJSON);
|
||||
for (int i = 0, count = jsonArray.length(); i < count; i++) {
|
||||
String pathComponent = jsonArray.getString(i);
|
||||
mPathStack.add(pathComponent);
|
||||
|
@ -292,7 +242,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
|
||||
public void pushPathStack(String path) {
|
||||
mPathStack.add(path);
|
||||
Apple2Preferences.CURRENT_DISK_PATH.saveString(mActivity, getPathStackJSON());
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_SEARCH_PATH, getPathStackJSON());
|
||||
}
|
||||
|
||||
public String popPathStack() {
|
||||
|
@ -300,10 +250,32 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return null;
|
||||
}
|
||||
String path = mPathStack.remove(mPathStack.size() - 1);
|
||||
Apple2Preferences.CURRENT_DISK_PATH.saveString(mActivity, getPathStackJSON());
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_SEARCH_PATH, getPathStackJSON());
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void insertDisk(String fullPath, boolean isDriveA, boolean isReadOnly) {
|
||||
File file = new File(fullPath);
|
||||
final String imageName = fullPath;
|
||||
if (!file.exists()) {
|
||||
fullPath = fullPath + ".gz";
|
||||
file = new File(fullPath);
|
||||
}
|
||||
if (file.exists()) {
|
||||
if (isDriveA) {
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_A_RO, isReadOnly);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_A, imageName);
|
||||
} else {
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_B_RO, isReadOnly);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_B, imageName);
|
||||
}
|
||||
nativeChooseDisk(fullPath, isDriveA, isReadOnly);
|
||||
} else {
|
||||
Log.d(TAG, "Cannot insert: " + fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean hasDiskExtension(String name) {
|
||||
|
||||
// check file extensions ... sigh ... no String.endsWithIgnoreCase() ?
|
||||
|
@ -360,119 +332,6 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
return pathBuffer.toString();
|
||||
}
|
||||
|
||||
// TODO FIXME : WARNING : this is super dangerous if there are symlinks !!!
|
||||
private static void recursivelyDelete(File file) {
|
||||
if (file.isDirectory()) {
|
||||
for (File f : file.listFiles()) {
|
||||
recursivelyDelete(f);
|
||||
}
|
||||
}
|
||||
if (!file.delete()) {
|
||||
Log.d(TAG, "Failed to delete file: " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void recursivelyCopyAPKAssets(Apple2Activity activity, String srcFileOrDir, String dstFileOrDir, boolean shouldGzip) {
|
||||
AssetManager assetManager = activity.getAssets();
|
||||
|
||||
final int maxAttempts = 5;
|
||||
String[] files = null;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
files = assetManager.list(srcFileOrDir);
|
||||
break;
|
||||
} catch (InterruptedIOException e) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
if (files == null) {
|
||||
Log.d(TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
// ensure destination directory exists
|
||||
File dstPath = new File(dstFileOrDir);
|
||||
if (!dstPath.mkdirs()) {
|
||||
if (!dstPath.exists()) {
|
||||
Log.d(TAG, "OOPS, could not mkdirs on " + dstPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (String filename : files) {
|
||||
// iterate on files and subdirectories
|
||||
recursivelyCopyAPKAssets(activity, srcFileOrDir + File.separator + filename, dstFileOrDir + File.separator + filename, shouldGzip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// presumably this is a file, not a subdirectory
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
attempts = 0;
|
||||
do {
|
||||
try {
|
||||
is = assetManager.open(srcFileOrDir);
|
||||
if (shouldGzip) {
|
||||
os = new GZIPOutputStream(new FileOutputStream(dstFileOrDir + ".gz"));
|
||||
} else {
|
||||
os = new FileOutputStream(dstFileOrDir);
|
||||
}
|
||||
copyFile(is, os);
|
||||
break;
|
||||
} catch (InterruptedIOException e) {
|
||||
/* EINTR, EAGAIN */
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to copy asset file: " + srcFileOrDir, e);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
}
|
||||
|
||||
private static void copyFile(InputStream is, OutputStream os) throws IOException {
|
||||
final int BUF_SZ = 4096;
|
||||
byte[] buf = new byte[BUF_SZ];
|
||||
while (true) {
|
||||
int len = is.read(buf, 0, BUF_SZ);
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
|
||||
private void dynamicSetup() {
|
||||
|
||||
final ListView disksList = (ListView) mDisksView.findViewById(R.id.listView_settings);
|
||||
|
@ -482,7 +341,7 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
boolean isRootPath = false;
|
||||
if (disksDir == null) {
|
||||
isRootPath = true;
|
||||
disksDir = sDataDir + File.separator + "disks"; // default path
|
||||
disksDir = Apple2Utils.getDataDir(mActivity) + File.separator + "disks"; // default path
|
||||
}
|
||||
|
||||
File dir = new File(disksDir);
|
||||
|
@ -511,21 +370,22 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
|
||||
Arrays.sort(files);
|
||||
|
||||
getExternalStorageDirectory(mActivity);
|
||||
final boolean includeExternalStoragePath = (sExternalFilesDir != null && isRootPath);
|
||||
final boolean includeDownloadsPath = (sDownloadFilesDir != null && isRootPath);
|
||||
File extStorageDir = Apple2Utils.getExternalStorageDirectory(mActivity);
|
||||
File downloadsDir = Apple2Utils.getDownloadsDirectory(mActivity);
|
||||
final boolean includeExternalStoragePath = (extStorageDir != null && isRootPath);
|
||||
final boolean includeDownloadsPath = (downloadsDir != null && isRootPath);
|
||||
final int offset = includeExternalStoragePath ? (includeDownloadsPath ? 2 : 1) : (includeDownloadsPath ? 1 : 0);
|
||||
final String[] fileNames = new String[files.length + offset];
|
||||
final boolean[] isDirectory = new boolean[files.length + offset];
|
||||
|
||||
int idx = 0;
|
||||
if (includeExternalStoragePath) {
|
||||
fileNames[idx] = sExternalFilesDir.getAbsolutePath();
|
||||
fileNames[idx] = Apple2Utils.getExternalStorageDirectory(mActivity).getAbsolutePath();
|
||||
isDirectory[idx] = true;
|
||||
++idx;
|
||||
}
|
||||
if (includeDownloadsPath) {
|
||||
fileNames[idx] = sDownloadFilesDir.getAbsolutePath();
|
||||
fileNames[idx] = downloadsDir.getAbsolutePath();
|
||||
isDirectory[idx] = true;
|
||||
++idx;
|
||||
}
|
||||
|
@ -570,26 +430,26 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
}
|
||||
|
||||
String eject = mActivity.getResources().getString(R.string.disk_eject);
|
||||
if (imageName.equals(Apple2Preferences.CURRENT_DISK_A.stringValue(mActivity))) {
|
||||
if (imageName.equals((String) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_A))) {
|
||||
Button ejectButton = new Button(mActivity);
|
||||
ejectButton.setText(eject + " 1");
|
||||
ejectButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mActivity.ejectDisk(/*driveA:*/true);
|
||||
Apple2Preferences.CURRENT_DISK_A.saveString(mActivity, "");
|
||||
nativeEjectDisk(/*driveA:*/true);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_A, "");
|
||||
dynamicSetup();
|
||||
}
|
||||
});
|
||||
layout.addView(ejectButton);
|
||||
} else if (imageName.equals(Apple2Preferences.CURRENT_DISK_B.stringValue(mActivity))) {
|
||||
} else if (imageName.equals((String) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_B))) {
|
||||
Button ejectButton = new Button(mActivity);
|
||||
ejectButton.setText(eject + " 2");
|
||||
ejectButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
mActivity.ejectDisk(/*driveA:*/false);
|
||||
Apple2Preferences.CURRENT_DISK_B.saveString(mActivity, "");
|
||||
nativeEjectDisk(/*driveA:*/false);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_B, "");
|
||||
dynamicSetup();
|
||||
}
|
||||
});
|
||||
|
@ -629,15 +489,15 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
}
|
||||
final String imageName = str;
|
||||
|
||||
if (imageName.equals(Apple2Preferences.CURRENT_DISK_A.stringValue(mActivity))) {
|
||||
mActivity.ejectDisk(/*driveA:*/true);
|
||||
Apple2Preferences.CURRENT_DISK_A.saveString(mActivity, "");
|
||||
if (imageName.equals((String) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_A))) {
|
||||
nativeEjectDisk(/*driveA:*/true);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_A, "");
|
||||
dynamicSetup();
|
||||
return;
|
||||
}
|
||||
if (imageName.equals(Apple2Preferences.CURRENT_DISK_B.stringValue(mActivity))) {
|
||||
mActivity.ejectDisk(/*driveA:*/false);
|
||||
Apple2Preferences.CURRENT_DISK_B.saveString(mActivity, "");
|
||||
if (imageName.equals((String) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_PATH_B))) {
|
||||
nativeEjectDisk(/*driveA:*/false);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_PATH_B, "");
|
||||
dynamicSetup();
|
||||
return;
|
||||
}
|
||||
|
@ -650,29 +510,31 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
final View diskConfirmationView = inflater.inflate(R.layout.a2disk_confirmation, null, false);
|
||||
builder.setView(diskConfirmationView);
|
||||
|
||||
final RadioButton diskA = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_diskA);
|
||||
diskA.setChecked(Apple2Preferences.CURRENT_DRIVE_A_BUTTON.booleanValue(mActivity));
|
||||
diskA.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
final RadioButton driveA = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_diskA);
|
||||
boolean driveAChecked = (boolean) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DRIVE_A);
|
||||
driveA.setChecked(driveAChecked);
|
||||
driveA.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.CURRENT_DRIVE_A_BUTTON.saveBoolean(mActivity, isChecked);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DRIVE_A, isChecked);
|
||||
}
|
||||
});
|
||||
final RadioButton diskB = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_diskB);
|
||||
diskB.setChecked(!Apple2Preferences.CURRENT_DRIVE_A_BUTTON.booleanValue(mActivity));
|
||||
final RadioButton driveB = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_diskB);
|
||||
driveB.setChecked(!driveAChecked);
|
||||
|
||||
|
||||
final RadioButton readOnly = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_readOnly);
|
||||
readOnly.setChecked(Apple2Preferences.CURRENT_DISK_RO_BUTTON.booleanValue(mActivity));
|
||||
boolean roChecked = (boolean) Apple2Preferences.getJSONPref(SETTINGS.CURRENT_DISK_RO_BUTTON);
|
||||
readOnly.setChecked(roChecked);
|
||||
readOnly.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.CURRENT_DISK_RO_BUTTON.saveBoolean(mActivity, isChecked);
|
||||
Apple2Preferences.setJSONPref(SETTINGS.CURRENT_DISK_RO_BUTTON, isChecked);
|
||||
}
|
||||
});
|
||||
|
||||
final RadioButton readWrite = (RadioButton) diskConfirmationView.findViewById(R.id.radioButton_readWrite);
|
||||
readWrite.setChecked(!Apple2Preferences.CURRENT_DISK_RO_BUTTON.booleanValue(mActivity));
|
||||
readWrite.setChecked(!roChecked);
|
||||
|
||||
builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
|
@ -683,14 +545,12 @@ public class Apple2DisksMenu implements Apple2MenuView {
|
|||
builder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
boolean isDriveA = diskA.isChecked();
|
||||
boolean isDriveA = driveA.isChecked();
|
||||
boolean diskReadOnly = readOnly.isChecked();
|
||||
if (isDriveA) {
|
||||
Apple2Preferences.CURRENT_DISK_A_RO.saveBoolean(mActivity, diskReadOnly);
|
||||
Apple2Preferences.CURRENT_DISK_A.saveString(mActivity, imageName);
|
||||
insertDisk(imageName, /*driveA:*/true, diskReadOnly);
|
||||
} else {
|
||||
Apple2Preferences.CURRENT_DISK_B_RO.saveBoolean(mActivity, diskReadOnly);
|
||||
Apple2Preferences.CURRENT_DISK_B.saveString(mActivity, imageName);
|
||||
insertDisk(imageName, /*driveA:*/false, diskReadOnly);
|
||||
}
|
||||
dialog.dismiss();
|
||||
mActivity.dismissAllMenus();
|
||||
|
|
|
@ -24,23 +24,26 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
|||
|
||||
private final static String TAG = "Apple2JoystickCalibration";
|
||||
|
||||
private final static int JOYSTICK_DIVIDER_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
|
||||
public final static String PREF_SCREEN_DIVISION = "screenDivider";
|
||||
|
||||
private Apple2Activity mActivity = null;
|
||||
private View mSettingsView = null;
|
||||
private ArrayList<Apple2MenuView> mViewStack = null;
|
||||
private boolean mTouchMenuEnabled = false;
|
||||
private int mSavedTouchDevice = Apple2Preferences.TouchDeviceVariant.NONE.ordinal();
|
||||
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
|
||||
|
||||
public Apple2JoystickCalibration(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack, Apple2Preferences.TouchDeviceVariant variant) {
|
||||
public Apple2JoystickCalibration(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack, Apple2SettingsMenu.TouchDeviceVariant variant) {
|
||||
mActivity = activity;
|
||||
mViewStack = viewStack;
|
||||
if (!(variant == Apple2Preferences.TouchDeviceVariant.JOYSTICK || variant == Apple2Preferences.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
|
||||
if (!(variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK || variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
|
||||
throw new RuntimeException("You're doing it wrong");
|
||||
}
|
||||
|
||||
setup(variant);
|
||||
}
|
||||
|
||||
private void setup(Apple2Preferences.TouchDeviceVariant variant) {
|
||||
private void setup(Apple2SettingsMenu.TouchDeviceVariant variant) {
|
||||
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mSettingsView = inflater.inflate(R.layout.activity_calibrate_joystick, null, false);
|
||||
|
||||
|
@ -51,7 +54,8 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
|||
if (!fromUser) {
|
||||
return;
|
||||
}
|
||||
Apple2Preferences.JOYSTICK_DIVIDER.saveInt(mActivity, progress);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_SCREEN_DIVISION, (float) progress / JOYSTICK_DIVIDER_NUM_CHOICES);
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_JOYSTICK);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -64,20 +68,17 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
|||
});
|
||||
|
||||
sb.setMax(0); // http://stackoverflow.com/questions/10278467/seekbar-not-setting-actual-progress-setprogress-not-working-on-early-android
|
||||
sb.setMax(Apple2Preferences.JOYSTICK_DIVIDER_NUM_CHOICES);
|
||||
sb.setProgress(Apple2Preferences.JOYSTICK_DIVIDER.intValue(mActivity));
|
||||
sb.setMax(JOYSTICK_DIVIDER_NUM_CHOICES);
|
||||
float val = Apple2Preferences.getFloatJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_SCREEN_DIVISION, (JOYSTICK_DIVIDER_NUM_CHOICES >> 1) / (float) JOYSTICK_DIVIDER_NUM_CHOICES);
|
||||
sb.setProgress((int) (val * JOYSTICK_DIVIDER_NUM_CHOICES));
|
||||
|
||||
mTouchMenuEnabled = Apple2Preferences.TOUCH_MENU_ENABLED.booleanValue(mActivity);
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(false);
|
||||
mSavedTouchDevice = Apple2Preferences.CURRENT_TOUCH_DEVICE.intValue(mActivity);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(variant.ordinal());
|
||||
if (variant == Apple2Preferences.TouchDeviceVariant.JOYSTICK) {
|
||||
Apple2Preferences.loadAllJoystickButtons(mActivity);
|
||||
} else {
|
||||
Apple2Preferences.loadAllKeypadKeys(mActivity);
|
||||
}
|
||||
mTouchMenuEnabled = (boolean) Apple2Preferences.getJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED);
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, false);
|
||||
mSavedTouchDevice = (int) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, variant.ordinal());
|
||||
|
||||
Apple2Preferences.nativeTouchDeviceBeginCalibrationMode();
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, true);
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
}
|
||||
|
||||
public final boolean isCalibrating() {
|
||||
|
@ -85,7 +86,6 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
|||
}
|
||||
|
||||
public void onKeyTapCalibrationEvent(char ascii, int scancode) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
public void show() {
|
||||
|
@ -102,9 +102,11 @@ public class Apple2JoystickCalibration implements Apple2MenuView {
|
|||
}
|
||||
}
|
||||
|
||||
Apple2Preferences.nativeTouchDeviceEndCalibrationMode();
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(mTouchMenuEnabled);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(mSavedTouchDevice);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, false);
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, mTouchMenuEnabled);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, mSavedTouchDevice);
|
||||
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
|
||||
mActivity.popApple2View(this);
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
package org.deadc0de.apple2ix;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
|
@ -24,6 +25,21 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
private final static String TAG = "Apple2JoystickSettingsMenu";
|
||||
|
||||
public final static int JOYSTICK_BUTTON_THRESHOLD_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
|
||||
|
||||
public final static float JOYSTICK_AXIS_SENSITIVITY_MIN = 0.25f;
|
||||
public final static float JOYSTICK_AXIS_SENSITIVITY_DEFAULT = 1.f;
|
||||
public final static float JOYSTICK_AXIS_SENSITIVITY_MAX = 4.f;
|
||||
public final static float JOYSTICK_AXIS_SENSITIVITY_DEC_STEP = 0.05f;
|
||||
public final static float JOYSTICK_AXIS_SENSITIVITY_INC_STEP = 0.25f;
|
||||
public final static int JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES = (int) ((JOYSTICK_AXIS_SENSITIVITY_DEFAULT - JOYSTICK_AXIS_SENSITIVITY_MIN) / JOYSTICK_AXIS_SENSITIVITY_DEC_STEP); // 15
|
||||
public final static int JOYSTICK_AXIS_SENSITIVITY_INC_NUMCHOICES = (int) ((JOYSTICK_AXIS_SENSITIVITY_MAX - JOYSTICK_AXIS_SENSITIVITY_DEFAULT) / JOYSTICK_AXIS_SENSITIVITY_INC_STEP); // 12
|
||||
public final static int JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES + JOYSTICK_AXIS_SENSITIVITY_INC_NUMCHOICES; // 15 + 12
|
||||
|
||||
public final static int TAPDELAY_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
|
||||
public final static float TAPDELAY_SCALE = 0.5f;
|
||||
|
||||
|
||||
public Apple2JoystickSettingsMenu(Apple2Activity activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
@ -51,6 +67,18 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return true;
|
||||
}
|
||||
|
||||
public enum TouchJoystickButtons {
|
||||
NONE(0),
|
||||
BUTTON1(1),
|
||||
BUTTON2(2),
|
||||
BOTH(3);
|
||||
private int butt;
|
||||
|
||||
TouchJoystickButtons(int butt) {
|
||||
this.butt = butt;
|
||||
}
|
||||
}
|
||||
|
||||
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
JOYSTICK_TAP_BUTTON {
|
||||
@Override
|
||||
|
@ -63,6 +91,16 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_button_tap_button_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "jsTouchDownChar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return TouchJoystickButtons.BUTTON1.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -72,6 +110,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
final IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, R.string.joystick_button_tap_button, new String[]{
|
||||
activity.getResources().getString(R.string.joystick_button_button_none),
|
||||
activity.getResources().getString(R.string.joystick_button_button1),
|
||||
|
@ -80,12 +119,12 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
}, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_TAP_BUTTON.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.JOYSTICK_TAP_BUTTON.saveTouchJoystickButtons(activity, Apple2Preferences.TouchJoystickButtons.values()[value]);
|
||||
Apple2Preferences.setJSONPref(self, TouchJoystickButtons.values()[value].ordinal());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -101,6 +140,16 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_button_swipe_up_button_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "jsSwipeNorthChar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return TouchJoystickButtons.BOTH.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -110,6 +159,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
final IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, R.string.joystick_button_swipe_up_button, new String[]{
|
||||
activity.getResources().getString(R.string.joystick_button_button_none),
|
||||
activity.getResources().getString(R.string.joystick_button_button1),
|
||||
|
@ -118,12 +168,12 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
}, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_SWIPEUP_BUTTON.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.JOYSTICK_SWIPEUP_BUTTON.saveTouchJoystickButtons(activity, Apple2Preferences.TouchJoystickButtons.values()[value]);
|
||||
Apple2Preferences.setJSONPref(self, TouchJoystickButtons.values()[value].ordinal());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -139,6 +189,16 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_button_swipe_down_button_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "jsSwipeSouthChar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return TouchJoystickButtons.BUTTON2.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -148,6 +208,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
final IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, R.string.joystick_button_swipe_down_button, new String[]{
|
||||
activity.getResources().getString(R.string.joystick_button_button_none),
|
||||
activity.getResources().getString(R.string.joystick_button_button1),
|
||||
|
@ -156,12 +217,12 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
}, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_SWIPEDOWN_BUTTON.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.JOYSTICK_SWIPEDOWN_BUTTON.saveTouchJoystickButtons(activity, Apple2Preferences.TouchJoystickButtons.values()[value]);
|
||||
Apple2Preferences.setJSONPref(self, TouchJoystickButtons.values()[value].ordinal());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -192,7 +253,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
|
||||
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2Preferences.TouchDeviceVariant.JOYSTICK);
|
||||
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK);
|
||||
|
||||
// show this new view...
|
||||
calibration.show();
|
||||
|
@ -214,22 +275,33 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_button_tapdelay_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "jsTapDelaySecs";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return ((float) 8 / TAPDELAY_NUM_CHOICES * TAPDELAY_SCALE); // -> 0.2f
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.JOYSTICK_TAPDELAY.saveInt(activity, progress);
|
||||
Apple2Preferences.setJSONPref(self, ((float) progress / TAPDELAY_NUM_CHOICES * TAPDELAY_SCALE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_TAPDELAY.intValue(activity);
|
||||
return (int) (Apple2Preferences.getFloatJSONPref(self) / TAPDELAY_SCALE * TAPDELAY_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showValue(int progress, final TextView seekBarValue) {
|
||||
seekBarValue.setText("" + (((float) progress / Apple2Preferences.TAPDELAY_NUM_CHOICES) * Apple2Preferences.TAPDELAY_SCALE));
|
||||
seekBarValue.setText("" + (((float) progress / TAPDELAY_NUM_CHOICES) * TAPDELAY_SCALE));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -253,9 +325,23 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_JOYSTICK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -316,14 +402,25 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_visible_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "showControls";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.JOYSTICK_VISIBILITY.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.JOYSTICK_VISIBILITY.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -340,14 +437,25 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_azimuth_visible_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "showAzimuth";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.JOYSTICK_AZIMUTH_VISIBILITY.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.JOYSTICK_AZIMUTH_VISIBILITY.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -364,20 +472,31 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_axisleft_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "axisIsOnLeft";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.JOYSTICK_AXIS_ON_LEFT.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.JOYSTICK_AXIS_ON_LEFT.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
}
|
||||
},
|
||||
JOYSTICK_AXIS_SENSITIVIY {
|
||||
JOYSTICK_AXIS_SENSITIVITY {
|
||||
@Override
|
||||
public final String getTitle(Apple2Activity activity) {
|
||||
return "";
|
||||
|
@ -388,23 +507,51 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_axis_sensitivity_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "axisSensitivity";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, JOYSTICK_AXIS_SENSITIVITY_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.JOYSTICK_AXIS_SENSITIVIY.saveInt(activity, progress);
|
||||
final int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
|
||||
float sensitivity = 1.f;
|
||||
if (progress < pivot) {
|
||||
int decAmount = (pivot - progress);
|
||||
sensitivity -= (JOYSTICK_AXIS_SENSITIVITY_DEC_STEP * decAmount);
|
||||
} else if (progress > pivot) {
|
||||
int incAmount = (progress - pivot);
|
||||
sensitivity += (JOYSTICK_AXIS_SENSITIVITY_INC_STEP * incAmount);
|
||||
}
|
||||
Apple2Preferences.setJSONPref(self, sensitivity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_AXIS_SENSITIVIY.intValue(activity);
|
||||
float sensitivity = Apple2Preferences.getFloatJSONPref(self);
|
||||
int pivot = JOYSTICK_AXIS_SENSITIVITY_DEC_NUMCHOICES;
|
||||
if (sensitivity < 1.f) {
|
||||
pivot = Math.round((sensitivity - JOYSTICK_AXIS_SENSITIVITY_MIN) / JOYSTICK_AXIS_SENSITIVITY_DEC_STEP);
|
||||
} else if (sensitivity > 1.f) {
|
||||
sensitivity -= 1.f;
|
||||
pivot += Math.round(sensitivity / JOYSTICK_AXIS_SENSITIVITY_INC_STEP);
|
||||
}
|
||||
return pivot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showValue(int progress, final TextView seekBarValue) {
|
||||
saveInt(progress);
|
||||
int percent = (int) (Apple2Preferences.JOYSTICK_AXIS_SENSITIVIY.floatValue(activity) * 100.f);
|
||||
int percent = (int) (Apple2Preferences.getFloatJSONPref(self) * 100.f);
|
||||
seekBarValue.setText("" + percent + "%");
|
||||
}
|
||||
});
|
||||
|
@ -421,25 +568,38 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.joystick_button_threshold_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "switchThreshold";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.JOYSTICK_BUTTON_THRESHOLD_NUM_CHOICES, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, JOYSTICK_BUTTON_THRESHOLD_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
if (progress == 0) {
|
||||
progress = 1;
|
||||
}
|
||||
Apple2Preferences.JOYSTICK_BUTTON_THRESHOLD.saveInt(activity, progress);
|
||||
progress *= getJoystickButtonSwitchThresholdScale(activity);
|
||||
Apple2Preferences.setJSONPref(self, progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.JOYSTICK_BUTTON_THRESHOLD.intValue(activity);
|
||||
int progress = (int) Apple2Preferences.getJSONPref(self);
|
||||
return (progress / getJoystickButtonSwitchThresholdScale(activity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showValue(int progress, final TextView seekBarValue) {
|
||||
int threshold = progress * Apple2Preferences.getJoystickButtonSwitchThresholdScale(activity);
|
||||
int threshold = progress * getJoystickButtonSwitchThresholdScale(activity);
|
||||
seekBarValue.setText("" + threshold + " pts");
|
||||
}
|
||||
});
|
||||
|
@ -448,9 +608,13 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_JOYSTICK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -468,4 +632,17 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int getJoystickButtonSwitchThresholdScale(Apple2Activity activity) {
|
||||
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
|
||||
int smallScreenAxis = dm.widthPixels < dm.heightPixels ? dm.widthPixels : dm.heightPixels;
|
||||
int oneThirdScreenAxis = smallScreenAxis / 3;
|
||||
|
||||
// largest switch threshold value is 1/3 small dimension of screen
|
||||
return oneThirdScreenAxis / JOYSTICK_BUTTON_THRESHOLD_NUM_CHOICES;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
package org.deadc0de.apple2ix;
|
||||
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
|
@ -106,14 +107,30 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.touch_menu_enable_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_KEYBOARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "touchMenuEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.TOUCH_MENU_ENABLED.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.TOUCH_MENU_ENABLED.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -130,17 +147,28 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_visibility_inactive_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "minAlpha";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return (float) 5 / Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.KEYBOARD_VISIBILITY_INACTIVE.saveInt(activity, progress);
|
||||
Apple2Preferences.setJSONPref(self, progress / Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.KEYBOARD_VISIBILITY_INACTIVE.intValue(activity);
|
||||
return Math.round(Apple2Preferences.getFloatJSONPref(self) * (Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -161,17 +189,28 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_visibility_active_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "maxAlpha";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 1.f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.KEYBOARD_VISIBILITY_ACTIVE.saveInt(activity, progress);
|
||||
Apple2Preferences.setJSONPref(self, (float) progress / Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.KEYBOARD_VISIBILITY_ACTIVE.intValue(activity);
|
||||
return Math.round(Apple2Preferences.getFloatJSONPref(self) * (Apple2Preferences.ALPHA_SLIDER_NUM_CHOICES));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -192,14 +231,25 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_click_enabled_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "keyClickEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.KEYBOARD_CLICK_ENABLED.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
final IMenuEnum self = this;
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.KEYBOARD_CLICK_ENABLED.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -216,14 +266,25 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_lowercase_enabled_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "lowercaseEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.KEYBOARD_LOWERCASE_ENABLED.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
final IMenuEnum self = this;
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.KEYBOARD_LOWERCASE_ENABLED.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -240,6 +301,16 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_choose_alt_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "altPathIndex";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -250,7 +321,7 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
|
||||
File extKeyboardDir = Apple2DisksMenu.getExternalStorageDirectory(activity);
|
||||
File extKeyboardDir = Apple2Utils.getExternalStorageDirectory(activity);
|
||||
|
||||
FilenameFilter kbdJsonFilter = new FilenameFilter() {
|
||||
public boolean accept(File dir, String name) {
|
||||
|
@ -279,7 +350,7 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
if (files == null) {
|
||||
// read keyboard data from /data/data/...
|
||||
File keyboardDir = new File(Apple2DisksMenu.getDataDir(activity) + File.separator + "keyboards");
|
||||
File keyboardDir = new File(Apple2Utils.getDataDir(activity) + File.separator + "keyboards");
|
||||
files = keyboardDir.listFiles(kbdJsonFilter);
|
||||
if (files == null) {
|
||||
Log.e(TAG, "OOPS, could not read keyboard data directory");
|
||||
|
@ -299,17 +370,18 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
final String keyboardDirName = extKeyboardDir == null ? "Keyboards" : extKeyboardDir.getPath();
|
||||
|
||||
final IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, keyboardDirName, titles, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.KEYBOARD_ALT.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.KEYBOARD_ALT.saveInt(activity, value);
|
||||
Apple2Preferences.setJSONPref(self, value);
|
||||
String path = allFiles[value].getPath();
|
||||
Apple2Preferences.KEYBOARD_ALT_PATH.saveString(activity, path);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_KEYBOARD, "altPath", path);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -325,15 +397,29 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keyboard_glyph_scale_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "glyphMultiplier";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
int glyphScale = Apple2Preferences.KEYBOARD_GLYPH_SCALE.intValue(activity);
|
||||
int glyphScale = (int) Apple2Preferences.getJSONPref(this);
|
||||
if (glyphScale <= 0) {
|
||||
glyphScale = 1;
|
||||
}
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, glyphScale > 1);
|
||||
final IMenuEnum self = this;
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.KEYBOARD_GLYPH_SCALE.saveInt(activity, isChecked ? 2 : 1);
|
||||
Apple2Preferences.setJSONPref(self, isChecked ? 2 : 1);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -342,9 +428,23 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_KEYBOARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -23,6 +23,8 @@ import android.widget.TextView;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class Apple2KeypadChooser implements Apple2MenuView {
|
||||
|
||||
|
@ -33,12 +35,10 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
private ArrayList<Apple2MenuView> mViewStack = null;
|
||||
private TextView mCurrentChoicePrompt = null;
|
||||
|
||||
private String[] foo = null;
|
||||
|
||||
private STATE_MACHINE mChooserState = STATE_MACHINE.CHOOSE_NORTHWEST;
|
||||
|
||||
private boolean mTouchMenuEnabled = false;
|
||||
private int mSavedTouchDevice = Apple2Preferences.TouchDeviceVariant.NONE.ordinal();
|
||||
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
|
||||
|
||||
public Apple2KeypadChooser(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack) {
|
||||
mActivity = activity;
|
||||
|
@ -60,8 +60,10 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
|
||||
String asciiStr = asciiRepresentation(ascii);
|
||||
Log.d(TAG, "ascii:'" + asciiStr + "' scancode:" + scancode);
|
||||
mChooserState.setValues(mActivity, ascii, scancode);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(Apple2Preferences.TouchDeviceVariant.JOYSTICK_KEYPAD.ordinal());
|
||||
mChooserState.setKey(mActivity, ascii, scancode);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD.ordinal());
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
|
||||
mCurrentChoicePrompt.setText(getNextChoiceString() + asciiStr);
|
||||
switch (mChooserState) {
|
||||
case CHOOSE_TAP:
|
||||
|
@ -88,7 +90,8 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
public void run() {
|
||||
mChooserState = mChooserState.next();
|
||||
mCurrentChoicePrompt.setText(getNextChoiceString());
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(Apple2Preferences.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, Apple2SettingsMenu.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
@ -101,15 +104,19 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
}
|
||||
|
||||
public void dismiss() {
|
||||
|
||||
for (Apple2MenuView apple2MenuView : mViewStack) {
|
||||
if (apple2MenuView != this) {
|
||||
mActivity.pushApple2View(apple2MenuView);
|
||||
}
|
||||
}
|
||||
|
||||
Apple2Preferences.nativeTouchDeviceEndCalibrationMode();
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(mTouchMenuEnabled);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(mSavedTouchDevice);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, false);
|
||||
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, mTouchMenuEnabled);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, mSavedTouchDevice);
|
||||
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
|
||||
mActivity.popApple2View(this);
|
||||
}
|
||||
|
@ -130,6 +137,10 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
// internals
|
||||
|
||||
private void setup() {
|
||||
JSONArray jsonChars = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, Apple2KeypadSettingsMenu.PREF_KPAD_ROSETTE_CHAR_ARRAY, null);
|
||||
JSONArray jsonScans = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, Apple2KeypadSettingsMenu.PREF_KPAD_ROSETTE_SCAN_ARRAY, null);
|
||||
mChooserState.start(jsonChars, jsonScans);
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
mSettingsView = inflater.inflate(R.layout.activity_chooser_keypad, null, false);
|
||||
|
||||
|
@ -140,17 +151,18 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
skipButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Apple2KeypadChooser.this.onKeyTapCalibrationEvent((char)Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
Apple2KeypadChooser.this.onKeyTapCalibrationEvent((char) Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
});
|
||||
|
||||
// temporarily undo these native touch settings while calibrating...
|
||||
mTouchMenuEnabled = Apple2Preferences.TOUCH_MENU_ENABLED.booleanValue(mActivity);
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(false);
|
||||
mSavedTouchDevice = Apple2Preferences.CURRENT_TOUCH_DEVICE.intValue(mActivity);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(Apple2Preferences.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
mTouchMenuEnabled = (boolean) Apple2Preferences.getJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED);
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, false);
|
||||
mSavedTouchDevice = (int) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, Apple2SettingsMenu.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
|
||||
Apple2Preferences.nativeTouchDeviceBeginCalibrationMode();
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, true);
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
}
|
||||
|
||||
private String asciiRepresentation(char ascii) {
|
||||
|
@ -194,140 +206,116 @@ public class Apple2KeypadChooser implements Apple2MenuView {
|
|||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_ul);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_NORTHWEST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_NORTH {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_up);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_NORTH_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_NORTHEAST {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_ur);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_NORTHEAST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_WEST {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_WEST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_CENTER {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_CENTER_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_EAST {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_r);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_EAST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_SOUTHWEST {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_dl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_SOUTHWEST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_SOUTH {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_dn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_SOUTH_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_SOUTHEAST {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_axis_dr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_SOUTHEAST_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_TAP {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_button_tap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_TAP_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_SWIPEUP {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_button_swipeup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_SWIPEUP_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
},
|
||||
CHOOSE_SWIPEDOWN {
|
||||
@Override
|
||||
public String getKeyName(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_key_button_swipedown);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValues(Apple2Activity activity, char ascii, int scancode) {
|
||||
Apple2Preferences.KEYPAD_SWIPEDOWN_KEY.saveChosenKey(activity, ascii, scancode);
|
||||
}
|
||||
};
|
||||
|
||||
public static final int size = STATE_MACHINE.values().length;
|
||||
|
||||
public abstract void setValues(Apple2Activity activity, char ascii, int scancode);
|
||||
private static ArrayList<String> chars = new ArrayList<String>();
|
||||
private static ArrayList<String> scans = new ArrayList<String>();
|
||||
|
||||
public void setKey(Apple2Activity activity, int ascii, int scancode) {
|
||||
int ord = ordinal();
|
||||
if (ord < CHOOSE_TAP.ordinal()) {
|
||||
chars.set(ord, "" + ascii);
|
||||
scans.set(ord, "" + scancode);
|
||||
Apple2KeypadSettingsMenu.KeypadPreset.saveRosettes(chars, scans);
|
||||
} else if (ord == CHOOSE_TAP.ordinal()) {
|
||||
Apple2KeypadSettingsMenu.KeypadPreset.saveTouchDownKey(ascii, scancode);
|
||||
} else if (ord == CHOOSE_SWIPEUP.ordinal()) {
|
||||
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeNorthKey(ascii, scancode);
|
||||
} else if (ord == CHOOSE_SWIPEDOWN.ordinal()) {
|
||||
Apple2KeypadSettingsMenu.KeypadPreset.saveSwipeSouthKey(ascii, scancode);
|
||||
} else {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
Apple2Preferences.sync(activity, Apple2Preferences.PREF_DOMAIN_JOYSTICK);
|
||||
}
|
||||
|
||||
public abstract String getKeyName(Apple2Activity activity);
|
||||
|
||||
public void start(JSONArray jsonChars, JSONArray jsonScans) {
|
||||
int len = jsonChars.length();
|
||||
if (len != size) {
|
||||
throw new RuntimeException("jsonChars not expected length");
|
||||
}
|
||||
if (len != jsonScans.length()) {
|
||||
throw new RuntimeException("jsonScans not expected length");
|
||||
}
|
||||
try {
|
||||
for (int i = 0; i < len; i++) {
|
||||
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, jsonChars.getInt(i), jsonScans.getInt(i));
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public STATE_MACHINE next() {
|
||||
int nextOrd = this.ordinal() + 1;
|
||||
if (nextOrd >= size) {
|
||||
|
|
|
@ -17,11 +17,23 @@ import android.widget.TextView;
|
|||
import java.util.ArrayList;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
import org.json.JSONArray;
|
||||
|
||||
public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
||||
|
||||
private final static String TAG = "Apple2KeypadSettingsMenu";
|
||||
|
||||
private final static int KEYREPEAT_NUM_CHOICES = Apple2Preferences.DECENT_AMOUNT_OF_CHOICES;
|
||||
|
||||
public final static String PREF_KPAD_ROSETTE_CHAR_ARRAY = "kpAxisRosetteChars";
|
||||
public final static String PREF_KPAD_ROSETTE_SCAN_ARRAY = "kpAxisRosetteScancodes";
|
||||
public final static String PREF_KPAD_SWIPE_NORTH_CHAR = "kpSwipeNorthChar";
|
||||
public final static String PREF_KPAD_SWIPE_NORTH_SCAN = "kpSwipeNorthScancode";
|
||||
public final static String PREF_KPAD_SWIPE_SOUTH_CHAR = "kpSwipeSouthChar";
|
||||
public final static String PREF_KPAD_SWIPE_SOUTH_SCAN = "kpSwipeSouthScancode";
|
||||
public final static String PREF_KPAD_TOUCHDOWN_CHAR = "kpTouchDownChar";
|
||||
public final static String PREF_KPAD_TOUCHDOWN_SCAN = "kpTouchDownScancode";
|
||||
|
||||
public Apple2KeypadSettingsMenu(Apple2Activity activity) {
|
||||
super(activity);
|
||||
}
|
||||
|
@ -49,6 +61,212 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
return true;
|
||||
}
|
||||
|
||||
public enum KeypadPreset {
|
||||
ARROWS_SPACE {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_arrows_space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_UP, Apple2KeyboardSettingsMenu.SCANCODE_UP);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_DOWN, Apple2KeyboardSettingsMenu.SCANCODE_DOWN);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
},
|
||||
AZ_LEFT_RIGHT_SPACE {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_az_left_right_space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'A', Apple2KeyboardSettingsMenu.SCANCODE_A);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'Z', Apple2KeyboardSettingsMenu.SCANCODE_Z);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
},
|
||||
LEFT_RIGHT_SPACE {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_left_right_space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_LEFT, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.MOUSETEXT_RIGHT, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
},
|
||||
IJKM_SPACE {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_ijkm_space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'I', Apple2KeyboardSettingsMenu.SCANCODE_I);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'J', Apple2KeyboardSettingsMenu.SCANCODE_J);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'M', Apple2KeyboardSettingsMenu.SCANCODE_M);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
},
|
||||
WADX_SPACE {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_wadx_space);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'W', Apple2KeyboardSettingsMenu.SCANCODE_W);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'A', Apple2KeyboardSettingsMenu.SCANCODE_A);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'D', Apple2KeyboardSettingsMenu.SCANCODE_D);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
addRosetteKey(chars, scans, 'X', Apple2KeyboardSettingsMenu.SCANCODE_X);
|
||||
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
|
||||
}
|
||||
},
|
||||
CRAZY_SEAFOX_KEYS {
|
||||
@Override
|
||||
public String getTitle(Apple2Activity activity) {
|
||||
return activity.getResources().getString(R.string.keypad_preset_crazy_seafox);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Apple2Activity activity) {
|
||||
// Heh, the entire purpose of the keypad-variant touch joystick is to make this possible ;-)
|
||||
ArrayList<String> chars = new ArrayList<String>();
|
||||
ArrayList<String> scans = new ArrayList<String>();
|
||||
addRosetteKey(chars, scans, 'Y', Apple2KeyboardSettingsMenu.SCANCODE_Y);
|
||||
addRosetteKey(chars, scans, 'U', Apple2KeyboardSettingsMenu.SCANCODE_U);
|
||||
addRosetteKey(chars, scans, 'I', Apple2KeyboardSettingsMenu.SCANCODE_I);
|
||||
addRosetteKey(chars, scans, 'H', Apple2KeyboardSettingsMenu.SCANCODE_H);
|
||||
addRosetteKey(chars, scans, 'J', Apple2KeyboardSettingsMenu.SCANCODE_J);
|
||||
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
|
||||
addRosetteKey(chars, scans, 'N', Apple2KeyboardSettingsMenu.SCANCODE_N);
|
||||
addRosetteKey(chars, scans, 'M', Apple2KeyboardSettingsMenu.SCANCODE_M);
|
||||
addRosetteKey(chars, scans, ',', Apple2KeyboardSettingsMenu.SCANCODE_COMMA);
|
||||
saveRosettes(chars, scans);
|
||||
|
||||
saveTouchDownKey('D', Apple2KeyboardSettingsMenu.SCANCODE_D);
|
||||
saveSwipeSouthKey('F', Apple2KeyboardSettingsMenu.SCANCODE_F);
|
||||
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
|
||||
}
|
||||
};
|
||||
|
||||
public static void addRosetteKey(ArrayList<String> chars, ArrayList<String> scans, int aChar, int aScan) {
|
||||
chars.add("" + aChar);
|
||||
scans.add("" + aScan);
|
||||
}
|
||||
|
||||
public static void saveRosettes(ArrayList<String> chars, ArrayList<String> scans) {
|
||||
if (chars.size() != 9) {
|
||||
throw new RuntimeException("rosette chars is not correct size");
|
||||
}
|
||||
if (scans.size() != 9) {
|
||||
throw new RuntimeException("rosette scans is not correct size");
|
||||
}
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_ROSETTE_CHAR_ARRAY, new JSONArray(chars));
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_ROSETTE_SCAN_ARRAY, new JSONArray(scans));
|
||||
}
|
||||
|
||||
public static void saveTouchDownKey(int aChar, int aScan) {
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_TOUCHDOWN_CHAR, aChar);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_TOUCHDOWN_SCAN, aScan);
|
||||
}
|
||||
|
||||
public static void saveSwipeSouthKey(int aChar, int aScan) {
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_SOUTH_CHAR, aChar);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_SOUTH_SCAN, aScan);
|
||||
}
|
||||
|
||||
public static void saveSwipeNorthKey(int aChar, int aScan) {
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_NORTH_CHAR, aChar);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_SWIPE_NORTH_SCAN, aScan);
|
||||
}
|
||||
|
||||
public abstract String getTitle(Apple2Activity activity);
|
||||
|
||||
public abstract void apply(Apple2Activity activity);
|
||||
|
||||
public static final int size = KeypadPreset.values().length;
|
||||
|
||||
public static String[] titles(Apple2Activity activity) {
|
||||
String[] titles = new String[size];
|
||||
int i = 0;
|
||||
for (KeypadPreset preset : values()) {
|
||||
titles[i++] = preset.getTitle(activity);
|
||||
}
|
||||
return titles;
|
||||
}
|
||||
}
|
||||
|
||||
enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
KEYPAD_CHOOSE_KEYS {
|
||||
@Override
|
||||
|
@ -61,6 +279,16 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keypad_choose_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "kpPresetChoice";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return KeypadPreset.IJKM_SPACE.ordinal() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -70,24 +298,25 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
String[] titles = new String[Apple2Preferences.KeypadPreset.size + 1];
|
||||
final IMenuEnum self = this;
|
||||
String[] titles = new String[KeypadPreset.size + 1];
|
||||
titles[0] = activity.getResources().getString(R.string.keypad_preset_custom);
|
||||
System.arraycopy(Apple2Preferences.KeypadPreset.titles(activity), 0, titles, 1, Apple2Preferences.KeypadPreset.size);
|
||||
System.arraycopy(KeypadPreset.titles(activity), 0, titles, 1, KeypadPreset.size);
|
||||
|
||||
_alertDialogHandleSelection(activity, R.string.keypad_choose_title, titles, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.KEYPAD_KEYS.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.KEYPAD_KEYS.saveInt(activity, value);
|
||||
Apple2Preferences.setJSONPref(self, value);
|
||||
if (value == 0) {
|
||||
Apple2KeypadSettingsMenu keypadSettingsMenu = (Apple2KeypadSettingsMenu) settingsMenu;
|
||||
keypadSettingsMenu.chooseKeys(activity);
|
||||
} else {
|
||||
Apple2Preferences.KeypadPreset.values()[value - 1].apply(activity);
|
||||
KeypadPreset.values()[value - 1].apply(activity);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -119,7 +348,7 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
|
||||
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2Preferences.TouchDeviceVariant.JOYSTICK_KEYPAD);
|
||||
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD);
|
||||
|
||||
// show this new view...
|
||||
calibration.show();
|
||||
|
@ -149,9 +378,23 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_JOYSTICK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -197,7 +440,7 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
|
||||
protected static class KeypadAdvanced extends Apple2AbstractMenu {
|
||||
public static class KeypadAdvanced extends Apple2AbstractMenu {
|
||||
|
||||
private final static String TAG = "KeypadAdvanced";
|
||||
|
||||
|
@ -240,22 +483,33 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.keypad_repeat_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "keyRepeatThresholdSecs";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return (float) 4 / KEYREPEAT_NUM_CHOICES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
return _sliderView(activity, this, Apple2Preferences.KEYREPEAT_NUM_CHOICES, new IPreferenceSlider() {
|
||||
final IMenuEnum self = this;
|
||||
return _sliderView(activity, this, KEYREPEAT_NUM_CHOICES, new IPreferenceSlider() {
|
||||
@Override
|
||||
public void saveInt(int progress) {
|
||||
Apple2Preferences.KEYREPEAT_THRESHOLD.saveInt(activity, progress);
|
||||
Apple2Preferences.setJSONPref(self, (float) progress / KEYREPEAT_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.KEYREPEAT_THRESHOLD.intValue(activity);
|
||||
return (int) (Apple2Preferences.getFloatJSONPref(self) * KEYREPEAT_NUM_CHOICES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showValue(int progress, final TextView seekBarValue) {
|
||||
seekBarValue.setText("" + ((float) progress / Apple2Preferences.KEYREPEAT_NUM_CHOICES));
|
||||
seekBarValue.setText("" + ((float) progress / KEYREPEAT_NUM_CHOICES));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -279,9 +533,23 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_JOYSTICK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -100,7 +100,7 @@ public class Apple2MainMenu {
|
|||
@Override
|
||||
public void handleSelection(Apple2MainMenu mainMenu) {
|
||||
if (!mainMenu.mShowingSaveRestore.compareAndSet(false, true)) {
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race around save/restore");
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race around sync/restore");
|
||||
return;
|
||||
}
|
||||
mainMenu.maybeSaveRestore();
|
||||
|
@ -278,7 +278,7 @@ public class Apple2MainMenu {
|
|||
public void maybeSaveRestore() {
|
||||
mActivity.pauseEmulation();
|
||||
|
||||
final String quickSavePath = Apple2DisksMenu.getDataDir(mActivity) + File.separator + SAVE_FILE;
|
||||
final String quickSavePath = Apple2Utils.getDataDir(mActivity) + File.separator + SAVE_FILE;
|
||||
|
||||
final AtomicBoolean selectionAlreadyHandled = new AtomicBoolean(false);
|
||||
|
||||
|
@ -286,7 +286,7 @@ public class Apple2MainMenu {
|
|||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!selectionAlreadyHandled.compareAndSet(false, true)) {
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race in save/restore onClick()");
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race in sync/restore onClick()");
|
||||
return;
|
||||
}
|
||||
mActivity.saveState(quickSavePath);
|
||||
|
@ -296,7 +296,7 @@ public class Apple2MainMenu {
|
|||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
if (!selectionAlreadyHandled.compareAndSet(false, true)) {
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race in save/restore onClick()");
|
||||
Log.v(TAG, "OMG, avoiding nasty UI race in sync/restore onClick()");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -305,13 +305,13 @@ public class Apple2MainMenu {
|
|||
JSONObject map = new JSONObject(jsonData);
|
||||
String diskPath1 = map.getString("disk1");
|
||||
boolean readOnly1 = map.getBoolean("readOnly1");
|
||||
Apple2Preferences.CURRENT_DISK_A.setPath(mActivity, diskPath1);
|
||||
Apple2Preferences.CURRENT_DISK_A_RO.saveBoolean(mActivity, readOnly1);
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A, diskPath1);
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_A_RO, readOnly1);
|
||||
|
||||
String diskPath2 = map.getString("disk2");
|
||||
boolean readOnly2 = map.getBoolean("readOnly2");
|
||||
Apple2Preferences.CURRENT_DISK_B.setPath(mActivity, diskPath2);
|
||||
Apple2Preferences.CURRENT_DISK_B_RO.saveBoolean(mActivity, readOnly2);
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B, diskPath2);
|
||||
Apple2Preferences.setJSONPref(Apple2DisksMenu.SETTINGS.CURRENT_DISK_PATH_B_RO, readOnly2);
|
||||
} catch (JSONException je) {
|
||||
Log.v(TAG, "OOPS : " + je);
|
||||
}
|
||||
|
|
|
@ -24,32 +24,88 @@ import java.util.ArrayList;
|
|||
|
||||
public class Apple2PortraitCalibration implements Apple2MenuView {
|
||||
|
||||
public enum States {
|
||||
CALIBRATE_KEYBOARD_HEIGHT_SCALE(0),
|
||||
CALIBRATE_FRAMEBUFFER_POSITION_SCALE(1),
|
||||
CALIBRATE_KEYBOARD_POSITION_SCALE(2);
|
||||
private int val;
|
||||
public enum States implements Apple2AbstractMenu.IMenuEnum {
|
||||
CALIBRATE_KEYBOARD_HEIGHT_SCALE {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "portraitHeightScale";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return (float) (PORTRAIT_CALIBRATE_NUM_CHOICES >> 1) / PORTRAIT_CALIBRATE_NUM_CHOICES;
|
||||
}
|
||||
},
|
||||
CALIBRATE_FRAMEBUFFER_POSITION_SCALE {
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_VIDEO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "portraitPositionScale";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 3.f / 4;
|
||||
}
|
||||
},
|
||||
CALIBRATE_KEYBOARD_POSITION_SCALE {
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "portraitPositionScale";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return 0.f;
|
||||
}
|
||||
};
|
||||
|
||||
public static final int size = States.values().length;
|
||||
|
||||
States(int val) {
|
||||
this.val = val;
|
||||
States next() {
|
||||
int ord = (ordinal() + 1) % size;
|
||||
return States.values()[ord];
|
||||
}
|
||||
|
||||
States next() {
|
||||
int ord = (val + 1) % size;
|
||||
return States.values()[ord];
|
||||
@Override
|
||||
public final String getTitle(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getSummary(Apple2Activity activity) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_KEYBOARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
}
|
||||
|
||||
private final static String TAG = "Apple2PortraitCalibration";
|
||||
|
||||
public final static int PORTRAIT_CALIBRATE_NUM_CHOICES = 100;
|
||||
|
||||
private Apple2Activity mActivity = null;
|
||||
private View mSettingsView = null;
|
||||
private ArrayList<Apple2MenuView> mViewStack = null;
|
||||
private boolean mTouchMenuEnabled = false;
|
||||
private int mSavedTouchDevice = Apple2Preferences.TouchDeviceVariant.NONE.ordinal();
|
||||
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
|
||||
private States mStateMachine = States.CALIBRATE_KEYBOARD_HEIGHT_SCALE;
|
||||
|
||||
public Apple2PortraitCalibration(Apple2Activity activity, ArrayList<Apple2MenuView> viewStack) {
|
||||
|
@ -62,7 +118,8 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
final Button calibrateButton = (Button) mSettingsView.findViewById(R.id.button_calibrate);
|
||||
final VerticalSeekBar vsb = (VerticalSeekBar) mSettingsView.findViewById(R.id.seekbar_vertical);
|
||||
|
||||
vsb.setProgress(Apple2Preferences.PORTRAIT_KEYBOARD_HEIGHT_SCALE.intValue(mActivity));
|
||||
final int firstProgress = Math.round(Apple2Preferences.getFloatJSONPref(States.CALIBRATE_KEYBOARD_HEIGHT_SCALE) * PORTRAIT_CALIBRATE_NUM_CHOICES);
|
||||
vsb.setProgress(firstProgress);
|
||||
|
||||
calibrateButton.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
|
@ -71,16 +128,16 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
switch (mStateMachine) {
|
||||
case CALIBRATE_KEYBOARD_HEIGHT_SCALE:
|
||||
calibrateButton.setText(R.string.portrait_calibrate_keyboard_height);
|
||||
vsb.setProgress(Apple2Preferences.PORTRAIT_KEYBOARD_HEIGHT_SCALE.intValue(mActivity));
|
||||
vsb.setProgress(Math.round(Apple2Preferences.getFloatJSONPref(States.CALIBRATE_KEYBOARD_HEIGHT_SCALE) * PORTRAIT_CALIBRATE_NUM_CHOICES));
|
||||
break;
|
||||
case CALIBRATE_FRAMEBUFFER_POSITION_SCALE:
|
||||
calibrateButton.setText(R.string.portrait_calibrate_framebuffer);
|
||||
vsb.setProgress(Apple2Preferences.PORTRAIT_FRAMEBUFFER_POSITION_SCALE.intValue(mActivity));
|
||||
vsb.setProgress(Math.round(Apple2Preferences.getFloatJSONPref(States.CALIBRATE_FRAMEBUFFER_POSITION_SCALE) * PORTRAIT_CALIBRATE_NUM_CHOICES));
|
||||
break;
|
||||
case CALIBRATE_KEYBOARD_POSITION_SCALE:
|
||||
default:
|
||||
calibrateButton.setText(R.string.portrait_calibrate_keyboard_position);
|
||||
vsb.setProgress(Apple2Preferences.PORTRAIT_KEYBOARD_POSITION_SCALE.intValue(mActivity));
|
||||
vsb.setProgress(Math.round(Apple2Preferences.getFloatJSONPref(States.CALIBRATE_KEYBOARD_POSITION_SCALE) * PORTRAIT_CALIBRATE_NUM_CHOICES));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -91,16 +148,17 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
switch (mStateMachine) {
|
||||
case CALIBRATE_KEYBOARD_HEIGHT_SCALE:
|
||||
Apple2Preferences.PORTRAIT_KEYBOARD_HEIGHT_SCALE.saveInt(mActivity, progress);
|
||||
Apple2Preferences.setJSONPref(States.CALIBRATE_KEYBOARD_HEIGHT_SCALE, (float) progress / PORTRAIT_CALIBRATE_NUM_CHOICES);
|
||||
break;
|
||||
case CALIBRATE_FRAMEBUFFER_POSITION_SCALE:
|
||||
Apple2Preferences.PORTRAIT_FRAMEBUFFER_POSITION_SCALE.saveInt(mActivity, progress);
|
||||
Apple2Preferences.setJSONPref(States.CALIBRATE_FRAMEBUFFER_POSITION_SCALE, (float) progress / PORTRAIT_CALIBRATE_NUM_CHOICES);
|
||||
break;
|
||||
case CALIBRATE_KEYBOARD_POSITION_SCALE:
|
||||
default:
|
||||
Apple2Preferences.PORTRAIT_KEYBOARD_POSITION_SCALE.saveInt(mActivity, progress);
|
||||
Apple2Preferences.setJSONPref(States.CALIBRATE_KEYBOARD_POSITION_SCALE, (float) progress / PORTRAIT_CALIBRATE_NUM_CHOICES);
|
||||
break;
|
||||
}
|
||||
Apple2Preferences.sync(mActivity, mStateMachine.getPrefDomain());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,11 +170,14 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
}
|
||||
});
|
||||
|
||||
mTouchMenuEnabled = Apple2Preferences.TOUCH_MENU_ENABLED.booleanValue(mActivity);
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(false);
|
||||
mSavedTouchDevice = Apple2Preferences.CURRENT_TOUCH_DEVICE.intValue(mActivity);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(Apple2Preferences.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
Apple2Preferences.nativeTouchDeviceBeginCalibrationMode();
|
||||
mTouchMenuEnabled = (boolean) Apple2Preferences.getJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED);
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, false);
|
||||
mSavedTouchDevice = (int) Apple2Preferences.getJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, Apple2SettingsMenu.TouchDeviceVariant.KEYBOARD.ordinal());
|
||||
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, true);
|
||||
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
}
|
||||
|
||||
public final boolean isCalibrating() {
|
||||
|
@ -124,7 +185,6 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
}
|
||||
|
||||
public void onKeyTapCalibrationEvent(char ascii, int scancode) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
public void show() {
|
||||
|
@ -141,9 +201,12 @@ public class Apple2PortraitCalibration implements Apple2MenuView {
|
|||
}
|
||||
}
|
||||
|
||||
Apple2Preferences.nativeTouchDeviceEndCalibrationMode();
|
||||
Apple2Preferences.nativeSetTouchMenuEnabled(mTouchMenuEnabled);
|
||||
Apple2Preferences.nativeSetCurrentTouchDevice(mSavedTouchDevice);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN, Apple2Preferences.PREF_CALIBRATING, false);
|
||||
|
||||
Apple2Preferences.setJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.TOUCH_MENU_ENABLED, mTouchMenuEnabled);
|
||||
Apple2Preferences.setJSONPref(Apple2SettingsMenu.SETTINGS.CURRENT_INPUT, mSavedTouchDevice);
|
||||
|
||||
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
|
||||
|
||||
mActivity.popApple2View(this);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -55,6 +55,29 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
return true;
|
||||
}
|
||||
|
||||
public enum TouchDeviceVariant {
|
||||
NONE(0),
|
||||
JOYSTICK(1),
|
||||
JOYSTICK_KEYPAD(2),
|
||||
KEYBOARD(3),
|
||||
TOPMENU(4),
|
||||
ALERT(5);
|
||||
private int dev;
|
||||
|
||||
public static final TouchDeviceVariant FRAMEBUFFER = NONE;
|
||||
|
||||
public static final int size = TouchDeviceVariant.values().length;
|
||||
|
||||
TouchDeviceVariant(int dev) {
|
||||
this.dev = dev;
|
||||
}
|
||||
|
||||
static TouchDeviceVariant next(int ord) {
|
||||
ord = (ord + 1) % size;
|
||||
return TouchDeviceVariant.values()[ord];
|
||||
}
|
||||
}
|
||||
|
||||
enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
CURRENT_INPUT {
|
||||
@Override
|
||||
|
@ -67,6 +90,21 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.input_current_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "screenOwner";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return TouchDeviceVariant.KEYBOARD.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -76,6 +114,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
final IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, R.string.input_current, new String[]{
|
||||
activity.getResources().getString(R.string.joystick),
|
||||
activity.getResources().getString(R.string.keypad),
|
||||
|
@ -83,12 +122,13 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
}, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.CURRENT_TOUCH_DEVICE.intValue(activity) - 1;
|
||||
int val = (int) Apple2Preferences.getJSONPref(self);
|
||||
return val - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.CURRENT_TOUCH_DEVICE.saveTouchDevice(activity, Apple2Preferences.TouchDeviceVariant.values()[value + 1]);
|
||||
Apple2Preferences.setJSONPref(self, TouchDeviceVariant.values()[value].ordinal() + 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -184,14 +224,25 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.disk_show_operation_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "diskAnimationsEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
final IMenuEnum self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.SHOW_DISK_OPERATIONS.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.SHOW_DISK_OPERATIONS.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -233,7 +284,7 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
dialog.dismiss();
|
||||
Apple2Preferences.resetPreferences(activity);
|
||||
Apple2Preferences.reset(activity);
|
||||
}
|
||||
}).setNegativeButton(R.string.no, null);
|
||||
AlertDialog dialog = builder.create();
|
||||
|
@ -260,15 +311,26 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "sendCrashReports";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(final Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
if (!BuildConfig.DEBUG) {
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.CRASH_CHECK.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
final IMenuEnum self = this;
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.CRASH_CHECK.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.setJSONPref(self, isChecked);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -312,9 +374,23 @@ public class Apple2SettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_INTERFACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
365
Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java
Normal file
365
Android/app/src/main/java/org/deadc0de/apple2ix/Apple2Utils.java
Normal file
|
@ -0,0 +1,365 @@
|
|||
/*
|
||||
* Apple // emulator for *nix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2016 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
package org.deadc0de.apple2ix;
|
||||
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import org.deadc0de.apple2ix.basic.R;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InterruptedIOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
public class Apple2Utils {
|
||||
|
||||
public final static String TAG = "Apple2Utils";
|
||||
|
||||
private static String sDataDir = null;
|
||||
private static File sExternalFilesDir = null;
|
||||
private static File sDownloadFilesDir = null;
|
||||
|
||||
public static boolean readEntireFile(File file, StringBuilder fileData) {
|
||||
final int maxAttempts = 5;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(file));
|
||||
char[] buf = new char[1024];
|
||||
int numRead = 0;
|
||||
while ((numRead = reader.read(buf)) != -1) {
|
||||
String readData = String.valueOf(buf, 0, numRead);
|
||||
fileData.append(readData);
|
||||
}
|
||||
reader.close();
|
||||
break;
|
||||
} catch (InterruptedIOException ie) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "Error reading file at path : " + file.toString());
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException e) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
return attempts < maxAttempts;
|
||||
}
|
||||
|
||||
public static boolean writeFile(final StringBuilder data, File file) {
|
||||
|
||||
final int maxAttempts = 5;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
|
||||
writer.append(data);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
break;
|
||||
} catch (InterruptedIOException ie) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Exception attempting to write data : " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException e) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
return attempts < maxAttempts;
|
||||
}
|
||||
|
||||
|
||||
public static File getExternalStorageDirectory(Apple2Activity activity) {
|
||||
|
||||
do {
|
||||
if (sExternalFilesDir != null) {
|
||||
break;
|
||||
}
|
||||
|
||||
String storageState = Environment.getExternalStorageState();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sExternalFilesDir = externalDir;
|
||||
sDownloadFilesDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
} while (false);
|
||||
|
||||
return sExternalFilesDir;
|
||||
}
|
||||
|
||||
public static File getDownloadsDirectory(Apple2Activity activity) {
|
||||
getExternalStorageDirectory(activity);
|
||||
return sDownloadFilesDir;
|
||||
}
|
||||
|
||||
// HACK NOTE 2015/02/22 : Apparently native code cannot easily access stuff in the APK ... so copy various resources
|
||||
// out of the APK and into the /data/data/... for ease of access. Because this is FOSS software we don't care about
|
||||
// security or DRM for these assets =)
|
||||
public static String getDataDir(Apple2Activity activity) {
|
||||
|
||||
if (sDataDir != null) {
|
||||
return sDataDir;
|
||||
}
|
||||
|
||||
try {
|
||||
PackageManager pm = activity.getPackageManager();
|
||||
PackageInfo pi = pm.getPackageInfo(activity.getPackageName(), 0);
|
||||
sDataDir = pi.applicationInfo.dataDir;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "" + e);
|
||||
if (sDataDir == null) {
|
||||
sDataDir = "/data/local/tmp";
|
||||
}
|
||||
}
|
||||
|
||||
return sDataDir;
|
||||
}
|
||||
|
||||
public static void exposeAPKAssetsToExternal(Apple2Activity activity) {
|
||||
getExternalStorageDirectory(activity);
|
||||
if (sExternalFilesDir == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.VISIBLE);
|
||||
bar.setIndeterminate(true);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Log.v(TAG, "Overwriting system files in /sdcard/apple2ix/ (external storage) ...");
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/sExternalFilesDir.getAbsolutePath(), false);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.INVISIBLE);
|
||||
bar.setIndeterminate(false);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssetsToExternal #2");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void exposeAPKAssets(Apple2Activity activity) {
|
||||
final ProgressBar bar = (ProgressBar) activity.findViewById(R.id.crash_progressBar);
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.VISIBLE);
|
||||
bar.setIndeterminate(true);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getDataDir(activity);
|
||||
|
||||
// FIXME TODO : Heavy-handed migration to 1.1.3 ...
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "blanks"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "demo"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "eamon"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "logo"));
|
||||
recursivelyDelete(new File(new File(sDataDir, "disks").getAbsolutePath(), "miscgame"));
|
||||
|
||||
Log.d(TAG, "First time copying stuff-n-things out of APK for ease-of-NDK access...");
|
||||
|
||||
getExternalStorageDirectory(activity);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"disks", /*to location:*/new File(sDataDir, "disks").getAbsolutePath(), true);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"keyboards", /*to location:*/new File(sDataDir, "keyboards").getAbsolutePath(), false);
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"shaders", /*to location:*/new File(sDataDir, "shaders").getAbsolutePath(), false);
|
||||
|
||||
activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
bar.setVisibility(View.INVISIBLE);
|
||||
bar.setIndeterminate(false);
|
||||
} catch (NullPointerException npe) {
|
||||
Log.v(TAG, "Avoid NPE in exposeAPKAssets #1");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void exposeSymbols(Apple2Activity activity) {
|
||||
recursivelyCopyAPKAssets(activity, /*from APK directory:*/"symbols", /*to location:*/new File(sDataDir, "symbols").getAbsolutePath(), false);
|
||||
}
|
||||
|
||||
public static void unexposeSymbols(Apple2Activity activity) {
|
||||
recursivelyDelete(new File(sDataDir, "symbols"));
|
||||
}
|
||||
|
||||
// TODO FIXME : WARNING : this is super dangerous if there are symlinks !!!
|
||||
private static void recursivelyDelete(File file) {
|
||||
if (file.isDirectory()) {
|
||||
for (File f : file.listFiles()) {
|
||||
recursivelyDelete(f);
|
||||
}
|
||||
}
|
||||
if (!file.delete()) {
|
||||
Log.d(TAG, "Failed to delete file: " + file);
|
||||
}
|
||||
}
|
||||
|
||||
private static void recursivelyCopyAPKAssets(Apple2Activity activity, String srcFileOrDir, String dstFileOrDir, boolean shouldGzip) {
|
||||
AssetManager assetManager = activity.getAssets();
|
||||
|
||||
final int maxAttempts = 5;
|
||||
String[] files = null;
|
||||
int attempts = 0;
|
||||
do {
|
||||
try {
|
||||
files = assetManager.list(srcFileOrDir);
|
||||
break;
|
||||
} catch (InterruptedIOException e) {
|
||||
/* EINTR, EAGAIN ... */
|
||||
} catch (IOException e) {
|
||||
Log.d(TAG, "OOPS exception attempting to list APK files at : " + srcFileOrDir + " : " + e);
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
|
||||
if (files == null) {
|
||||
Log.d(TAG, "OOPS, could not list APK assets at : " + srcFileOrDir);
|
||||
return;
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
// ensure destination directory exists
|
||||
File dstPath = new File(dstFileOrDir);
|
||||
if (!dstPath.mkdirs()) {
|
||||
if (!dstPath.exists()) {
|
||||
Log.d(TAG, "OOPS, could not mkdirs on " + dstPath);
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (String filename : files) {
|
||||
// iterate on files and subdirectories
|
||||
recursivelyCopyAPKAssets(activity, srcFileOrDir + File.separator + filename, dstFileOrDir + File.separator + filename, shouldGzip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// presumably this is a file, not a subdirectory
|
||||
InputStream is = null;
|
||||
OutputStream os = null;
|
||||
attempts = 0;
|
||||
do {
|
||||
try {
|
||||
is = assetManager.open(srcFileOrDir);
|
||||
if (shouldGzip) {
|
||||
os = new GZIPOutputStream(new FileOutputStream(dstFileOrDir + ".gz"));
|
||||
} else {
|
||||
os = new FileOutputStream(dstFileOrDir);
|
||||
}
|
||||
copyFile(is, os);
|
||||
break;
|
||||
} catch (InterruptedIOException e) {
|
||||
/* EINTR, EAGAIN */
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "Failed to copy asset file: " + srcFileOrDir, e);
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
if (os != null) {
|
||||
try {
|
||||
os.close();
|
||||
} catch (IOException e) {
|
||||
// NOOP
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(100, 0);
|
||||
} catch (InterruptedException ie) {
|
||||
/* ... */
|
||||
}
|
||||
++attempts;
|
||||
} while (attempts < maxAttempts);
|
||||
}
|
||||
|
||||
private static void copyFile(InputStream is, OutputStream os) throws IOException {
|
||||
final int BUF_SZ = 4096;
|
||||
byte[] buf = new byte[BUF_SZ];
|
||||
while (true) {
|
||||
int len = is.read(buf, 0, BUF_SZ);
|
||||
if (len < 0) {
|
||||
break;
|
||||
}
|
||||
os.write(buf, 0, len);
|
||||
}
|
||||
os.flush();
|
||||
}
|
||||
}
|
||||
|
|
@ -11,10 +11,10 @@
|
|||
|
||||
package org.deadc0de.apple2ix;
|
||||
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.view.View;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -49,13 +49,19 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
if (position == SETTINGS.PORTRAIT_CALIBRATE.ordinal()) {
|
||||
if (Apple2Preferences.LANDSCAPE_MODE.booleanValue(mActivity)) {
|
||||
if ((boolean) Apple2Preferences.getJSONPref(SETTINGS.LANDSCAPE_MODE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public enum HiresColor {
|
||||
BW,
|
||||
COLOR,
|
||||
INTERPOLATED
|
||||
}
|
||||
|
||||
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
|
||||
LANDSCAPE_MODE {
|
||||
@Override
|
||||
|
@ -68,15 +74,31 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.mode_landscape_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_INTERFACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "landscapeEnabled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final View getView(final Apple2Activity activity, View convertView) {
|
||||
final Object self = this;
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, Apple2Preferences.LANDSCAPE_MODE.booleanValue(activity));
|
||||
CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
|
||||
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
|
||||
Apple2Preferences.LANDSCAPE_MODE.saveBoolean(activity, isChecked);
|
||||
Apple2Preferences.LANDSCAPE_MODE.load(activity);
|
||||
Apple2Preferences.setJSONPref((IMenuEnum)self, isChecked);
|
||||
applyLandscapeMode(activity);
|
||||
}
|
||||
});
|
||||
return convertView;
|
||||
|
@ -108,7 +130,9 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
}
|
||||
}
|
||||
|
||||
Apple2Preferences.LANDSCAPE_MODE.saveBoolean(activity, false);
|
||||
// switch to portrait
|
||||
Apple2Preferences.setJSONPref(SETTINGS.LANDSCAPE_MODE, false);
|
||||
applyLandscapeMode(activity);
|
||||
Apple2PortraitCalibration calibration = new Apple2PortraitCalibration(activity, viewStack);
|
||||
|
||||
// show this new view...
|
||||
|
@ -131,6 +155,21 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
return activity.getResources().getString(R.string.color_configure_summary);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return Apple2Preferences.PREF_DOMAIN_VIDEO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return "colorMode";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return HiresColor.INTERPOLATED.ordinal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(Apple2Activity activity, View convertView) {
|
||||
convertView = _basicView(activity, this, convertView);
|
||||
|
@ -140,6 +179,7 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
@Override
|
||||
public void handleSelection(final Apple2Activity activity, final Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
final Apple2AbstractMenu.IMenuEnum self = this;
|
||||
_alertDialogHandleSelection(activity, R.string.video_configure, new String[]{
|
||||
settingsMenu.mActivity.getResources().getString(R.string.color_bw),
|
||||
settingsMenu.mActivity.getResources().getString(R.string.color_color),
|
||||
|
@ -147,12 +187,12 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
}, new IPreferenceLoadSave() {
|
||||
@Override
|
||||
public int intValue() {
|
||||
return Apple2Preferences.HIRES_COLOR.intValue(activity);
|
||||
return (int) Apple2Preferences.getJSONPref(self);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveInt(int value) {
|
||||
Apple2Preferences.HIRES_COLOR.saveHiresColor(settingsMenu.mActivity, Apple2Preferences.HiresColor.values()[value]);
|
||||
Apple2Preferences.setJSONPref(self, HiresColor.values()[value].ordinal());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -160,9 +200,23 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
|
||||
public static final int size = SETTINGS.values().length;
|
||||
|
||||
@Override
|
||||
public String getPrefDomain() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefKey() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getPrefDefault() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -170,6 +224,14 @@ public class Apple2VideoSettingsMenu extends Apple2AbstractMenu {
|
|||
return _basicView(activity, this, convertView);
|
||||
}
|
||||
|
||||
public static void applyLandscapeMode(final Apple2Activity activity) {
|
||||
if ((boolean) Apple2Preferences.getJSONPref(SETTINGS.LANDSCAPE_MODE)) {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
|
||||
} else {
|
||||
activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] titles(Apple2Activity activity) {
|
||||
String[] titles = new String[size];
|
||||
int i = 0;
|
||||
|
|
|
@ -50,8 +50,6 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
public final static long NATIVE_TOUCH_JOY_KPAD = (1 << 8);
|
||||
|
||||
public final static long NATIVE_TOUCH_INPUT_DEVICE_CHANGED = (1 << 16);
|
||||
public final static long NATIVE_TOUCH_CPU_SPEED_DEC = (1 << 17);
|
||||
public final static long NATIVE_TOUCH_CPU_SPEED_INC = (1 << 18);
|
||||
|
||||
public final static long NATIVE_TOUCH_ASCII_SCANCODE_SHIFT = 32;
|
||||
public final static long NATIVE_TOUCH_ASCII_SCANCODE_MASK = 0xFFFFL;
|
||||
|
@ -60,16 +58,15 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
|
||||
|
||||
private Apple2Activity mActivity;
|
||||
private Runnable mGraphicsInitializedRunnable;
|
||||
private final InputManagerCompat mInputManager;
|
||||
|
||||
private float[] mXCoords = new float[MAX_FINGERS];
|
||||
private float[] mYCoords = new float[MAX_FINGERS];
|
||||
|
||||
private int mWidth = 0;
|
||||
private int mHeight = 0;
|
||||
|
||||
private static native void nativeGraphicsInitialized(int width, int height, boolean landscape);
|
||||
|
||||
private static native void nativeGraphicsChanged(int width, int height, boolean landscape);
|
||||
private static native void nativeGraphicsInitialized();
|
||||
|
||||
private static native void nativeRender();
|
||||
|
||||
|
@ -78,10 +75,9 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
public static native long nativeOnTouch(int action, int pointerCount, int pointerIndex, float[] xCoords, float[] yCoords);
|
||||
|
||||
|
||||
public Apple2View(Apple2Activity activity, Runnable graphicsInitializedRunnable) {
|
||||
public Apple2View(Apple2Activity activity) {
|
||||
super(activity.getApplication());
|
||||
mActivity = activity;
|
||||
mGraphicsInitializedRunnable = graphicsInitializedRunnable;
|
||||
|
||||
setFocusable(true);
|
||||
setFocusableInTouchMode(true);
|
||||
|
@ -123,13 +119,12 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
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_;
|
||||
if (w != mWidth || h != mHeight) {
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_DEVICE_WIDTH, mWidth);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_DEVICE_HEIGHT, mHeight);
|
||||
}
|
||||
nativeGraphicsChanged(w, h, Apple2Preferences.LANDSCAPE_MODE.booleanValue(Apple2View.this.mActivity));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -358,25 +353,17 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
|
||||
@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));
|
||||
Apple2Preferences.setJSONPref(Apple2CrashHandler.SETTINGS.GL_VENDOR, GLES20.glGetString(GLES20.GL_VENDOR));
|
||||
Apple2Preferences.setJSONPref(Apple2CrashHandler.SETTINGS.GL_RENDERER, GLES20.glGetString(GLES20.GL_RENDERER));
|
||||
Apple2Preferences.setJSONPref(Apple2CrashHandler.SETTINGS.GL_VERSION, GLES20.glGetString(GLES20.GL_VERSION));
|
||||
|
||||
Log.v(TAG, "graphicsInitialized(" + width + ", " + height + ")");
|
||||
|
||||
if (width < height) {
|
||||
// assure landscape dimensions
|
||||
final int w_ = width;
|
||||
width = height;
|
||||
height = w_;
|
||||
}
|
||||
|
||||
nativeGraphicsInitialized(width, height, Apple2Preferences.LANDSCAPE_MODE.booleanValue(Apple2View.this.mActivity));
|
||||
|
||||
if (Apple2View.this.mGraphicsInitializedRunnable != null) {
|
||||
Apple2View.this.mGraphicsInitializedRunnable.run();
|
||||
Apple2View.this.mGraphicsInitializedRunnable = null;
|
||||
}
|
||||
Apple2View.this.mWidth = width;
|
||||
Apple2View.this.mHeight = height;
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_DEVICE_WIDTH, mWidth);
|
||||
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_INTERFACE, Apple2Preferences.PREF_DEVICE_HEIGHT, mHeight);
|
||||
nativeGraphicsInitialized();
|
||||
|
||||
Apple2View.this.mActivity.maybeResumeEmulation();
|
||||
}
|
||||
|
@ -517,7 +504,7 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
}
|
||||
|
||||
if ((nativeFlags & NATIVE_TOUCH_KEY_TAP) != 0) {
|
||||
if (Apple2Preferences.KEYBOARD_CLICK_ENABLED.booleanValue(mActivity)) {
|
||||
if ((boolean) Apple2Preferences.getJSONPref(Apple2KeyboardSettingsMenu.SETTINGS.KEYBOARD_ENABLE_CLICK)) {
|
||||
AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);
|
||||
if (am != null) {
|
||||
am.playSoundEffect(AudioManager.FX_KEY_CLICK);
|
||||
|
@ -532,45 +519,6 @@ class Apple2View extends GLSurfaceView implements InputManagerCompat.InputDevice
|
|||
apple2MenuView.onKeyTapCalibrationEvent(ascii, scancode);
|
||||
}
|
||||
}
|
||||
|
||||
if ((nativeFlags & NATIVE_TOUCH_MENU) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// handle menu-specific actions
|
||||
|
||||
if ((nativeFlags & NATIVE_TOUCH_INPUT_DEVICE_CHANGED) != 0) {
|
||||
Apple2Preferences.TouchDeviceVariant nextVariant;
|
||||
if ((nativeFlags & NATIVE_TOUCH_KBD) != 0) {
|
||||
nextVariant = Apple2Preferences.TouchDeviceVariant.KEYBOARD;
|
||||
} else if ((nativeFlags & NATIVE_TOUCH_JOY) != 0) {
|
||||
nextVariant = Apple2Preferences.TouchDeviceVariant.JOYSTICK;
|
||||
} else if ((nativeFlags & NATIVE_TOUCH_JOY_KPAD) != 0) {
|
||||
nextVariant = Apple2Preferences.TouchDeviceVariant.JOYSTICK_KEYPAD;
|
||||
} else {
|
||||
int touchDevice = Apple2Preferences.nativeGetCurrentTouchDevice();
|
||||
nextVariant = Apple2Preferences.TouchDeviceVariant.next(touchDevice);
|
||||
}
|
||||
Apple2Preferences.CURRENT_TOUCH_DEVICE.saveTouchDevice(mActivity, nextVariant);
|
||||
} else if ((nativeFlags & NATIVE_TOUCH_CPU_SPEED_DEC) != 0) {
|
||||
int percentSpeed = Apple2Preferences.nativeGetCPUSpeed();
|
||||
if (percentSpeed > 400) { // HACK: max value from native side
|
||||
percentSpeed = 375;
|
||||
} else if (percentSpeed > 100) {
|
||||
percentSpeed -= 25;
|
||||
} else {
|
||||
percentSpeed -= 5;
|
||||
}
|
||||
Apple2Preferences.CPU_SPEED_PERCENT.saveInt(mActivity, percentSpeed);
|
||||
} else if ((nativeFlags & NATIVE_TOUCH_CPU_SPEED_INC) != 0) {
|
||||
int percentSpeed = Apple2Preferences.nativeGetCPUSpeed();
|
||||
if (percentSpeed >= 100) {
|
||||
percentSpeed += 25;
|
||||
} else {
|
||||
percentSpeed += 5;
|
||||
}
|
||||
Apple2Preferences.CPU_SPEED_PERCENT.saveInt(mActivity, percentSpeed);
|
||||
}
|
||||
} while (false);
|
||||
|
||||
return true;
|
||||
|
|
|
@ -182,29 +182,24 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeOnCreate(JNIEnv *env, jclas
|
|||
FREE(trfile);
|
||||
#endif
|
||||
|
||||
#if !TESTING
|
||||
#if TESTING
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
timing_initialize();
|
||||
#else
|
||||
cpu_pause();
|
||||
emulator_start();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsChanged(JNIEnv *env, jclass cls, jint width, jint height, jboolean landscape) {
|
||||
// WARNING : this can happen on non-GL thread
|
||||
LOG("width:%d height:%d landscape:%d", width, height, landscape);
|
||||
video_reshape(width, height, landscape);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsInitialized(JNIEnv *env, jclass cls, jint width, jint height, jboolean landscape) {
|
||||
// WARNING : this needs to happen on the GL thread only
|
||||
LOG("width:%d height:%d landscape:%d", width, height, landscape);
|
||||
_video_setRenderThread(pthread_self()); // Assume Android knows what it's doing ;-P
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeGraphicsInitialized(JNIEnv *env, jclass cls) {
|
||||
LOG("...");
|
||||
_video_setRenderThread(pthread_self()); // by definition, this method is called on the render thread ...
|
||||
video_shutdown(false);
|
||||
video_reshape(width, height, landscape);
|
||||
video_init();
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationResume(JNIEnv *env, jclass cls) {
|
||||
jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationResume(JNIEnv *env, jclass cls) {
|
||||
#if TESTING
|
||||
// test driver thread is managing CPU
|
||||
if (!running_tests) {
|
||||
|
@ -214,16 +209,18 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationResume(JNIEnv *env
|
|||
}
|
||||
#else
|
||||
if (!cpu_isPaused()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
LOG("...");
|
||||
cpu_resume();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env, jclass cls) {
|
||||
jboolean Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env, jclass cls) {
|
||||
if (appState != APP_RUNNING) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
#if DO_CPU65_TRACING
|
||||
|
@ -234,7 +231,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env,
|
|||
disk6_flush(1);
|
||||
|
||||
if (cpu_isPaused()) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
LOG("...");
|
||||
|
||||
|
@ -242,7 +239,10 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEmulationPause(JNIEnv *env,
|
|||
// test driver thread is managing CPU
|
||||
#else
|
||||
cpu_pause();
|
||||
prefs_save();
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2View_nativeRender(JNIEnv *env, jclass cls) {
|
||||
|
@ -341,7 +341,7 @@ jlong Java_org_deadc0de_apple2ix_Apple2View_nativeOnTouch(JNIEnv *env, jclass cl
|
|||
return flags;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, jclass cls, jstring jPath, jboolean driveA, jboolean readOnly) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2DisksMenu_nativeChooseDisk(JNIEnv *env, jclass cls, jstring jPath, jboolean driveA, jboolean readOnly) {
|
||||
const char *path = (*env)->GetStringUTFChars(env, jPath, NULL);
|
||||
int drive = driveA ? 0 : 1;
|
||||
int ro = readOnly ? 1 : 0;
|
||||
|
@ -366,7 +366,7 @@ void Java_org_deadc0de_apple2ix_Apple2Activity_nativeChooseDisk(JNIEnv *env, jcl
|
|||
(*env)->ReleaseStringUTFChars(env, jPath, path);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Activity_nativeEjectDisk(JNIEnv *env, jclass cls, jboolean driveA) {
|
||||
void Java_org_deadc0de_apple2ix_Apple2DisksMenu_nativeEjectDisk(JNIEnv *env, jclass cls, jboolean driveA) {
|
||||
LOG("...");
|
||||
disk6_eject(!driveA);
|
||||
}
|
||||
|
@ -413,3 +413,19 @@ jstring Java_org_deadc0de_apple2ix_Apple2Activity_nativeLoadState(JNIEnv *env, j
|
|||
return jstr;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativePrefsSync(JNIEnv *env, jclass cls, jstring jDomain) {
|
||||
const char *domain = NULL;
|
||||
|
||||
if (jDomain) {
|
||||
domain = (*env)->GetStringUTFChars(env, jDomain, 0);
|
||||
}
|
||||
|
||||
LOG("... domain: %s", domain);
|
||||
prefs_load();
|
||||
prefs_sync(domain);
|
||||
|
||||
if (jDomain) {
|
||||
(*env)->ReleaseStringUTFChars(env, jDomain, domain);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,334 +0,0 @@
|
|||
/*
|
||||
* Apple // emulator for *ix
|
||||
*
|
||||
* This software package is subject to the GNU General Public License
|
||||
* version 3 or later (your choice) as published by the Free Software
|
||||
* Foundation.
|
||||
*
|
||||
* Copyright 2015 Aaron Culliney
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
typedef enum AndroidTouchJoystickButtonValues {
|
||||
//ANDROID_TOUCHJOY_NONE = 0,
|
||||
ANDROID_TOUCHJOY_BUTTON0 = 1,
|
||||
ANDROID_TOUCHJOY_BUTTON1,
|
||||
ANDROID_TOUCHJOY_BUTTON_BOTH,
|
||||
} AndroidTouchJoystickButtonValues;
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetColor(JNIEnv *env, jclass cls, jint color) {
|
||||
LOG("color : %d", color);
|
||||
#if TESTING
|
||||
color_mode = COLOR;
|
||||
#else
|
||||
if (color < COLOR_NONE || color > COLOR_INTERP) {
|
||||
return;
|
||||
}
|
||||
color_mode = color;
|
||||
|
||||
video_reset();
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetSpeakerEnabled(JNIEnv *env, jclass cls, jboolean enabled) {
|
||||
LOG("enabled : %d", true);
|
||||
// NO-OP ... speaker should always be enabled (but volume could be zero)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetSpeakerVolume(JNIEnv *env, jclass cls, jint goesToTen) {
|
||||
LOG("volume : %d", goesToTen);
|
||||
assert(goesToTen >= 0);
|
||||
sound_volume = goesToTen;
|
||||
#warning FIXME TODO refactor/remove sound_volume ?
|
||||
vm_reinitializeAudio();
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetAudioLatency(JNIEnv *env, jclass cls, jfloat latencySecs) {
|
||||
#if !TESTING
|
||||
LOG("audio latency : %fsecs", latencySecs);
|
||||
assert(cpu_isPaused());
|
||||
audio_setLatency(latencySecs);
|
||||
timing_reinitializeAudio();
|
||||
#endif
|
||||
}
|
||||
|
||||
jboolean Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetMockingboardEnabled(JNIEnv *env, jclass cls, jboolean enabled) {
|
||||
#if !TESTING
|
||||
LOG("mockingboard enabled : %d", enabled);
|
||||
assert(cpu_isPaused());
|
||||
MB_SetEnabled(enabled);
|
||||
timing_reinitializeAudio();
|
||||
#endif
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetMockingboardVolume(JNIEnv *env, jclass cls, jint goesToTen) {
|
||||
LOG("mockingboard volume : %d", goesToTen);
|
||||
assert(goesToTen >= 0);
|
||||
MB_SetVolumeZeroToTen(goesToTen);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetCurrentTouchDevice(JNIEnv *env, jclass cls, jint touchDevice) {
|
||||
LOG("current touch device : %d", touchDevice);
|
||||
assert(touchDevice >= 0 && touchDevice < TOUCH_DEVICE_DEVICE_MAX);
|
||||
switch (touchDevice) {
|
||||
case TOUCH_DEVICE_JOYSTICK:
|
||||
keydriver_setTouchKeyboardOwnsScreen(false);
|
||||
joydriver_setTouchJoystickOwnsScreen(true);
|
||||
joydriver_setTouchVariant(EMULATED_JOYSTICK);
|
||||
video_animations->animation_showTouchJoystick();
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_JOYSTICK_KEYPAD:
|
||||
keydriver_setTouchKeyboardOwnsScreen(false);
|
||||
joydriver_setTouchJoystickOwnsScreen(true);
|
||||
joydriver_setTouchVariant(EMULATED_KEYPAD);
|
||||
video_animations->animation_showTouchJoystick();
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_KEYBOARD:
|
||||
keydriver_setTouchKeyboardOwnsScreen(true);
|
||||
joydriver_setTouchJoystickOwnsScreen(false);
|
||||
video_animations->animation_showTouchKeyboard();
|
||||
break;
|
||||
|
||||
case TOUCH_DEVICE_NONE:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickVisibility(JNIEnv *env, jclass cls, jboolean visibility) {
|
||||
LOG("visibility: %d", visibility);
|
||||
joydriver_setShowControls(visibility);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickAzimuthVisibility(JNIEnv *env, jclass cls, jboolean visibility) {
|
||||
LOG("visibility: %d", visibility);
|
||||
joydriver_setShowAzimuth(visibility);
|
||||
}
|
||||
|
||||
jint Java_org_deadc0de_apple2ix_Apple2Preferences_nativeGetCurrentTouchDevice(JNIEnv *env, jclass cls) {
|
||||
LOG("%s", "");
|
||||
if (joydriver_ownsScreen()) {
|
||||
touchjoy_variant_t variant = joydriver_getTouchVariant();
|
||||
if (variant == EMULATED_JOYSTICK) {
|
||||
return TOUCH_DEVICE_JOYSTICK;
|
||||
} else if (variant == EMULATED_KEYPAD) {
|
||||
return TOUCH_DEVICE_JOYSTICK_KEYPAD;
|
||||
}
|
||||
} else if (keydriver_ownsScreen()) {
|
||||
return TOUCH_DEVICE_KEYBOARD;
|
||||
}
|
||||
return TOUCH_DEVICE_NONE;
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchMenuEnabled(JNIEnv *env, jclass cls, jboolean enabled) {
|
||||
LOG("enabled : %d", enabled);
|
||||
interface_setTouchMenuEnabled(enabled);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetShowDiskOperationAnimation(JNIEnv *env, jclass cls, jboolean enabled) {
|
||||
LOG("enabled : %d", enabled);
|
||||
if (video_animations && video_animations->animation_setEnableShowTrackSector) {
|
||||
video_animations->animation_setEnableShowTrackSector(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchKeyboardLowercaseEnabled(JNIEnv *env, jclass cls, jboolean enabled) {
|
||||
LOG("enabled : %d", enabled);
|
||||
keydriver_setLowercaseEnabled(enabled);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchKeyboardVisibility(JNIEnv *env, jclass cls, jfloat inactiveAlpha, jfloat activeAlpha) {
|
||||
LOG("inactive:%f active:%f", inactiveAlpha, activeAlpha);
|
||||
keydriver_setVisibilityWhenOwnsScreen(inactiveAlpha, activeAlpha);
|
||||
interface_setTouchMenuVisibility(inactiveAlpha, activeAlpha);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchKeyboardGlyphScale(JNIEnv *env, jclass cls, jint glyphScale) {
|
||||
LOG("glyphScale:%d", glyphScale);
|
||||
keydriver_setGlyphScale(glyphScale);
|
||||
interface_setGlyphScale(glyphScale);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickButtonTypes(JNIEnv *env, jclass cls, jint touchDownButton, jint northButton, jint southButton) {
|
||||
LOG(": %d,%d,%d", touchDownButton, northButton, southButton);
|
||||
|
||||
touchDownButton -= 1;
|
||||
northButton -= 1;
|
||||
southButton -= 1;
|
||||
if (touchDownButton < TOUCH_NONE || touchDownButton > TOUCH_BOTH) {
|
||||
ERRLOG("OOPS, invalid parameter!");
|
||||
return;
|
||||
}
|
||||
if (northButton < TOUCH_NONE || northButton > TOUCH_BOTH) {
|
||||
ERRLOG("OOPS, invalid parameter!");
|
||||
return;
|
||||
}
|
||||
if (southButton < TOUCH_NONE || southButton > TOUCH_BOTH) {
|
||||
ERRLOG("OOPS, invalid parameter!");
|
||||
return;
|
||||
}
|
||||
|
||||
uint8_t rosetteChars[ROSETTE_COLS * ROSETTE_ROWS];
|
||||
int rosetteScancodes[ROSETTE_COLS * ROSETTE_ROWS];
|
||||
rosetteChars[ROSETTE_NORTHWEST] = ' '; rosetteScancodes[ROSETTE_NORTHWEST] = -1;
|
||||
rosetteChars[ROSETTE_NORTH] = (uint8_t)MOUSETEXT_UP; rosetteScancodes[ROSETTE_NORTH] = -1;
|
||||
rosetteChars[ROSETTE_NORTHEAST] = ' '; rosetteScancodes[ROSETTE_NORTHEAST] = -1;
|
||||
rosetteChars[ROSETTE_WEST] = (uint8_t)MOUSETEXT_LEFT; rosetteScancodes[ROSETTE_WEST] = -1;
|
||||
rosetteChars[ROSETTE_CENTER] = ICONTEXT_MENU_TOUCHJOY; rosetteScancodes[ROSETTE_CENTER] = -1;
|
||||
rosetteChars[ROSETTE_EAST] = (uint8_t)MOUSETEXT_RIGHT; rosetteScancodes[ROSETTE_EAST] = -1;
|
||||
rosetteChars[ROSETTE_SOUTHWEST] = ' '; rosetteScancodes[ROSETTE_SOUTHWEST] = -1;
|
||||
rosetteChars[ROSETTE_SOUTH] = (uint8_t)MOUSETEXT_DOWN; rosetteScancodes[ROSETTE_SOUTH] = -1;
|
||||
rosetteChars[ROSETTE_SOUTHEAST] = ' '; rosetteScancodes[ROSETTE_SOUTHEAST] = -1;
|
||||
joydriver_setTouchAxisTypes(rosetteChars, rosetteScancodes);
|
||||
joydriver_setTouchButtonTypes((touchjoy_button_type_t)touchDownButton, -1, (touchjoy_button_type_t)northButton, -1, (touchjoy_button_type_t)southButton, -1);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickTapDelay(JNIEnv *env, jclass cls, jfloat secs) {
|
||||
LOG("tap delay : %f", secs);
|
||||
joydriver_setTapDelay(secs);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickAxisSensitivity(JNIEnv *env, jclass cls, jfloat multiplier) {
|
||||
LOG("axis sensitivity : %f", multiplier);
|
||||
joydriver_setTouchAxisSensitivity(multiplier);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchJoystickButtonSwitchThreshold(JNIEnv *env, jclass cls, jint delta) {
|
||||
LOG("delta : %d", delta);
|
||||
joydriver_setButtonSwitchThreshold(delta);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickSetScreenDivision(JNIEnv *env, jclass cls, jfloat division) {
|
||||
LOG("division : %f", division);
|
||||
joydriver_setScreenDivision(division);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickSetAxisOnLeft(JNIEnv *env, jclass cls, jboolean axisIsOnLeft) {
|
||||
LOG("axis on left : %d", axisIsOnLeft);
|
||||
joydriver_setAxisOnLeft(axisIsOnLeft);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchJoystickSetKeypadTypes(JNIEnv *env, jclass cls,
|
||||
jintArray jRosetteChars, jintArray jRosetteScans, jintArray jButtonsChars, jintArray jButtonsScans)
|
||||
{
|
||||
jint *rosetteChars = (*env)->GetIntArrayElements(env, jRosetteChars, 0);
|
||||
jint *rosetteScans = (*env)->GetIntArrayElements(env, jRosetteScans, 0);
|
||||
jint *buttonsChars = (*env)->GetIntArrayElements(env, jButtonsChars, 0);
|
||||
jint *buttonsScans = (*env)->GetIntArrayElements(env, jButtonsScans, 0);
|
||||
|
||||
LOG("NW:%c/%d, N:%c/%d, NE:%c/%d, ... SWIPEUP:%c/%d",
|
||||
(char)rosetteChars[0], rosetteScans[0], (char)rosetteChars[1], rosetteScans[1], (char)rosetteChars[2], rosetteScans[2],
|
||||
(char)buttonsChars[1], buttonsScans[1]);
|
||||
LOG(" W:%c/%d, C:%c/%d, E:%c/%d, ... TAP:%c/%d",
|
||||
(char)rosetteChars[3], rosetteScans[3], (char)rosetteChars[4], rosetteScans[4], (char)rosetteChars[5], rosetteScans[5],
|
||||
(char)buttonsChars[0], buttonsScans[0]);
|
||||
LOG("SW:%c/%d, S:%c/%d, SE:%c/%d, ... SWIPEDN:%c/%d",
|
||||
(char)rosetteChars[6], rosetteScans[6], (char)rosetteChars[7], rosetteScans[7], (char)rosetteChars[8], rosetteScans[8],
|
||||
(char)buttonsChars[2], buttonsScans[2]);
|
||||
|
||||
// we could just pass these as jcharArray ... but this isn't a tight loop =P
|
||||
uint8_t actualChars[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
for (unsigned int i=0; i<(ROSETTE_ROWS * ROSETTE_COLS); i++) {
|
||||
actualChars[i] = (uint8_t)rosetteChars[i];
|
||||
}
|
||||
if (actualChars[4] == ICONTEXT_NONACTIONABLE) {
|
||||
actualChars[4] = ICONTEXT_MENU_TOUCHJOY;
|
||||
}
|
||||
joydriver_setTouchAxisTypes(actualChars, rosetteScans);
|
||||
joydriver_setTouchButtonTypes(
|
||||
(touchjoy_button_type_t)buttonsChars[0], buttonsScans[0],
|
||||
(touchjoy_button_type_t)buttonsChars[1], buttonsScans[1],
|
||||
(touchjoy_button_type_t)buttonsChars[2], buttonsScans[2]);
|
||||
|
||||
(*env)->ReleaseIntArrayElements(env, jRosetteChars, rosetteChars, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jRosetteScans, rosetteScans, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jButtonsChars, buttonsChars, 0);
|
||||
(*env)->ReleaseIntArrayElements(env, jButtonsScans, buttonsScans, 0);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchDeviceBeginCalibrationMode(JNIEnv *env, jclass cls) {
|
||||
LOG("%s", "");
|
||||
if (joydriver_ownsScreen()) {
|
||||
joydriver_beginCalibration();
|
||||
} else if (keydriver_ownsScreen()) {
|
||||
keydriver_beginCalibration();
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeTouchDeviceEndCalibrationMode(JNIEnv *env, jclass cls) {
|
||||
LOG("%s", "");
|
||||
if (joydriver_ownsScreen()) {
|
||||
joydriver_endCalibration();
|
||||
} else if (keydriver_ownsScreen()) {
|
||||
keydriver_endCalibration();
|
||||
}
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchDeviceKeyRepeatThreshold(JNIEnv *env, jclass cls, jfloat threshold) {
|
||||
LOG("threshold : %f", threshold);
|
||||
joydriver_setKeyRepeatThreshold(threshold);
|
||||
}
|
||||
|
||||
jint Java_org_deadc0de_apple2ix_Apple2Preferences_nativeGetCPUSpeed(JNIEnv *env, jclass cls) {
|
||||
LOG("%s", "");
|
||||
return (jint)round(cpu_scale_factor * 100.0);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetCPUSpeed(JNIEnv *env, jclass cls, jint percentSpeed) {
|
||||
LOG("percentSpeed : %d%%", percentSpeed);
|
||||
#if TESTING
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
timing_initialize();
|
||||
#else
|
||||
bool wasPaused = cpu_isPaused();
|
||||
|
||||
if (!wasPaused) {
|
||||
cpu_pause();
|
||||
}
|
||||
|
||||
cpu_scale_factor = percentSpeed/100.0;
|
||||
if (cpu_scale_factor > CPU_SCALE_FASTEST) {
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
if (cpu_scale_factor < CPU_SCALE_SLOWEST) {
|
||||
cpu_scale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
|
||||
if (video_animations->animation_showCPUSpeed) {
|
||||
video_animations->animation_showCPUSpeed();
|
||||
}
|
||||
|
||||
timing_initialize();
|
||||
|
||||
if (!wasPaused) {
|
||||
cpu_resume();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeLoadTouchKeyboardJSON(JNIEnv *env, jclass cls, jstring j_jsonPath) {
|
||||
const char *jsonPath = (*env)->GetStringUTFChars(env, j_jsonPath, 0);
|
||||
LOG("jsonPath: %s", jsonPath);
|
||||
keydriver_loadAltKbd(jsonPath);
|
||||
(*env)->ReleaseStringUTFChars(env, j_jsonPath, jsonPath);
|
||||
}
|
||||
|
||||
void Java_org_deadc0de_apple2ix_Apple2Preferences_nativeSetTouchModelPreferences(JNIEnv *env, jclass cls, jint modelType, jstring j_jsonString) {
|
||||
const char *jsonString = (*env)->GetStringUTFChars(env, j_jsonString, 0);
|
||||
LOG("model: %d", modelType);
|
||||
|
||||
void (*setData)(const char *) = interface_getModelDataSetter((interface_device_t)modelType);
|
||||
setData(jsonString);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, j_jsonString, jsonString);
|
||||
}
|
||||
|
|
@ -31,13 +31,13 @@ APPLE2_AUDIO_SRC = \
|
|||
APPLE2_META_SRC = \
|
||||
$(APPLE2_SRC_PATH)/meta/debug.c $(APPLE2_SRC_PATH)/meta/debugger.c $(APPLE2_SRC_PATH)/meta/opcodes.c \
|
||||
$(APPLE2_SRC_PATH)/meta/lintrace.c $(APPLE2_SRC_PATH)/test/sha1.c $(APPLE2_SRC_PATH)/json_parse.c \
|
||||
$(APPLE2_SRC_PATH)/../externals/jsmn/jsmn.c
|
||||
$(APPLE2_SRC_PATH)/memmngt.c $(APPLE2_SRC_PATH)/../externals/jsmn/jsmn.c
|
||||
|
||||
APPLE2_MAIN_SRC = \
|
||||
$(APPLE2_SRC_PATH)/font.c $(APPLE2_SRC_PATH)/rom.c $(APPLE2_SRC_PATH)/misc.c $(APPLE2_SRC_PATH)/display.c $(APPLE2_SRC_PATH)/vm.c \
|
||||
$(APPLE2_SRC_PATH)/timing.c $(APPLE2_SRC_PATH)/zlib-helpers.c $(APPLE2_SRC_PATH)/joystick.c $(APPLE2_SRC_PATH)/keys.c \
|
||||
$(APPLE2_SRC_PATH)/interface.c $(APPLE2_SRC_PATH)/disk.c $(APPLE2_SRC_PATH)/cpu-supp.c \
|
||||
jnihooks.c jniprefs.c androidkeys.c
|
||||
$(APPLE2_SRC_PATH)/interface.c $(APPLE2_SRC_PATH)/disk.c $(APPLE2_SRC_PATH)/cpu-supp.c $(APPLE2_SRC_PATH)/prefs.c \
|
||||
jnihooks.c androidkeys.c
|
||||
|
||||
APPLE2_OPTIM_CFLAGS := -O2
|
||||
APPLE2_BASE_CFLAGS := -DAPPLE2IX=1 -DINTERFACE_TOUCH=1 -DMOBILE_DEVICE=1 -DVIDEO_OPENGL=1 -DDEBUGGER=1 -DAUDIO_ENABLED=1 -std=gnu11 -DPREVENT_TEXTREL=1 -fPIC $(APPLE2_OPTIM_CFLAGS) -I$(APPLE2_SRC_PATH)
|
||||
|
|
|
@ -2370,3 +2370,20 @@ unsigned long MB_SetSnapshot(SS_CARD_MOCKINGBOARD* pSS, unsigned long dwSlot_unu
|
|||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void mb_prefsChanged(const char *domain) {
|
||||
long goesToTen = 0;
|
||||
prefs_parseLongValue(domain, PREF_MOCKINGBOARD_VOLUME, &goesToTen, /*base:*/10); // expected range 0-10
|
||||
if (goesToTen < 0) {
|
||||
goesToTen = 0;
|
||||
}
|
||||
if (goesToTen > 10) {
|
||||
goesToTen = 10;
|
||||
}
|
||||
MB_SetVolumeZeroToTen(goesToTen);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_mockingboard(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_AUDIO, &mb_prefsChanged);
|
||||
}
|
||||
|
|
|
@ -648,8 +648,6 @@ void cpu65_uninterrupt(int reason)
|
|||
}
|
||||
|
||||
void cpu65_reboot(void) {
|
||||
timing_initialize();
|
||||
video_reset();
|
||||
joy_button0 = 0xff; // OpenApple
|
||||
cpu65_interrupt(ResetSig);
|
||||
}
|
||||
|
|
|
@ -477,13 +477,13 @@ static void _initialize_color() {
|
|||
}
|
||||
|
||||
static void video_prefsChanged(const char *domain) {
|
||||
long val = 0;
|
||||
long val = COLOR_INTERP;
|
||||
prefs_parseLongValue(domain, PREF_COLOR_MODE, &val, /*base:*/10);
|
||||
if (val < 0) {
|
||||
val = 0;
|
||||
val = COLOR_INTERP;
|
||||
}
|
||||
if (val >= NUM_COLOROPTS) {
|
||||
val = NUM_COLOROPTS-1;
|
||||
val = COLOR_INTERP;
|
||||
}
|
||||
color_mode = (color_mode_t)val;
|
||||
video_reset();
|
||||
|
@ -1212,26 +1212,25 @@ void _video_setRenderThread(pthread_t id) {
|
|||
render_thread_id = id;
|
||||
}
|
||||
|
||||
bool video_isRenderThread(void) {
|
||||
return (pthread_self() == render_thread_id);
|
||||
}
|
||||
|
||||
void video_shutdown(bool emulatorShuttingDown) {
|
||||
|
||||
#if MOBILE_DEVICE
|
||||
// WARNING : shutdown should occur on the render thread. Platform code (iOS, Android) should ensure this is called
|
||||
// from within a render pass...
|
||||
assert(pthread_self() == render_thread_id);
|
||||
assert(!render_thread_id || pthread_self() == render_thread_id);
|
||||
#endif
|
||||
|
||||
video_backend->shutdown(emulatorShuttingDown);
|
||||
render_thread_id = 0;
|
||||
|
||||
if (pthread_self() == render_thread_id) {
|
||||
FREE(video__fb);
|
||||
}
|
||||
}
|
||||
|
||||
void video_reshape(int w, int h, bool landscape) {
|
||||
video_backend->reshape(w, h, landscape);
|
||||
}
|
||||
|
||||
void video_render(void) {
|
||||
assert(pthread_self() == render_thread_id);
|
||||
video_backend->render();
|
||||
|
|
|
@ -39,7 +39,6 @@ typedef struct video_animation_s {
|
|||
void (*animation_showCPUSpeed)(void);
|
||||
void (*animation_showDiskChosen)(int drive);
|
||||
void (*animation_showTrackSector)(int drive, int track, int sect);
|
||||
void (*animation_setEnableShowTrackSector)(bool enabled);
|
||||
|
||||
} video_animation_s;
|
||||
|
||||
|
@ -78,9 +77,9 @@ void video_render(void);
|
|||
void _video_setRenderThread(pthread_t id);
|
||||
|
||||
/*
|
||||
* Reshape the display to particular dimensions.
|
||||
* Check if running on render thread.
|
||||
*/
|
||||
void video_reshape(int w, int h, bool landscape);
|
||||
bool video_isRenderThread(void);
|
||||
|
||||
/*
|
||||
* Setup the display. This may be called multiple times in a run, and is
|
||||
|
|
|
@ -18,11 +18,6 @@
|
|||
#if INTERFACE_TOUCH
|
||||
// touch interface managed elsewhere
|
||||
int64_t (*interface_onTouchEvent)(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) = NULL;
|
||||
bool (*interface_isTouchMenuAvailable)(void) = NULL;
|
||||
void (*interface_setTouchMenuEnabled)(bool enabled) = NULL;
|
||||
void (*interface_setTouchMenuVisibility)(float inactiveAlpha, float activeAlpha) = NULL;
|
||||
void (*interface_setGlyphScale)(int glyphScale) = NULL;
|
||||
void (*(*interface_getModelDataSetter)(interface_device_t device))(const char *jsonData) = NULL;
|
||||
#endif
|
||||
|
||||
static char disk_path[PATH_MAX] = { 0 };
|
||||
|
@ -976,7 +971,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_scale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, cpu_scale_factor);
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, roundf(cpu_scale_factor * 100.f));
|
||||
break;
|
||||
|
||||
case OPT_ALTCPU:
|
||||
|
@ -985,7 +980,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_altscale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, cpu_altscale_factor);
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, roundf(cpu_altscale_factor * 100.f));
|
||||
break;
|
||||
|
||||
case OPT_PATH:
|
||||
|
@ -1061,7 +1056,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, cpu_scale_factor);
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, roundf(cpu_scale_factor * 100.f));
|
||||
break;
|
||||
|
||||
case OPT_ALTCPU:
|
||||
|
@ -1070,7 +1065,7 @@ void c_interface_parameters()
|
|||
{
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, cpu_altscale_factor);
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE_ALT, roundf(cpu_altscale_factor * 100.f));
|
||||
break;
|
||||
|
||||
case OPT_PATH:
|
||||
|
|
|
@ -82,22 +82,7 @@ typedef enum interface_touch_event_flags {
|
|||
|
||||
// handle touch event
|
||||
extern int64_t (*interface_onTouchEvent)(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords);
|
||||
|
||||
// is the touch menu module itself available?
|
||||
extern bool (*interface_isTouchMenuAvailable)(void);
|
||||
|
||||
// enable/disable touch menu HUD element
|
||||
extern void (*interface_setTouchMenuEnabled)(bool enabled);
|
||||
|
||||
// set min/max alpha visibility of touch menu HUD element
|
||||
extern void (*interface_setTouchMenuVisibility)(float inactiveAlpha, float activeAlpha);
|
||||
|
||||
// set a finer-grained font size (if glyphScale > 1)
|
||||
extern void (*interface_setGlyphScale)(int glyphScale);
|
||||
|
||||
// get model-specific functions
|
||||
extern void (*(*interface_getModelDataSetter)(interface_device_t device))(const char *jsonData);
|
||||
#endif
|
||||
#endif // INTERFACE_TOUCH
|
||||
|
||||
#define EXT_GZ ".gz"
|
||||
#define _GZLEN (sizeof(EXT_GZ)-1)
|
||||
|
|
|
@ -330,28 +330,3 @@ void joydriver_setButton1Pressed(bool pressed) {
|
|||
joy_button1 = (pressed) ? 0x80 : 0x0;
|
||||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
bool (*joydriver_isTouchJoystickAvailable)(void) = NULL;
|
||||
void (*joydriver_setTouchJoystickEnabled)(bool enabled) = NULL;
|
||||
void (*joydriver_setTouchJoystickOwnsScreen)(bool pwnd) = NULL;
|
||||
bool (*joydriver_ownsScreen)(void) = NULL;
|
||||
void (*joydriver_setTouchButtonTypes)(
|
||||
touchjoy_button_type_t touchDownChar, int downScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode) = NULL;
|
||||
void (*joydriver_setTapDelay)(float secs) = NULL;
|
||||
void (*joydriver_setTouchAxisSensitivity)(float multiplier) = NULL;
|
||||
void (*joydriver_setButtonSwitchThreshold)(int delta) = NULL;
|
||||
void (*joydriver_setTouchVariant)(touchjoy_variant_t variant) = NULL;
|
||||
touchjoy_variant_t (*joydriver_getTouchVariant)(void) = NULL;
|
||||
void (*joydriver_setTouchAxisTypes)(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]) = NULL;
|
||||
void (*joydriver_setScreenDivision)(float division) = NULL;
|
||||
void (*joydriver_setAxisOnLeft)(bool axisIsOnLeft) = NULL;
|
||||
void (*joydriver_beginCalibration)(void) = NULL;
|
||||
void (*joydriver_endCalibration)(void) = NULL;
|
||||
bool (*joydriver_isCalibrating)(void) = NULL;
|
||||
void (*joydriver_setShowControls)(bool showControls) = NULL;
|
||||
void (*joydriver_setShowAzimuth)(bool showAzimuth) = NULL;
|
||||
void (*joydriver_setKeyRepeatThreshold)(float repeatThresholdSecs) = NULL;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -64,103 +64,14 @@ void joydriver_setButton1Pressed(bool pressed);
|
|||
|
||||
#if INTERFACE_TOUCH
|
||||
|
||||
typedef enum touchjoy_variant_t {
|
||||
EMULATED_NONE = 0,
|
||||
EMULATED_JOYSTICK, // touch interface emulates a physical joystick device
|
||||
EMULATED_KEYPAD, // touch interface generates key presses
|
||||
} touchjoy_variant_t;
|
||||
|
||||
typedef enum touchjoy_button_type_t {
|
||||
TOUCH_NONE = -1,
|
||||
TOUCH_BUTTON0 = 0,
|
||||
TOUCH_BUTTON1,
|
||||
TOUCH_NONE = 0,
|
||||
TOUCH_BUTTON1 = 1,
|
||||
TOUCH_BUTTON2,
|
||||
TOUCH_BOTH,
|
||||
// --or-- an ASCII/fonttext value ...
|
||||
} touchjoy_button_type_t;
|
||||
|
||||
#define ROSETTE_ROWS 3
|
||||
#define ROSETTE_COLS 3
|
||||
|
||||
enum {
|
||||
ROSETTE_NORTHWEST=0,
|
||||
ROSETTE_NORTH,
|
||||
ROSETTE_NORTHEAST,
|
||||
ROSETTE_WEST,
|
||||
ROSETTE_CENTER,
|
||||
ROSETTE_EAST,
|
||||
ROSETTE_SOUTHWEST,
|
||||
ROSETTE_SOUTH,
|
||||
ROSETTE_SOUTHEAST,
|
||||
};
|
||||
|
||||
// is the touch joystick available
|
||||
extern bool (*joydriver_isTouchJoystickAvailable)(void);
|
||||
|
||||
// enable/disable touch joystick
|
||||
extern void (*joydriver_setTouchJoystickEnabled)(bool enabled);
|
||||
|
||||
// grant/remove ownership of touch screen
|
||||
extern void (*joydriver_setTouchJoystickOwnsScreen)(bool pwnd);
|
||||
|
||||
// query touch screen ownership
|
||||
extern bool (*joydriver_ownsScreen)(void);
|
||||
|
||||
/*
|
||||
* set the joystick button types/visuals (scancodes are fired for EMULATED_KEYPAD variant)
|
||||
*
|
||||
* - for EMULATED_JOYSTICK, there is an implicit extra layer-of-indirection for the touchjoy_button_type_t, which maps
|
||||
* to the open apple, closed apple, or "both" visual keys
|
||||
*
|
||||
* - for EMULATED_KEYPAD, the touchjoy_button_type_t is the displayed visual (as ASCII value and lookup into font
|
||||
* table)
|
||||
*/
|
||||
extern void (*joydriver_setTouchButtonTypes)(
|
||||
touchjoy_button_type_t touchDownChar, int downScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode);
|
||||
|
||||
// set the button tap delay (to differentiate between single tap and north/south/etc swipe)
|
||||
extern void (*joydriver_setTapDelay)(float secs);
|
||||
|
||||
// set the sensitivity multiplier
|
||||
extern void (*joydriver_setTouchAxisSensitivity)(float multiplier);
|
||||
|
||||
// set the touch button switch threshold
|
||||
extern void (*joydriver_setButtonSwitchThreshold)(int delta);
|
||||
|
||||
// set the joystick variant
|
||||
extern void (*joydriver_setTouchVariant)(touchjoy_variant_t variant);
|
||||
|
||||
// get the joystick variant
|
||||
extern touchjoy_variant_t (*joydriver_getTouchVariant)(void);
|
||||
|
||||
// set the axis visuals (scancodes are fired for EMULATED_KEYPAD variant)
|
||||
extern void (*joydriver_setTouchAxisTypes)(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]);
|
||||
|
||||
// set screen divide between axis and buttons
|
||||
extern void (*joydriver_setScreenDivision)(float division);
|
||||
|
||||
// swap axis and buttons sides
|
||||
extern void (*joydriver_setAxisOnLeft)(bool axisIsOnLeft);
|
||||
|
||||
// begin calibration mode
|
||||
extern void (*joydriver_beginCalibration)(void);
|
||||
|
||||
// end calibration mode
|
||||
extern void (*joydriver_endCalibration)(void);
|
||||
|
||||
// end calibration mode
|
||||
extern bool (*joydriver_isCalibrating)(void);
|
||||
|
||||
// set controls visibility
|
||||
extern void (*joydriver_setShowControls)(bool showControls);
|
||||
|
||||
// set azimuth visibility
|
||||
extern void (*joydriver_setShowAzimuth)(bool showAzimuth);
|
||||
|
||||
// set key repeat threshold (keypad joystick)
|
||||
extern void (*joydriver_setKeyRepeatThreshold)(float repeatThresholdSecs);
|
||||
|
||||
#endif // INTERFACE_TOUCH
|
||||
|
||||
#endif // whole file
|
||||
|
|
|
@ -1065,6 +1065,30 @@ bool json_serialize(JSON_ref jsonRef, int fd, bool pretty) {
|
|||
}
|
||||
}
|
||||
|
||||
bool json_unescapeSlashes(char **kbdPath) {
|
||||
// A big "fhank-you" to the Java org.json.JSONStringer API which "helpfully" escapes slash '/' characters
|
||||
if (!kbdPath) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char *str = *kbdPath;
|
||||
size_t len = strlen(str) + 1; // include termination \0
|
||||
char *p0 = NULL;
|
||||
char *p = str;
|
||||
while (*p) {
|
||||
if (*p == '/') {
|
||||
if (p0 && *p0 == '\\') {
|
||||
memmove(p0, p, ((str+len)-p));
|
||||
p = p0;
|
||||
}
|
||||
}
|
||||
p0 = p;
|
||||
++p;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void json_destroy(JSON_ref *jsonRef) {
|
||||
if (!jsonRef) {
|
||||
return;
|
||||
|
|
|
@ -101,11 +101,14 @@ bool json_arrayParseFloatValueAtIndex(const JSON_ref array, unsigned long index,
|
|||
//bool json_arraySetFloatValue(const JSON_ref array, unsigned long index, float val);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// serialization
|
||||
// serialization & misc
|
||||
|
||||
// serialize to file descriptor
|
||||
bool json_serialize(JSON_ref json, int fd, bool pretty);
|
||||
|
||||
// unescape all \/ characters (<sigh> a big fhank you to Java org.json.JSONXXX !)
|
||||
bool json_unescapeSlashes(char **kbdPath);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// destructor
|
||||
void json_destroy(JSON_ref *jsonRef);
|
||||
|
|
10
src/keys.c
10
src/keys.c
|
@ -494,16 +494,6 @@ static __attribute__((constructor)) void __init_keys(void) {
|
|||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
bool (*keydriver_isTouchKeyboardAvailable)(void) = NULL;
|
||||
void (*keydriver_setTouchKeyboardEnabled)(bool enabled) = NULL;
|
||||
void (*keydriver_setTouchKeyboardOwnsScreen)(bool pwnd) = NULL;
|
||||
bool (*keydriver_ownsScreen)(void) = NULL;
|
||||
void (*keydriver_setGlyphScale)(int glyphScale) = NULL;
|
||||
void (*keydriver_setVisibilityWhenOwnsScreen)(float inactiveAlpha, float activeAlpha) = NULL;
|
||||
void (*keydriver_setLowercaseEnabled)(bool enabled) = NULL;
|
||||
void (*keydriver_keyboardReadCallback)(void) = NULL;
|
||||
void (*keydriver_beginCalibration)(void) = NULL;
|
||||
void (*keydriver_endCalibration)(void) = NULL;
|
||||
void (*keydriver_loadAltKbd)(const char *kbdPath) = NULL;
|
||||
#endif
|
||||
|
||||
|
|
30
src/keys.h
30
src/keys.h
|
@ -149,38 +149,8 @@ int c_keys_ascii_to_scancode(int ch);
|
|||
void c_keys_handle_input(int scancode, int pressed, int is_cooked);
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
// is the touch keyboard module itself available?
|
||||
extern bool (*keydriver_isTouchKeyboardAvailable)(void);
|
||||
|
||||
// enable/disable touch keyboard HUD element
|
||||
extern void (*keydriver_setTouchKeyboardEnabled)(bool enabled);
|
||||
|
||||
// grant/remove ownership of touch screen
|
||||
extern void (*keydriver_setTouchKeyboardOwnsScreen)(bool pwnd);
|
||||
|
||||
// query touch screen ownership
|
||||
extern bool (*keydriver_ownsScreen)(void);
|
||||
|
||||
// set a finer-grained font size (if glyphScale > 1)
|
||||
extern void (*keydriver_setGlyphScale)(int glyphScale);
|
||||
|
||||
// set visibility
|
||||
extern void (*keydriver_setVisibilityWhenOwnsScreen)(float inactiveAlpha, float activeAlpha);
|
||||
|
||||
// set lowercase enabled
|
||||
extern void (*keydriver_setLowercaseEnabled)(bool enabled);
|
||||
|
||||
// keyboard read callback
|
||||
extern void (*keydriver_keyboardReadCallback)(void);
|
||||
|
||||
// begin calibration mode
|
||||
extern void (*keydriver_beginCalibration)(void);
|
||||
|
||||
// end calibration mode
|
||||
extern void (*keydriver_endCalibration)(void);
|
||||
|
||||
// load an alternate keyboard variant
|
||||
extern void (*keydriver_loadAltKbd)(const char *kbdPath);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -273,12 +273,13 @@ void emulator_start(void) {
|
|||
}
|
||||
head = NULL;
|
||||
|
||||
#ifdef INTERFACE_CLASSIC
|
||||
#if defined(INTERFACE_CLASSIC) && !TESTING
|
||||
cpu_pause();
|
||||
prefs_load(); // user prefs
|
||||
#if !TESTING
|
||||
prefs_sync(NULL);
|
||||
cpu_resume();
|
||||
c_keys_set_key(kF8); // show credits before emulation start
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(__APPLE__) && !defined(ANDROID)
|
||||
video_init();
|
||||
|
|
70
src/prefs.c
70
src/prefs.c
|
@ -30,12 +30,15 @@ typedef struct prefs_domain_s {
|
|||
static JSON_ref jsonPrefs = NULL;
|
||||
static prefs_domain_s *domains = NULL;
|
||||
static char *prefsFile = NULL;
|
||||
static unsigned long listenerCount = 0;
|
||||
|
||||
static pthread_mutex_t prefsLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
void prefs_load(void) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
FREE(prefsFile);
|
||||
|
||||
const char *apple2JSON = getenv("APPLE2IX_JSON");
|
||||
|
@ -44,7 +47,11 @@ void prefs_load(void) {
|
|||
}
|
||||
|
||||
if (!prefsFile) {
|
||||
#ifdef ANDROID
|
||||
ASPRINTF(&prefsFile, "%s/.apple2.json", data_dir);
|
||||
#else
|
||||
ASPRINTF(&prefsFile, "%s/.apple2.json", getenv("HOME"));
|
||||
#endif
|
||||
}
|
||||
assert(prefsFile);
|
||||
|
||||
|
@ -55,21 +62,22 @@ void prefs_load(void) {
|
|||
assert(tokCount > 0);
|
||||
}
|
||||
|
||||
prefs_sync(NULL);
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
void prefs_loadString(const char *jsonString) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
json_destroy(&jsonPrefs);
|
||||
int tokCount = json_createFromString(jsonString, &jsonPrefs);
|
||||
if (tokCount < 0) {
|
||||
tokCount = json_createFromString("{}", &jsonPrefs);
|
||||
assert(tokCount > 0);
|
||||
}
|
||||
|
||||
prefs_sync(NULL);
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
bool prefs_save(void) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
int fd = -1;
|
||||
bool success = false;
|
||||
|
@ -112,7 +120,7 @@ bool prefs_save(void) {
|
|||
"||||||||||||||||||||||||||||||||||||||||" };
|
||||
#endif
|
||||
|
||||
TEMP_FAILURE_RETRY(fd = open(prefsFile, O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR));
|
||||
TEMP_FAILURE_RETRY(fd = open(prefsFile, O_WRONLY|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR));
|
||||
if (fd == -1) {
|
||||
PREFS_ERRPRINT();
|
||||
#if defined(INTERFACE_CLASSIC) && !TESTING
|
||||
|
@ -132,9 +140,31 @@ bool prefs_save(void) {
|
|||
TEMP_FAILURE_RETRY(close(fd));
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool prefs_copyJSONValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT JSON_ref *jsonVal) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
bool ret = false;
|
||||
JSON_ref jsonRef = NULL;
|
||||
do {
|
||||
int errCount = json_mapCopyJSON(jsonPrefs, domain, &jsonRef);
|
||||
if (errCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
ret = json_mapCopyJSON(jsonRef, key, jsonVal);
|
||||
} while (0);
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool prefs_copyStringValue(const char *domain, const char *key, INOUT char **val) {
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
|
@ -362,11 +392,13 @@ void prefs_registerListener(const char *domain, prefs_change_callback_f callback
|
|||
newL->nextListener = oldL;
|
||||
newL->prefsChanged = callback;
|
||||
|
||||
++listenerCount;
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
void prefs_sync(const char *domain) {
|
||||
static int syncCount = 0;
|
||||
static unsigned long syncCount = 0;
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
++syncCount;
|
||||
if (syncCount > 1) {
|
||||
|
@ -375,16 +407,34 @@ void prefs_sync(const char *domain) {
|
|||
}
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
|
||||
void **alreadySynced = MALLOC(listenerCount * sizeof(void *));
|
||||
unsigned long idx = 0;
|
||||
|
||||
prefs_domain_s *dom = domains;
|
||||
do {
|
||||
while (dom) {
|
||||
if (domain && (strcmp(domain, dom->domain) != 0)) {
|
||||
dom = dom->nextDomain;
|
||||
continue;
|
||||
}
|
||||
|
||||
prefs_listener_s *listener = dom->listeners;
|
||||
while (listener) {
|
||||
listener->prefsChanged(dom->domain);
|
||||
|
||||
bool foundAlready = false;
|
||||
for (unsigned long i = 0; i < idx; i++) {
|
||||
if (alreadySynced[i] == (void *)listener->prefsChanged) {
|
||||
LOG("ignoring already synced listener %p for domain %s", alreadySynced[i], dom->domain);
|
||||
foundAlready = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundAlready) {
|
||||
alreadySynced[idx++] = (void *)listener->prefsChanged;
|
||||
assert(idx <= listenerCount);
|
||||
listener->prefsChanged(dom->domain);
|
||||
}
|
||||
listener = listener->nextListener;
|
||||
}
|
||||
|
||||
|
@ -404,6 +454,8 @@ void prefs_sync(const char *domain) {
|
|||
pthread_mutex_unlock(&prefsLock);
|
||||
|
||||
} while (1);
|
||||
|
||||
FREE(alreadySynced);
|
||||
}
|
||||
|
||||
void prefs_shutdown(bool emulatorShuttingDown) {
|
||||
|
@ -411,10 +463,10 @@ void prefs_shutdown(bool emulatorShuttingDown) {
|
|||
return;
|
||||
}
|
||||
|
||||
FREE(prefsFile);
|
||||
|
||||
pthread_mutex_lock(&prefsLock);
|
||||
|
||||
FREE(prefsFile);
|
||||
|
||||
prefs_domain_s *dom = domains;
|
||||
domains = NULL;
|
||||
|
||||
|
@ -433,6 +485,8 @@ void prefs_shutdown(bool emulatorShuttingDown) {
|
|||
FREE(dead);
|
||||
}
|
||||
|
||||
listenerCount = 0;
|
||||
|
||||
pthread_mutex_unlock(&prefsLock);
|
||||
}
|
||||
|
||||
|
|
66
src/prefs.h
66
src/prefs.h
|
@ -23,27 +23,66 @@
|
|||
#define PREF_DOMAIN_INTERFACE "interface"
|
||||
#define PREF_DOMAIN_JOYSTICK "joystick"
|
||||
#define PREF_DOMAIN_KEYBOARD "keyboard"
|
||||
#define PREF_DOMAIN_TOUCHSCREEN "touchscreen"
|
||||
#define PREF_DOMAIN_VIDEO "video"
|
||||
#define PREF_DOMAIN_VM "vm"
|
||||
|
||||
// audio
|
||||
#define PREF_SPEAKER_VOLUME "speakerVolume"
|
||||
#define PREF_MOCKINGBOARD_ENABLED "mbEnabled"
|
||||
#define PREF_MOCKINGBOARD_VOLUME "mbVolume"
|
||||
#define PREF_SPEAKER_VOLUME "speakerVolume"
|
||||
#define PREF_AUDIO_LATENCY "audioLatency"
|
||||
|
||||
// interface
|
||||
#define PREF_DEVICE_HEIGHT "deviceHeight"
|
||||
#define PREF_DEVICE_WIDTH "deviceWidth"
|
||||
#define PREF_DEVICE_LANDSCAPE "landscapeEnabled"
|
||||
#define PREF_DISK_PATH "diskPath"
|
||||
#define PREF_DISK_ANIMATIONS_ENABLED "diskAnimationsEnabled"
|
||||
|
||||
// joystick
|
||||
#define PREF_JOYSTICK_KPAD_AUTO_RECENTER "kpAutoRecenter"
|
||||
#define PREF_JOYSTICK_KPAD_STEP "kpStep"
|
||||
#define PREF_JOYSTICK_MODE "joystickMode"
|
||||
// joystick (touchscreen)
|
||||
#define PREF_AXIS_ON_LEFT "axisIsOnLeft"
|
||||
#define PREF_AXIS_SENSITIVITY "axisSensitivity"
|
||||
#define PREF_JOY_SWIPE_NORTH_CHAR "jsSwipeNorthChar"
|
||||
#define PREF_JOY_SWIPE_SOUTH_CHAR "jsSwipeSouthChar"
|
||||
#define PREF_JOY_TAP_DELAY "jsTapDelaySecs"
|
||||
#define PREF_JOY_TOUCHDOWN_CHAR "jsTouchDownChar"
|
||||
#define PREF_KPAD_REPEAT_THRESH "keyRepeatThresholdSecs"
|
||||
#define PREF_KPAD_ROSETTE_CHAR_ARRAY "kpAxisRosetteChars"
|
||||
#define PREF_KPAD_ROSETTE_SCAN_ARRAY "kpAxisRosetteScancodes"
|
||||
#define PREF_KPAD_SWIPE_NORTH_CHAR "kpSwipeNorthChar"
|
||||
#define PREF_KPAD_SWIPE_NORTH_SCAN "kpSwipeNorthScancode"
|
||||
#define PREF_KPAD_SWIPE_SOUTH_CHAR "kpSwipeSouthChar"
|
||||
#define PREF_KPAD_SWIPE_SOUTH_SCAN "kpSwipeSouthScancode"
|
||||
#define PREF_KPAD_TOUCHDOWN_CHAR "kpTouchDownChar"
|
||||
#define PREF_KPAD_TOUCHDOWN_SCAN "kpTouchDownScancode"
|
||||
#define PREF_SCREEN_DIVISION "screenDivider"
|
||||
#define PREF_SHOW_CONTROLS "showControls"
|
||||
#define PREF_SHOW_AZIMUTH "showAzimuth"
|
||||
#define PREF_SWITCH_THRESHOLD "switchThreshold"
|
||||
|
||||
// keyboard
|
||||
#define PREF_GLYPH_MULTIPLIER "glyphMultiplier"
|
||||
#define PREF_KEYBOARD_CAPS "caps"
|
||||
#define PREF_KEYBOARD_ALT_PATH "altPath"
|
||||
#define PREF_LOWERCASE_ENABLED "lowercaseEnabled"
|
||||
#define PREF_PORTRAIT_HEIGHT_SCALE "portraitHeightScale"
|
||||
#define PREF_PORTRAIT_POSITION_SCALE "portraitPositionScale"
|
||||
|
||||
// touchscreen
|
||||
#define PREF_CALIBRATING "isCalibrating"
|
||||
#define PREF_MAX_ALPHA "maxAlpha"
|
||||
#define PREF_MIN_ALPHA "minAlpha"
|
||||
#define PREF_SCREEN_OWNER "screenOwner"
|
||||
#define PREF_TOUCHMENU_ENABLED "touchMenuEnabled"
|
||||
|
||||
// video
|
||||
#define PREF_COLOR_MODE "colorMode"
|
||||
|
||||
// joystick
|
||||
#define PREF_JOYSTICK_MODE "joystickMode"
|
||||
#define PREF_JOYSTICK_KPAD_AUTO_RECENTER "kpAutoRecenter"
|
||||
#define PREF_JOYSTICK_KPAD_STEP "kpStep"
|
||||
|
||||
// keyboard
|
||||
#define PREF_KEYBOARD_CAPS "caps"
|
||||
|
||||
// interface
|
||||
#define PREF_DISK_PATH "diskPath"
|
||||
|
||||
// vm
|
||||
#define PREF_CPU_SCALE "cpuScale"
|
||||
#define PREF_CPU_SCALE_ALT "cpuScaleAlt"
|
||||
|
@ -62,6 +101,9 @@ extern bool prefs_save(void);
|
|||
// copy string value for key in prefs domain, returns true upon success and strdup()'d value in *val
|
||||
extern bool prefs_copyStringValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT char ** _NONNULL val);
|
||||
|
||||
// create JSON_ref value for key in prefs domain, returns true upon success and newly allocated value in jsonVal
|
||||
extern bool prefs_copyJSONValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT JSON_ref *jsonVal);
|
||||
|
||||
// get long value for key in prefs domain, returns true upon success
|
||||
extern bool prefs_parseLongValue(const char * _NONNULL domain, const char * _NONNULL key, INOUT long *val, const long base);
|
||||
|
||||
|
|
|
@ -35,7 +35,8 @@ static const char *get_default_preferences(void) {
|
|||
" \"joystick\" : {"
|
||||
" \"joystickMode\" : 1,"
|
||||
" \"pcJoystickParms\" : \"128 128 255 1 255 1\","
|
||||
" \"kpJoystickParms\" : \"8 1\""
|
||||
" \"kpJoystickParms\" : \"8 1\","
|
||||
" \"kpAxisRosetteChars\" : [ 121, 255, 122, 255, 123, 255, 124, 255, 125 ]"
|
||||
" },"
|
||||
" \"keyboard\" : {"
|
||||
" \"caps\" : true"
|
||||
|
@ -1729,9 +1730,32 @@ TEST test_json_array() {
|
|||
PASS();
|
||||
}
|
||||
|
||||
TEST test_json_unescaping() {
|
||||
JSON_ref jsonRef = NULL;
|
||||
int errCount = json_createFromString("\"http:\\/\\/deadc0de.org\\/apple2ix\\/android\\/\"", &jsonRef);
|
||||
ASSERT(errCount == 1);
|
||||
ASSERT(jsonRef);
|
||||
|
||||
JSON_s parsedData = *((JSON_s *)jsonRef);
|
||||
ASSERT(strcmp(parsedData.jsonString, "\"http:\\/\\/deadc0de.org\\/apple2ix\\/android\\/\"") == 0);
|
||||
ASSERT(parsedData.jsonLen == strlen(parsedData.jsonString));
|
||||
|
||||
bool ok = json_unescapeSlashes(&parsedData.jsonString);
|
||||
ASSERT(ok);
|
||||
ASSERT(strcmp(parsedData.jsonString, "\"http://deadc0de.org/apple2ix/android/\"") == 0);
|
||||
size_t newLen = strlen(parsedData.jsonString);
|
||||
ASSERT(parsedData.jsonLen != newLen);
|
||||
ASSERT(newLen == 39);
|
||||
|
||||
json_destroy(&jsonRef);
|
||||
|
||||
PASS();
|
||||
}
|
||||
|
||||
TEST test_prefs_loadString_1() {
|
||||
const char *prefsJSON = get_default_preferences();
|
||||
prefs_loadString(prefsJSON);
|
||||
prefs_sync(NULL);
|
||||
|
||||
char *val = NULL;
|
||||
bool bVal = false;
|
||||
|
@ -1766,6 +1790,44 @@ TEST test_prefs_loadString_1() {
|
|||
ASSERT(strcmp(val, "8 1") == 0);
|
||||
FREE(val);
|
||||
|
||||
JSON_ref jsonVal = NULL;
|
||||
ok = prefs_copyJSONValue(PREF_DOMAIN_JOYSTICK, PREF_KPAD_ROSETTE_CHAR_ARRAY, &jsonVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(json_isArray(jsonVal));
|
||||
ok = json_arrayCount(jsonVal, &lVal);
|
||||
ASSERT(ok);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 0, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 121); // 121, 255, 122, 255, 123, 255, 124, 255, 125
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 1, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 255);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 2, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 122);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 3, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 255);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 4, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 123);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 5, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 255);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 6, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 124);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 7, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 255);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 8, &lVal, /*base:*/10);
|
||||
ASSERT(ok);
|
||||
ASSERT(lVal == 125);
|
||||
ok = json_arrayParseLongValueAtIndex(jsonVal, 9, &lVal, /*base:*/10);
|
||||
ASSERT(!ok);
|
||||
ASSERT(lVal == 125);
|
||||
json_destroy(&jsonVal);
|
||||
|
||||
ok = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_CAPS, &bVal);
|
||||
ASSERT(ok);
|
||||
ASSERT(bVal == true);
|
||||
|
@ -1789,6 +1851,7 @@ TEST test_prefs_loadString_1() {
|
|||
TEST test_prefs_set_props() {
|
||||
const char *prefsJSON = get_default_preferences();
|
||||
prefs_loadString(prefsJSON);
|
||||
prefs_sync(NULL);
|
||||
|
||||
char *val = NULL;
|
||||
bool bVal = true;
|
||||
|
@ -1830,6 +1893,7 @@ TEST test_prefs_load_and_save() {
|
|||
unlink(TEST_JSON);
|
||||
putenv("APPLE2IX_JSON=" TEST_JSON);
|
||||
prefs_load();
|
||||
prefs_sync(NULL);
|
||||
prefs_save();
|
||||
|
||||
bool ok = false;
|
||||
|
@ -1958,6 +2022,8 @@ GREATEST_SUITE(test_suite_prefs) {
|
|||
RUN_TESTp(test_json_map_mutation_1);
|
||||
RUN_TESTp(test_json_array);
|
||||
|
||||
RUN_TESTp(test_json_unescaping);
|
||||
|
||||
RUN_TESTp(test_prefs_loadString_1);
|
||||
RUN_TESTp(test_prefs_set_props);
|
||||
RUN_TESTp(test_prefs_load_and_save);
|
||||
|
|
47
src/timing.c
47
src/timing.c
|
@ -188,7 +188,9 @@ void timing_toggleCPUSpeed(void) {
|
|||
void timing_reinitializeAudio(void) {
|
||||
SPINLOCK_ACQUIRE(&_pause_spinLock);
|
||||
assert(pthread_self() != cpu_thread_id);
|
||||
#if !TESTING
|
||||
assert(cpu_isPaused());
|
||||
#endif
|
||||
emul_reinitialize_audio = true;
|
||||
emul_pause_audio = false;
|
||||
emul_resume_audio = false;
|
||||
|
@ -589,16 +591,49 @@ void timing_testCyclesCountOverflow(void) {
|
|||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void vm_prefsChanged(const char *domain) {
|
||||
assert(strcmp(domain, PREF_DOMAIN_VM) == 0);
|
||||
|
||||
float fVal = 1.0;
|
||||
prefs_parseFloatValue(domain, PREF_CPU_SCALE, &fVal);
|
||||
cpu_scale_factor = fVal;
|
||||
prefs_parseFloatValue(domain, PREF_CPU_SCALE_ALT, &fVal);
|
||||
cpu_altscale_factor = fVal;
|
||||
|
||||
if (strcmp(domain, PREF_DOMAIN_VM) == 0) {
|
||||
cpu_scale_factor = prefs_parseFloatValue(domain, PREF_CPU_SCALE, &fVal) ? fVal / 100.f : 1.f;
|
||||
if (cpu_scale_factor < CPU_SCALE_SLOWEST) {
|
||||
cpu_scale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
if (cpu_scale_factor > CPU_SCALE_FASTEST) {
|
||||
cpu_scale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
cpu_altscale_factor = prefs_parseFloatValue(domain, PREF_CPU_SCALE_ALT, &fVal) ? fVal / 100.f : 1.f;
|
||||
if (cpu_altscale_factor < CPU_SCALE_SLOWEST) {
|
||||
cpu_altscale_factor = CPU_SCALE_SLOWEST;
|
||||
}
|
||||
if (cpu_altscale_factor > CPU_SCALE_FASTEST) {
|
||||
cpu_altscale_factor = CPU_SCALE_FASTEST;
|
||||
}
|
||||
#ifdef AUDIO_ENABLED
|
||||
} else if (strcmp(domain, PREF_DOMAIN_AUDIO) == 0) {
|
||||
|
||||
static float audioLatency = 0.f;
|
||||
float latency = prefs_parseFloatValue(domain, PREF_AUDIO_LATENCY, &fVal) ? fVal : 0.25f;
|
||||
#define SMALL_EPSILON (1.f/1024.f)
|
||||
if (fabsf(audioLatency - latency) > SMALL_EPSILON) {
|
||||
audioLatency = latency;
|
||||
audio_setLatency(latency);
|
||||
timing_reinitializeAudio();
|
||||
}
|
||||
|
||||
static bool mbEnabled = false;
|
||||
bool bVal = false;
|
||||
bool enabled = prefs_parseBoolValue(domain, PREF_MOCKINGBOARD_ENABLED, &bVal) ? bVal : true;
|
||||
if (enabled != mbEnabled) {
|
||||
mbEnabled = enabled;
|
||||
MB_SetEnabled(enabled);
|
||||
timing_reinitializeAudio();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void _init_vm(void) {
|
||||
prefs_registerListener(PREF_DOMAIN_VM, &vm_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_AUDIO, &vm_prefsChanged);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@ static unsigned int nextMessageRows = 0;
|
|||
static struct timespec messageTimingBegin = { 0 };
|
||||
static GLModel *messageModel = NULL;
|
||||
static GLfloat landscapeScale = 1.f;
|
||||
static bool prefsChanged = true;
|
||||
|
||||
static void alert_applyPrefs(void);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -147,6 +150,9 @@ static void _alertToModel(char *message, unsigned int messageCols, unsigned int
|
|||
|
||||
static void alert_init(void) {
|
||||
// no-op
|
||||
if (prefsChanged) {
|
||||
alert_applyPrefs();
|
||||
}
|
||||
}
|
||||
|
||||
static void alert_shutdown(void) {
|
||||
|
@ -173,6 +179,10 @@ static void alert_render(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (prefsChanged) {
|
||||
alert_applyPrefs();
|
||||
}
|
||||
|
||||
struct timespec now = { 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
|
||||
|
@ -202,6 +212,7 @@ static void alert_render(void) {
|
|||
}
|
||||
|
||||
static void alert_reshape(int w, int h, bool landscape) {
|
||||
assert(video_isRenderThread());
|
||||
swizzleDimensions(&w, &h, landscape);
|
||||
landscapeScale = landscape ? 1.f : ((GLfloat)w/h);
|
||||
}
|
||||
|
@ -269,13 +280,13 @@ static void _animation_showCPUSpeed(void) {
|
|||
|
||||
char buf[8] = { 0 };
|
||||
double scale = (alt_speed_enabled ? cpu_altscale_factor : cpu_scale_factor);
|
||||
int percentScale = scale * 100;
|
||||
if (percentScale < 100.0) {
|
||||
int percentScale = roundf(scale * 100);
|
||||
if (percentScale < 100) {
|
||||
snprintf(buf, 3, "%d", percentScale);
|
||||
cpuTemplate[0][1] = ' ';
|
||||
cpuTemplate[0][2] = buf[0];
|
||||
cpuTemplate[0][3] = buf[1];
|
||||
} else if (scale == CPU_SCALE_FASTEST) {
|
||||
} else if (scale >= CPU_SCALE_FASTEST) {
|
||||
cpuTemplate[0][1] = 'm';
|
||||
cpuTemplate[0][2] = 'a';
|
||||
cpuTemplate[0][3] = 'x';
|
||||
|
@ -361,12 +372,29 @@ static void _animation_showTrackSector(int drive, int track, int sect) {
|
|||
_animation_showMessage(template, DISK_TRACK_SECTOR_COLS, DISK_TRACK_SECTOR_ROWS);
|
||||
}
|
||||
|
||||
static void _animation_setEnableShowTrackSector(bool enabled) {
|
||||
static void alert_applyPrefs(void) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
prefsChanged = false;
|
||||
|
||||
bool bVal = false;
|
||||
bool enabled = prefs_parseBoolValue(PREF_DOMAIN_INTERFACE, PREF_DISK_ANIMATIONS_ENABLED, &bVal) ? bVal : true;
|
||||
if (enabled) {
|
||||
video_animations->animation_showTrackSector = &_animation_showTrackSector;
|
||||
} else {
|
||||
video_animations->animation_showTrackSector = NULL;
|
||||
}
|
||||
|
||||
long lVal = 0;
|
||||
long width = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, &lVal, 10) ? lVal : (long)(SCANWIDTH*1.5);
|
||||
long height = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, &lVal, 10) ? lVal : (long)(SCANHEIGHT*1.5);
|
||||
bool isLandscape = prefs_parseBoolValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, &bVal) ? bVal : true;
|
||||
|
||||
alert_reshape(width, height, isLandscape);
|
||||
}
|
||||
|
||||
static void alert_prefsChanged(const char *domain) {
|
||||
prefsChanged = true;
|
||||
}
|
||||
|
||||
static void _init_glalert(void) {
|
||||
|
@ -377,19 +405,18 @@ static void _init_glalert(void) {
|
|||
video_animations->animation_showCPUSpeed = &_animation_showCPUSpeed;
|
||||
video_animations->animation_showDiskChosen = &_animation_showDiskChosen;
|
||||
video_animations->animation_showTrackSector = &_animation_showTrackSector;
|
||||
video_animations->animation_setEnableShowTrackSector = &_animation_setEnableShowTrackSector;
|
||||
|
||||
glnode_registerNode(RENDER_MIDDLE, (GLNode){
|
||||
.setup = &alert_init,
|
||||
.shutdown = &alert_shutdown,
|
||||
.render = &alert_render,
|
||||
.reshape = &alert_reshape,
|
||||
#if INTERFACE_TOUCH
|
||||
.type = TOUCH_DEVICE_ALERT,
|
||||
.onTouchEvent = &alert_onTouchEvent,
|
||||
.setData = NULL,
|
||||
#endif
|
||||
});
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_INTERFACE, &alert_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_glalert(void) {
|
||||
|
|
|
@ -116,7 +116,10 @@ static void _glnode_initGLUTPre(void) {
|
|||
}
|
||||
|
||||
static void _glnode_reshapeGLUT(int w, int h) {
|
||||
video_reshape(w, h, /*landscape:*/true);
|
||||
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, w);
|
||||
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, h);
|
||||
prefs_setLongValue(PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, true);
|
||||
prefs_sync(PREF_DOMAIN_INTERFACE);
|
||||
}
|
||||
|
||||
static void _glnode_initGLUTPost(void) {
|
||||
|
@ -205,14 +208,6 @@ static void glnode_renderNodes(void) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static void glnode_reshapeNodes(int w, int h, bool landscape) {
|
||||
glnode_array_node_s *p = head;
|
||||
while (p) {
|
||||
p->node.reshape(w, h, landscape);
|
||||
p = p->next;
|
||||
}
|
||||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
static int64_t glnode_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
||||
SCOPE_TRACE_TOUCH("glnode onTouchEvent");
|
||||
|
@ -227,17 +222,6 @@ static int64_t glnode_onTouchEvent(interface_touch_event_t action, int pointer_c
|
|||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
void (*glnode_getModelDataSetter(interface_device_t type))(const char *jsonData) {
|
||||
glnode_array_node_s *p = head;
|
||||
while (p) {
|
||||
if (p->node.type == type) {
|
||||
return p->node.setData;
|
||||
}
|
||||
p = p->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void glnode_mainLoop(void) {
|
||||
|
@ -260,7 +244,6 @@ static void _init_glnode_manager(void) {
|
|||
|
||||
glnode_backend.init = &glnode_setupNodes;
|
||||
glnode_backend.main_loop = &glnode_mainLoop;
|
||||
glnode_backend.reshape = &glnode_reshapeNodes;
|
||||
glnode_backend.render = &glnode_renderNodes;
|
||||
glnode_backend.shutdown = &glnode_shutdownNodes;
|
||||
|
||||
|
@ -269,7 +252,6 @@ static void _init_glnode_manager(void) {
|
|||
|
||||
#if INTERFACE_TOUCH
|
||||
interface_onTouchEvent = &glnode_onTouchEvent;
|
||||
interface_getModelDataSetter = &glnode_getModelDataSetter;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -100,11 +100,9 @@ typedef struct GLNode {
|
|||
void (*setup)(void);
|
||||
void (*shutdown)(void);
|
||||
void (*render)(void);
|
||||
void (*reshape)(int w, int h, bool landscape);
|
||||
#if INTERFACE_TOUCH
|
||||
interface_device_t type;
|
||||
int64_t (*onTouchEvent)(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords);
|
||||
void (*setData)(const char *jsonData);
|
||||
#endif
|
||||
} GLNode;
|
||||
|
||||
|
|
|
@ -68,9 +68,6 @@ static struct {
|
|||
int buttonY;
|
||||
int buttonYMax;
|
||||
|
||||
// Are we in landscape mode
|
||||
bool landscape;
|
||||
|
||||
// TODO FIXME : support 2-players!
|
||||
} touchport = { 0 };
|
||||
|
||||
|
@ -83,6 +80,9 @@ static struct {
|
|||
|
||||
AZIMUTH_CLASS(GLModelJoystickAzimuth);
|
||||
|
||||
static void gltouchjoy_applyPrefs(void);
|
||||
static void gltouchjoy_shutdown(void);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// joystick azimuth model
|
||||
|
||||
|
@ -266,9 +266,10 @@ static void _setup_axis_hud(GLModel *parent) {
|
|||
|
||||
const unsigned int row = (AXIS_TEMPLATE_COLS+1);
|
||||
|
||||
uint8_t *rosetteChars = variant.curr->rosetteChars();
|
||||
for (unsigned int i=0; i<ROSETTE_ROWS; i++) {
|
||||
for (unsigned int j=0; j<ROSETTE_COLS; j++) {
|
||||
((hudElement->tpl)+(row*i))[j] = axes.rosetteChars[(i*ROSETTE_ROWS)+j];
|
||||
((hudElement->tpl)+(row*i))[j] = rosetteChars[(i*ROSETTE_ROWS)+j];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,13 +351,11 @@ static inline void resetState() {
|
|||
static void gltouchjoy_setup(void) {
|
||||
LOG("...");
|
||||
|
||||
resetState();
|
||||
gltouchjoy_shutdown();
|
||||
|
||||
mdlDestroyModel(&axes.model);
|
||||
mdlDestroyModel(&axes.azimuthModel);
|
||||
mdlDestroyModel(&buttons.model);
|
||||
|
||||
joyglobals.isShuttingDown = false;
|
||||
if (joyglobals.prefsChanged) {
|
||||
gltouchjoy_applyPrefs();
|
||||
}
|
||||
|
||||
// axis origin object
|
||||
|
||||
|
@ -418,6 +417,12 @@ static void gltouchjoy_setup(void) {
|
|||
}
|
||||
|
||||
// button object
|
||||
long lVal = 0;
|
||||
if (variant.curr->variant() == TOUCH_DEVICE_JOYSTICK_KEYPAD) {
|
||||
buttons.activeChar = prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_KPAD_TOUCHDOWN_CHAR, &lVal, /*base:*/10) ? lVal : ICONTEXT_SPACE_VISUAL;
|
||||
} else {
|
||||
buttons.activeChar = prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOY_TOUCHDOWN_CHAR, &lVal, /*base:*/10) ? lVal : MOUSETEXT_OPENAPPLE;
|
||||
}
|
||||
|
||||
buttons.model = mdlCreateQuad((GLModelParams_s){
|
||||
.skew_x = 1.05-BUTTON_OBJ_W,
|
||||
|
@ -451,6 +456,10 @@ static void gltouchjoy_setup(void) {
|
|||
buttons.timingBegin = now;
|
||||
|
||||
joyglobals.isAvailable = true;
|
||||
|
||||
if (joyglobals.ownsScreen) {
|
||||
video_animations->animation_showTouchJoystick();
|
||||
}
|
||||
}
|
||||
|
||||
static void gltouchjoy_shutdown(void) {
|
||||
|
@ -462,7 +471,6 @@ static void gltouchjoy_shutdown(void) {
|
|||
resetState();
|
||||
|
||||
joyglobals.isAvailable = false;
|
||||
joyglobals.isShuttingDown = true;
|
||||
|
||||
variant.joys->shutdown();
|
||||
variant.kpad->shutdown();
|
||||
|
@ -476,8 +484,8 @@ static void gltouchjoy_render(void) {
|
|||
if (!joyglobals.isAvailable) {
|
||||
return;
|
||||
}
|
||||
if (!joyglobals.isEnabled) {
|
||||
return;
|
||||
if (UNLIKELY(joyglobals.prefsChanged)) {
|
||||
gltouchjoy_setup(); // fully set up again on prefs change
|
||||
}
|
||||
if (!joyglobals.ownsScreen) {
|
||||
return;
|
||||
|
@ -550,8 +558,8 @@ static void gltouchjoy_render(void) {
|
|||
|
||||
static void gltouchjoy_reshape(int w, int h, bool landscape) {
|
||||
LOG("w:%d h:%d landscape:%d", w, h, landscape);
|
||||
assert(video_isRenderThread());
|
||||
|
||||
touchport.landscape = landscape;
|
||||
swizzleDimensions(&w, &h, landscape);
|
||||
touchport.width = w;
|
||||
touchport.height = h;
|
||||
|
@ -703,7 +711,7 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
|
|||
if (!joyglobals.isAvailable) {
|
||||
return 0x0LL;
|
||||
}
|
||||
if (!joyglobals.isEnabled) {
|
||||
if (UNLIKELY(joyglobals.prefsChanged)) {
|
||||
return 0x0LL;
|
||||
}
|
||||
if (!joyglobals.ownsScreen) {
|
||||
|
@ -804,36 +812,7 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
|
|||
return TOUCH_FLAGS_HANDLED | TOUCH_FLAGS_JOY;
|
||||
}
|
||||
|
||||
static bool gltouchjoy_isTouchJoystickAvailable(void) {
|
||||
return joyglobals.isAvailable;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchJoystickEnabled(bool enabled) {
|
||||
joyglobals.isEnabled = enabled;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchJoystickOwnsScreen(bool pwnd) {
|
||||
joyglobals.ownsScreen = pwnd;
|
||||
resetState();
|
||||
if (joyglobals.ownsScreen) {
|
||||
caps_lock = true; // HACK FOR NOW : force uppercase scancodes for touchjoy_kpad variant
|
||||
joyglobals.minAlpha = joyglobals.minAlphaWhenOwnsScreen;
|
||||
} else {
|
||||
joyglobals.minAlpha = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool gltouchjoy_ownsScreen(void) {
|
||||
return joyglobals.ownsScreen;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setShowControls(bool showControls) {
|
||||
joyglobals.showControls = showControls;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setShowAzimuth(bool showAzimuth) {
|
||||
joyglobals.showAzimuth = showAzimuth;
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void _animation_showTouchJoystick(void) {
|
||||
if (!joyglobals.isAvailable) {
|
||||
|
@ -864,95 +843,48 @@ static void _animation_hideTouchJoystick(void) {
|
|||
buttons.timingBegin = (struct timespec){ 0 };
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchButtonTypes(
|
||||
touchjoy_button_type_t touchDownChar, int touchDownScancode,
|
||||
touchjoy_button_type_t northChar, int northScancode,
|
||||
touchjoy_button_type_t southChar, int southScancode)
|
||||
{
|
||||
buttons.touchDownChar = touchDownChar;
|
||||
buttons.touchDownScancode = touchDownScancode;
|
||||
static void gltouchjoy_applyPrefs(void) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
buttons.southChar = southChar;
|
||||
buttons.southScancode = southScancode;
|
||||
joyglobals.prefsChanged = false;
|
||||
|
||||
buttons.northChar = northChar;
|
||||
buttons.northScancode = northScancode;
|
||||
bool bVal = false;
|
||||
float fVal = 0.f;
|
||||
long lVal = 0;
|
||||
|
||||
buttons.activeChar = ICONTEXT_NONACTIONABLE;
|
||||
|
||||
char currButtonDisplayChar = touchDownChar;
|
||||
if (touchDownChar == TOUCH_BUTTON0) {
|
||||
currButtonDisplayChar = MOUSETEXT_OPENAPPLE;
|
||||
} else if (touchDownChar == TOUCH_BUTTON1) {
|
||||
currButtonDisplayChar = MOUSETEXT_CLOSEDAPPLE;
|
||||
} else if (touchDownChar == TOUCH_BOTH) {
|
||||
currButtonDisplayChar = ICONTEXT_MENU_TOUCHJOY;
|
||||
} else if (touchDownScancode < 0) {
|
||||
currButtonDisplayChar = ' ';
|
||||
}
|
||||
_setup_button_object_with_char(currButtonDisplayChar);
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchAxisSensitivity(float multiplier) {
|
||||
axes.multiplier = multiplier;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setButtonSwitchThreshold(int delta) {
|
||||
joyglobals.switchThreshold = delta;
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchVariant(touchjoy_variant_t variantType) {
|
||||
resetState();
|
||||
|
||||
switch (variantType) {
|
||||
case EMULATED_JOYSTICK:
|
||||
variant.curr = variant.joys;
|
||||
break;
|
||||
|
||||
case EMULATED_KEYPAD:
|
||||
variant.curr = variant.kpad;
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false && "touch variant set to invalid");
|
||||
break;
|
||||
}
|
||||
const interface_device_t screenOwner = prefs_parseLongValue(PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, &lVal, /*base:*/10) ? lVal : TOUCH_DEVICE_NONE;
|
||||
joyglobals.ownsScreen = (screenOwner == TOUCH_DEVICE_JOYSTICK || screenOwner == TOUCH_DEVICE_JOYSTICK_KEYPAD);
|
||||
|
||||
resetState();
|
||||
if (joyglobals.ownsScreen) {
|
||||
caps_lock = true; // HACK FOR NOW : force uppercase scancodes for touchjoy_kpad variant
|
||||
joyglobals.minAlpha = joyglobals.minAlphaWhenOwnsScreen;
|
||||
variant.curr = (screenOwner == TOUCH_DEVICE_JOYSTICK) ? variant.joys : variant.kpad;
|
||||
} else {
|
||||
joyglobals.minAlpha = 0.0;
|
||||
}
|
||||
|
||||
joyglobals.showControls = prefs_parseBoolValue (PREF_DOMAIN_JOYSTICK, PREF_SHOW_CONTROLS, &bVal) ? bVal : true;
|
||||
joyglobals.showAzimuth = prefs_parseBoolValue (PREF_DOMAIN_JOYSTICK, PREF_SHOW_AZIMUTH, &bVal) ? bVal : true;
|
||||
joyglobals.switchThreshold = prefs_parseLongValue (PREF_DOMAIN_JOYSTICK, PREF_SWITCH_THRESHOLD, &lVal, 10) ? lVal : BUTTON_SWITCH_THRESHOLD_DEFAULT;
|
||||
joyglobals.screenDivider = prefs_parseFloatValue(PREF_DOMAIN_JOYSTICK, PREF_SCREEN_DIVISION, &fVal) ? fVal : 0.5f;
|
||||
joyglobals.axisIsOnLeft = prefs_parseBoolValue (PREF_DOMAIN_JOYSTICK, PREF_AXIS_ON_LEFT, &bVal) ? bVal : true;
|
||||
axes.multiplier = prefs_parseFloatValue(PREF_DOMAIN_JOYSTICK, PREF_AXIS_SENSITIVITY, &fVal) ? fVal : 1.f;
|
||||
|
||||
joyglobals.isCalibrating = prefs_parseBoolValue (PREF_DOMAIN_TOUCHSCREEN, PREF_CALIBRATING, &bVal) ? bVal : false;
|
||||
|
||||
variant.joys->prefsChanged(PREF_DOMAIN_JOYSTICK);
|
||||
variant.kpad->prefsChanged(PREF_DOMAIN_JOYSTICK);
|
||||
|
||||
long width = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, &lVal, 10) ? lVal : (long)(SCANWIDTH*1.5);
|
||||
long height = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, &lVal, 10) ? lVal : (long)(SCANHEIGHT*1.5);
|
||||
bool isLandscape = prefs_parseBoolValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, &bVal) ? bVal : true;
|
||||
|
||||
gltouchjoy_reshape(width, height, isLandscape);
|
||||
}
|
||||
|
||||
static touchjoy_variant_t gltouchjoy_getTouchVariant(void) {
|
||||
return variant.curr->variant();
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTouchAxisTypes(uint8_t rosetteChars[(ROSETTE_ROWS * ROSETTE_COLS)], int rosetteScancodes[(ROSETTE_ROWS * ROSETTE_COLS)]) {
|
||||
memcpy(axes.rosetteChars, rosetteChars, sizeof(uint8_t)*(ROSETTE_ROWS * ROSETTE_COLS));
|
||||
memcpy(axes.rosetteScancodes, rosetteScancodes, sizeof(int) *(ROSETTE_ROWS * ROSETTE_COLS));
|
||||
_setup_axis_hud(axes.model);
|
||||
}
|
||||
|
||||
static void gltouchjoy_setScreenDivision(float screenDivider) {
|
||||
joyglobals.screenDivider = screenDivider;
|
||||
// force reshape here to apply changes ...
|
||||
gltouchjoy_reshape(touchport.width, touchport.height, touchport.landscape);
|
||||
}
|
||||
|
||||
static void gltouchjoy_setAxisOnLeft(bool axisIsOnLeft) {
|
||||
joyglobals.axisIsOnLeft = axisIsOnLeft;
|
||||
// force reshape here to apply changes ...
|
||||
gltouchjoy_reshape(touchport.width, touchport.height, touchport.landscape);
|
||||
}
|
||||
|
||||
static void gltouchjoy_beginCalibration(void) {
|
||||
joyglobals.isCalibrating = true;
|
||||
}
|
||||
|
||||
static void gltouchjoy_endCalibration(void) {
|
||||
joyglobals.isCalibrating = false;
|
||||
}
|
||||
|
||||
static bool gltouchjoy_isCalibrating(void) {
|
||||
return joyglobals.isCalibrating;
|
||||
static void gltouchjoy_prefsChanged(const char *domain) {
|
||||
joyglobals.prefsChanged = true;
|
||||
}
|
||||
|
||||
static void _init_gltouchjoy(void) {
|
||||
|
@ -960,97 +892,43 @@ static void _init_gltouchjoy(void) {
|
|||
|
||||
axes.centerX = 240;
|
||||
axes.centerY = 160;
|
||||
axes.multiplier = 1.f;
|
||||
axes.trackingIndex = TRACKING_NONE;
|
||||
|
||||
axes.rosetteChars[0] = ' ';
|
||||
axes.rosetteScancodes[0] = -1;
|
||||
axes.rosetteChars[1] = MOUSETEXT_UP;
|
||||
axes.rosetteScancodes[1] = -1;
|
||||
axes.rosetteChars[2] = ' ';
|
||||
axes.rosetteScancodes[2] = -1;
|
||||
|
||||
axes.rosetteChars[3] = MOUSETEXT_LEFT;
|
||||
axes.rosetteScancodes[3] = -1;
|
||||
axes.rosetteChars[4] = ICONTEXT_MENU_TOUCHJOY;
|
||||
axes.rosetteScancodes[4] = -1;
|
||||
axes.rosetteChars[5] = MOUSETEXT_RIGHT;
|
||||
axes.rosetteScancodes[5] = -1;
|
||||
|
||||
axes.rosetteChars[6] = ' ';
|
||||
axes.rosetteScancodes[6] = -1;
|
||||
axes.rosetteChars[7] = MOUSETEXT_DOWN;
|
||||
axes.rosetteScancodes[7] = -1;
|
||||
axes.rosetteChars[8] = ' ';
|
||||
axes.rosetteScancodes[8] = -1;
|
||||
|
||||
buttons.centerX = 240;
|
||||
buttons.centerY = 160;
|
||||
buttons.trackingIndex = TRACKING_NONE;
|
||||
|
||||
buttons.touchDownChar = TOUCH_BUTTON0;
|
||||
buttons.touchDownScancode = -1;
|
||||
|
||||
buttons.southChar = TOUCH_BUTTON1;
|
||||
buttons.southScancode = -1;
|
||||
|
||||
buttons.northChar = TOUCH_BOTH;
|
||||
buttons.northScancode = -1;
|
||||
|
||||
buttons.activeChar = MOUSETEXT_OPENAPPLE;
|
||||
|
||||
joyglobals.isEnabled = true;
|
||||
joyglobals.ownsScreen = true;
|
||||
joyglobals.showControls = true;
|
||||
joyglobals.showAzimuth = true;
|
||||
joyglobals.screenDivider = 0.5f;
|
||||
joyglobals.axisIsOnLeft = true;
|
||||
joyglobals.switchThreshold = BUTTON_SWITCH_THRESHOLD_DEFAULT;
|
||||
joyglobals.prefsChanged = true; // force reload preferences/defaults
|
||||
|
||||
video_animations->animation_showTouchJoystick = &_animation_showTouchJoystick;
|
||||
video_animations->animation_hideTouchJoystick = &_animation_hideTouchJoystick;
|
||||
|
||||
joydriver_isTouchJoystickAvailable = &gltouchjoy_isTouchJoystickAvailable;
|
||||
joydriver_setTouchJoystickEnabled = &gltouchjoy_setTouchJoystickEnabled;
|
||||
joydriver_setTouchJoystickOwnsScreen = &gltouchjoy_setTouchJoystickOwnsScreen;
|
||||
joydriver_ownsScreen = &gltouchjoy_ownsScreen;
|
||||
joydriver_setShowControls = &gltouchjoy_setShowControls;
|
||||
joydriver_setShowAzimuth = &gltouchjoy_setShowAzimuth;
|
||||
joydriver_setTouchButtonTypes = &gltouchjoy_setTouchButtonTypes;
|
||||
joydriver_setTouchAxisSensitivity = &gltouchjoy_setTouchAxisSensitivity;
|
||||
joydriver_setButtonSwitchThreshold = &gltouchjoy_setButtonSwitchThreshold;
|
||||
joydriver_setTouchVariant = &gltouchjoy_setTouchVariant;
|
||||
joydriver_getTouchVariant = &gltouchjoy_getTouchVariant;
|
||||
joydriver_setTouchAxisTypes = &gltouchjoy_setTouchAxisTypes;
|
||||
joydriver_setScreenDivision = &gltouchjoy_setScreenDivision;
|
||||
joydriver_setAxisOnLeft = &gltouchjoy_setAxisOnLeft;
|
||||
joydriver_beginCalibration = &gltouchjoy_beginCalibration;
|
||||
joydriver_endCalibration = &gltouchjoy_endCalibration;
|
||||
joydriver_isCalibrating = &gltouchjoy_isCalibrating;
|
||||
|
||||
glnode_registerNode(RENDER_LOW, (GLNode){
|
||||
.type = TOUCH_DEVICE_JOYSTICK,
|
||||
.setup = &gltouchjoy_setup,
|
||||
.shutdown = &gltouchjoy_shutdown,
|
||||
.render = &gltouchjoy_render,
|
||||
.reshape = &gltouchjoy_reshape,
|
||||
.onTouchEvent = &gltouchjoy_onTouchEvent,
|
||||
.setData = NULL,
|
||||
});
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_JOYSTICK, &gltouchjoy_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_TOUCHSCREEN, &gltouchjoy_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_INTERFACE, &gltouchjoy_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_gltouchjoy(void) {
|
||||
emulator_registerStartupCallback(CTOR_PRIORITY_LATE, &_init_gltouchjoy);
|
||||
}
|
||||
|
||||
void gltouchjoy_registerVariant(touchjoy_variant_t variantType, GLTouchJoyVariant *touchJoyVariant) {
|
||||
void gltouchjoy_registerVariant(interface_device_t variantType, GLTouchJoyVariant *touchJoyVariant) {
|
||||
switch (variantType) {
|
||||
case EMULATED_JOYSTICK:
|
||||
case TOUCH_DEVICE_JOYSTICK:
|
||||
variant.joys = touchJoyVariant;
|
||||
variant.curr = variant.kpad;
|
||||
variant.curr = variant.joys;
|
||||
break;
|
||||
|
||||
case EMULATED_KEYPAD:
|
||||
case TOUCH_DEVICE_JOYSTICK_KEYPAD:
|
||||
variant.kpad = touchJoyVariant;
|
||||
variant.curr = variant.kpad;
|
||||
break;
|
||||
|
|
|
@ -22,14 +22,28 @@
|
|||
# define TOUCH_JOY_LOG(...)
|
||||
#endif
|
||||
|
||||
#define ROSETTE_ROWS 3
|
||||
#define ROSETTE_COLS 3
|
||||
|
||||
enum {
|
||||
ROSETTE_NORTHWEST=0,
|
||||
ROSETTE_NORTH,
|
||||
ROSETTE_NORTHEAST,
|
||||
ROSETTE_WEST,
|
||||
ROSETTE_CENTER,
|
||||
ROSETTE_EAST,
|
||||
ROSETTE_SOUTHWEST,
|
||||
ROSETTE_SOUTH,
|
||||
ROSETTE_SOUTHEAST,
|
||||
};
|
||||
|
||||
// globals
|
||||
|
||||
typedef struct GLTouchJoyGlobals {
|
||||
|
||||
bool prefsChanged;
|
||||
bool isAvailable; // Were there any OpenGL/memory errors on gltouchjoy initialization?
|
||||
bool isShuttingDown;
|
||||
bool isCalibrating; // Are we running in calibration mode?
|
||||
bool isEnabled; // Does player want touchjoy enabled?
|
||||
bool ownsScreen; // Does the touchjoy currently own the screen?
|
||||
bool showControls; // Are controls visible?
|
||||
bool showAzimuth; // Is joystick azimuth shown?
|
||||
|
@ -37,7 +51,6 @@ typedef struct GLTouchJoyGlobals {
|
|||
float minAlpha;
|
||||
float screenDivider;
|
||||
bool axisIsOnLeft;
|
||||
|
||||
int switchThreshold;
|
||||
|
||||
} GLTouchJoyGlobals;
|
||||
|
@ -54,9 +67,6 @@ typedef struct GLTouchJoyAxes {
|
|||
// azimuth model
|
||||
GLModel *azimuthModel;
|
||||
|
||||
uint8_t rosetteChars[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
int rosetteScancodes[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
float multiplier;
|
||||
|
@ -73,13 +83,6 @@ typedef struct GLTouchJoyButtons {
|
|||
uint8_t activeChar;
|
||||
bool modelDirty;
|
||||
|
||||
touchjoy_button_type_t touchDownChar;
|
||||
int touchDownScancode;
|
||||
touchjoy_button_type_t northChar;
|
||||
int northScancode;
|
||||
touchjoy_button_type_t southChar;
|
||||
int southScancode;
|
||||
|
||||
int centerX;
|
||||
int centerY;
|
||||
int trackingIndex;
|
||||
|
@ -89,11 +92,13 @@ typedef struct GLTouchJoyButtons {
|
|||
extern GLTouchJoyButtons buttons;
|
||||
|
||||
typedef struct GLTouchJoyVariant {
|
||||
touchjoy_variant_t (*variant)(void);
|
||||
interface_device_t (*variant)(void);
|
||||
void (*resetState)(void);
|
||||
void (*setup)(void (*buttonDrawCallback)(char newChar));
|
||||
void (*shutdown)(void);
|
||||
|
||||
void (*prefsChanged)(const char *domain);
|
||||
|
||||
void (*buttonDown)(void);
|
||||
void (*buttonMove)(int dx, int dy);
|
||||
void (*buttonUp)(int dx, int dy);
|
||||
|
@ -102,9 +107,11 @@ typedef struct GLTouchJoyVariant {
|
|||
void (*axisMove)(int dx, int dy);
|
||||
void (*axisUp)(int dx, int dy);
|
||||
|
||||
uint8_t *(*rosetteChars)(void);
|
||||
|
||||
} GLTouchJoyVariant;
|
||||
|
||||
// registers a touch joystick variant with manager
|
||||
void gltouchjoy_registerVariant(touchjoy_variant_t variant, GLTouchJoyVariant *touchJoyVariant);
|
||||
void gltouchjoy_registerVariant(interface_device_t variant, GLTouchJoyVariant *touchJoyVariant);
|
||||
|
||||
#endif // whole file
|
||||
|
|
|
@ -31,12 +31,16 @@ static struct {
|
|||
pthread_mutex_t tapDelayMutex;
|
||||
pthread_cond_t tapDelayCond;
|
||||
unsigned int tapDelayNanos;
|
||||
|
||||
touchjoy_button_type_t touchDownChar;
|
||||
touchjoy_button_type_t northChar;
|
||||
touchjoy_button_type_t southChar;
|
||||
} joys;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static touchjoy_variant_t touchjoy_variant(void) {
|
||||
return EMULATED_JOYSTICK;
|
||||
static interface_device_t touchjoy_variant(void) {
|
||||
return TOUCH_DEVICE_JOYSTICK;
|
||||
}
|
||||
|
||||
static inline void _reset_axis_state(void) {
|
||||
|
@ -77,7 +81,7 @@ static void *_button_tap_delayed_thread(void *dummyptr) {
|
|||
uint8_t currJoyButtonValue1 = 0x0;
|
||||
uint8_t currButtonDisplayChar = ' ';
|
||||
for (;;) {
|
||||
if (UNLIKELY(joyglobals.isShuttingDown)) {
|
||||
if (UNLIKELY(!joyglobals.isAvailable)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -130,6 +134,10 @@ static void *_button_tap_delayed_thread(void *dummyptr) {
|
|||
|
||||
pthread_mutex_unlock(&joys.tapDelayMutex);
|
||||
|
||||
joys.tapDelayThreadId = 0;
|
||||
joys.tapDelayMutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
joys.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
|
||||
LOG(">>> [DELAYEDTAP] thread exit ...");
|
||||
|
||||
return NULL;
|
||||
|
@ -146,9 +154,6 @@ static void touchjoy_shutdown(void) {
|
|||
if (pthread_join(joys.tapDelayThreadId, NULL)) {
|
||||
ERRLOG("OOPS: pthread_join tap delay thread ...");
|
||||
}
|
||||
joys.tapDelayThreadId = 0;
|
||||
joys.tapDelayMutex = (pthread_mutex_t){ 0 };
|
||||
joys.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -192,11 +197,11 @@ static void touchjoy_axisUp(int x, int y) {
|
|||
// button state
|
||||
|
||||
static void _set_current_button_state(touchjoy_button_type_t theButtonChar) {
|
||||
if (theButtonChar == TOUCH_BUTTON0) {
|
||||
if (theButtonChar == TOUCH_BUTTON1) {
|
||||
joys.currJoyButtonValue0 = 0x80;
|
||||
joys.currJoyButtonValue1 = 0;
|
||||
joys.currButtonDisplayChar = MOUSETEXT_OPENAPPLE;
|
||||
} else if (theButtonChar == TOUCH_BUTTON1) {
|
||||
} else if (theButtonChar == TOUCH_BUTTON2) {
|
||||
joys.currJoyButtonValue0 = 0;
|
||||
joys.currJoyButtonValue1 = 0x80;
|
||||
joys.currButtonDisplayChar = MOUSETEXT_CLOSEDAPPLE;
|
||||
|
@ -212,7 +217,7 @@ static void _set_current_button_state(touchjoy_button_type_t theButtonChar) {
|
|||
}
|
||||
|
||||
static void touchjoy_buttonDown(void) {
|
||||
_set_current_button_state(buttons.touchDownChar);
|
||||
_set_current_button_state(joys.touchDownChar);
|
||||
joys.trackingButton = true;
|
||||
_signal_tap_delay();
|
||||
}
|
||||
|
@ -222,9 +227,9 @@ static void touchjoy_buttonMove(int dx, int dy) {
|
|||
|
||||
touchjoy_button_type_t theButtonChar = -1;
|
||||
if (dy < 0) {
|
||||
theButtonChar = buttons.northChar;
|
||||
theButtonChar = joys.northChar;
|
||||
} else {
|
||||
theButtonChar = buttons.southChar;
|
||||
theButtonChar = joys.southChar;
|
||||
}
|
||||
|
||||
_set_current_button_state(theButtonChar);
|
||||
|
@ -239,19 +244,35 @@ static void touchjoy_buttonUp(int dx, int dy) {
|
|||
_signal_tap_delay();
|
||||
}
|
||||
|
||||
static void gltouchjoy_setTapDelay(float secs) {
|
||||
if (UNLIKELY(secs < 0.f)) {
|
||||
ERRLOG("Clamping tap delay to 0.0 secs");
|
||||
static void touchjoy_prefsChanged(const char *domain) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
long lVal = 0;
|
||||
|
||||
joys.touchDownChar = prefs_parseLongValue(domain, PREF_JOY_TOUCHDOWN_CHAR, &lVal, /*base:*/10) ? lVal : TOUCH_BUTTON1;
|
||||
joys.northChar = prefs_parseLongValue(domain, PREF_JOY_SWIPE_NORTH_CHAR, &lVal, /*base:*/10) ? lVal : TOUCH_BOTH;
|
||||
joys.southChar = prefs_parseLongValue(domain, PREF_JOY_SWIPE_SOUTH_CHAR, &lVal, /*base:*/10) ? lVal : TOUCH_BUTTON2;
|
||||
|
||||
float fVal = 0.f;
|
||||
joys.tapDelayNanos = prefs_parseFloatValue(domain, PREF_JOY_TAP_DELAY, &fVal) ? (fVal * NANOSECONDS_PER_SECOND) : BUTTON_TAP_DELAY_NANOS_DEFAULT;
|
||||
}
|
||||
|
||||
static uint8_t *touchjoy_rosetteChars(void) {
|
||||
static uint8_t rosetteChars[ROSETTE_ROWS * ROSETTE_COLS] = { 0 };
|
||||
if (rosetteChars[0] == 0x0) {
|
||||
rosetteChars[0] = ' ';
|
||||
rosetteChars[1] = MOUSETEXT_UP;
|
||||
rosetteChars[2] = ' ';
|
||||
|
||||
rosetteChars[3] = MOUSETEXT_LEFT;
|
||||
rosetteChars[4] = ICONTEXT_MENU_TOUCHJOY;
|
||||
rosetteChars[5] = MOUSETEXT_RIGHT;
|
||||
|
||||
rosetteChars[6] = ' ';
|
||||
rosetteChars[7] = MOUSETEXT_DOWN;
|
||||
rosetteChars[8] = ' ';
|
||||
}
|
||||
if (UNLIKELY(secs > 1.f)) {
|
||||
ERRLOG("Clamping tap delay to 1.0 secs");
|
||||
}
|
||||
unsigned int tapDelayNanos = (unsigned int)((float)NANOSECONDS_PER_SECOND * secs);
|
||||
#define MIN_WAIT_NANOS 1000000
|
||||
if (tapDelayNanos < MIN_WAIT_NANOS) {
|
||||
tapDelayNanos = MIN_WAIT_NANOS;
|
||||
}
|
||||
joys.tapDelayNanos = tapDelayNanos;
|
||||
return rosetteChars;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -264,6 +285,8 @@ static void _init_gltouchjoy_joy(void) {
|
|||
happyHappyJoyJoy.setup = &touchjoy_setup,
|
||||
happyHappyJoyJoy.shutdown = &touchjoy_shutdown,
|
||||
|
||||
happyHappyJoyJoy.prefsChanged = &touchjoy_prefsChanged;
|
||||
|
||||
happyHappyJoyJoy.buttonDown = &touchjoy_buttonDown,
|
||||
happyHappyJoyJoy.buttonMove = &touchjoy_buttonMove,
|
||||
happyHappyJoyJoy.buttonUp = &touchjoy_buttonUp,
|
||||
|
@ -272,13 +295,12 @@ static void _init_gltouchjoy_joy(void) {
|
|||
happyHappyJoyJoy.axisMove = &touchjoy_axisMove,
|
||||
happyHappyJoyJoy.axisUp = &touchjoy_axisUp,
|
||||
|
||||
joys.tapDelayMutex = (pthread_mutex_t){ 0 };
|
||||
happyHappyJoyJoy.rosetteChars = &touchjoy_rosetteChars;
|
||||
|
||||
joys.tapDelayMutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
|
||||
joys.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
|
||||
joys.tapDelayNanos = BUTTON_TAP_DELAY_NANOS_DEFAULT;
|
||||
|
||||
joydriver_setTapDelay = &gltouchjoy_setTapDelay;
|
||||
|
||||
gltouchjoy_registerVariant(EMULATED_JOYSTICK, &happyHappyJoyJoy);
|
||||
gltouchjoy_registerVariant(TOUCH_DEVICE_JOYSTICK, &happyHappyJoyJoy);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_gltouchjoy_joy(void) {
|
||||
|
|
|
@ -68,6 +68,18 @@ static struct {
|
|||
|
||||
float repeatThresholdNanos;
|
||||
|
||||
// axis rosette
|
||||
uint8_t rosetteChars[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
long rosetteScancodes[ROSETTE_ROWS * ROSETTE_COLS];
|
||||
|
||||
// touch/swipe buttons
|
||||
touchjoy_button_type_t touchDownChar;
|
||||
int touchDownScancode;
|
||||
touchjoy_button_type_t northChar;
|
||||
int northScancode;
|
||||
touchjoy_button_type_t southChar;
|
||||
int southScancode;
|
||||
|
||||
} kpad = { 0 };
|
||||
|
||||
static GLTouchJoyVariant kpadJoy = { 0 };
|
||||
|
@ -188,8 +200,8 @@ static void touchkpad_keyboardReadCallback(void) {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static touchjoy_variant_t touchkpad_variant(void) {
|
||||
return EMULATED_KEYPAD;
|
||||
static interface_device_t touchkpad_variant(void) {
|
||||
return TOUCH_DEVICE_JOYSTICK_KEYPAD;
|
||||
}
|
||||
|
||||
static void touchkpad_resetState(void) {
|
||||
|
@ -212,13 +224,13 @@ static void touchkpad_resetState(void) {
|
|||
|
||||
for (unsigned int i=0; i<ROSETTE_COLS; i++) {
|
||||
for (unsigned int j=0; j<ROSETTE_ROWS; j++) {
|
||||
c_keys_handle_input(axes.rosetteScancodes[i], /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(kpad.rosetteScancodes[i], /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
}
|
||||
|
||||
c_keys_handle_input(buttons.touchDownScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(buttons.northScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(buttons.southScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(kpad.touchDownScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(kpad.northScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
c_keys_handle_input(kpad.southScancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
|
||||
static void touchkpad_setup(void (*buttonDrawCallback)(char newChar)) {
|
||||
|
@ -247,8 +259,8 @@ static void touchkpad_axisDown(void) {
|
|||
kpad.timingBegins[REPEAT_AXIS_ALT] = now;
|
||||
|
||||
kpad.axisCurrentOctant = ORIGIN;
|
||||
if (axes.rosetteScancodes[ROSETTE_CENTER] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_CENTER];
|
||||
if (kpad.rosetteScancodes[ROSETTE_CENTER] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_CENTER];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
}
|
||||
}
|
||||
|
@ -257,7 +269,7 @@ static void touchkpad_axisMove(int dx, int dy) {
|
|||
TOUCH_JOY_LOG("...");
|
||||
|
||||
if ((dx > -joyglobals.switchThreshold) && (dx < joyglobals.switchThreshold) && (dy > -joyglobals.switchThreshold) && (dy < joyglobals.switchThreshold)) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_CENTER];
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_CENTER];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
return;
|
||||
}
|
||||
|
@ -296,98 +308,98 @@ static void touchkpad_axisMove(int dx, int dy) {
|
|||
kpad.scancodes[REPEAT_AXIS_ALT] = -1;
|
||||
switch (kpad.axisCurrentOctant) {
|
||||
case OCTANT_NORTHWEST:
|
||||
if (axes.rosetteScancodes[ROSETTE_NORTHWEST] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST, (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
if (kpad.rosetteScancodes[ROSETTE_NORTHWEST] >= 0) {
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST, (%d)", kpad.rosetteScancodes[ROSETTE_WEST]);
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : WEST (%d) & NORTH (%d)", axes.rosetteScancodes[ROSETTE_WEST], axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
TOUCH_JOY_LOG("XY : WEST (%d) & NORTH (%d)", kpad.rosetteScancodes[ROSETTE_WEST], kpad.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = kpad.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_NORTH:
|
||||
if (axes.rosetteScancodes[ROSETTE_NORTH] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : NORTH (%d)", axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
if (kpad.rosetteScancodes[ROSETTE_NORTH] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : NORTH (%d)", kpad.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTH];
|
||||
} else if (radians < RADIANS_NORTH) {
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST (%d)", axes.rosetteScancodes[ROSETTE_NORTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST (%d)", kpad.rosetteScancodes[ROSETTE_NORTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", axes.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_NORTHEAST:
|
||||
if (axes.rosetteScancodes[ROSETTE_NORTHEAST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", axes.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
if (kpad.rosetteScancodes[ROSETTE_NORTHEAST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : EAST (%d) & NORTH (%d)", axes.rosetteScancodes[ROSETTE_EAST], axes.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_NORTH];
|
||||
TOUCH_JOY_LOG("XY : EAST (%d) & NORTH (%d)", kpad.rosetteScancodes[ROSETTE_EAST], kpad.rosetteScancodes[ROSETTE_NORTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = kpad.rosetteScancodes[ROSETTE_NORTH];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_WEST:
|
||||
if (axes.rosetteScancodes[ROSETTE_WEST] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : WEST (%d)", axes.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
if (kpad.rosetteScancodes[ROSETTE_WEST] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : WEST (%d)", kpad.rosetteScancodes[ROSETTE_WEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_WEST];
|
||||
} else if (radians > RADIANS_WEST_NEG && radians < 0) {
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST (%d)", axes.rosetteScancodes[ROSETTE_NORTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
TOUCH_JOY_LOG("XY : NORTHWEST (%d)", kpad.rosetteScancodes[ROSETTE_NORTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHWEST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_EAST:
|
||||
if (axes.rosetteScancodes[ROSETTE_EAST] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : EAST (%d)", axes.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
if (kpad.rosetteScancodes[ROSETTE_EAST] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : EAST (%d)", kpad.rosetteScancodes[ROSETTE_EAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_EAST];
|
||||
} else if (radians < RADIANS_EAST) {
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", axes.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
TOUCH_JOY_LOG("XY : NORTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_NORTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_NORTHEAST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTHWEST:
|
||||
if (axes.rosetteScancodes[ROSETTE_SOUTHWEST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
if (kpad.rosetteScancodes[ROSETTE_SOUTHWEST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : WEST (%d) & SOUTH (%d)", axes.rosetteScancodes[ROSETTE_WEST], axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
TOUCH_JOY_LOG("XY : WEST (%d) & SOUTH (%d)", kpad.rosetteScancodes[ROSETTE_WEST], kpad.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_WEST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = kpad.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTH:
|
||||
if (axes.rosetteScancodes[ROSETTE_SOUTH] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : SOUTH (%d)", axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
if (kpad.rosetteScancodes[ROSETTE_SOUTH] >= 0) {
|
||||
TOUCH_JOY_LOG("Y : SOUTH (%d)", kpad.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTH];
|
||||
} else if (radians > RADIANS_SOUTH) {
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
TOUCH_JOY_LOG("XY : SOUTHWEST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHWEST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHWEST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
}
|
||||
break;
|
||||
|
||||
case OCTANT_SOUTHEAST:
|
||||
if (axes.rosetteScancodes[ROSETTE_SOUTHEAST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", axes.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
if (kpad.rosetteScancodes[ROSETTE_SOUTHEAST] >= 0) {
|
||||
TOUCH_JOY_LOG("XY : SOUTHEAST (%d)", kpad.rosetteScancodes[ROSETTE_SOUTHEAST]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_SOUTHEAST];
|
||||
} else {
|
||||
TOUCH_JOY_LOG("XY : EAST (%d) & SOUTH (%d)", axes.rosetteScancodes[ROSETTE_EAST], axes.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = axes.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = axes.rosetteScancodes[ROSETTE_SOUTH];
|
||||
TOUCH_JOY_LOG("XY : EAST (%d) & SOUTH (%d)", kpad.rosetteScancodes[ROSETTE_EAST], kpad.rosetteScancodes[ROSETTE_SOUTH]);
|
||||
kpad.scancodes[REPEAT_AXIS] = kpad.rosetteScancodes[ROSETTE_EAST];
|
||||
kpad.scancodes[REPEAT_AXIS_ALT] = kpad.rosetteScancodes[ROSETTE_SOUTH];
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -431,7 +443,7 @@ static void touchkpad_buttonDown(void) {
|
|||
kpad.buttonBegan = true;
|
||||
_touch_sourceBegin(&kpad.buttonLock);
|
||||
}
|
||||
_set_current_button_state(buttons.touchDownChar, buttons.touchDownScancode);
|
||||
_set_current_button_state(kpad.touchDownChar, kpad.touchDownScancode);
|
||||
if (kpad.scancodes[REPEAT_BUTTON] >= 0) {
|
||||
TOUCH_JOY_LOG("->BUTT : %d/'%c'", kpad.scancodes[REPEAT_BUTTON], kpad.currButtonDisplayChar);
|
||||
clock_gettime(CLOCK_MONOTONIC, &kpad.timingBegins[REPEAT_BUTTON]);
|
||||
|
@ -447,11 +459,11 @@ static void touchkpad_buttonMove(int dx, int dy) {
|
|||
touchjoy_button_type_t theButtonChar = -1;
|
||||
int theButtonScancode = -1;
|
||||
if (dy < 0) {
|
||||
theButtonChar = buttons.northChar;
|
||||
theButtonScancode = buttons.northScancode;
|
||||
theButtonChar = kpad.northChar;
|
||||
theButtonScancode = kpad.northScancode;
|
||||
} else {
|
||||
theButtonChar = buttons.southChar;
|
||||
theButtonScancode = buttons.southScancode;
|
||||
theButtonChar = kpad.southChar;
|
||||
theButtonScancode = kpad.southScancode;
|
||||
}
|
||||
_set_current_button_state(theButtonChar, theButtonScancode);
|
||||
}
|
||||
|
@ -467,8 +479,91 @@ static void touchkpad_buttonUp(int dx, int dy) {
|
|||
}
|
||||
}
|
||||
|
||||
static void touchkpad_setKeyRepeatThreshold(float repeatThresholdSecs) {
|
||||
kpad.repeatThresholdNanos = repeatThresholdSecs * NANOSECONDS_PER_SECOND;
|
||||
static void touchkpad_prefsChanged(const char *domain) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
bool bVal = false;
|
||||
float fVal = 0.f;
|
||||
long lVal = 0;
|
||||
|
||||
kpad.repeatThresholdNanos = prefs_parseFloatValue(domain, PREF_KPAD_REPEAT_THRESH, &fVal) ? fVal*NANOSECONDS_PER_SECOND : NANOSECONDS_PER_SECOND;
|
||||
|
||||
kpad.touchDownChar = prefs_parseLongValue (domain, PREF_KPAD_TOUCHDOWN_CHAR, &lVal, /*base:*/10) ? lVal : ICONTEXT_SPACE_VISUAL;
|
||||
kpad.touchDownScancode = prefs_parseLongValue (domain, PREF_KPAD_TOUCHDOWN_SCAN, &lVal, /*base:*/10) ? lVal : c_keys_ascii_to_scancode(' ');
|
||||
|
||||
kpad.southChar = prefs_parseLongValue (domain, PREF_KPAD_SWIPE_SOUTH_CHAR, &lVal, /*base:*/10) ? lVal : ICONTEXT_NONACTIONABLE;
|
||||
kpad.southScancode = prefs_parseLongValue (domain, PREF_KPAD_SWIPE_SOUTH_SCAN, &lVal, /*base:*/10) ? lVal : -1;
|
||||
|
||||
kpad.northChar = prefs_parseLongValue (domain, PREF_KPAD_SWIPE_NORTH_CHAR, &lVal, /*base:*/10) ? lVal : ICONTEXT_NONACTIONABLE;
|
||||
kpad.northScancode = prefs_parseLongValue (domain, PREF_KPAD_SWIPE_NORTH_SCAN, &lVal, /*base:*/10) ? lVal : -1;
|
||||
|
||||
const unsigned long rosetteCount = ROSETTE_ROWS*ROSETTE_COLS;
|
||||
|
||||
do {
|
||||
const int rosetteChars[ROSETTE_ROWS*ROSETTE_COLS] = {
|
||||
ICONTEXT_NONACTIONABLE, 'I', ICONTEXT_NONACTIONABLE,
|
||||
'J', ICONTEXT_NONACTIONABLE, 'K',
|
||||
ICONTEXT_NONACTIONABLE, 'M', ICONTEXT_NONACTIONABLE,
|
||||
};
|
||||
const int rosetteScans[ROSETTE_ROWS*ROSETTE_COLS] = {
|
||||
-1, c_keys_ascii_to_scancode('I'), -1,
|
||||
c_keys_ascii_to_scancode('J'), -1, c_keys_ascii_to_scancode('K'),
|
||||
-1, c_keys_ascii_to_scancode('M'), -1,
|
||||
};
|
||||
for (unsigned long i=0; i<rosetteCount; i++) {
|
||||
kpad.rosetteChars[i] = rosetteChars[i];
|
||||
kpad.rosetteScancodes[i] = rosetteScans[i];
|
||||
}
|
||||
} while (0);
|
||||
|
||||
// ASCII : "rosetteChars" : [ 121, 127, 130, 100, 101, 121, 132, 120, 99 ]
|
||||
JSON_ref array = NULL;
|
||||
do {
|
||||
if (!prefs_copyJSONValue(domain, PREF_KPAD_ROSETTE_CHAR_ARRAY, &array)) {
|
||||
LOG("could not parse touch keypad rosette");
|
||||
break;
|
||||
}
|
||||
long count = 0;
|
||||
if (!json_arrayCount(array, &count)) {
|
||||
LOG("rosette is not an array!");
|
||||
break;
|
||||
}
|
||||
if (count != rosetteCount) {
|
||||
LOG("rosette count unexpected : %lu!", rosetteCount);
|
||||
break;
|
||||
}
|
||||
for (unsigned long i=0; i<rosetteCount; i++) {
|
||||
kpad.rosetteChars[i] = json_arrayParseLongValueAtIndex(array, i, &lVal, /*base:*/10) ? (uint8_t)lVal : ' ';
|
||||
}
|
||||
} while (0);
|
||||
|
||||
json_destroy(&array);
|
||||
|
||||
// long : "rosetteScancodes" : [ -1, 100, -1, 99, -1, 96, -1, 101, -1 ]
|
||||
do {
|
||||
if (!prefs_copyJSONValue(domain, PREF_KPAD_ROSETTE_SCAN_ARRAY, &array)) {
|
||||
LOG("could not parse touch keypad rosette");
|
||||
break;
|
||||
}
|
||||
long count = 0;
|
||||
if (!json_arrayCount(array, &count)) {
|
||||
LOG("rosette is not an array!");
|
||||
break;
|
||||
}
|
||||
if (count != rosetteCount) {
|
||||
LOG("rosette count unexpected : %lu!", rosetteCount);
|
||||
break;
|
||||
}
|
||||
for (unsigned long i=0; i<rosetteCount; i++) {
|
||||
kpad.rosetteScancodes[i] = json_arrayParseLongValueAtIndex(array, i, &lVal, /*base:*/10) ? lVal : -1;
|
||||
}
|
||||
} while (0);
|
||||
|
||||
json_destroy(&array);
|
||||
}
|
||||
|
||||
static uint8_t *touchkpad_rosetteChars(void) {
|
||||
return kpad.rosetteChars;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -482,13 +577,13 @@ static void _init_gltouchjoy_kpad(void) {
|
|||
|
||||
kpad.currButtonDisplayChar = ' ';
|
||||
|
||||
kpad.repeatThresholdNanos = KEY_REPEAT_THRESHOLD_NANOS;
|
||||
|
||||
kpadJoy.variant = &touchkpad_variant,
|
||||
kpadJoy.resetState = &touchkpad_resetState,
|
||||
kpadJoy.setup = &touchkpad_setup,
|
||||
kpadJoy.shutdown = &touchkpad_shutdown,
|
||||
|
||||
kpadJoy.prefsChanged = &touchkpad_prefsChanged;
|
||||
|
||||
kpadJoy.buttonDown = &touchkpad_buttonDown,
|
||||
kpadJoy.buttonMove = &touchkpad_buttonMove,
|
||||
kpadJoy.buttonUp = &touchkpad_buttonUp,
|
||||
|
@ -497,9 +592,9 @@ static void _init_gltouchjoy_kpad(void) {
|
|||
kpadJoy.axisMove = &touchkpad_axisMove,
|
||||
kpadJoy.axisUp = &touchkpad_axisUp,
|
||||
|
||||
joydriver_setKeyRepeatThreshold = &touchkpad_setKeyRepeatThreshold;
|
||||
kpadJoy.rosetteChars = &touchkpad_rosetteChars;
|
||||
|
||||
gltouchjoy_registerVariant(EMULATED_KEYPAD, &kpadJoy);
|
||||
gltouchjoy_registerVariant(TOUCH_DEVICE_JOYSTICK_KEYPAD, &kpadJoy);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_gltouchjoy_kpad(void) {
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
#define KBD_OBJ_H_LANDSCAPE GL_MODEL_MAX
|
||||
|
||||
static bool isAvailable = false; // Were there any OpenGL/memory errors on gltouchkbd initialization?
|
||||
static bool isEnabled = true; // Does player want touchkbd enabled?
|
||||
static bool ownsScreen = false; // Does the touchkbd currently own the screen to the exclusion?
|
||||
static bool isCalibrating = false; // Are we in calibration mode?
|
||||
static bool allowLowercase = false; // show lowercase keyboard
|
||||
|
@ -108,11 +107,6 @@ static struct {
|
|||
|
||||
int kbdW;
|
||||
int kbdH;
|
||||
|
||||
// raw device dimensions
|
||||
int rawWidth;
|
||||
int rawHeight;
|
||||
int isLandscape;
|
||||
} touchport = { 0 };
|
||||
|
||||
// keyboard variables
|
||||
|
@ -138,10 +132,11 @@ static struct {
|
|||
|
||||
struct timespec timingBegin;
|
||||
|
||||
// pending changes requiring reinitialization
|
||||
unsigned int nextGlyphMultiplier;
|
||||
bool prefsChanged;
|
||||
} kbd = { 0 };
|
||||
|
||||
static void gltouchkbd_applyPrefs(void);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Misc internal methods
|
||||
|
||||
|
@ -435,7 +430,7 @@ static inline int64_t _tap_key_at_point(float x, float y) {
|
|||
}
|
||||
} else if (isCTRL) {
|
||||
c_keys_handle_input(scancode, /*pressed:*/kbd.ctrlPressed, /*ASCII:*/false);
|
||||
} else if (scancode != -1) {
|
||||
} else if (scancode) {
|
||||
// perform a press of other keys (ESC, Arrows, etc)
|
||||
c_keys_handle_input(scancode, /*pressed:*/true, /*ASCII:*/false);
|
||||
c_keys_handle_input(scancode, /*pressed:*/false, /*ASCII:*/false);
|
||||
|
@ -506,8 +501,6 @@ static void gltouchkbd_shutdown(void) {
|
|||
kbd.selectedCol = -1;
|
||||
kbd.selectedRow = -1;
|
||||
kbd.ctrlPressed = false;
|
||||
|
||||
kbd.nextGlyphMultiplier = 0;
|
||||
}
|
||||
|
||||
static void gltouchkbd_setup(void) {
|
||||
|
@ -515,6 +508,10 @@ static void gltouchkbd_setup(void) {
|
|||
|
||||
gltouchkbd_shutdown();
|
||||
|
||||
if (kbd.prefsChanged) {
|
||||
gltouchkbd_applyPrefs();
|
||||
}
|
||||
|
||||
kbd.model = mdlCreateQuad((GLModelParams_s){
|
||||
.skew_x = -GL_MODEL_HALF,
|
||||
.skew_y = kbd.modelSkewY,
|
||||
|
@ -524,7 +521,7 @@ static void gltouchkbd_setup(void) {
|
|||
.positionUsageHint = GL_DYNAMIC_DRAW, // positions might change
|
||||
.tex_w = KBD_FB_WIDTH * kbd.glyphMultiplier,
|
||||
.tex_h = KBD_FB_HEIGHT * kbd.glyphMultiplier,
|
||||
.texcoordUsageHint = GL_DYNAMIC_DRAW, // but key texture does
|
||||
.texcoordUsageHint = GL_DYNAMIC_DRAW, // and key texture does
|
||||
}, (GLCustom){
|
||||
.create = &_create_touchkbd_hud,
|
||||
.destroy = &glhud_destroyDefault,
|
||||
|
@ -541,25 +538,23 @@ static void gltouchkbd_setup(void) {
|
|||
clock_gettime(CLOCK_MONOTONIC, &kbd.timingBegin);
|
||||
|
||||
isAvailable = true;
|
||||
|
||||
if (ownsScreen) {
|
||||
video_animations->animation_showTouchKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
static void gltouchkbd_render(void) {
|
||||
if (!isAvailable) {
|
||||
return;
|
||||
}
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
if (UNLIKELY(kbd.prefsChanged)) {
|
||||
gltouchkbd_setup();
|
||||
}
|
||||
if (!ownsScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (kbd.nextGlyphMultiplier) {
|
||||
kbd.glyphMultiplier = kbd.nextGlyphMultiplier;
|
||||
kbd.nextGlyphMultiplier = 0;
|
||||
gltouchkbd_setup();
|
||||
}
|
||||
|
||||
float alpha = glhud_getTimedVisibility(kbd.timingBegin, minAlpha, maxAlpha);
|
||||
if (alpha < minAlpha) {
|
||||
alpha = minAlpha;
|
||||
|
@ -593,10 +588,7 @@ static void gltouchkbd_render(void) {
|
|||
|
||||
static void gltouchkbd_reshape(int w, int h, bool landscape) {
|
||||
LOG("w:%d h:%d landscape:%d", w, h, landscape);
|
||||
|
||||
touchport.rawWidth = w;
|
||||
touchport.rawHeight = h;
|
||||
touchport.isLandscape = landscape;
|
||||
assert(video_isRenderThread());
|
||||
|
||||
touchport.kbdX = 0;
|
||||
|
||||
|
@ -623,31 +615,13 @@ static void gltouchkbd_reshape(int w, int h, bool landscape) {
|
|||
}
|
||||
}
|
||||
|
||||
static void gltouchkbd_setData(const char *jsonData) {
|
||||
JSON_ref parsedData = NULL;
|
||||
int tokCount = json_createFromString(jsonData, &parsedData);
|
||||
|
||||
do {
|
||||
if (tokCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
json_mapParseFloatValue(parsedData, PREF_PORTRAIT_HEIGHT_SCALE, &kbd.portraitHeightScale);
|
||||
json_mapParseFloatValue(parsedData, PREF_PORTRAIT_POSITION_SCALE, &kbd.portraitPositionScale);
|
||||
|
||||
gltouchkbd_reshape(touchport.rawWidth, touchport.rawHeight, touchport.isLandscape);
|
||||
} while (0);
|
||||
|
||||
json_destroy(&parsedData);
|
||||
}
|
||||
|
||||
static int64_t gltouchkbd_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
||||
|
||||
if (!isAvailable) {
|
||||
return 0x0LL;
|
||||
}
|
||||
if (!isEnabled) {
|
||||
return 0x0LL;
|
||||
if (UNLIKELY(kbd.prefsChanged)) {
|
||||
return 0x0;
|
||||
}
|
||||
if (!ownsScreen) {
|
||||
return 0x0LL;
|
||||
|
@ -702,90 +676,21 @@ static int64_t gltouchkbd_onTouchEvent(interface_touch_event_t action, int point
|
|||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Animation and settings handling
|
||||
|
||||
static bool gltouchkbd_isTouchKeyboardAvailable(void) {
|
||||
return isAvailable;
|
||||
}
|
||||
|
||||
static void gltouchkbd_setTouchKeyboardEnabled(bool enabled) {
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
static void gltouchkbd_setTouchKeyboardOwnsScreen(bool pwnd) {
|
||||
ownsScreen = pwnd;
|
||||
if (ownsScreen) {
|
||||
minAlpha = minAlphaWhenOwnsScreen;
|
||||
if (allowLowercase) {
|
||||
caps_lock = false;
|
||||
} else {
|
||||
caps_lock = true;
|
||||
}
|
||||
} else {
|
||||
// reset visuals
|
||||
minAlpha = 0.0;
|
||||
|
||||
kbd.selectedCol = -1;
|
||||
kbd.selectedRow = -1;
|
||||
|
||||
if (kbd.model) {
|
||||
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)kbd.model->custom;
|
||||
hudKeyboard->colorScheme = RED_ON_BLACK;
|
||||
glhud_setupDefault(kbd.model);
|
||||
}
|
||||
|
||||
// reset CTRL state upon leaving this touch device
|
||||
kbd.ctrlPressed = false;
|
||||
c_keys_handle_input(SCODE_L_CTRL, /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
}
|
||||
|
||||
static bool gltouchkbd_ownsScreen(void) {
|
||||
return ownsScreen;
|
||||
}
|
||||
|
||||
static void gltouchkbd_setGlyphScale(int glyphScale) {
|
||||
if (glyphScale == 0) {
|
||||
glyphScale = 1;
|
||||
}
|
||||
kbd.nextGlyphMultiplier = glyphScale;
|
||||
}
|
||||
|
||||
static void gltouchkbd_setVisibilityWhenOwnsScreen(float inactiveAlpha, float activeAlpha) {
|
||||
minAlphaWhenOwnsScreen = inactiveAlpha;
|
||||
maxAlpha = activeAlpha;
|
||||
if (ownsScreen) {
|
||||
minAlpha = minAlphaWhenOwnsScreen;
|
||||
}
|
||||
}
|
||||
|
||||
static void gltouchkbd_setLowercaseEnabled(bool enabled) {
|
||||
allowLowercase = enabled;
|
||||
if (allowLowercase && ownsScreen) {
|
||||
caps_lock = false;
|
||||
} else {
|
||||
caps_lock = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void gltouchkbd_beginCalibration(void) {
|
||||
isCalibrating = true;
|
||||
}
|
||||
|
||||
static void gltouchkbd_endCalibration(void) {
|
||||
isCalibrating = false;
|
||||
}
|
||||
|
||||
static void gltouchkbd_loadAltKbd(const char *kbdPath) {
|
||||
static void _loadAltKbd(const char *kbdPath) {
|
||||
JSON_ref jsonRef = NULL;
|
||||
|
||||
json_unescapeSlashes(&kbdPath);
|
||||
int tokCount = json_createFromFile(kbdPath, &jsonRef);
|
||||
JSON_s parsedData = (JSON_s)parsedData;
|
||||
JSON_s parsedData = { 0 };
|
||||
|
||||
do {
|
||||
if (tokCount < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
parsedData = (JSON_s)(*jsonRef);
|
||||
|
||||
// we are expecting a very specific layout ... abort if anything is not correct
|
||||
int idx=0;
|
||||
|
||||
|
@ -922,7 +827,79 @@ static void gltouchkbd_loadAltKbd(const char *kbdPath) {
|
|||
|
||||
} while (0);
|
||||
|
||||
json_destroy(&parsedData);
|
||||
json_destroy(&jsonRef);
|
||||
}
|
||||
|
||||
static void gltouchkbd_applyPrefs(void) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
kbd.prefsChanged = false;
|
||||
|
||||
bool bVal = false;
|
||||
float fVal = 0.f;
|
||||
long lVal = 0;
|
||||
|
||||
allowLowercase = prefs_parseBoolValue (PREF_DOMAIN_KEYBOARD, PREF_LOWERCASE_ENABLED, &bVal) ? bVal : false;
|
||||
|
||||
minAlphaWhenOwnsScreen = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_MIN_ALPHA, &fVal) ? fVal : 1/4.f;
|
||||
maxAlpha = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_MAX_ALPHA, &fVal) ? fVal : 1.f;
|
||||
|
||||
kbd.glyphMultiplier = prefs_parseLongValue (PREF_DOMAIN_KEYBOARD, PREF_GLYPH_MULTIPLIER, &lVal, /*base:*/10) ? lVal : 2;
|
||||
if (kbd.glyphMultiplier == 0) {
|
||||
kbd.glyphMultiplier = 1;
|
||||
}
|
||||
if (kbd.glyphMultiplier > 4) {
|
||||
kbd.glyphMultiplier = 4;
|
||||
}
|
||||
|
||||
kbd.portraitHeightScale = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_PORTRAIT_HEIGHT_SCALE, &fVal) ? fVal : 0.5f;
|
||||
kbd.portraitPositionScale = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_PORTRAIT_POSITION_SCALE, &fVal) ? fVal : 0.f;
|
||||
|
||||
isCalibrating = prefs_parseBoolValue (PREF_DOMAIN_TOUCHSCREEN, PREF_CALIBRATING, &bVal) ? bVal : false;
|
||||
const interface_device_t screenOwner
|
||||
= prefs_parseLongValue (PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, &lVal, /*base:*/10) ? (interface_device_t)lVal : TOUCH_DEVICE_KEYBOARD;
|
||||
ownsScreen = (screenOwner == TOUCH_DEVICE_KEYBOARD || screenOwner == TOUCH_DEVICE_NONE);
|
||||
|
||||
if (ownsScreen) {
|
||||
minAlpha = minAlphaWhenOwnsScreen;
|
||||
if (allowLowercase) {
|
||||
caps_lock = false;
|
||||
} else {
|
||||
caps_lock = true;
|
||||
}
|
||||
} else {
|
||||
// reset visuals
|
||||
minAlpha = 0.0;
|
||||
|
||||
kbd.selectedCol = -1;
|
||||
kbd.selectedRow = -1;
|
||||
|
||||
if (kbd.model) {
|
||||
GLModelHUDElement *hudKeyboard = (GLModelHUDElement *)kbd.model->custom;
|
||||
hudKeyboard->colorScheme = RED_ON_BLACK;
|
||||
glhud_setupDefault(kbd.model);
|
||||
}
|
||||
|
||||
// reset CTRL state upon leaving this touch device
|
||||
kbd.ctrlPressed = false;
|
||||
c_keys_handle_input(SCODE_L_CTRL, /*pressed:*/false, /*ASCII:*/false);
|
||||
}
|
||||
|
||||
char *kbdPath = NULL;
|
||||
if (prefs_copyStringValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_ALT_PATH, &kbdPath)) {
|
||||
_loadAltKbd(kbdPath);
|
||||
FREE(kbdPath);
|
||||
}
|
||||
|
||||
long width = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, &lVal, 10) ? lVal : (long)(SCANWIDTH*1.5);
|
||||
long height = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, &lVal, 10) ? lVal : (long)(SCANHEIGHT*1.5);
|
||||
bool isLandscape = prefs_parseBoolValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, &bVal) ? bVal : true;
|
||||
|
||||
gltouchkbd_reshape(width, height, isLandscape);
|
||||
}
|
||||
|
||||
static void gltouchkbd_prefsChanged(const char *domain) {
|
||||
kbd.prefsChanged = true;
|
||||
}
|
||||
|
||||
static void _animation_showTouchKeyboard(void) {
|
||||
|
@ -1049,37 +1026,24 @@ static void _init_gltouchkbd(void) {
|
|||
video_animations->animation_showTouchKeyboard = &_animation_showTouchKeyboard;
|
||||
video_animations->animation_hideTouchKeyboard = &_animation_hideTouchKeyboard;
|
||||
|
||||
keydriver_isTouchKeyboardAvailable = &gltouchkbd_isTouchKeyboardAvailable;
|
||||
keydriver_setTouchKeyboardEnabled = &gltouchkbd_setTouchKeyboardEnabled;
|
||||
keydriver_setTouchKeyboardOwnsScreen = &gltouchkbd_setTouchKeyboardOwnsScreen;
|
||||
keydriver_ownsScreen = &gltouchkbd_ownsScreen;
|
||||
keydriver_setVisibilityWhenOwnsScreen = &gltouchkbd_setVisibilityWhenOwnsScreen;
|
||||
keydriver_setLowercaseEnabled = &gltouchkbd_setLowercaseEnabled;
|
||||
keydriver_beginCalibration = &gltouchkbd_beginCalibration;
|
||||
keydriver_endCalibration = &gltouchkbd_endCalibration;
|
||||
keydriver_loadAltKbd = &gltouchkbd_loadAltKbd;
|
||||
keydriver_setGlyphScale = &gltouchkbd_setGlyphScale;
|
||||
|
||||
kbd.portraitHeightScale = 0.5f;
|
||||
kbd.portraitPositionScale = 0.f;
|
||||
|
||||
kbd.prefsChanged = true;
|
||||
kbd.selectedCol = -1;
|
||||
kbd.selectedRow = -1;
|
||||
|
||||
kbd.ctrlCol = DEFAULT_CTRL_COL;
|
||||
kbd.ctrlRow = CTRLROW;
|
||||
|
||||
kbd.glyphMultiplier = 1;
|
||||
|
||||
glnode_registerNode(RENDER_LOW, (GLNode){
|
||||
.type = TOUCH_DEVICE_KEYBOARD,
|
||||
.setup = &gltouchkbd_setup,
|
||||
.shutdown = &gltouchkbd_shutdown,
|
||||
.render = &gltouchkbd_render,
|
||||
.reshape = &gltouchkbd_reshape,
|
||||
.onTouchEvent = &gltouchkbd_onTouchEvent,
|
||||
.setData = &gltouchkbd_setData,
|
||||
});
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_KEYBOARD, &gltouchkbd_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_TOUCHSCREEN, &gltouchkbd_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_INTERFACE, &gltouchkbd_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_gltouchkbd(void) {
|
||||
|
|
|
@ -75,9 +75,11 @@ static struct {
|
|||
float maxAlpha;
|
||||
|
||||
// pending changes requiring reinitialization
|
||||
unsigned int nextGlyphMultiplier;
|
||||
bool prefsChanged;
|
||||
} menu = { 0 };
|
||||
|
||||
static void gltouchmenu_applyPrefs(void);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static inline void _present_menu(GLModel *parent) {
|
||||
|
@ -92,9 +94,12 @@ static inline void _show_top_left(void) {
|
|||
topMenuTemplate[0][0] = ICONTEXT_MENU_SPROUT;
|
||||
topMenuTemplate[0][1] = MOUSETEXT_RIGHT;
|
||||
|
||||
if (joydriver_ownsScreen()) {
|
||||
long lVal = 0;
|
||||
interface_device_t screenOwner = prefs_parseLongValue(PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, &lVal, /*base:*/10) ? (interface_device_t)lVal : TOUCH_DEVICE_NONE;
|
||||
|
||||
if (screenOwner == TOUCH_DEVICE_JOYSTICK || screenOwner == TOUCH_DEVICE_JOYSTICK_KEYPAD) {
|
||||
topMenuTemplate[1][0] = ICONTEXT_UPPERCASE;
|
||||
if (joydriver_getTouchVariant() == EMULATED_JOYSTICK) {
|
||||
if (screenOwner == TOUCH_DEVICE_JOYSTICK) {
|
||||
topMenuTemplate[1][1] = ICONTEXT_MENU_TOUCHJOY_KPAD;
|
||||
} else {
|
||||
topMenuTemplate[1][1] = ICONTEXT_MENU_TOUCHJOY;
|
||||
|
@ -174,7 +179,7 @@ static inline void _screen_to_menu(float x, float y, OUTPARM int *col, OUTPARM i
|
|||
//LOG("SCREEN TO MENU : menuX:%d menuXMax:%d menuW:%d keyW:%d ... scrn:(%f,%f)->kybd:(%d,%d)", touchport.topLeftX, touchport.topLeftXMax, touchport.width, keyW, x, y, *col, *row);
|
||||
}
|
||||
|
||||
static inline bool _sprout_menu(float x, float y) {
|
||||
static bool _sprout_menu(float x, float y) {
|
||||
|
||||
if (! (_is_point_on_left_menu(x, y) || _is_point_on_right_menu(x, y)) ) {
|
||||
return false;
|
||||
|
@ -226,7 +231,46 @@ static inline bool _sprout_menu(float x, float y) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline int64_t _tap_menu_item(float x, float y) {
|
||||
static inline void _step_cpu_speed(int delta) {
|
||||
bool wasPaused = cpu_isPaused();
|
||||
|
||||
if (!wasPaused) {
|
||||
cpu_pause();
|
||||
}
|
||||
|
||||
// TODO FIXME : consolidate with other CPU stepping code in interface.c/timing.c/glalert.c animation =D
|
||||
float scale = roundf(cpu_scale_factor * 100.f);
|
||||
if (delta < 0) {
|
||||
if (scale > 400.f) {
|
||||
scale = 375.f;
|
||||
} else if (scale > 100.f) {
|
||||
scale -= 25.f;
|
||||
} else {
|
||||
scale -= 5.f;
|
||||
}
|
||||
} else {
|
||||
if (scale >= 100.f) {
|
||||
scale += 25.f;
|
||||
} else {
|
||||
scale += 5.f;
|
||||
}
|
||||
}
|
||||
|
||||
prefs_setFloatValue(PREF_DOMAIN_VM, PREF_CPU_SCALE, scale);
|
||||
prefs_sync(PREF_DOMAIN_VM);
|
||||
|
||||
if (video_animations->animation_showCPUSpeed) {
|
||||
video_animations->animation_showCPUSpeed();
|
||||
}
|
||||
|
||||
timing_initialize();
|
||||
|
||||
if (!wasPaused) {
|
||||
cpu_resume();
|
||||
}
|
||||
}
|
||||
|
||||
static int64_t _tap_menu_item(float x, float y) {
|
||||
if (! (_is_point_on_left_menu(x, y) || _is_point_on_right_menu(x, y)) ) {
|
||||
return 0x0LL;
|
||||
}
|
||||
|
@ -243,18 +287,19 @@ static inline int64_t _tap_menu_item(float x, float y) {
|
|||
|
||||
case MOUSETEXT_LEFT:
|
||||
LOG("decreasing cpu speed...");
|
||||
flags |= TOUCH_FLAGS_CPU_SPEED_DEC;
|
||||
_step_cpu_speed(-1);
|
||||
break;
|
||||
|
||||
case MOUSETEXT_RIGHT:
|
||||
LOG("increasing cpu speed...");
|
||||
flags |= TOUCH_FLAGS_CPU_SPEED_INC;
|
||||
_step_cpu_speed(1);
|
||||
break;
|
||||
|
||||
case MOUSETEXT_CHECKMARK:
|
||||
LOG("showing main menu...");
|
||||
flags |= TOUCH_FLAGS_REQUEST_HOST_MENU;
|
||||
_hide_top_right();
|
||||
prefs_save();
|
||||
break;
|
||||
|
||||
case ICONTEXT_MENU_TOUCHJOY:
|
||||
|
@ -262,6 +307,8 @@ static inline int64_t _tap_menu_item(float x, float y) {
|
|||
flags |= TOUCH_FLAGS_INPUT_DEVICE_CHANGE;
|
||||
flags |= TOUCH_FLAGS_JOY;
|
||||
_hide_top_left();
|
||||
prefs_setLongValue(PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, TOUCH_DEVICE_JOYSTICK);
|
||||
prefs_sync(PREF_DOMAIN_TOUCHSCREEN);
|
||||
break;
|
||||
|
||||
case ICONTEXT_MENU_TOUCHJOY_KPAD:
|
||||
|
@ -269,6 +316,8 @@ static inline int64_t _tap_menu_item(float x, float y) {
|
|||
flags |= TOUCH_FLAGS_INPUT_DEVICE_CHANGE;
|
||||
flags |= TOUCH_FLAGS_JOY_KPAD;
|
||||
_hide_top_left();
|
||||
prefs_setLongValue(PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, TOUCH_DEVICE_JOYSTICK_KEYPAD);
|
||||
prefs_sync(PREF_DOMAIN_TOUCHSCREEN);
|
||||
break;
|
||||
|
||||
case ICONTEXT_UPPERCASE:
|
||||
|
@ -276,6 +325,8 @@ static inline int64_t _tap_menu_item(float x, float y) {
|
|||
flags |= TOUCH_FLAGS_INPUT_DEVICE_CHANGE;
|
||||
flags |= TOUCH_FLAGS_KBD;
|
||||
_hide_top_left();
|
||||
prefs_setLongValue(PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, TOUCH_DEVICE_KEYBOARD);
|
||||
prefs_sync(PREF_DOMAIN_TOUCHSCREEN);
|
||||
break;
|
||||
|
||||
case ICONTEXT_MENU_SPROUT:
|
||||
|
@ -352,7 +403,6 @@ static void gltouchmenu_shutdown(void) {
|
|||
|
||||
menu.topLeftShowing = false;
|
||||
menu.topRightShowing = false;
|
||||
menu.nextGlyphMultiplier = 0;
|
||||
|
||||
mdlDestroyModel(&menu.model);
|
||||
}
|
||||
|
@ -362,6 +412,10 @@ static void gltouchmenu_setup(void) {
|
|||
|
||||
gltouchmenu_shutdown();
|
||||
|
||||
if (menu.prefsChanged) {
|
||||
gltouchmenu_applyPrefs();
|
||||
}
|
||||
|
||||
menu.model = mdlCreateQuad((GLModelParams_s){
|
||||
.skew_x = -1.0,
|
||||
.skew_y = 1.0-touchport.modelHeight,
|
||||
|
@ -399,11 +453,8 @@ static void gltouchmenu_render(void) {
|
|||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (menu.nextGlyphMultiplier) {
|
||||
menu.glyphMultiplier = menu.nextGlyphMultiplier;
|
||||
menu.nextGlyphMultiplier = 0;
|
||||
gltouchmenu_setup();
|
||||
if (UNLIKELY(menu.prefsChanged)) {
|
||||
gltouchmenu_setup(); // fully set up again on prefs change
|
||||
}
|
||||
|
||||
float alpha = glhud_getTimedVisibility(menu.timingBegin, menu.minAlpha, menu.maxAlpha);
|
||||
|
@ -434,6 +485,7 @@ static void gltouchmenu_render(void) {
|
|||
|
||||
static void gltouchmenu_reshape(int w, int h, bool landscape) {
|
||||
LOG("w:%d h:%d landscape:%d", w, h, landscape);
|
||||
assert(video_isRenderThread());
|
||||
|
||||
touchport.topLeftX = 0;
|
||||
touchport.topLeftY = 0;
|
||||
|
@ -467,6 +519,9 @@ static int64_t gltouchmenu_onTouchEvent(interface_touch_event_t action, int poin
|
|||
if (!isEnabled) {
|
||||
return 0x0;
|
||||
}
|
||||
if (UNLIKELY(menu.prefsChanged)) {
|
||||
return 0x0;
|
||||
}
|
||||
|
||||
//LOG("gltouchmenu_onTouchEvent ...");
|
||||
|
||||
|
@ -520,14 +575,6 @@ static int64_t gltouchmenu_onTouchEvent(interface_touch_event_t action, int poin
|
|||
// ----------------------------------------------------------------------------
|
||||
// Animation and settings handling
|
||||
|
||||
static bool gltouchmenu_isTouchMenuAvailable(void) {
|
||||
return isAvailable;
|
||||
}
|
||||
|
||||
static void gltouchmenu_setTouchMenuEnabled(bool enabled) {
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
static void _animation_showTouchMenu(void) {
|
||||
clock_gettime(CLOCK_MONOTONIC, &menu.timingBegin);
|
||||
}
|
||||
|
@ -538,16 +585,36 @@ static void _animation_hideTouchMenu(void) {
|
|||
menu.timingBegin = (struct timespec){ 0 };
|
||||
}
|
||||
|
||||
static void gltouchmenu_setTouchMenuVisibility(float inactiveAlpha, float activeAlpha) {
|
||||
menu.minAlpha = inactiveAlpha;
|
||||
menu.maxAlpha = activeAlpha;
|
||||
static void gltouchmenu_applyPrefs(void) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
menu.prefsChanged = false;
|
||||
|
||||
bool bVal = false;
|
||||
float fVal = 0.f;
|
||||
long lVal = 0;
|
||||
|
||||
isEnabled = prefs_parseBoolValue (PREF_DOMAIN_KEYBOARD, PREF_TOUCHMENU_ENABLED, &bVal) ? bVal : true;
|
||||
|
||||
menu.minAlpha = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_MIN_ALPHA, &fVal) ? fVal : 1/4.f;
|
||||
menu.maxAlpha = prefs_parseFloatValue(PREF_DOMAIN_KEYBOARD, PREF_MAX_ALPHA, &fVal) ? fVal : 1.f;
|
||||
menu.glyphMultiplier = prefs_parseLongValue (PREF_DOMAIN_KEYBOARD, PREF_GLYPH_MULTIPLIER, &lVal, /*base:*/10) ? lVal : 2;
|
||||
if (menu.glyphMultiplier == 0) {
|
||||
menu.glyphMultiplier = 1;
|
||||
}
|
||||
if (menu.glyphMultiplier > 4) {
|
||||
menu.glyphMultiplier = 4;
|
||||
}
|
||||
|
||||
long width = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, &lVal, 10) ? lVal : (long)(SCANWIDTH*1.5);
|
||||
long height = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, &lVal, 10) ? lVal : (long)(SCANHEIGHT*1.5);
|
||||
bool isLandscape = prefs_parseBoolValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, &bVal) ? bVal : true;
|
||||
|
||||
gltouchmenu_reshape(width, height, isLandscape);
|
||||
}
|
||||
|
||||
static void gltouchmenu_setGlyphScale(int glyphScale) {
|
||||
if (glyphScale == 0) {
|
||||
glyphScale = 1;
|
||||
}
|
||||
menu.nextGlyphMultiplier = glyphScale;
|
||||
static void gltouchmenu_prefsChanged(const char *domain) {
|
||||
menu.prefsChanged = true;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -559,24 +626,18 @@ static void _init_gltouchmenu(void) {
|
|||
video_animations->animation_showTouchMenu = &_animation_showTouchMenu;
|
||||
video_animations->animation_hideTouchMenu = &_animation_hideTouchMenu;
|
||||
|
||||
interface_isTouchMenuAvailable = &gltouchmenu_isTouchMenuAvailable;
|
||||
interface_setTouchMenuEnabled = &gltouchmenu_setTouchMenuEnabled;
|
||||
interface_setTouchMenuVisibility = &gltouchmenu_setTouchMenuVisibility;
|
||||
interface_setGlyphScale = &gltouchmenu_setGlyphScale;
|
||||
|
||||
menu.glyphMultiplier = 1;
|
||||
menu.minAlpha = 1/4.f; // Minimum alpha value of components (at zero, will not render)
|
||||
menu.maxAlpha = 1.f;
|
||||
menu.prefsChanged = true;
|
||||
|
||||
glnode_registerNode(RENDER_TOP, (GLNode){
|
||||
.type = TOUCH_DEVICE_TOPMENU,
|
||||
.setup = &gltouchmenu_setup,
|
||||
.shutdown = &gltouchmenu_shutdown,
|
||||
.render = &gltouchmenu_render,
|
||||
.reshape = &gltouchmenu_reshape,
|
||||
.onTouchEvent = &gltouchmenu_onTouchEvent,
|
||||
.setData = NULL,
|
||||
});
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_KEYBOARD, &gltouchmenu_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_INTERFACE, &gltouchmenu_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_gltouchmenu(void) {
|
||||
|
|
|
@ -18,9 +18,6 @@ static int viewportY = 0;
|
|||
static int viewportWidth = SCANWIDTH*1.5;
|
||||
static int viewportHeight = SCANHEIGHT*1.5;
|
||||
|
||||
static int rawWidth = SCANWIDTH*1.5;
|
||||
static int rawHeight = SCANHEIGHT*1.5;
|
||||
static bool isLandscape = true;
|
||||
static float portraitPositionScale = 3/4.f;
|
||||
|
||||
GLint texSamplerLoc = UNINITIALIZED_GL;
|
||||
|
@ -37,6 +34,9 @@ static GLModel *crtModel = NULL;
|
|||
static GLuint vertexShader = UNINITIALIZED_GL;
|
||||
static GLuint fragShader = UNINITIALIZED_GL;
|
||||
|
||||
static bool prefsChanged = true;
|
||||
static void glvideo_applyPrefs(void);
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static void _glvideo_setup_hackarounds(void) {
|
||||
|
@ -131,6 +131,10 @@ static void _glvideo_setup_hackarounds(void) {
|
|||
|
||||
static void glvideo_init(void) {
|
||||
|
||||
if (prefsChanged) {
|
||||
glvideo_applyPrefs();
|
||||
}
|
||||
|
||||
_glvideo_setup_hackarounds();
|
||||
|
||||
#if !PERSPECTIVE
|
||||
|
@ -279,6 +283,10 @@ static void glvideo_render(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
if (UNLIKELY(prefsChanged)) {
|
||||
glvideo_applyPrefs();
|
||||
}
|
||||
|
||||
glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
|
||||
#if PERSPECTIVE
|
||||
|
@ -388,10 +396,7 @@ static void glvideo_render(void) {
|
|||
|
||||
static void glvideo_reshape(int w, int h, bool landscape) {
|
||||
LOG("w:%d h:%d landscape:%d", w, h, landscape);
|
||||
|
||||
rawWidth = w;
|
||||
rawHeight = h;
|
||||
isLandscape = landscape;
|
||||
assert(video_isRenderThread());
|
||||
|
||||
swizzleDimensions(&w, &h, landscape);
|
||||
|
||||
|
@ -410,7 +415,7 @@ static void glvideo_reshape(int w, int h, bool landscape) {
|
|||
viewportY = (h-h2)/2;
|
||||
viewportWidth = w;
|
||||
viewportHeight = h2;
|
||||
if (!isLandscape) {
|
||||
if (!landscape) {
|
||||
viewportY = (h-h2) * portraitPositionScale;
|
||||
}
|
||||
//LOG("OK2 : x:%d,y:%d w:%d,h:%d", viewportX, viewportY, viewportWidth, viewportHeight);
|
||||
|
@ -423,24 +428,29 @@ static void glvideo_reshape(int w, int h, bool landscape) {
|
|||
}
|
||||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
static void glvideo_setData(const char *jsonData) {
|
||||
JSON_ref parsedData = NULL;
|
||||
int tokCount = json_createFromString(jsonData, &parsedData);
|
||||
static void glvideo_applyPrefs(void) {
|
||||
assert(video_isRenderThread());
|
||||
|
||||
do {
|
||||
if (tokCount < 0) {
|
||||
break;
|
||||
}
|
||||
prefsChanged = false;
|
||||
|
||||
json_mapParseFloatValue(parsedData, PREF_PORTRAIT_POSITION_SCALE, &portraitPositionScale);
|
||||
float fVal = 0.f;
|
||||
long lVal = 0;
|
||||
bool bVal = false;
|
||||
|
||||
glvideo_reshape(rawWidth, rawHeight, isLandscape);
|
||||
} while (0);
|
||||
portraitPositionScale = prefs_parseFloatValue(PREF_DOMAIN_VIDEO, PREF_PORTRAIT_POSITION_SCALE, &fVal) ? fVal : 3/4.f;
|
||||
|
||||
json_destroy(&parsedData);
|
||||
long width = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_WIDTH, &lVal, 10) ? lVal : (long)(SCANWIDTH*1.5);
|
||||
long height = prefs_parseLongValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_HEIGHT, &lVal, 10) ? lVal : (long)(SCANHEIGHT*1.5);
|
||||
bool isLandscape = prefs_parseBoolValue (PREF_DOMAIN_INTERFACE, PREF_DEVICE_LANDSCAPE, &bVal) ? bVal : true;
|
||||
|
||||
glvideo_reshape(width, height, isLandscape);
|
||||
}
|
||||
|
||||
static void glvideo_prefsChanged(const char *domain) {
|
||||
prefsChanged = true;
|
||||
}
|
||||
|
||||
#if INTERFACE_TOUCH
|
||||
static int64_t glvideo_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
|
||||
// no-op
|
||||
return 0x0;
|
||||
|
@ -456,13 +466,14 @@ static void _init_glvideo(void) {
|
|||
.setup = &glvideo_init,
|
||||
.shutdown = &glvideo_shutdown,
|
||||
.render = &glvideo_render,
|
||||
.reshape = &glvideo_reshape,
|
||||
#if INTERFACE_TOUCH
|
||||
.type = TOUCH_DEVICE_FRAMEBUFFER,
|
||||
.onTouchEvent = &glvideo_onTouchEvent,
|
||||
.setData = &glvideo_setData,
|
||||
#endif
|
||||
});
|
||||
|
||||
prefs_registerListener(PREF_DOMAIN_VIDEO, &glvideo_prefsChanged);
|
||||
prefs_registerListener(PREF_DOMAIN_INTERFACE, &glvideo_prefsChanged);
|
||||
}
|
||||
|
||||
static __attribute__((constructor)) void __init_glvideo(void) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user