diff --git a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java
index 729b540c..ddad97a1 100644
--- a/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java
+++ b/Android/app/src/main/java/org/deadc0de/apple2ix/Apple2KeyboardSettingsMenu.java
@@ -351,6 +351,41 @@ public class Apple2KeyboardSettingsMenu extends Apple2AbstractMenu {
return convertView;
}
},
+ KEYBOARD_ENABLE_DUO_TOUCH {
+ @Override
+ public final String getTitle(Apple2Activity activity) {
+ return activity.getResources().getString(R.string.keyboard_duotouch_enabled);
+ }
+
+ @Override
+ public final String getSummary(Apple2Activity activity) {
+ return activity.getResources().getString(R.string.keyboard_duotouch_enabled_summary);
+ }
+
+ @Override
+ public String getPrefKey() {
+ return "duoTouchEnabled";
+ }
+
+ @Override
+ public Object getPrefDefault() {
+ return false;
+ }
+
+ @Override
+ public View getView(final Apple2Activity activity, View convertView) {
+ convertView = _basicView(activity, this, convertView);
+ CheckBox cb = _addCheckbox(activity, this, convertView, (boolean) Apple2Preferences.getJSONPref(this));
+ final IMenuEnum self = this;
+ cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ Apple2Preferences.setJSONPref(self, isChecked);
+ }
+ });
+ return convertView;
+ }
+ },
KEYBOARD_ENABLE_LOWERCASE {
@Override
public final String getTitle(Apple2Activity activity) {
diff --git a/Android/app/src/main/res/values-de/strings.xml b/Android/app/src/main/res/values-de/strings.xml
index 590e7e30..85023dc4 100644
--- a/Android/app/src/main/res/values-de/strings.xml
+++ b/Android/app/src/main/res/values-de/strings.xml
@@ -201,5 +201,7 @@
Renders pixel vertical divisions
Release notes
View notes for this release
+ Enable dual-touch
+ Support two-thumb input
diff --git a/Android/app/src/main/res/values-es/strings.xml b/Android/app/src/main/res/values-es/strings.xml
index 4faf59f9..bf475eed 100644
--- a/Android/app/src/main/res/values-es/strings.xml
+++ b/Android/app/src/main/res/values-es/strings.xml
@@ -201,5 +201,7 @@
Renders pixel vertical divisions
Release notes
View notes for this release
+ Enable dual-touch
+ Support two-thumb input
diff --git a/Android/app/src/main/res/values-fr/strings.xml b/Android/app/src/main/res/values-fr/strings.xml
index e9d952a7..96b92568 100644
--- a/Android/app/src/main/res/values-fr/strings.xml
+++ b/Android/app/src/main/res/values-fr/strings.xml
@@ -201,5 +201,7 @@
Renders pixel vertical divisions
Release notes
View notes for this release
+ Enable dual-touch
+ Support two-thumb input
diff --git a/Android/app/src/main/res/values/strings.xml b/Android/app/src/main/res/values/strings.xml
index 45228940..52d4fd0e 100644
--- a/Android/app/src/main/res/values/strings.xml
+++ b/Android/app/src/main/res/values/strings.xml
@@ -201,5 +201,7 @@
Renders pixel vertical divisions
Release notes
View notes for this release
+ Enable dual-touch
+ Support two-thumb input
diff --git a/src/prefs.h b/src/prefs.h
index 6770ea83..7987267a 100644
--- a/src/prefs.h
+++ b/src/prefs.h
@@ -72,6 +72,7 @@
#define PREF_KEYBOARD_CAPS "caps"
#define PREF_KEYBOARD_ALT_PATH "altPath"
#define PREF_KEYBOARD_VARIANT "variant"
+#define PREF_KEYBOARD_DUO_TOUCH "duoTouchEnabled"
#define PREF_LOWERCASE_ENABLED "lowercaseEnabled"
#define PREF_PORTRAIT_HEIGHT_SCALE "portraitHeightScale"
#define PREF_PORTRAIT_POSITION_SCALE "portraitPositionScale"
diff --git a/src/video/gltouchkbd.c b/src/video/gltouchkbd.c
index c17b7cbf..53252bd9 100644
--- a/src/video/gltouchkbd.c
+++ b/src/video/gltouchkbd.c
@@ -133,6 +133,7 @@ static struct {
int ctrlRow;
bool ctrlPressed;
+ bool duoTouch;
unsigned int glyphMultiplier;
float portraitHeightScale;
@@ -364,7 +365,6 @@ static inline int64_t _tap_key_at_point(float x, float y) {
break;
case ICONTEXT_NONACTIONABLE:
- scancode = 0;
handled = false;
break;
@@ -455,15 +455,17 @@ static inline int64_t _tap_key_at_point(float x, float y) {
assert(key < 0x80);
scancode = c_keys_ascii_to_scancode(key);
if (kbd.ctrlPressed) {
- c_keys_handle_input(scancode, /*pressed*/true, /*ASCII:*/false);
+ c_keys_handle_input(scancode, /*pressed*/true, /*ASCII:*/false);
c_keys_handle_input(scancode, /*pressed*/false, /*ASCII:*/false);
} else {
c_keys_handle_input(key, /*pressed:*/true, /*ASCII:*/true);
+ c_keys_handle_input(key, /*pressed:*/false, /*ASCII:*/true);
}
if (key == ' ' && isCalibrating) {
key = ICONTEXT_SPACE_VISUAL;
}
} else if (isCTRL) {
+ assert(scancode == SCODE_L_CTRL);
c_keys_handle_input(scancode, /*pressed:*/kbd.ctrlPressed, /*ASCII:*/false);
} else if (scancode) {
// perform a press of other keys (ESC, Arrows, etc)
@@ -481,9 +483,6 @@ static inline int64_t _tap_key_at_point(float x, float y) {
flags |= TOUCH_FLAGS_HANDLED;
}
- key = key & 0xff;
- scancode = scancode & 0xff;
-
flags |= ( (int64_t)((key << 8) | scancode) << TOUCH_FLAGS_ASCII_AND_SCANCODE_SHIFT);
return flags;
}
@@ -670,13 +669,27 @@ static void gltouchkbd_reshape(int w, int h, bool landscape) {
static int64_t gltouchkbd_onTouchEvent(interface_touch_event_t action, int pointer_count, int pointer_idx, float *x_coords, float *y_coords) {
- if (!isAvailable) {
+ if (UNLIKELY(pointer_idx < 0)) {
+ TOUCH_KBD_LOG("!!!KBD : IGNORING TRACKING INDEX %d", pointer_idx);
+ return 0x0LL;
+ }
+
+ static int trackingIndex0 = TRACKING_NONE;
+ static int trackingIndex1 = TRACKING_NONE;
+
+ if (UNLIKELY(!isAvailable)) {
+ trackingIndex0 = TRACKING_NONE;
+ trackingIndex1 = TRACKING_NONE;
return 0x0LL;
}
if (UNLIKELY(kbd.prefsChanged)) {
- return 0x0;
+ trackingIndex0 = TRACKING_NONE;
+ trackingIndex1 = TRACKING_NONE;
+ return 0x0LL;
}
if (!ownsScreen) {
+ trackingIndex0 = TRACKING_NONE;
+ trackingIndex1 = TRACKING_NONE;
return 0x0LL;
}
@@ -687,41 +700,72 @@ static int64_t gltouchkbd_onTouchEvent(interface_touch_event_t action, int point
clock_gettime(CLOCK_MONOTONIC, &kbd.timingBegin);
- static int trackingIndex = TRACKING_NONE;
-
switch (action) {
case TOUCH_DOWN:
case TOUCH_POINTER_DOWN:
if (/*isOnKeyboardModel:*/true) {// TODO FIXME : nonactionable areas could defer to joystick ...
- trackingIndex = pointer_idx;
- flags |= TOUCH_FLAGS_HANDLED;
- }
- break;
- case TOUCH_MOVE:
- flags |= ((pointer_idx == trackingIndex) ? TOUCH_FLAGS_HANDLED : 0);
- break;
-
- case TOUCH_UP:
- case TOUCH_POINTER_UP:
- {
- if (trackingIndex == pointer_idx) {
- int64_t handledAndData = _tap_key_at_point(x, y);
- flags |= ((handledAndData & TOUCH_FLAGS_HANDLED) ? (TOUCH_FLAGS_HANDLED|TOUCH_FLAGS_KEY_TAP) : 0x0LL);
- flags |= (handledAndData & TOUCH_FLAGS_REQUEST_SYSTEM_KBD);
- flags |= (handledAndData & TOUCH_FLAGS_ASCII_AND_SCANCODE_MASK);
- trackingIndex = TRACKING_NONE;
+ if (trackingIndex0 == TRACKING_NONE) {
+ trackingIndex0 = pointer_idx;
+ flags |= TOUCH_FLAGS_HANDLED;
+ TOUCH_KBD_LOG("---KBD TOUCH DOWN 0");
+ } else if (kbd.duoTouch && trackingIndex1 == TRACKING_NONE) {
+ trackingIndex1 = pointer_idx;
+ flags |= TOUCH_FLAGS_HANDLED;
+ TOUCH_KBD_LOG("---KBD TOUCH DOWN 1");
+ } else {
+ TOUCH_KBD_LOG("!!!KBD : IGNORING OTHER TOUCH DOWN %d", pointer_idx);
}
}
break;
+ case TOUCH_MOVE:
+ if (pointer_idx == trackingIndex0) {
+ flags |= TOUCH_FLAGS_HANDLED;
+ } else if (kbd.duoTouch && pointer_idx == trackingIndex1) {
+ flags |= TOUCH_FLAGS_HANDLED;
+ } else {
+ // ...
+ }
+ break;
+
+ case TOUCH_UP:
+ case TOUCH_POINTER_UP:
+ // tap is performed on touch up ...
+ if (trackingIndex0 == pointer_idx || (kbd.duoTouch && trackingIndex1 == pointer_idx)) {
+ int64_t handledAndData = _tap_key_at_point(x, y);
+ flags |= ((handledAndData & TOUCH_FLAGS_HANDLED) ? (TOUCH_FLAGS_HANDLED|TOUCH_FLAGS_KEY_TAP) : 0x0LL);
+ flags |= (handledAndData & TOUCH_FLAGS_REQUEST_SYSTEM_KBD);
+ flags |= (handledAndData & TOUCH_FLAGS_ASCII_AND_SCANCODE_MASK);
+
+ if (trackingIndex0 == pointer_idx) {
+ TOUCH_KBD_LOG("---KBD TOUCH UP 0");
+ if (trackingIndex1 != TRACKING_NONE) {
+ // TODO FIXME ... verify this is needed for iOS ... it is apparently needed for Android, ugh
+ TOUCH_KBD_LOG("---KBD MIGRATING TRACKING 1 -> 0");
+ trackingIndex1 = TRACKING_NONE;
+ } else {
+ trackingIndex0 = TRACKING_NONE;
+ }
+ } else {
+ TOUCH_KBD_LOG("---KBD TOUCH UP 1");
+ trackingIndex1 = TRACKING_NONE;
+ }
+ } else {
+ TOUCH_KBD_LOG("!!!KBD : IGNORING OTHER TOUCH UP %d", pointer_idx);
+ // ...
+ }
+ break;
+
case TOUCH_CANCEL:
- trackingIndex = TRACKING_NONE;
+ trackingIndex0 = TRACKING_NONE;
+ trackingIndex1 = TRACKING_NONE;
LOG("---KBD TOUCH CANCEL");
return 0x0LL;
default:
- trackingIndex = TRACKING_NONE;
+ trackingIndex0 = TRACKING_NONE;
+ trackingIndex1 = TRACKING_NONE;
LOG("!!!KBD UNKNOWN TOUCH EVENT : %d", action);
return 0x0LL;
}
@@ -913,6 +957,8 @@ static void gltouchkbd_applyPrefs(void) {
= prefs_parseLongValue (PREF_DOMAIN_TOUCHSCREEN, PREF_SCREEN_OWNER, &lVal, /*base:*/10) ? (interface_device_t)lVal : TOUCH_DEVICE_KEYBOARD;
ownsScreen = (screenOwner == TOUCH_DEVICE_KEYBOARD || screenOwner == TOUCH_DEVICE_NONE);
+ kbd.duoTouch = prefs_parseBoolValue(PREF_DOMAIN_KEYBOARD, PREF_KEYBOARD_DUO_TOUCH, &bVal) ? bVal : false;
+
if (ownsScreen) {
minAlpha = minAlphaWhenOwnsScreen;
if (allowLowercase) {
@@ -931,7 +977,7 @@ static void gltouchkbd_applyPrefs(void) {
glhud_setupDefault(kbd.model);
}
- // reset CTRL state upon leaving this touch device
+ // reset CTRL pressed state upon leaving this touch device
kbd.ctrlPressed = false;
c_keys_handle_input(SCODE_L_CTRL, /*pressed:*/false, /*ASCII:*/false);
}