Refactor touch joystick variants

- Use end-of-video-frame callbacks for more conformant delay timing
    - Allow full octant for button side in touch keypad joystick
    - Improved response upon unambiguous touch event (e.g., octant change) to immediately press key
This commit is contained in:
Aaron Culliney 2019-06-02 08:26:27 -07:00
parent a25d68a1d2
commit 788c6cb172
20 changed files with 1796 additions and 1520 deletions

View File

@ -36,8 +36,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
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 final static int TAPDELAY_NUM_CHOICES = (30 + 1); // 0-30 (30Frames == ~0.5sec)
public Apple2JoystickSettingsMenu(Apple2Activity activity) {
@ -227,85 +226,6 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
});
}
},
JOYSTICK_CALIBRATE {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
}
}
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
}
},
JOYSTICK_TAPDELAY {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
}
@Override
public final String getSummary(Apple2Activity activity) {
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) {
final IMenuEnum self = this;
return _sliderView(activity, this, TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
Apple2Preferences.setJSONPref(self, ((float) progress / TAPDELAY_NUM_CHOICES * TAPDELAY_SCALE));
}
@Override
public int intValue() {
return (int) (Apple2Preferences.getFloatJSONPref(self) / TAPDELAY_SCALE * TAPDELAY_NUM_CHOICES);
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
seekBarValue.setText("" + (((float) progress / TAPDELAY_NUM_CHOICES) * TAPDELAY_SCALE));
}
});
}
},
JOYSTICK_ADVANCED {
@Override
public final String getTitle(Apple2Activity activity) {
@ -319,7 +239,7 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity).show();
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK).show();
}
};
@ -363,8 +283,16 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
private final static String TAG = "JoystickAdvanced";
public JoystickAdvanced(Apple2Activity activity) {
private Apple2SettingsMenu.TouchDeviceVariant mVariant;
public JoystickAdvanced(Apple2Activity activity, Apple2SettingsMenu.TouchDeviceVariant variant) {
super(activity);
if (!(variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK || variant == Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD)) {
throw new RuntimeException("You're doing it wrong");
}
mVariant = variant;
}
@Override
@ -387,10 +315,58 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
if (position < 0 || position >= SETTINGS.size) {
throw new ArrayIndexOutOfBoundsException();
}
return position == SETTINGS.JOYSTICK_AXIS_ON_LEFT.ordinal();
return position <= SETTINGS.JOYSTICK_AXIS_ON_LEFT.ordinal();
}
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
JOYSTICK_CALIBRATE {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_calibrate_summary);
}
@Override
public String getPrefKey() {
return null;
}
@Override
public Object getPrefDefault() {
return null;
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
}
}
JoystickAdvanced advancedMenu = (JoystickAdvanced)settingsMenu;
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, advancedMenu.mVariant);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
}
},
JOYSTICK_VISIBILITY {
@Override
public final String getTitle(Apple2Activity activity) {
@ -461,6 +437,52 @@ public class Apple2JoystickSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
JOYSTICK_TAPDELAY {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.joystick_button_tapdelay_summary);
}
@Override
public String getPrefKey() {
return "jsTapDelayFrames";
}
@Override
public Object getPrefDefault() {
return 12; // 12 * 16.688millis == ~0.2secs
}
@Override
public View getView(final Apple2Activity activity, View convertView) {
final IMenuEnum self = this;
return _sliderView(activity, this, TAPDELAY_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
Apple2Preferences.setJSONPref(self, progress);
}
@Override
public int intValue() {
return Apple2Preferences.getIntJSONPref(self);
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
float millis = progress * 16.688f;
String framesStr = activity.getResources().getString(R.string.string_frames);
String millisStr = activity.getResources().getString(R.string.string_millis);
String textSummary = "" + progress + " " + framesStr + " (" + millis + " " + millisStr + ")";
seekBarValue.setText(textSummary);
}
});
}
},
JOYSTICK_AXIS_SENSITIVITY {
@Override
public final String getTitle(Apple2Activity activity) {

View File

@ -46,7 +46,9 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
public final static int ICONTEXT_NONACTION = ICONTEXT_KBD_BEGIN + 0x0C;
public final static int SCANCODE_A = 30;
public final static int SCANCODE_C = 46;
public final static int SCANCODE_D = 32;
public final static int SCANCODE_E = 18;
public final static int SCANCODE_F = 33;
public final static int SCANCODE_H = 35;
public final static int SCANCODE_I = 23;
@ -56,17 +58,23 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
public final static int SCANCODE_M = 50;
public final static int SCANCODE_N = 49;
public final static int SCANCODE_O = 24;
public final static int SCANCODE_P = 25;
public final static int SCANCODE_Q = 16;
public final static int SCANCODE_S = 31;
public final static int SCANCODE_U = 22;
public final static int SCANCODE_W = 17;
public final static int SCANCODE_X = 45;
public final static int SCANCODE_Y = 21;
public final static int SCANCODE_Z = 44;
public final static int SCANCODE_SPACE = 57;
public final static int SCANCODE_SEMICOLON = 39;
public final static int SCANCODE_UP = 103;
public final static int SCANCODE_LEFT = 105;
public final static int SCANCODE_RIGHT = 106;
public final static int SCANCODE_DOWN = 108;
public final static int SCANCODE_COMMA = 51;
public final static int SCANCODE_PERIOD = 52;
public final static int SCANCODE_SLASH = 53;
public Apple2KeyboardSettingsMenu(Apple2Activity activity) {
super(activity);

View File

@ -35,7 +35,7 @@ public class Apple2KeypadChooser implements Apple2MenuView {
private ArrayList<Apple2MenuView> mViewStack = null;
private TextView mCurrentChoicePrompt = null;
private STATE_MACHINE mChooserState = STATE_MACHINE.CHOOSE_NORTHWEST;
private STATE_MACHINE mChooserState = STATE_MACHINE.CHOOSE_AXIS_NORTHWEST;
private boolean mTouchMenuEnabled = false;
private int mSavedTouchDevice = Apple2SettingsMenu.TouchDeviceVariant.NONE.ordinal();
@ -68,24 +68,6 @@ public class Apple2KeypadChooser implements Apple2MenuView {
Apple2Preferences.sync(mActivity, Apple2Preferences.PREF_DOMAIN_TOUCHSCREEN);
mCurrentChoicePrompt.setText(getNextChoiceString() + asciiStr);
switch (mChooserState) {
case CHOOSE_TAP:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{400.f});
break;
case CHOOSE_SWIPEDOWN:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_MOVE, 1, 0, new float[]{400.f}, new float[]{600.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{600.f});
break;
case CHOOSE_SWIPEUP:
Apple2View.nativeOnTouch(MotionEvent.ACTION_DOWN, 1, 0, new float[]{400.f}, new float[]{400.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_MOVE, 1, 0, new float[]{400.f}, new float[]{200.f});
Apple2View.nativeOnTouch(MotionEvent.ACTION_UP, 1, 0, new float[]{400.f}, new float[]{200.f});
break;
default:
break;
}
calibrationContinue();
}
@ -220,98 +202,134 @@ public class Apple2KeypadChooser implements Apple2MenuView {
}
private enum STATE_MACHINE {
CHOOSE_NORTHWEST {
CHOOSE_AXIS_NORTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_ul);
}
},
CHOOSE_NORTH {
CHOOSE_AXIS_NORTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_up);
}
},
CHOOSE_NORTHEAST {
CHOOSE_AXIS_NORTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_ur);
}
},
CHOOSE_WEST {
CHOOSE_AXIS_WEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_l);
}
},
CHOOSE_CENTER {
CHOOSE_AXIS_CENTER {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_c);
}
},
CHOOSE_EAST {
CHOOSE_AXIS_EAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_r);
}
},
CHOOSE_SOUTHWEST {
CHOOSE_AXIS_SOUTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dl);
}
},
CHOOSE_SOUTH {
CHOOSE_AXIS_SOUTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dn);
}
},
CHOOSE_SOUTHEAST {
CHOOSE_AXIS_SOUTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dr);
}
},
CHOOSE_TAP {
CHOOSE_BUTT_NORTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_tap);
return activity.getResources().getString(R.string.keypad_key_axis_ul);
}
},
CHOOSE_SWIPEUP {
CHOOSE_BUTT_NORTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_swipeup);
return activity.getResources().getString(R.string.keypad_key_axis_up);
}
},
CHOOSE_SWIPEDOWN {
CHOOSE_BUTT_NORTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_button_swipedown);
return activity.getResources().getString(R.string.keypad_key_axis_ur);
}
},
CHOOSE_BUTT_WEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_l);
}
},
CHOOSE_BUTT_CENTER {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_c);
}
},
CHOOSE_BUTT_EAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_r);
}
},
CHOOSE_BUTT_SOUTHWEST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dl);
}
},
CHOOSE_BUTT_SOUTH {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dn);
}
},
CHOOSE_BUTT_SOUTHEAST {
@Override
public String getKeyName(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_key_axis_dr);
}
};
public static final int size = STATE_MACHINE.values().length;
private static ArrayList<String> chars = null;
private static ArrayList<String> scans = null;
private static ArrayList<String> axisChars = new ArrayList<String>();
private static ArrayList<String> axisScans = new ArrayList<String>();
private static ArrayList<String> buttChars = new ArrayList<String>();
private static ArrayList<String> buttScans = 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);
int buttbegin = CHOOSE_BUTT_NORTHWEST.ordinal();
if (ord < buttbegin) {
axisChars.set(ord, "" + ascii);
axisScans.set(ord, "" + scancode);
Apple2KeypadSettingsMenu.KeypadPreset.saveAxisRosettes(axisChars, axisScans);
} else {
throw new RuntimeException();
ord -= buttbegin;
buttChars.set(ord, "" + ascii);
buttScans.set(ord, "" + scancode);
Apple2KeypadSettingsMenu.KeypadPreset.saveButtRosettes(buttChars, buttScans);
}
Apple2Preferences.sync(activity, Apple2Preferences.PREF_DOMAIN_JOYSTICK);
}
@ -319,12 +337,19 @@ public class Apple2KeypadChooser implements Apple2MenuView {
public abstract String getKeyName(Apple2Activity activity);
public void start() {
setupCharsAndScans(axisChars, axisScans, Apple2KeypadSettingsMenu.PREF_KPAD_AXIS_ROSETTE_CHAR_ARRAY, Apple2KeypadSettingsMenu.PREF_KPAD_AXIS_ROSETTE_SCAN_ARRAY);
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);
setupCharsAndScans(buttChars, buttScans, Apple2KeypadSettingsMenu.PREF_KPAD_BUTT_ROSETTE_CHAR_ARRAY, Apple2KeypadSettingsMenu.PREF_KPAD_BUTT_ROSETTE_SCAN_ARRAY);
}
private void setupCharsAndScans(final ArrayList<String> chars, final ArrayList<String> scans, final String charArrayPref, final String scanArrayPref) {
chars.clear();
scans.clear();
JSONArray jsonChars = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, charArrayPref, null);
JSONArray jsonScans = (JSONArray) Apple2Preferences.getJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, scanArrayPref, null);
if (jsonChars == null || jsonScans == null) {
Log.v(TAG, "Creating new keypad joystick JSON...");
jsonChars = new JSONArray();
jsonScans = new JSONArray();
for (int i = 0; i < Apple2KeypadSettingsMenu.ROSETTE_SIZE; i++) {
@ -341,8 +366,6 @@ public class Apple2KeypadChooser implements Apple2MenuView {
throw new RuntimeException("jsonScans not expected length");
}
chars = new ArrayList<String>();
scans = new ArrayList<String>();
try {
for (int i = 0; i < len; i++) {
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, jsonChars.getInt(i), jsonScans.getInt(i));
@ -350,6 +373,13 @@ public class Apple2KeypadChooser implements Apple2MenuView {
} catch (JSONException e) {
e.printStackTrace();
}
if (chars.size() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
throw new RuntimeException("rosette chars is not correct size");
}
if (scans.size() != Apple2KeypadSettingsMenu.ROSETTE_SIZE) {
throw new RuntimeException("rosette chars is not correct size");
}
}
public STATE_MACHINE next() {

View File

@ -12,7 +12,9 @@
package org.deadc0de.apple2ix;
import android.view.View;
import android.widget.TextView;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.Toast;
import java.util.ArrayList;
@ -23,16 +25,10 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
private final static String TAG = "Apple2KeypadSettingsMenu";
public 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 final static String PREF_KPAD_AXIS_ROSETTE_CHAR_ARRAY = "kpAxisRosetteChars";
public final static String PREF_KPAD_AXIS_ROSETTE_SCAN_ARRAY = "kpAxisRosetteScancodes";
public final static String PREF_KPAD_BUTT_ROSETTE_CHAR_ARRAY = "kpButtRosetteChars";
public final static String PREF_KPAD_BUTT_ROSETTE_SCAN_ARRAY = "kpButtRosetteScancodes";
public final static int ROSETTE_SIZE = 9;
@ -72,22 +68,39 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@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);
{
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);
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
AZ_LEFT_RIGHT_SPACE {
@ -98,22 +111,39 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@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);
{
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);
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
LEFT_RIGHT_SPACE {
@ -124,22 +154,81 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@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);
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(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, Apple2KeyboardSettingsMenu.SCANCODE_LEFT);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_RIGHT);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
QAZ_LEFT_RIGHT_SPACE {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_qaz_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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_Q);
addRosetteKey(chars, scans, 'Q', Apple2KeyboardSettingsMenu.SCANCODE_Q);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_Q);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
IJKM_SPACE {
@ -150,22 +239,39 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@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);
{
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);
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
WADX_SPACE {
@ -176,49 +282,173 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@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);
{
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);
saveTouchDownKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveSwipeSouthKey(Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveSwipeNorthKey(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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
saveButtRosettes(chars, scans);
}
}
},
CRAZY_SEAFOX_KEYS {
LODERUNNER_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_crazy_seafox);
return activity.getResources().getString(R.string.keypad_preset_loderunner);
}
@Override
public String getToast(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_loderunner_toast);
}
@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);
{
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);
saveTouchDownKey('D', Apple2KeyboardSettingsMenu.SCANCODE_D);
saveSwipeSouthKey('F', Apple2KeyboardSettingsMenu.SCANCODE_F);
saveSwipeNorthKey(Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, 'J', Apple2KeyboardSettingsMenu.SCANCODE_J);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, 'L', Apple2KeyboardSettingsMenu.SCANCODE_L);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_U);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_O);
addRosetteKey(chars, scans, 'U', Apple2KeyboardSettingsMenu.SCANCODE_U);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, 'O', Apple2KeyboardSettingsMenu.SCANCODE_O);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_U);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_O);
saveButtRosettes(chars, scans);
}
}
},
ROBOTRON_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_robotron);
}
@Override
public void apply(Apple2Activity activity) {
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, 'Q', Apple2KeyboardSettingsMenu.SCANCODE_Q);
addRosetteKey(chars, scans, 'W', Apple2KeyboardSettingsMenu.SCANCODE_W);
addRosetteKey(chars, scans, 'E', Apple2KeyboardSettingsMenu.SCANCODE_E);
addRosetteKey(chars, scans, 'A', Apple2KeyboardSettingsMenu.SCANCODE_A);
addRosetteKey(chars, scans, 'S', Apple2KeyboardSettingsMenu.SCANCODE_S);
addRosetteKey(chars, scans, 'D', Apple2KeyboardSettingsMenu.SCANCODE_D);
addRosetteKey(chars, scans, 'Z', Apple2KeyboardSettingsMenu.SCANCODE_Z);
addRosetteKey(chars, scans, 'X', Apple2KeyboardSettingsMenu.SCANCODE_X);
addRosetteKey(chars, scans, 'C', Apple2KeyboardSettingsMenu.SCANCODE_C);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, 'I', Apple2KeyboardSettingsMenu.SCANCODE_I);
addRosetteKey(chars, scans, 'O', Apple2KeyboardSettingsMenu.SCANCODE_O);
addRosetteKey(chars, scans, 'P', Apple2KeyboardSettingsMenu.SCANCODE_P);
addRosetteKey(chars, scans, 'K', Apple2KeyboardSettingsMenu.SCANCODE_K);
addRosetteKey(chars, scans, 'L', Apple2KeyboardSettingsMenu.SCANCODE_L);
addRosetteKey(chars, scans, ';', Apple2KeyboardSettingsMenu.SCANCODE_SEMICOLON);
addRosetteKey(chars, scans, ',', Apple2KeyboardSettingsMenu.SCANCODE_COMMA);
addRosetteKey(chars, scans, '.', Apple2KeyboardSettingsMenu.SCANCODE_PERIOD);
addRosetteKey(chars, scans, '/', Apple2KeyboardSettingsMenu.SCANCODE_SLASH);
saveButtRosettes(chars, scans);
}
}
},
SEAFOX_KEYS {
@Override
public String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_preset_seafox);
}
@Override
public void apply(Apple2Activity activity) {
{
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);
saveAxisRosettes(chars, scans);
}
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_D);
addRosetteKey(chars, scans, 'D', Apple2KeyboardSettingsMenu.SCANCODE_D);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, Apple2KeyboardSettingsMenu.SCANCODE_F);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_VISUAL_SPACE, Apple2KeyboardSettingsMenu.SCANCODE_SPACE);
addRosetteKey(chars, scans, 'F', Apple2KeyboardSettingsMenu.SCANCODE_F);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
saveButtRosettes(chars, scans);
}
}
};
@ -227,36 +457,36 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
scans.add("" + aScan);
}
public static void saveRosettes(ArrayList<String> chars, ArrayList<String> scans) {
public static void saveAxisRosettes(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));
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_AXIS_ROSETTE_CHAR_ARRAY, new JSONArray(chars));
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_AXIS_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 static void saveButtRosettes(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_BUTT_ROSETTE_CHAR_ARRAY, new JSONArray(chars));
Apple2Preferences.setJSONPref(Apple2Preferences.PREF_DOMAIN_JOYSTICK, PREF_KPAD_BUTT_ROSETTE_SCAN_ARRAY, new JSONArray(scans));
}
public abstract String getTitle(Apple2Activity activity);
public abstract void apply(Apple2Activity activity);
public String getToast(Apple2Activity activity) {
return null;
}
public static final int size = KeypadPreset.values().length;
public static String[] titles(Apple2Activity activity) {
@ -319,52 +549,55 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
keypadSettingsMenu.chooseKeys(activity);
} else {
KeypadPreset.values()[value - 1].apply(activity);
String toast = KeypadPreset.values()[value - 1].getToast(activity);
if (toast != null) {
Toast.makeText(activity, toast, Toast.LENGTH_SHORT).show();
}
}
}
});
}
},
KEYPAD_CALIBRATE {
FAST_AUTOREPEAT {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_calibrate);
return activity.getResources().getString(R.string.keypad_autorepeat_fast);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.keypad_calibrate_summary);
return activity.getResources().getString(R.string.keypad_autorepeat_fast_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
ArrayList<Apple2MenuView> viewStack = new ArrayList<Apple2MenuView>();
{
int idx = 0;
while (true) {
Apple2MenuView apple2MenuView = activity.peekApple2View(idx);
if (apple2MenuView == null) {
break;
}
viewStack.add(apple2MenuView);
++idx;
public String getPrefKey() {
return "kpFastAutoRepeat";
}
@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, (boolean) Apple2Preferences.getJSONPref(this));
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Apple2Preferences.setJSONPref(self, isChecked);
}
}
Apple2JoystickCalibration calibration = new Apple2JoystickCalibration(activity, viewStack, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD);
// show this new view...
calibration.show();
// ...with nothing else underneath 'cept the emulator OpenGL layer
for (Apple2MenuView apple2MenuView : viewStack) {
activity.popApple2View(apple2MenuView);
}
});
return convertView;
}
},
KEYPAD_ADVANCED {
JOYSTICK_ADVANCED {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced);
return activity.getResources().getString(R.string.settings_advanced_joystick);
}
@Override
@ -374,7 +607,7 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2KeypadSettingsMenu.KeypadAdvanced(activity).show();
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity, Apple2SettingsMenu.TouchDeviceVariant.JOYSTICK_KEYPAD).show();
}
};
@ -441,133 +674,4 @@ public class Apple2KeypadSettingsMenu extends Apple2AbstractMenu {
activity.popApple2View(apple2MenuView);
}
}
public static class KeypadAdvanced extends Apple2AbstractMenu {
private final static String TAG = "KeypadAdvanced";
public KeypadAdvanced(Apple2Activity activity) {
super(activity);
}
@Override
public final String[] allTitles() {
return SETTINGS.titles(mActivity);
}
@Override
public final IMenuEnum[] allValues() {
return SETTINGS.values();
}
@Override
public final boolean areAllItemsEnabled() {
return false;
}
@Override
public final boolean isEnabled(int position) {
if (position < 0 || position >= SETTINGS.size) {
throw new ArrayIndexOutOfBoundsException();
}
return position == SETTINGS.JOYSTICK_ADVANCED.ordinal();
}
protected enum SETTINGS implements Apple2AbstractMenu.IMenuEnum {
KEYREPEAT_THRESHOLD {
@Override
public final String getTitle(Apple2Activity activity) {
return "";
}
@Override
public final String getSummary(Apple2Activity activity) {
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) {
final IMenuEnum self = this;
return _sliderView(activity, this, KEYREPEAT_NUM_CHOICES, new IPreferenceSlider() {
@Override
public void saveInt(int progress) {
Apple2Preferences.setJSONPref(self, (float) progress / KEYREPEAT_NUM_CHOICES);
}
@Override
public int intValue() {
return (int) (Apple2Preferences.getFloatJSONPref(self) * KEYREPEAT_NUM_CHOICES);
}
@Override
public void showValue(int progress, final TextView seekBarValue) {
seekBarValue.setText("" + ((float) progress / KEYREPEAT_NUM_CHOICES));
}
});
}
},
JOYSTICK_ADVANCED {
@Override
public final String getTitle(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced_joystick);
}
@Override
public final String getSummary(Apple2Activity activity) {
return activity.getResources().getString(R.string.settings_advanced_joystick_summary);
}
@Override
public void handleSelection(Apple2Activity activity, Apple2AbstractMenu settingsMenu, boolean isChecked) {
new Apple2JoystickSettingsMenu.JoystickAdvanced(activity).show();
}
};
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
public View getView(Apple2Activity activity, View convertView) {
return _basicView(activity, this, convertView);
}
public static String[] titles(Apple2Activity activity) {
String[] titles = new String[size];
int i = 0;
for (SETTINGS setting : values()) {
titles[i++] = setting.getTitle(activity);
}
return titles;
}
}
}
}

View File

@ -158,6 +158,28 @@ public class Apple2Preferences {
return (float) ((long) obj);
}
private static int _convertToInt(Object obj) {
if (obj == null) {
return 0;
}
try {
return (int) obj;
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as int");
}
try {
return (int) ((long) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as long");
}
try {
return (int) ((float) obj);
} catch (ClassCastException e) {
Log.d(TAG, "could not cast object as float");
}
return (int) ((double) obj);
}
public static float getFloatJSONPref(Apple2AbstractMenu.IMenuEnum menu) {
return _convertToFloat(getJSONPref(menu));
}
@ -166,6 +188,14 @@ public class Apple2Preferences {
return _convertToFloat(getJSONPref(domain, key, defaultVal));
}
public static int getIntJSONPref(Apple2AbstractMenu.IMenuEnum menu) {
return _convertToInt(getJSONPref(menu));
}
public static int getIntJSONPref(String domain, String key, Object defaultVal) {
return _convertToInt(getJSONPref(domain, key, defaultVal));
}
public static boolean migrate(Apple2Activity activity) {
int versionCode = (int) getJSONPref(PREF_DOMAIN_INTERFACE, PREF_EMULATOR_VERSION, 0);
final boolean firstTime = (versionCode != BuildConfig.VERSION_CODE);
@ -185,6 +215,57 @@ public class Apple2Preferences {
Apple2Utils.migrateToExternalStorage(activity);
if (versionCode < 24) {
// migrate tap delay from seconds to frames ...
float secs = getFloatJSONPref(PREF_DOMAIN_JOYSTICK, "jsTapDelaySecs", 9999f);
if (secs != 9999f) {
// UtAIIe 3-13 : "The duration of the television scan is 262 horizontal scans. This is [16.688 milliseconds]"
// recalculate this to a frames value between 0-30 inclusive ...
int framesDelay = Math.round(secs / 0.016688f);
if (framesDelay < 0) {
framesDelay = 0;
} else if (framesDelay > 30) {
framesDelay = 30;
}
setJSONPref(Apple2JoystickSettingsMenu.JoystickAdvanced.SETTINGS.JOYSTICK_TAPDELAY, framesDelay);
}
// migrate individual keypad button actions to new button rosette actions ...
{
ArrayList<String> chars = new ArrayList<String>();
ArrayList<String> scans = new ArrayList<String>();
int northChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeNorthChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int northScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeNorthScancode",-1 );
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, northChar, northScan);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
int downChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpTouchDownChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int downScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpTouchDownScancode", -1);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, downChar, downScan);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
int southChar = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeSouthChar", Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION);
int southScan = getIntJSONPref(PREF_DOMAIN_JOYSTICK, "kpSwipeSouthScancode", -1);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, southChar, southScan);
Apple2KeypadSettingsMenu.KeypadPreset.addRosetteKey(chars, scans, Apple2KeyboardSettingsMenu.ICONTEXT_NONACTION, -1);
Apple2KeypadSettingsMenu.KeypadPreset.saveButtRosettes(chars, scans);
}
JSONObject map = _prefDomain(PREF_DOMAIN_JOYSTICK);
map.remove("jsTapDelaySecs");
map.remove("kpSwipeNorthChar");
map.remove("kpSwipeNorthScancode");
map.remove("kpSwipeSouthChar");
map.remove("kpSwipeSouthScancode");
map.remove("kpTouchDownChar");
map.remove("kpTouchDownScancode");
}
save(activity);
return firstTime;
}

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Keine</string>
<string name="joystick_button_tap_button">Drücke den Feuerknopf</string>
<string name="joystick_button_tap_button_summary">Ausgewählter Feuerknopf</string>
<string name="joystick_button_tapdelay_summary">Joystickknopf-Zeitverzögerung in Sek.</string>
<string name="joystick_button_swipe_up_button">Aufwärts wischen zum feuern</string>
<string name="joystick_button_swipe_up_button_summary">Feuerknopf zum feuern beim aufwärts wischen</string>
<string name="joystick_button_swipe_down_button">Abwärts wischen zum feuern</string>
@ -97,8 +96,6 @@
<string name="keyboard_visibility_inactive">Sichtbarkeit wenn deaktiviert</string>
<string name="keyboard_visibility_inactive_summary">Sichtbarkeit des Keyboard und Touch Menüs wenn deaktiviert</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Auswahl der Keypad Tasten…</string>
<string name="keypad_choose_summary">Auswahl der Achsen und Knopf Tasten</string>
<string name="keypad_choose_title">Achse &amp; Knöpfe</string>
@ -117,14 +114,13 @@
<string name="keypad_key_button_tap">antippen</string>
<string name="keypad_key_button_swipeup">Nach oben wischen</string>
<string name="keypad_key_button_swipedown">Nach unten wischen</string>
<string name="keypad_preset_crazy_seafox">Seafox Tasten ;-)</string>
<string name="keypad_preset_seafox">Seafox Tasten…</string>
<string name="keypad_preset_custom">Auswahl einer Anpassung…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, tippe auf die Leerstaste</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, tippe auf die Leertaste</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, tippe auf die Leertaste</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, tippe auf die Leertaste</string>
<string name="keypad_preset_wadx_space">W,A,D,X, tippe auf die Leertaste</string>
<string name="keypad_repeat_summary">Tastenwiederholungsschwellwert in Sek.</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), tippe auf die Leerstaste</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), tippe auf die Leerstaste</string>
<string name="keypad_preset_ijkm_space">(I J K M), tippe auf die Leertaste</string>
<string name="keypad_preset_left_right_space">(← →), tippe auf die Leertaste</string>
<string name="keypad_preset_wadx_space">(W A D X), tippe auf die Leertaste</string>
<string name="menu_disks">Lade Disk-Image…</string>
<string name="menu_disks_summary">Einlegen eines Disk ][ Image</string>
<string name="menu_settings">Emulator Einstellungen…</string>
@ -203,5 +199,15 @@
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), Leertaste, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Robotron Tasten…</string>
<string name="keypad_preset_loderunner">Lode Runner Tasten…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
</resources>

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Ninguno</string>
<string name="joystick_button_tap_button">Toque para disparar</string>
<string name="joystick_button_tap_button_summary">Botón para disparar sobre toque abajo</string>
<string name="joystick_button_tapdelay_summary">Toque retardo del joystick en segundos</string>
<string name="joystick_button_swipe_up_button">Pase hacia arriba</string>
<string name="joystick_button_swipe_up_button_summary">Botón para disparar sobre pase el dedo hacia arriba</string>
<string name="joystick_button_swipe_down_button">Pase hacia abajo</string>
@ -95,8 +94,6 @@
<string name="keyboard_visibility_inactive">Visibilidad cuando está inactivo</string>
<string name="keyboard_visibility_inactive_summary">Visibilidad del teclado y menú cuando está inactivo</string>
<string name="keypad">Joystick como teclado numérico</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Teclas del teclado numérico…</string>
<string name="keypad_choose_summary">Elegir las teclas del joystick del teclado numérico</string>
<string name="keypad_choose_title">Ejes y botones</string>
@ -115,14 +112,13 @@
<string name="keypad_key_button_tap">Toque</string>
<string name="keypad_key_button_swipeup">Desliza el dedo hacia arriba</string>
<string name="keypad_key_button_swipedown"> Desliza el dedo hacia abajo</string>
<string name="keypad_preset_crazy_seafox">Seafox keys ;-)</string>
<string name="keypad_preset_seafox">Tecla para Seafox…</string>
<string name="keypad_preset_custom">Elija personalizado…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, pulse espaciadora</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, pulse espaciadora</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, pulse espaciadora</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, pulse espaciadora</string>
<string name="keypad_preset_wadx_space">W,A,D,X, pulse espaciadora</string>
<string name="keypad_repeat_summary">Umbral de repetición de teclas en segundos</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), pulse barra espaciadora</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), pulse barra espaciadora</string>
<string name="keypad_preset_ijkm_space">(I J K M), pulse barra espaciadora</string>
<string name="keypad_preset_left_right_space">(← →), pulse barra espaciadora</string>
<string name="keypad_preset_wadx_space">(W A D X), pulse barra espaciadora</string>
<string name="menu_disks">Insertar imagen de disco…</string>
<string name="menu_disks_summary">Insertar imagen de "Disk ]["</string>
<string name="menu_settings">Configuración del emulador…</string>
@ -203,5 +199,15 @@
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), barra espaciadora, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Tecla para Robotron…</string>
<string name="keypad_preset_loderunner">Lode Runner keys…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
</resources>

View File

@ -56,7 +56,6 @@
<string name="joystick_button_button_none">Rien</string>
<string name="joystick_button_tap_button">Sélectionner l\'action</string>
<string name="joystick_button_tap_button_summary">Bouton à activer lors d\'une pression vers le bas</string>
<string name="joystick_button_tapdelay_summary">Délai de pression du bouton Joystick en secondes</string>
<string name="joystick_button_swipe_up_button">Lancement du swipe up</string>
<string name="joystick_button_swipe_up_button_summary">Bouton à lancer sur swipe up</string>
<string name="joystick_button_swipe_down_button">Lancement du swipe down</string>
@ -95,8 +94,6 @@
<string name="keyboard_visibility_inactive">Visibilité quand inactif</string>
<string name="keyboard_visibility_inactive_summary">Clavier et menu tactile visible quand inactif</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Sélection des touches du keypad…</string>
<string name="keypad_choose_summary">Sélection des touches pour les axes et boutons</string>
<string name="keypad_choose_title">Axis &amp; boutons</string>
@ -115,14 +112,13 @@
<string name="keypad_key_button_tap">Presser</string>
<string name="keypad_key_button_swipeup">Slider vers le haut</string>
<string name="keypad_key_button_swipedown">Slider vers le bas</string>
<string name="keypad_preset_crazy_seafox">Touches Seafox ;-)</string>
<string name="keypad_preset_seafox">Touches Seafox…</string>
<string name="keypad_preset_custom">Choisir customisation…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, pressez barre d\'espace</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, pressez barre d\'espace</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, pressez barre d\'espace</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, pressez barre d\'espace</string>
<string name="keypad_preset_wadx_space">W,A,D,X, pressez barre d\'espace</string>
<string name="keypad_repeat_summary">Répétition des touches en secs</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), pressez barre d\'espace</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), pressez barre d\'espace</string>
<string name="keypad_preset_ijkm_space">(I J K M), pressez barre d\'espace</string>
<string name="keypad_preset_left_right_space">(← →), pressez barre d\'espace</string>
<string name="keypad_preset_wadx_space">(W A D X), pressez barre d\'espace</string>
<string name="menu_disks">Chargement de l\'image disque…</string>
<string name="menu_disks_summary">Insérer un fichier image (disque) ][</string>
<string name="menu_settings">Paramètres de l\'émulateur…</string>
@ -203,5 +199,15 @@
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →) barre d\'espace, Q</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="keypad_preset_robotron">Touches Robotron…</string>
<string name="keypad_preset_loderunner">Touches Lode Runner…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
</resources>

View File

@ -60,7 +60,7 @@
<string name="joystick_button_button_none">None</string>
<string name="joystick_button_tap_button">Tap fire</string>
<string name="joystick_button_tap_button_summary">Button to fire on tap down</string>
<string name="joystick_button_tapdelay_summary">Joystick button tap delay in secs</string>
<string name="joystick_button_tapdelay_summary">Joystick touch down delay in video frames</string>
<string name="joystick_button_swipe_up_button">Swipe up fire</string>
<string name="joystick_button_swipe_up_button_summary">Button to fire on swipe up</string>
<string name="joystick_button_swipe_down_button">Swipe down fire</string>
@ -68,7 +68,7 @@
<string name="joystick_button_threshold_summary">Joystick/keypad button switch threshold in pts (max: &#8531; screen height)</string>
<string name="joystick_calibrate">Calibrate…</string>
<string name="joystick_calibrate_summary">Configure and test current settings</string>
<string name="joystick_configure">Configure joystick…</string>
<string name="joystick_configure">Configure touch joystick…</string>
<string name="joystick_configure_summary">Axis touch, buttons, etc</string>
<string name="joystick_axisleft">Joystick/keypad axis on left</string>
<string name="joystick_axisleft_summary">Joystick/keypad axis on left (buttons on right)</string>
@ -92,7 +92,7 @@
<string name="keyboard_choose_alt_summary">Choose alternative customized layout</string>
<string name="keyboard_click_enabled">Enable key click</string>
<string name="keyboard_click_enabled_summary">Enables key click sound if available</string>
<string name="keyboard_configure">Configure keyboard…</string>
<string name="keyboard_configure">Configure touch keyboard…</string>
<string name="keyboard_configure_summary">Transparency, lowercase, custom keys</string>
<string name="keyboard_glyph_scale">Keyboard glyphs scaled 2x</string>
<string name="keyboard_glyph_scale_summary">(Makes keyboard appear less pixelated on large screens)</string>
@ -103,13 +103,11 @@
<string name="keyboard_visibility_inactive">Visibility when inactive</string>
<string name="keyboard_visibility_inactive_summary">Keyboard and touch menu visibility when inactive</string>
<string name="keypad">Keypad Joystick</string>
<string name="keypad_calibrate">@string/joystick_calibrate</string>
<string name="keypad_calibrate_summary">@string/joystick_calibrate</string>
<string name="keypad_choose">Choose keypad keys…</string>
<string name="keypad_choose_summary">Choose axis and button keys</string>
<string name="keypad_choose_title">Axis &amp; buttons</string>
<string name="keypad_choose_current">Choose XXX Key: </string>
<string name="keypad_configure">Configure keypad joystick…</string>
<string name="keypad_configure">Configure touch keypad joystick…</string>
<string name="keypad_configure_summary">@string/joystick_configure_summary</string>
<string name="keypad_key_axis_c">Center</string>
<string name="keypad_key_axis_dn">Down</string>
@ -123,14 +121,14 @@
<string name="keypad_key_button_tap">Tap</string>
<string name="keypad_key_button_swipeup">Swipe Up</string>
<string name="keypad_key_button_swipedown">Swipe Down</string>
<string name="keypad_preset_crazy_seafox">Seafox keys ;-)</string>
<string name="keypad_preset_seafox">Seafox keys…</string>
<string name="keypad_preset_custom">Choose custom…</string>
<string name="keypad_preset_arrows_space">&#8593;,&#8592;,&#8594;,&#8595;, tap spacebar</string>
<string name="keypad_preset_az_left_right_space">A,Z,&#8592;,&#8594;, tap spacebar</string>
<string name="keypad_preset_ijkm_space">I,J,K,M, tap spacebar</string>
<string name="keypad_preset_left_right_space">&#8592;,&#8594;, tap spacebar</string>
<string name="keypad_preset_wadx_space">W,A,D,X, tap spacebar</string>
<string name="keypad_repeat_summary">Key repeat threshold in secs</string>
<string name="keypad_preset_arrows_space">(↑ ← → ↓), tap spacebar</string>
<string name="keypad_preset_az_left_right_space">(A Z ← →), tap spacebar</string>
<string name="keypad_preset_ijkm_space">(I J K M), tap spacebar</string>
<string name="keypad_preset_left_right_space">(← →), tap spacebar</string>
<string name="keypad_preset_wadx_space">(W A D X), tap spacebar</string>
<string name="keypad_tapdelay_summary">Keypad touch down delay in secs</string>
<string name="menu_disks">Load image or state file…</string>
<string name="menu_disks_summary">Insert Disk ][ image or state file</string>
<string name="menu_settings">Emulator settings…</string>
@ -203,5 +201,13 @@
<string name="release_notes_summary">View notes for this release</string>
<string name="keyboard_duotouch_enabled">Enable dual-touch</string>
<string name="keyboard_duotouch_enabled_summary">Support two-thumb input</string>
<string name="keypad_preset_qaz_left_right_space">(A Z ← →), tap spacebar, Q up</string>
<string name="keypad_autorepeat_fast">Keypad Immediate Autorepeat</string>
<string name="keypad_autorepeat_fast_summary">Allows immediate auto-repeat (avoids original 534-801 millis delay)</string>
<string name="string_frames">Frames</string>
<string name="string_millis">Millis</string>
<string name="keypad_preset_robotron">Robotron keys…</string>
<string name="keypad_preset_loderunner">Lode Runner keys…</string>
<string name="keypad_preset_loderunner_toast">Start game with Ctrl-K to activate keyboard</string>
</resources>

View File

@ -1,16 +1,9 @@
Apple2ix (a2ix) 2.0.0-Android Release Notes
TL;DR : new video mode settings!
Apple2ix (a2ix) 2.0.1-Beta
CHANGES:
- Implemented a more conformant video scanner. This improves emulation fidelity for programs that implement custom video modes (e.g., custom split screen between text & graphics).
- New NTSC video display modes including "Color monitor", "Monochrome TV", and "Color TV" modes. Thanks to the AppleWin project and Bill Simms for these modes. Also support "Green screen" monochrome video.
- New preference to enable/disable half-scanline video effects.
- EXPERIMENTAL: New preference to enable/disable fast disk image loading. This may cause audio glitches or other instability. Use at your own risk!
- Improved two-thumb touch keyboard support.
- Improved response of touch keypad joystick.
GENERAL INFO:

View File

@ -70,16 +70,4 @@ void joydriver_setButton1Pressed(bool pressed);
// backend joystick driver reset procedure
extern void (*joydriver_resetJoystick)(void);
#if INTERFACE_TOUCH
typedef enum touchjoy_button_type_t {
TOUCH_NONE = 0,
TOUCH_BUTTON1 = 1,
TOUCH_BUTTON2,
TOUCH_BOTH,
// --or-- an ASCII/fonttext value ...
} touchjoy_button_type_t;
#endif // INTERFACE_TOUCH
#endif // whole file

View File

@ -515,7 +515,3 @@ static __attribute__((constructor)) void __init_keys(void) {
prefs_registerListener(PREF_DOMAIN_KEYBOARD, &keys_prefsChanged);
}
#if INTERFACE_TOUCH
void (*keydriver_keyboardReadCallback)(void) = NULL;
#endif

View File

@ -152,10 +152,5 @@ void keys_handleInput(int scan_or_key, bool is_pressed, bool is_ascii);
int keys_scancode2ASCII(int scancode, bool is_shifted, bool is_ctrl);
uint8_t keys_apple2ASCII(uint8_t c, OUTPARM font_mode_t *mode);
#if INTERFACE_TOUCH
// keyboard read callback
extern void (*keydriver_keyboardReadCallback)(void);
#endif
#endif

View File

@ -37,6 +37,17 @@ void log_outputString(const char * const str);
#define _MYFILE_ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
#define _SIMPLE_LOG(...) \
do { \
char *buf = NULL; \
int ignored = asprintf(&buf, __VA_ARGS__); \
(void)ignored; \
\
log_outputString(buf); \
\
free(buf); \
} while (0)
#define _LOG(...) \
do { \
int _err = errno; \

View File

@ -52,11 +52,13 @@
#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_TAP_DELAY "jsTapDelayFrames"
#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_FAST_AUTOREPEAT "kpFastAutoRepeat"
#define PREF_KPAD_AXIS_ROSETTE_CHAR_ARRAY "kpAxisRosetteChars"
#define PREF_KPAD_AXIS_ROSETTE_SCAN_ARRAY "kpAxisRosetteScancodes"
#define PREF_KPAD_BUTT_ROSETTE_CHAR_ARRAY "kpButtRosetteChars"
#define PREF_KPAD_BUTT_ROSETTE_SCAN_ARRAY "kpButtRosetteScancodes"
#define PREF_KPAD_SWIPE_NORTH_CHAR "kpSwipeNorthChar"
#define PREF_KPAD_SWIPE_NORTH_SCAN "kpSwipeNorthScancode"
#define PREF_KPAD_SWIPE_SOUTH_CHAR "kpSwipeSouthChar"

View File

@ -31,19 +31,32 @@
#define AXIS_OBJ_W 0.15
#define AXIS_OBJ_H 0.2
#define AXIS_OBJ_HALF_W (AXIS_OBJ_W/2.f)
#define AXIS_OBJ_HALF_H (AXIS_OBJ_H/2.f)
#define BUTTON_OBJ_W 0.075
#define BUTTON_OBJ_H 0.1
#define BUTTON_OBJ_HALF_W (BUTTON_OBJ_W/2.f)
#define BUTTON_OBJ_HALF_H (BUTTON_OBJ_H/2.f)
#define BUTTON_SWITCH_THRESHOLD_DEFAULT 22
GLTouchJoyGlobals joyglobals = { 0 };
GLTouchJoyAxes axes = { 0 };
GLTouchJoyButtons buttons = { 0 };
typedef struct variant_params_s {
GLModel *model; // origin model/texture
GLModel *azimuthModel; // azimuth model
void (*setupButtonModel)(char);
int centerX;
int centerY;
int trackingIndex;
GLfloat obj_w;
GLfloat obj_h;
struct timespec timingBegin;
uint8_t activeChar;
bool modelDirty;
} variant_params_s;
static variant_params_s axes = { 0 };
static variant_params_s butt = { 0 };
static struct {
GLTouchJoyVariant *joys;
@ -63,12 +76,10 @@ static struct {
int axisYMax;
// Button box
int buttonX;
int buttonXMax;
int buttonY;
int buttonYMax;
// TODO FIXME : support 2-players!
int buttX;
int buttXMax;
int buttY;
int buttYMax;
} touchport = { 0 };
#define AZIMUTH_CLASS(CLS, ...) \
@ -195,12 +206,12 @@ static void *_azimuth_create_model(GLModel *parent) {
return azimuthJoystick;
}
static void _azimuth_render(void) {
if (!axes.azimuthModel) {
static void _azimuth_render(GLModel *azimuthModel) {
if (!azimuthModel) {
return;
}
GLModelJoystickAzimuth *azimuthJoystick = (GLModelJoystickAzimuth *)axes.azimuthModel->custom;
GLModelJoystickAzimuth *azimuthJoystick = (GLModelJoystickAzimuth *)azimuthModel->custom;
// use azimuth (SolidColor) program
glUseProgram(azimuthJoystick->program);
@ -235,32 +246,32 @@ static void _azimuth_render(void) {
}
// NOTE : assuming we should just upload new postion data every time ...
glBindBuffer(GL_ARRAY_BUFFER, axes.azimuthModel->posBufferName);
glBufferData(GL_ARRAY_BUFFER, axes.azimuthModel->positionArraySize, axes.azimuthModel->positions, GL_DYNAMIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, azimuthModel->posBufferName);
glBufferData(GL_ARRAY_BUFFER, azimuthModel->positionArraySize, azimuthModel->positions, GL_DYNAMIC_DRAW);
// Bind our vertex array object
#if USE_VAO
glBindVertexArray(axes.azimuthModel->vaoName);
glBindVertexArray(azimuthModel->vaoName);
#else
glBindBuffer(GL_ARRAY_BUFFER, axes.azimuthModel->posBufferName);
glBindBuffer(GL_ARRAY_BUFFER, azimuthModel->posBufferName);
GLsizei posTypeSize = getGLTypeSize(axes.azimuthModel->positionType);
GLsizei posTypeSize = getGLTypeSize(azimuthModel->positionType);
// Set up parmeters for position attribute in the VAO including, size, type, stride, and offset in the currenly
// bound VAO This also attaches the position VBO to the VAO
glVertexAttribPointer(POS_ATTRIB_IDX, // What attibute index will this array feed in the vertex shader (see buildProgram)
axes.azimuthModel->positionSize, // How many elements are there per position?
axes.azimuthModel->positionType, // What is the type of this data?
azimuthModel->positionSize, // How many elements are there per position?
azimuthModel->positionType, // What is the type of this data?
GL_FALSE, // Do we want to normalize this data (0-1 range for fixed-pont types)
axes.azimuthModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
azimuthModel->positionSize*posTypeSize, // What is the stride (i.e. bytes between positions)?
0); // What is the offset in the VBO to the position data?
glEnableVertexAttribArray(POS_ATTRIB_IDX);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, axes.azimuthModel->elementBufferName);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, azimuthModel->elementBufferName);
#endif
// draw it
glDrawElements(axes.azimuthModel->primType, axes.azimuthModel->numElements, axes.azimuthModel->elementType, 0);
glDrawElements(azimuthModel->primType, azimuthModel->numElements, azimuthModel->elementType, 0);
// back to main framebuffer/quad program
glUseProgram(mainShaderProgram);
@ -303,10 +314,15 @@ 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] = rosetteChars[(i*ROSETTE_ROWS)+j];
uint8_t *rosetteChars = (parent == axes.model)
? variant.curr->axisRosetteChars()
: variant.curr->buttRosetteChars();
if (rosetteChars) {
for (unsigned int i=0; i<ROSETTE_ROWS; i++) {
for (unsigned int j=0; j<ROSETTE_COLS; j++) {
((hudElement->tpl)+(row*i))[j] = rosetteChars[(i*ROSETTE_ROWS)+j];
}
}
}
@ -353,7 +369,7 @@ static void _setup_button_hud(GLModel *parent) {
}
const unsigned int row = (BUTTON_TEMPLATE_COLS+1);
((hudElement->tpl)+(row*0))[0] = buttons.activeChar;
((hudElement->tpl)+(row*0))[0] = butt.activeChar;
glhud_setupDefault(parent);
}
@ -368,10 +384,14 @@ static void *_create_button_hud(GLModel *parent) {
return hudElement;
}
static inline void _setup_button_object_nop(char newChar) {
(void)newChar;
}
static inline void _setup_button_object_with_char(char newChar) {
if (buttons.activeChar != newChar) {
buttons.activeChar = newChar;
_setup_button_hud(buttons.model);
if (butt.activeChar != newChar) {
butt.activeChar = newChar;
_setup_button_hud(butt.model);
}
}
@ -380,23 +400,13 @@ static inline void _setup_button_object_with_char(char newChar) {
static inline void resetState() {
LOG("...");
axes.trackingIndex = TRACKING_NONE;
buttons.trackingIndex = TRACKING_NONE;
butt.trackingIndex = TRACKING_NONE;
variant.joys->resetState();
variant.kpad->resetState();
}
static void gltouchjoy_setup(void) {
LOG("...");
gltouchjoy_shutdown();
if (joyglobals.prefsChanged) {
gltouchjoy_applyPrefs();
}
// axis origin object
axes.model = mdlCreateQuad((GLModelParams_s){
static void _setup_axis_models(variant_params_s *params) {
params->model = mdlCreateQuad((GLModelParams_s){
.skew_x = -1.05,
.skew_y = -1.0,
.z = MODEL_DEPTH,
@ -410,20 +420,23 @@ static void gltouchjoy_setup(void) {
.create = &_create_axis_hud,
.destroy = &glhud_destroyDefault,
});
if (!axes.model) {
if (!params->model) {
LOG("gltouchjoy not initializing axis");
return;
}
if (!axes.model->custom) {
if (!params->model->custom) {
LOG("gltouchjoy axes initialization problem");
return;
}
_setup_axis_hud(params->model);// HACK : twice to ensure correct template ...
params->obj_w = AXIS_OBJ_W;
params->obj_h = AXIS_OBJ_H;
// axis azimuth object
bool azimuthError = true;
do {
axes.azimuthModel = mdlCreateQuad((GLModelParams_s){
params->azimuthModel = mdlCreateQuad((GLModelParams_s){
.skew_x = -1.05,
.skew_y = -1.0,
.z = MODEL_DEPTH,
@ -437,70 +450,87 @@ static void gltouchjoy_setup(void) {
.create = &_azimuth_create_model,
.destroy = &_azimuth_destroy_model,
});
if (!axes.azimuthModel) {
if (!params->azimuthModel) {
LOG("gltouchjoy azimuth model initialization problem");
break;
}
if (!axes.azimuthModel->custom) {
if (!params->azimuthModel->custom) {
LOG("gltouchjoy azimuth custom model initialization problem");
break;
}
azimuthError = false;
} while (0);
if (azimuthError) {
mdlDestroyModel(&axes.azimuthModel);
mdlDestroyModel(&params->azimuthModel);
}
}
static void gltouchjoy_setup(void) {
LOG("...");
gltouchjoy_shutdown();
if (joyglobals.prefsChanged) {
gltouchjoy_applyPrefs();
}
// axis origin object
_setup_axis_models(&axes);
// 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;
_setup_axis_models(&butt);
butt.setupButtonModel = &_setup_button_object_nop;
} else {
butt.setupButtonModel = &_setup_button_object_with_char;
if (!prefs_parseLongValue(PREF_DOMAIN_JOYSTICK, PREF_JOY_TOUCHDOWN_CHAR, &lVal, /*base:*/10)) {
buttons.activeChar = MOUSETEXT_OPENAPPLE;
butt.activeChar = MOUSETEXT_OPENAPPLE;
} else {
if (lVal == TOUCH_BUTTON2) {
buttons.activeChar = MOUSETEXT_CLOSEDAPPLE;
butt.activeChar = MOUSETEXT_CLOSEDAPPLE;
} else if (lVal == TOUCH_BOTH) {
buttons.activeChar = '+';
butt.activeChar = '+';
} else {
buttons.activeChar = MOUSETEXT_OPENAPPLE;
butt.activeChar = MOUSETEXT_OPENAPPLE;
}
}
butt.azimuthModel = NULL;
butt.model = mdlCreateQuad((GLModelParams_s){
.skew_x = 1.05-BUTTON_OBJ_W,
.skew_y = -1.0,
.z = MODEL_DEPTH,
.obj_w = BUTTON_OBJ_W,
.obj_h = BUTTON_OBJ_H,
.positionUsageHint = GL_DYNAMIC_DRAW, // positions can change
.tex_w = BUTTON_FB_WIDTH,
.tex_h = BUTTON_FB_HEIGHT,
.texcoordUsageHint = GL_DYNAMIC_DRAW, // so can texture
}, (GLCustom){
.create = &_create_button_hud,
.destroy = &glhud_destroyDefault,
});
if (!butt.model) {
LOG("gltouchjoy not initializing buttons");
return;
}
if (!butt.model->custom) {
LOG("gltouchjoy buttons initialization problem");
return;
}
butt.obj_w = BUTTON_OBJ_W;
butt.obj_h = BUTTON_OBJ_H;
}
buttons.model = mdlCreateQuad((GLModelParams_s){
.skew_x = 1.05-BUTTON_OBJ_W,
.skew_y = -1.0,
.z = MODEL_DEPTH,
.obj_w = BUTTON_OBJ_W,
.obj_h = BUTTON_OBJ_H,
.positionUsageHint = GL_DYNAMIC_DRAW, // positions can change
.tex_w = BUTTON_FB_WIDTH,
.tex_h = BUTTON_FB_HEIGHT,
.texcoordUsageHint = GL_DYNAMIC_DRAW, // so can texture
}, (GLCustom){
.create = &_create_button_hud,
.destroy = &glhud_destroyDefault,
});
if (!buttons.model) {
LOG("gltouchjoy not initializing buttons");
return;
}
if (!buttons.model->custom) {
LOG("gltouchjoy buttons initialization problem");
return;
}
variant.joys->setup(&_setup_button_object_with_char);
variant.kpad->setup(&_setup_button_object_with_char);
variant.joys->setup();
variant.kpad->setup();
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
axes.timingBegin = now;
buttons.timingBegin = now;
butt.timingBegin = now;
joyglobals.isAvailable = true;
@ -524,7 +554,41 @@ static void gltouchjoy_shutdown(void) {
mdlDestroyModel(&axes.model);
mdlDestroyModel(&axes.azimuthModel);
mdlDestroyModel(&buttons.model);
mdlDestroyModel(&butt.model);
mdlDestroyModel(&butt.azimuthModel);
}
static void _render_glmodels(variant_params_s *params, int textureActive, int textureId) {
float alpha = 1.f;
if (params->trackingIndex == TRACKING_NONE) {
alpha = glhud_getTimedVisibility(params->timingBegin, joyglobals.minAlpha, 1.0);
if (alpha < joyglobals.minAlpha) {
alpha = joyglobals.minAlpha;
}
}
if (alpha > 0.0) {
GLModel *model = params->model;
glUniform1f(alphaValue, alpha);
glActiveTexture(textureActive);
glBindTexture(GL_TEXTURE_2D, model->textureName);
if (model->texDirty) {
model->texDirty = false;
_HACKAROUND_GLTEXIMAGE2D_PRE(textureActive, model->textureName);
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, model->texWidth, model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, model->texPixels);
}
if (params->modelDirty) {
params->modelDirty = false;
glBindBuffer(GL_ARRAY_BUFFER, model->posBufferName);
glBufferData(GL_ARRAY_BUFFER, model->positionArraySize, model->positions, GL_DYNAMIC_DRAW);
}
glUniform1i(texSamplerLoc, textureId);
glhud_renderDefault(model);
}
if (joyglobals.showAzimuth && params->trackingIndex != TRACKING_NONE) {
_azimuth_render(params->azimuthModel);
}
}
static void gltouchjoy_render(void) {
@ -544,63 +608,12 @@ static void gltouchjoy_render(void) {
glViewport(0, 0, touchport.width, touchport.height); // NOTE : show these HUD elements beyond the A2 framebuffer dimensions
// draw axis
float alpha = 1.f;
if (axes.trackingIndex == TRACKING_NONE) {
alpha = glhud_getTimedVisibility(axes.timingBegin, joyglobals.minAlpha, 1.0);
if (alpha < joyglobals.minAlpha) {
alpha = joyglobals.minAlpha;
}
}
if (alpha > 0.0) {
glUniform1f(alphaValue, alpha);
glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_AXIS);
glBindTexture(GL_TEXTURE_2D, axes.model->textureName);
if (axes.model->texDirty) {
axes.model->texDirty = false;
_HACKAROUND_GLTEXIMAGE2D_PRE(TEXTURE_ACTIVE_TOUCHJOY_AXIS, axes.model->textureName);
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, axes.model->texWidth, axes.model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, axes.model->texPixels);
}
if (axes.modelDirty) {
axes.modelDirty = false;
glBindBuffer(GL_ARRAY_BUFFER, axes.model->posBufferName);
glBufferData(GL_ARRAY_BUFFER, axes.model->positionArraySize, axes.model->positions, GL_DYNAMIC_DRAW);
}
glUniform1i(texSamplerLoc, TEXTURE_ID_TOUCHJOY_AXIS);
glhud_renderDefault(axes.model);
}
if (joyglobals.showAzimuth && axes.trackingIndex != TRACKING_NONE) {
_azimuth_render();
}
_render_glmodels(&axes, TEXTURE_ACTIVE_TOUCHJOY_AXIS, TEXTURE_ID_TOUCHJOY_AXIS);
// draw button(s)
alpha = 1.f;
if (buttons.trackingIndex == TRACKING_NONE) {
alpha = glhud_getTimedVisibility(buttons.timingBegin, joyglobals.minAlpha, 1.0);
if (alpha < joyglobals.minAlpha) {
alpha = joyglobals.minAlpha;
}
}
if (alpha > 0.0) {
glUniform1f(alphaValue, alpha);
glActiveTexture(TEXTURE_ACTIVE_TOUCHJOY_BUTTON);
glBindTexture(GL_TEXTURE_2D, buttons.model->textureName);
if (buttons.model->texDirty) {
buttons.model->texDirty = false;
_HACKAROUND_GLTEXIMAGE2D_PRE(TEXTURE_ACTIVE_TOUCHJOY_BUTTON, buttons.model->textureName);
glTexImage2D(GL_TEXTURE_2D, /*level*/0, TEX_FORMAT_INTERNAL, buttons.model->texWidth, buttons.model->texHeight, /*border*/0, TEX_FORMAT, TEX_TYPE, buttons.model->texPixels);
}
if (buttons.modelDirty) {
buttons.modelDirty = false;
glBindBuffer(GL_ARRAY_BUFFER, buttons.model->posBufferName);
glBufferData(GL_ARRAY_BUFFER, buttons.model->positionArraySize, buttons.model->positions, GL_DYNAMIC_DRAW);
}
glUniform1i(texSamplerLoc, TEXTURE_ID_TOUCHJOY_BUTTON);
glhud_renderDefault(buttons.model);
}
char active = variant.curr->buttActiveChar();
butt.setupButtonModel(active);
_render_glmodels(&butt, TEXTURE_ACTIVE_TOUCHJOY_BUTTON, TEXTURE_ID_TOUCHJOY_BUTTON);
}
static void gltouchjoy_reshape(int w, int h, bool landscape) {
@ -613,17 +626,17 @@ static void gltouchjoy_reshape(int w, int h, bool landscape) {
touchport.axisY = 0;
touchport.axisYMax = h;
touchport.buttonY = 0;
touchport.buttonYMax = h;
touchport.buttY = 0;
touchport.buttYMax = h;
if (joyglobals.axisIsOnLeft) {
touchport.axisX = 0;
touchport.axisXMax = (w * joyglobals.screenDivider);
touchport.buttonX = (w * joyglobals.screenDivider);
touchport.buttonXMax = w;
touchport.buttX = (w * joyglobals.screenDivider);
touchport.buttXMax = w;
} else {
touchport.buttonX = 0;
touchport.buttonXMax = (w * joyglobals.screenDivider);
touchport.buttX = 0;
touchport.buttXMax = (w * joyglobals.screenDivider);
touchport.axisX = (w * joyglobals.screenDivider);
touchport.axisXMax = w;
}
@ -669,90 +682,50 @@ static inline void _reset_model_position(GLModel *model, float touchX, float tou
}
}
static inline void _axis_touch_down(interface_touch_event_t action, int x, int y) {
axes.centerX = x;
axes.centerY = y;
_reset_model_position(axes.model, x, y, AXIS_OBJ_HALF_W, AXIS_OBJ_HALF_H, axes.azimuthModel);
axes.modelDirty = true;
TOUCH_JOY_LOG("---TOUCH %sDOWN (axis index %d) center:(%d,%d) -> joy(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "), axes.trackingIndex, axes.centerX, axes.centerY, joy_x, joy_y);
variant.curr->axisDown();
static void _touch_down(variant_params_s *params, int pointer_idx, int x, int y, void(*downFn)(void)) {
params->trackingIndex = pointer_idx;
params->centerX = x;
params->centerY = y;
_reset_model_position(params->model, x, y, (params->obj_w/2.f), (params->obj_h/2.f), params->azimuthModel);
params->modelDirty = true;
downFn();
}
static inline void _button_touch_down(interface_touch_event_t action, int x, int y) {
buttons.centerX = x;
buttons.centerY = y;
static void _touch_move(variant_params_s *params, float *x_coords, float *y_coords, void (*moveFn)(int, int)) {
int x = (int)x_coords[params->trackingIndex];
int y = (int)y_coords[params->trackingIndex];
_reset_model_position(buttons.model, x, y, BUTTON_OBJ_HALF_W, BUTTON_OBJ_HALF_H, NULL);
buttons.modelDirty = true;
TOUCH_JOY_LOG("---TOUCH %sDOWN (buttons index %d) center:(%d,%d) -> buttons(0x%02X,0x%02X)", (action == TOUCH_DOWN ? "" : "POINTER "), buttons.trackingIndex, buttons.centerX, buttons.centerY, joy_button0, joy_button1);
variant.curr->buttonDown();
}
static inline void _axis_move(int x, int y) {
if (joyglobals.showAzimuth && axes.azimuthModel) {
if (joyglobals.showAzimuth && params->azimuthModel) {
float centerX = 0.f;
float centerY = 0.f;
glhud_screenToModel(x, y, touchport.width, touchport.height, &centerX, &centerY);
GLfloat *quadAzimuth = (GLfloat *)axes.azimuthModel->positions;
GLfloat *quadAzimuth = (GLfloat *)params->azimuthModel->positions;
quadAzimuth[4 +0] = centerX;
quadAzimuth[4 +1] = centerY;
};
x -= axes.centerX;
y -= axes.centerY;
TOUCH_JOY_LOG("---TOUCH MOVE ...tracking axis:%d (%d,%d) -> joy(0x%02X,0x%02X)", axes.trackingIndex, x, y, joy_x, joy_y);
variant.curr->axisMove(x, y);
x -= params->centerX;
y -= params->centerY;
moveFn(x, y);
}
static inline void _button_move(int x, int y) {
x -= buttons.centerX;
y -= buttons.centerY;
TOUCH_JOY_LOG("+++TOUCH MOVE ...tracking button:%d (%d,%d) -> buttons(0x%02X,0x%02X)", buttons.trackingIndex, x, y, joy_button0, joy_button1);
variant.curr->buttonMove(x, y);
}
static void _touch_up(variant_params_s *params, float *x_coords, float *y_coords, variant_params_s *altParams, void(*upFn)(int, int), const char *altTag) {
static inline void _axis_touch_up(interface_touch_event_t action, int x, int y) {
#if DEBUG_TOUCH_JOY
bool resetIndex = false;
if (buttons.trackingIndex > axes.trackingIndex) {
// TODO FIXME : is resetting the pointer index just an Android-ism?
resetIndex = true;
}
TOUCH_JOY_LOG("---TOUCH %sUP (axis went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset buttons index!)" : ""));
#endif
x -= axes.centerX;
y -= axes.centerY;
if (buttons.trackingIndex > axes.trackingIndex) {
TOUCH_JOY_LOG("!!! : DECREMENTING buttons.trackingIndex");
--buttons.trackingIndex;
}
variant.curr->axisUp(x, y);
axes.trackingIndex = TRACKING_NONE;
}
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
params->timingBegin = now;
static inline void _button_touch_up(interface_touch_event_t action, int x, int y) {
#if DEBUG_TOUCH_JOY
bool resetIndex = false;
if (axes.trackingIndex > buttons.trackingIndex) {
// TODO FIXME : is resetting the pointer index just an Android-ism?
resetIndex = true;
}
TOUCH_JOY_LOG("---TOUCH %sUP (buttons went up)%s", (action == TOUCH_UP ? "" : "POINTER "), (resetIndex ? " (reset axis index!)" : ""));
#endif
x -= buttons.centerX;
y -= buttons.centerY;
if (axes.trackingIndex > buttons.trackingIndex) {
TOUCH_JOY_LOG("!!! : DECREMENTING axes.trackingIndex");
--axes.trackingIndex;
}
variant.curr->buttonUp(x, y);
buttons.trackingIndex = TRACKING_NONE;
}
int x = (int)x_coords[params->trackingIndex] - params->centerX;
int y = (int)y_coords[params->trackingIndex] - params->centerY;
if (altParams->trackingIndex > params->trackingIndex) {
TOUCH_JOY_LOG("\t!!! : DECREMENTING %s.trackingIndex", altTag);
--altParams->trackingIndex;
}
upFn(x, y);
params->trackingIndex = TRACKING_NONE;
}
static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
if (!joyglobals.isAvailable) {
@ -765,9 +738,6 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
return 0x0LL;
}
bool axisConsumed = false;
bool buttonConsumed = false;
switch (action) {
case TOUCH_DOWN:
case TOUCH_POINTER_DOWN:
@ -776,28 +746,24 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
int x = (int)x_coords[pointer_idx];
int y = (int)y_coords[pointer_idx];
if (_is_point_on_axis_side(x, y)) {
if (pointer_idx == buttons.trackingIndex) {
TOUCH_JOY_LOG("!!! : INCREMENTING buttons.trackingIndex");
++buttons.trackingIndex;
if (pointer_idx == butt.trackingIndex) {
TOUCH_JOY_LOG("\t!!! : INCREMENTING buttons.trackingIndex");
++butt.trackingIndex;
}
if (axes.trackingIndex != TRACKING_NONE) {
TOUCH_JOY_LOG("!!! : IGNORING OTHER AXIS TOUCH DOWN %d", pointer_idx);
TOUCH_JOY_LOG("\t!!! : IGNORING OTHER AXIS TOUCH DOWN %d", pointer_idx);
} else {
axisConsumed = true;
axes.trackingIndex = pointer_idx;
_axis_touch_down(action, x, y);
_touch_down(&axes, pointer_idx, x, y, variant.curr->axisDown);
}
} else {
if (pointer_idx == axes.trackingIndex) {
TOUCH_JOY_LOG("!!! : INCREMENTING axes.trackingIndex");
TOUCH_JOY_LOG("\t!!! : INCREMENTING axes.trackingIndex");
++axes.trackingIndex;
}
if (buttons.trackingIndex != TRACKING_NONE) {
TOUCH_JOY_LOG("!!! : IGNORING OTHER BUTTON TOUCH DOWN %d", pointer_idx);
if (butt.trackingIndex != TRACKING_NONE) {
TOUCH_JOY_LOG("\t!!! : IGNORING OTHER BUTTON TOUCH DOWN %d", pointer_idx);
} else {
buttonConsumed = true;
buttons.trackingIndex = pointer_idx;
_button_touch_down(action, x, y);
_touch_down(&butt, pointer_idx, x, y, variant.curr->buttonDown);
}
}
}
@ -806,42 +772,26 @@ static int64_t gltouchjoy_onTouchEvent(interface_touch_event_t action, int point
case TOUCH_MOVE:
TOUCH_JOY_LOG("------MOVE:");
if (axes.trackingIndex >= 0) {
axisConsumed = true;
int x = (int)x_coords[axes.trackingIndex];
int y = (int)y_coords[axes.trackingIndex];
_axis_move(x, y);
_touch_move(&axes, x_coords, y_coords, variant.curr->axisMove);
}
if (buttons.trackingIndex >= 0) {
buttonConsumed = true;
int x = (int)x_coords[buttons.trackingIndex];
int y = (int)y_coords[buttons.trackingIndex];
_button_move(x, y);
if (butt.trackingIndex >= 0) {
_touch_move(&butt, x_coords, y_coords, variant.curr->buttonMove);
}
break;
case TOUCH_UP:
case TOUCH_POINTER_UP:
{
TOUCH_JOY_LOG("------UP:");
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (pointer_idx == axes.trackingIndex) {
axes.timingBegin = now;
int x = (int)x_coords[axes.trackingIndex];
int y = (int)y_coords[axes.trackingIndex];
_axis_touch_up(action, x, y);
} else if (pointer_idx == buttons.trackingIndex) {
buttons.timingBegin = now;
int x = (int)x_coords[buttons.trackingIndex];
int y = (int)y_coords[buttons.trackingIndex];
_button_touch_up(action, x, y);
TOUCH_JOY_LOG("------UP:");
if (pointer_idx == axes.trackingIndex) {
_touch_up(&axes, x_coords, y_coords, /*alt:*/&butt, variant.curr->axisUp, "buttons");
} else if (pointer_idx == butt.trackingIndex) {
_touch_up(&butt, x_coords, y_coords, /*alt:*/&axes, variant.curr->buttonUp, "axis");
} else {
if (pointer_count == 1) {
TOUCH_JOY_LOG("\t!!! : RESETTING TOUCH JOYSTICK STATE MACHINE");
resetState();
} else {
if (pointer_count == 1) {
TOUCH_JOY_LOG("!!! : RESETTING TOUCH JOYSTICK STATE MACHINE");
resetState();
} else {
TOUCH_JOY_LOG("!!! : IGNORING OTHER TOUCH UP %d", pointer_idx);
}
TOUCH_JOY_LOG("\t!!! : IGNORING OTHER TOUCH UP %d", pointer_idx);
}
}
break;
@ -866,20 +816,23 @@ static void _animation_showTouchJoystick(void) {
return;
}
int x = touchport.axisX + ((touchport.axisXMax - touchport.axisX)/2);
int y = touchport.axisY + ((touchport.axisYMax - touchport.axisY)/2);
_reset_model_position(axes.model, x, y, AXIS_OBJ_HALF_W, AXIS_OBJ_HALF_H, NULL);
int x;
int y;
x = touchport.axisX + ((touchport.axisXMax - touchport.axisX)/2);
y = touchport.axisY + ((touchport.axisYMax - touchport.axisY)/2);
_reset_model_position(axes.model, x, y, (axes.obj_w/2.f), (axes.obj_h/2.f), NULL);
axes.modelDirty = true;
x = touchport.buttonX + ((touchport.buttonXMax - touchport.buttonX)/2);
y = touchport.buttonY + ((touchport.buttonYMax - touchport.buttonY)/2);
_reset_model_position(buttons.model, x, y, BUTTON_OBJ_HALF_W, BUTTON_OBJ_HALF_H, NULL);
buttons.modelDirty = true;
x = touchport.buttX + ((touchport.buttXMax - touchport.buttX)/2);
y = touchport.buttY + ((touchport.buttYMax - touchport.buttY)/2);
_reset_model_position(butt.model, x, y, (butt.obj_w/2.f), (butt.obj_h/2.f), NULL);
butt.modelDirty = true;
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
axes.timingBegin = now;
buttons.timingBegin = now;
butt.timingBegin = now;
}
static void _animation_hideTouchJoystick(void) {
@ -887,7 +840,7 @@ static void _animation_hideTouchJoystick(void) {
return;
}
axes.timingBegin = (struct timespec){ 0 };
buttons.timingBegin = (struct timespec){ 0 };
butt.timingBegin = (struct timespec){ 0 };
}
static void gltouchjoy_applyPrefs(void) {
@ -911,12 +864,19 @@ static void gltouchjoy_applyPrefs(void) {
joyglobals.minAlpha = 0.0;
}
joyglobals.tapDelayFrames = prefs_parseLongValue (PREF_DOMAIN_JOYSTICK, PREF_JOY_TAP_DELAY, &lVal, 10) ? lVal : BUTTON_TAP_DELAY_FRAMES_DEFAULT;
if (joyglobals.tapDelayFrames < 0) {
joyglobals.tapDelayFrames = 0;
} else if (joyglobals.tapDelayFrames > BUTTON_TAP_DELAY_FRAMES_MAX) {
joyglobals.tapDelayFrames = BUTTON_TAP_DELAY_FRAMES_MAX;
}
joyglobals.showControls = prefs_parseBoolValue (PREF_DOMAIN_JOYSTICK, PREF_SHOW_CONTROLS, &bVal) ? bVal : true;
joyglobals.showAzimuth = true;//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.axisMultiplier = prefs_parseFloatValue(PREF_DOMAIN_JOYSTICK, PREF_AXIS_SENSITIVITY, &fVal) ? fVal : 1.f;
joyglobals.isCalibrating = prefs_parseBoolValue (PREF_DOMAIN_TOUCHSCREEN, PREF_CALIBRATING, &bVal) ? bVal : false;
@ -942,11 +902,12 @@ static void _init_gltouchjoy(void) {
axes.centerX = 240;
axes.centerY = 160;
axes.trackingIndex = TRACKING_NONE;
axes.activeChar = ' ';
buttons.centerX = 240;
buttons.centerY = 160;
buttons.trackingIndex = TRACKING_NONE;
buttons.activeChar = MOUSETEXT_OPENAPPLE;
butt.centerX = 240;
butt.centerY = 160;
butt.trackingIndex = TRACKING_NONE;
butt.activeChar = ' ';
joyglobals.prefsChanged = true; // force reload preferences/defaults

View File

@ -17,20 +17,79 @@
#define DEBUG_TOUCH_JOY 0
#if DEBUG_TOUCH_JOY
# define TOUCH_JOY_LOG(...) LOG(__VA_ARGS__)
# define TOUCH_JOY_LOG(...) _SIMPLE_LOG(__VA_ARGS__)
#else
# define TOUCH_JOY_LOG(...)
#endif
#define DEBUG_TOUCH_JOY_GESTURE 0
#if DEBUG_TOUCH_JOY_GESTURE
# define TOUCH_JOY_GESTURE_LOG(...) LOG(__VA_ARGS__)
#else
# define TOUCH_JOY_GESTURE_LOG(...)
#endif
#define ROSETTE_ROWS 3
#define ROSETTE_COLS 3
#define ROSETTE_COUNT (ROSETTE_ROWS*ROSETTE_COLS)
#define BUTTON_TAP_DELAY_FRAMES_DEFAULT 12L // 12 * 16.688 millis == ~0.2sec
#define BUTTON_TAP_DELAY_FRAMES_MAX 30L // 30 * 16.688 millis == ~0.5sec
// TOUCH EVENT LIFECYCLE MAPPING TO APPLE ][ JOYSTICK BUTTONS OR KEYS
// ------------------------------------------------------------------
//
// The touch screen event lifecycle (touch down, touch move, touch up) has a direct 1:1 correspondence to the Apple ][
// joystick axis. Touch down zeros the joystick axis, touch move will convert the screen coordinates to x,y joystick
// axis value, touch up will re-zero the joystick axis. The only user configuration here is to normalize and scale
// between the touchscreen coordinates to the 0-255 Apple ][ joystick axes.
//
// Difficulty arises in the mapping between the touch screen events and which button/key should be fired. The gltouchjoy
// implementation (both traditional joystick and keypad joystick) allows tap/long-press and swipe gestures to fire
// different configured buttons/keys.
//
// BUTTON TAP DELAY RATIONALE
// --------------------------
//
// A touch-down can be an ambiguous event ... does the user intend to tap/long-press or swipe/swipe-and-hold? Our
// mapping of the touch lifecycle to the joystick buttons/keys uses a user-configurable delay before firing the
// appropriate button/key.
//
// BUTTON TAP DELAY STATE MACHINE
// ------------------------------
//
// - On touch-down, we need to delay for a certain amount of time (video frames) to determine the user's intent
// + This latches the end-of-video-frame callback (on CPU thread). The amount of frames to delay is a configurable
// preference -- (who are we to hardcode this determination?)
// * Users likely want a custom twitch response setting, (at smaller thresholds, running the risk of firing a
// tap when they intended just a swipe)
// * Some games may not make use of the second joystick button, and so may be more effectively played with only
// tapping (no swipes) and a `tapDelayFrames` configured to zero. (In which case the button down event is
// non-ambiguous) and should immediately fire the configured tap button.
// - Ambiguity about user's intent is also resolved the moment there is a touch-up or a touch-move beyond the `switchThreshold`
// + Touch-up will immediately fire the tap button (if not yet fired)
// + Touch-move beyond the configured `switchThreshold` will immediately fire the appropriate configured swipe button
// + Touch-move beneath the `switchThreshold` is considered ambiguous "jitter"
// - The end-of-video-frame callback on the CPU thread...
// + ...handles tap delay counting
// + ...handles continual fire of current button (during touch-and-hold, or swipe-and-hold)
// + ...will unregister itself after touch-up event
//
// SCENARIOS:
// - SINGLE TAP:
// + down ... (move < `switchThreshold`)* ... up ... [cbX]
// * up: fires configured tap button on UI thread, resets `tapDelayFrames`
// * cbX: after `tapDelayFrames`, resets joystick button state and unregisters itself from the e-o-f callback
//
// - SINGLE TOUCH-AND-HOLD:
// + down ... ([cb], move < `switchThreshold`)* ... up ... [cbX]
// + down ... (move < `switchThreshold`, [cb])* ... up ... [cbX]
// * [cb] fires configured tap button on CPU thread end-of-frame callback
//
// - down, move >= `switchThreshold`, up
// - down, (move >= `switchThreshold`, move back to < `switchThreshold`)+, up
// - down, ... cancel/reset
typedef enum touchjoy_button_type_t {
TOUCH_NONE = 0,
TOUCH_BUTTON1 = 1,
TOUCH_BUTTON2,
TOUCH_BOTH,
// --or-- an ASCII/fonttext value ...
} touchjoy_button_type_t;
enum {
ROSETTE_NORTHWEST=0,
@ -57,51 +116,18 @@ typedef struct GLTouchJoyGlobals {
float minAlphaWhenOwnsScreen;
float minAlpha;
float screenDivider;
float axisMultiplier; // multiplier for joystick axis
bool axisIsOnLeft;
int switchThreshold;
long switchThreshold; // threshold over which a move is in one of the octants
long tapDelayFrames; // tap/swipe discrimination delay
} GLTouchJoyGlobals;
extern GLTouchJoyGlobals joyglobals;
// touch axis variables
typedef struct GLTouchJoyAxes {
// origin model/texture
GLModel *model;
bool modelDirty;
// azimuth model
GLModel *azimuthModel;
int centerX;
int centerY;
float multiplier;
int trackingIndex;
struct timespec timingBegin;
} GLTouchJoyAxes;
extern GLTouchJoyAxes axes;
// button object variables
typedef struct GLTouchJoyButtons {
GLModel *model;
uint8_t activeChar;
bool modelDirty;
int centerX;
int centerY;
int trackingIndex;
struct timespec timingBegin;
} GLTouchJoyButtons;
extern GLTouchJoyButtons buttons;
typedef struct GLTouchJoyVariant {
interface_device_t (*variant)(void);
void (*resetState)(void);
void (*setup)(void (*buttonDrawCallback)(char newChar));
void (*setup)(void);
void (*shutdown)(void);
void (*prefsChanged)(const char *domain);
@ -114,7 +140,9 @@ typedef struct GLTouchJoyVariant {
void (*axisMove)(int dx, int dy);
void (*axisUp)(int dx, int dy);
uint8_t *(*rosetteChars)(void);
uint8_t *(*axisRosetteChars)(void);
uint8_t *(*buttRosetteChars)(void);
uint8_t (*buttActiveChar)(void);
} GLTouchJoyVariant;

View File

@ -15,32 +15,26 @@
#error this is a touch interface module, possibly you mean to not compile this at all?
#endif
#define BUTTON_TAP_DELAY_NANOS_DEFAULT (NANOSECONDS_PER_SECOND/20) // 0.2 secs
#define BUTTON_TAP_DELAY_NANOS_MIN (NANOSECONDS_PER_SECOND/10000) // 0.0001 secs
typedef struct touch_event_s {
struct touch_event_s *next;
interface_touch_event_t event;
uint8_t currJoyButtonValue0;
uint8_t currJoyButtonValue1;
uint8_t currButtonDisplayChar;
} touch_event_s;
static touch_event_s *touchEventQ = NULL;
static GLTouchJoyVariant joyVariant = { 0 };
static struct {
void (*buttonDrawCallback)(char newChar);
video_frame_callback_fn frameCallback;
volatile unsigned long spinlock;
unsigned long tapDelayFrameCount;
pthread_t tapDelayThreadId;
pthread_mutex_t tapDelayMutex;
pthread_cond_t tapDelayCond;
unsigned int tapDelayNanos;
interface_touch_event_t currEventType;
touchjoy_button_type_t touchDownChar;
touchjoy_button_type_t northChar;
touchjoy_button_type_t southChar;
uint8_t currJoyButtonValue0;
uint8_t currJoyButtonValue1;
uint8_t currButtonDisplayChar;
uint8_t lastButtonDisplayChar;
bool buttonThresholdExceeded;
bool justTapConfigured;
} joys = { 0 };
// ----------------------------------------------------------------------------
@ -49,178 +43,44 @@ static interface_device_t touchjoy_variant(void) {
return TOUCH_DEVICE_JOYSTICK;
}
static inline void _reset_button_state(void) {
run_args.joy_button0 = 0;
run_args.joy_button1 = 0;
}
static inline void _reset_axis_state(void) {
joy_x = HALF_JOY_RANGE;
joy_y = HALF_JOY_RANGE;
}
static inline void _reset_buttons_state(void) {
run_args.joy_button0 = 0x0;
run_args.joy_button1 = 0x0;
}
static void touchjoy_resetState(void) {
TOUCH_JOY_LOG("\t\tjoy resetState");
_reset_axis_state();
//_reset_buttons_state(); -- do not reset button state here, it may interfere with other code performing reboot/reset
//NOTE : do not reset button state here, it may interfere with other code performing reboot/reset
joys.spinlock = SPINLOCK_INIT;
joys.frameCallback = NULL;
joys.tapDelayFrameCount = 0;
}
// ----------------------------------------------------------------------------
// Tap Delay Thread : implements a gesture recognizer that differentiates between a "long touch", "tap", and "swipe
// down/up" gestures. Necessarily delays processing of initial touch down event to make a proper determination. Also
// delays resetting joystick button state after touch up event to avoid resetting joystick button state too soon.
//
// * long touch and tap are interpreted as one (configurable) joystick button fire event
// * swipe up and swipe down are the other two (configurable) joystick button fire events
//
static struct timespec *_tap_wait(void) {
static struct timespec wait = { 0 };
clock_gettime(CLOCK_REALTIME, &wait);
wait = timespec_add(wait, joys.tapDelayNanos);
return &wait;
}
static void *_button_tap_delayed_thread(void *dummyptr) {
TRACE_INTERFACE_MARK("_button_tap_delayed_thread ...");
LOG(">>> [DELAYEDTAP] thread start ...");
pthread_mutex_lock(&joys.tapDelayMutex);
int timedOut = ETIMEDOUT;
for (;;) {
if (UNLIKELY(emulator_isShuttingDown())) {
break;
}
if (timedOut) {
// reset state and deep sleep waiting for touch down
_reset_buttons_state();
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] deep sleep ...");
pthread_cond_wait(&joys.tapDelayCond, &joys.tapDelayMutex);
} else {
// delays reset of button state while remaining ready to process a touch down
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] event looping ...");
timedOut = pthread_cond_timedwait(&joys.tapDelayCond, &joys.tapDelayMutex, _tap_wait()); // wait and possibly consume event
assert((!timedOut || timedOut == ETIMEDOUT) && "should not fail any other way");
if (timedOut) {
// reset state and go into deep sleep
continue;
}
_reset_buttons_state();
}
if (UNLIKELY(emulator_isShuttingDown())) {
break;
}
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] touch down ...");
touch_event_s *touchCurrEvent = NULL;
touch_event_s *touchPrevEvent = touchEventQ;
assert(touchPrevEvent && "should be a touch event ready to consume");
touchEventQ = touchEventQ->next;
assert(touchPrevEvent->event == TOUCH_DOWN && "event queue head should be a touch down event");
for (;;) {
// delay processing of touch down to perform simple gesture recognition
timedOut = pthread_cond_timedwait(&joys.tapDelayCond, &joys.tapDelayMutex, _tap_wait());
assert((!timedOut || timedOut == ETIMEDOUT) && "should not fail any other way");
if (UNLIKELY(emulator_isShuttingDown())) {
break;
}
touchCurrEvent = touchEventQ;
if (!touchCurrEvent) {
assert(timedOut);
// touch-down-and-hold
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] long touch ...");
run_args.joy_button0 = touchPrevEvent->currJoyButtonValue0;
run_args.joy_button1 = touchPrevEvent->currJoyButtonValue1;
joys.buttonDrawCallback(touchPrevEvent->currButtonDisplayChar);
continue;
}
touchEventQ = touchEventQ->next;
if (touchCurrEvent->event == TOUCH_MOVE) {
// dragging ...
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] move ...");
run_args.joy_button0 = touchCurrEvent->currJoyButtonValue0;
run_args.joy_button1 = touchCurrEvent->currJoyButtonValue1;
joys.buttonDrawCallback(touchCurrEvent->currButtonDisplayChar);
FREE(touchPrevEvent);
touchPrevEvent = touchCurrEvent;
} else if (touchCurrEvent->event == TOUCH_UP) {
// tap
TOUCH_JOY_GESTURE_LOG(">>> [DELAYEDTAP] touch up ...");
run_args.joy_button0 = touchPrevEvent->currJoyButtonValue0;
run_args.joy_button1 = touchPrevEvent->currJoyButtonValue1;
joys.buttonDrawCallback(touchPrevEvent->currButtonDisplayChar);
timedOut = 0;
break;
} else if (touchCurrEvent->event == TOUCH_DOWN) {
LOG("WHOA : unexpected touch down, are you spamming the touchscreen?!");
FREE(touchPrevEvent);
touchPrevEvent = touchCurrEvent;
continue;
} else {
__builtin_unreachable();
}
}
FREE(touchPrevEvent);
FREE(touchCurrEvent);
}
// clear out event queue
touch_event_s *p = touchEventQ;
while (p) {
touch_event_s *dead = p;
p = p->next;
FREE(dead);
}
pthread_mutex_unlock(&joys.tapDelayMutex);
joys.tapDelayMutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
joys.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
LOG(">>> [DELAYEDTAP] thread exit ...");
return NULL;
}
static void touchjoy_setup(void (*buttonDrawCallback)(char newChar)) {
joys.buttonDrawCallback = buttonDrawCallback;
if (joys.tapDelayThreadId == 0) {
int err = TEMP_FAILURE_RETRY(pthread_create(&joys.tapDelayThreadId, NULL, (void *)&_button_tap_delayed_thread, (void *)NULL));
assert(!err);
}
static void touchjoy_setup(void) {
// ...
}
static void touchjoy_shutdown(void) {
if (joys.tapDelayThreadId && emulator_isShuttingDown()) {
pthread_mutex_lock(&joys.tapDelayMutex);
pthread_cond_signal(&joys.tapDelayCond);
pthread_mutex_unlock(&joys.tapDelayMutex);
pthread_join(joys.tapDelayThreadId, NULL);
joys.tapDelayThreadId = 0;
}
// ...
}
// ----------------------------------------------------------------------------
// axis state
// axis state machine
static void touchjoy_axisDown(void) {
_reset_axis_state();
}
static void touchjoy_axisMove(int x, int y) {
if (axes.multiplier != 1.f) {
x = (int) ((float)x * axes.multiplier);
y = (int) ((float)y * axes.multiplier);
}
x = (int) ((float)x * joyglobals.axisMultiplier);
y = (int) ((float)y * joyglobals.axisMultiplier);
x += 0x80;
y += 0x80;
@ -228,13 +88,13 @@ static void touchjoy_axisMove(int x, int y) {
if (x < 0) {
x = 0;
}
if (x > 0xff) {
else if (x > 0xff) {
x = 0xff;
}
if (y < 0) {
y = 0;
}
if (y > 0xff) {
else if (y > 0xff) {
y = 0xff;
}
@ -243,70 +103,165 @@ static void touchjoy_axisMove(int x, int y) {
}
static void touchjoy_axisUp(int x, int y) {
//x = (int) ((float)x * joyglobals.axisMultiplier);
//y = (int) ((float)y * joyglobals.axisMultiplier);
(void)x;
(void)y;
_reset_axis_state();
}
// ----------------------------------------------------------------------------
// button state
// button state machine
static void _fire_current_buttons(void) {
TOUCH_JOY_LOG("\t\t\tfire buttons 0:%02x 1:%02X char:%02x", joys.currJoyButtonValue0, joys.currJoyButtonValue1, joys.currButtonDisplayChar);
run_args.joy_button0 = joys.currJoyButtonValue0;
run_args.joy_button1 = joys.currJoyButtonValue1;
joys.lastButtonDisplayChar = joys.currButtonDisplayChar;
}
static void _signal_tap_delay_event(interface_touch_event_t eventType, touchjoy_button_type_t theButtonChar) {
joys.currEventType = eventType;
static void _signal_tap_delay_event(interface_touch_event_t type, touchjoy_button_type_t theButtonChar) {
touch_event_s *touchEvent = MALLOC(sizeof(*touchEvent));
touchEvent->next = NULL;
touchEvent->event = type;
if (theButtonChar == TOUCH_BUTTON1) {
touchEvent->currJoyButtonValue0 = 0x80;
touchEvent->currJoyButtonValue1 = 0;
touchEvent->currButtonDisplayChar = MOUSETEXT_OPENAPPLE;
joys.currJoyButtonValue0 = 0x80;
joys.currJoyButtonValue1 = 0;
joys.currButtonDisplayChar = MOUSETEXT_OPENAPPLE;
} else if (theButtonChar == TOUCH_BUTTON2) {
touchEvent->currJoyButtonValue0 = 0;
touchEvent->currJoyButtonValue1 = 0x80;
touchEvent->currButtonDisplayChar = MOUSETEXT_CLOSEDAPPLE;
joys.currJoyButtonValue0 = 0;
joys.currJoyButtonValue1 = 0x80;
joys.currButtonDisplayChar = MOUSETEXT_CLOSEDAPPLE;
} else if (theButtonChar == TOUCH_BOTH) {
touchEvent->currJoyButtonValue0 = 0x80;
touchEvent->currJoyButtonValue1 = 0x80;
touchEvent->currButtonDisplayChar = '+';
joys.currJoyButtonValue0 = 0x80;
joys.currJoyButtonValue1 = 0x80;
joys.currButtonDisplayChar = '+';
} else {
touchEvent->currJoyButtonValue0 = 0;
touchEvent->currJoyButtonValue1 = 0;
touchEvent->currButtonDisplayChar = ' ';
joys.currJoyButtonValue0 = 0;
joys.currJoyButtonValue1 = 0;
//joys.currButtonDisplayChar = ' '; -- avoid truncating normal render cycle
}
}
pthread_mutex_lock(&joys.tapDelayMutex);
touch_event_s *p0 = NULL;
touch_event_s *p = touchEventQ;
while (p) {
p0 = p;
p = p->next;
}
if (p0) {
p0->next = touchEvent;
} else {
touchEventQ = touchEvent;
}
pthread_cond_signal(&joys.tapDelayCond);
pthread_mutex_unlock(&joys.tapDelayMutex);
// End-of-video-frame callback (handling tap delay and auto-fire)
static void touchjoy_frameCallback(uint8_t textFlashCounter) {
(void)textFlashCounter;
TOUCH_JOY_LOG("\t\t+++joy frameCallback");
// When activated, this is called every video frame -- ~16.688 millis
ASSERT_ON_CPU_THREAD();
SPIN_LOCK_FULL(&joys.spinlock);
do {
++joys.tapDelayFrameCount;
TOUCH_JOY_LOG("\t\t+++joy frameCallback acquire (%lu)", joys.tapDelayFrameCount);
if (joys.tapDelayFrameCount < joyglobals.tapDelayFrames) {
break;
}
_fire_current_buttons();
if (joys.currEventType == TOUCH_UP) {
// unregister callback ...
joys.frameCallback = NULL;
TOUCH_JOY_LOG("\t\t+++joy callback done");
}
} while (0);
TOUCH_JOY_LOG("\t\t+++joy frameCallback release");
SPIN_UNLOCK_FULL(&joys.spinlock);
}
static void touchjoy_buttonDown(void) {
TOUCH_JOY_LOG("\t\tjoy buttonDown");
SPIN_LOCK_FULL(&joys.spinlock);
TOUCH_JOY_LOG("\t\tjoy buttonDown acquire");
joys.buttonThresholdExceeded = false;
joys.tapDelayFrameCount = 0UL;
_reset_button_state();
_signal_tap_delay_event(TOUCH_DOWN, joys.touchDownChar);
if (joyglobals.tapDelayFrames == 0 || joys.justTapConfigured) {
// unambiguous intent : no tap delay or only the tap button is configured
_fire_current_buttons();
}
// hook into end-of-video-frame callback
joys.frameCallback = &touchjoy_frameCallback;
TOUCH_JOY_LOG("\t\tjoy buttonDown release");
SPIN_UNLOCK_FULL(&joys.spinlock);
}
static void touchjoy_buttonMove(int dx, int dy) {
if ((dy < -joyglobals.switchThreshold) || (dy > joyglobals.switchThreshold)) {
static void _touchjoy_buttonMove(int dx, int dy) {
touchjoy_button_type_t theButtonChar = -1;
if (dy < 0) {
theButtonChar = joys.northChar;
} else {
theButtonChar = joys.southChar;
bool shouldFire = false;
touchjoy_button_type_t theButtonChar = joys.touchDownChar;
int delta = abs(dy);
if (!joys.buttonThresholdExceeded) {
if (delta >= joyglobals.switchThreshold) {
// unambiguous intent : user swiped beyond threshold
joys.buttonThresholdExceeded = true;
shouldFire = true;
theButtonChar = (dy < 0) ? joys.northChar : joys.southChar;
}
} else {
if (delta < joyglobals.switchThreshold) {
// 2019/04/20 NOTE: originally we did not re-zero back to touchDownChar ... this allowed a progression between
// southChar/northChar (or vice-versa)
//
// touchDownChar should be fired on a tap or long-press (once we swipe beyond the threshold, we should only switch
// between northChar and southChar)
//shouldFire = true;
joys.buttonThresholdExceeded = false;
}
}
if (shouldFire) {
// immediately fire current button(s) upon threshold [re]-change ... and remove delay for auto-fire
_signal_tap_delay_event(TOUCH_MOVE, theButtonChar);
_fire_current_buttons();
}
}
static void touchjoy_buttonMove(int dx, int dy) {
TOUCH_JOY_LOG("\t\tjoy buttonMove");
SPIN_LOCK_FULL(&joys.spinlock);
TOUCH_JOY_LOG("\t\tjoy buttonMove acquire");
_touchjoy_buttonMove(dx, dy);
TOUCH_JOY_LOG("\t\tjoy buttonMove release");
SPIN_UNLOCK_FULL(&joys.spinlock);
}
static void touchjoy_buttonUp(int dx, int dy) {
_signal_tap_delay_event(TOUCH_UP, ' ');
TOUCH_JOY_LOG("\t\tjoy buttonUp");
SPIN_LOCK_FULL(&joys.spinlock);
TOUCH_JOY_LOG("\t\tjoy buttonUp acquire");
bool ignored = false;
_touchjoy_buttonMove(dx, dy);
_fire_current_buttons();
// force CPU thread callback into recount before unsetting button(s) state ... this allows time for CPU thread to consume current button(s)
joys.tapDelayFrameCount = 0UL;
_signal_tap_delay_event(TOUCH_UP, TOUCH_NONE);
TOUCH_JOY_LOG("\t\tjoy buttonUp release");
SPIN_UNLOCK_FULL(&joys.spinlock);
}
static void touchjoy_prefsChanged(const char *domain) {
@ -318,31 +273,28 @@ static void touchjoy_prefsChanged(const char *domain) {
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;
if (joys.tapDelayNanos < BUTTON_TAP_DELAY_NANOS_MIN) {
joys.tapDelayNanos = BUTTON_TAP_DELAY_NANOS_MIN;
}
joys.justTapConfigured = (joys.touchDownChar != TOUCH_NONE) &&
(joys.northChar == TOUCH_NONE) &&
(joys.southChar == TOUCH_NONE);
}
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] = ' ';
}
static uint8_t *touchjoy_axisRosetteChars(void) {
static uint8_t rosetteChars[ROSETTE_ROWS * ROSETTE_COLS] = {
' ', MOUSETEXT_UP, ' ',
MOUSETEXT_LEFT, ICONTEXT_MENU_TOUCHJOY, MOUSETEXT_RIGHT,
' ', MOUSETEXT_DOWN, ' ',
};
return rosetteChars;
}
static uint8_t *touchjoy_buttRosetteChars(void) {
return NULL;
}
static uint8_t touchjoy_buttActiveChar(void) {
return joys.lastButtonDisplayChar;
}
// ----------------------------------------------------------------------------
static void _init_gltouchjoy_joy(void) {
@ -363,12 +315,13 @@ static void _init_gltouchjoy_joy(void) {
joyVariant.axisMove = &touchjoy_axisMove;
joyVariant.axisUp = &touchjoy_axisUp;
joyVariant.rosetteChars = &touchjoy_rosetteChars;
joys.tapDelayMutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
joys.tapDelayCond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
joyVariant.axisRosetteChars = &touchjoy_axisRosetteChars;
joyVariant.buttRosetteChars = &touchjoy_buttRosetteChars;
joyVariant.buttActiveChar = &touchjoy_buttActiveChar;
gltouchjoy_registerVariant(TOUCH_DEVICE_JOYSTICK, &joyVariant);
video_registerFrameCallback(&joys.frameCallback);
}
static __attribute__((constructor)) void __init_gltouchjoy_joy(void) {

File diff suppressed because it is too large Load Diff

View File

@ -104,13 +104,6 @@ GLUE_C_READ(iie_read_peripheral_card)
GLUE_C_READ(read_keyboard)
{
uint8_t b = apple_ii_64k[0][0xC000];
#if INTERFACE_TOUCH
// touch interface is expected to rate limit this callback by unregistering/NULLifying
void (*readCallback)(void) = keydriver_keyboardReadCallback;
if (readCallback) {
readCallback();
}
#endif
return b;
}