First pass at Joystick. Add a settings menu.

This commit is contained in:
James Sanford 2012-08-21 03:41:05 -07:00
parent f0648382c8
commit ad994b8232
12 changed files with 389 additions and 64 deletions

View File

@ -8,6 +8,8 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false"/>
<application android:icon="@drawable/icon" android:label="@string/app_name" android:description="@string/app_description" android:hardwareAccelerated="true">
<activity android:name="KegsMain" android:launchMode="singleInstance" android:configChanges="orientation|keyboardHidden|screenSize" android:windowSoftInputMode="stateAlwaysVisible|adjustPan">

Binary file not shown.

After

Width:  |  Height:  |  Size: 956 B

View File

@ -11,9 +11,12 @@
android:layout_centerHorizontal="true"
/>
<Button android:id="@+id/key_settings" android:drawableStart="@drawable/settings" android:drawablePadding="0px" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|bottom" android:layout_below="@id/kegsview" android:onClick="showPopup" />
<LinearLayout android:id="@+id/b1" android:layout_below="@id/kegsview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:gravity="center">
<Button android:id="@+id/key_escape" android:text="Esc" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left|bottom" android:layout_below="@id/kegsview" />

11
res/menu/options.xml Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/input_mouse"
android:title="@string/input_joystick" />
<item android:id="@+id/input_keyboard"
android:title="@string/input_keyboard" />
<item android:id="@+id/input_controls"
android:title="@string/input_controls_hide" />
<item android:id="@+id/emulation_speed"
android:title="@string/emulation_speed" />
</menu>

View File

@ -12,4 +12,20 @@
<item>I have a ROM 01</item>
<item>I have a ROM 03</item>
</string-array>
<string name="speed_title">Emulation Speed</string>
<string-array name="speed_choices">
<item>1.0 megahertz</item>
<item>2.8 megahertz</item>
<item>8.0 megahertz</item>
<item>Unlimited</item>
</string-array>
<!-- R.menu.options -->
<string name="input_joystick">Use Joystick</string>
<string name="input_mouse">Use Mouse</string>
<string name="input_keyboard">Keyboard</string>
<string name="input_controls_hide">Hide Controls</string>
<string name="input_controls_show">Show Controls</string>
<string name="emulation_speed">Emulation Speed</string>
</resources>

View File

@ -0,0 +1,41 @@
package com.froop.app.kegs;
// Event objects are stuffed into a ConcurrentLinkedQueue
// and consumed by JNI android_driver.c:check_input_events()
class Event {
static class KegsEvent {}
static class KeyKegsEvent extends KegsEvent {
public KeyKegsEvent(int key_id, boolean up) {
this.key_id = key_id;
this.up = up;
}
public int key_id;
public boolean up;
}
static class MouseKegsEvent extends KegsEvent {
public MouseKegsEvent(int x, int y, int buttons, int buttons_valid) {
this.x = x;
this.y = y;
this.buttons = buttons;
this.buttons_valid = buttons_valid;
}
public int x; // change in X since last event
public int y; // change in Y since last event
public int buttons;
public int buttons_valid;
}
static class JoystickKegsEvent extends KegsEvent {
public JoystickKegsEvent(int x, int y, int buttons) {
this.x = x;
this.y = y;
this.buttons = buttons;
}
public int x; // absolute X, -32767 to 32767
public int y; // absolute Y, -32767 to 32767
public int buttons;
}
}

View File

@ -57,9 +57,9 @@ class KegsKeyboard {
key_id -= 0x20;
final KeyTable.A2Key a2key = KeyTable.ascii.get(key_id);
if (a2key.use_shift) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_SHIFT, false));
mEventQueue.add(new Event.KeyKegsEvent(KEY_SHIFT, false));
keyDownUp(a2key.keycode);
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_SHIFT, true));
mEventQueue.add(new Event.KeyKegsEvent(KEY_SHIFT, true));
} else {
keyDownUp(a2key.keycode);
}
@ -67,6 +67,11 @@ class KegsKeyboard {
}
public boolean keyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_MULTIPLE && event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN) {
// TODO: support event.getCharacters(), but we may need to use InputConnection instead of key events.
// Log.w("kegs", "key CHARACTERS " + event.getCharacters());
// return true;
}
if (event.getAction() == KeyEvent.ACTION_DOWN) {
int keyCode = event.getKeyCode();
if (keyCode == KeyEvent.KEYCODE_ENTER) {
@ -94,13 +99,13 @@ class KegsKeyboard {
private void resetStickyKeys() {
if (mSticky != 0) {
if ((mSticky & STICKY_CONTROL) != 0) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_CONTROL, true));
mEventQueue.add(new Event.KeyKegsEvent(KEY_CONTROL, true));
}
if ((mSticky & STICKY_OPEN_APPLE) != 0) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_OPEN_APPLE, true));
mEventQueue.add(new Event.KeyKegsEvent(KEY_OPEN_APPLE, true));
}
if ((mSticky & STICKY_CLOSED_APPLE) != 0) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_CLOSED_APPLE, true));
mEventQueue.add(new Event.KeyKegsEvent(KEY_CLOSED_APPLE, true));
}
mSticky = 0;
if (mNotify != null) {
@ -112,13 +117,13 @@ class KegsKeyboard {
public void keyDownSticky(int key_id, boolean key_up) {
int mask = 0;
if (key_id == KEY_CONTROL) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_CONTROL, key_up));
mEventQueue.add(new Event.KeyKegsEvent(KEY_CONTROL, key_up));
mask = STICKY_CONTROL;
} else if (key_id == KEY_OPEN_APPLE) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_OPEN_APPLE, key_up));
mEventQueue.add(new Event.KeyKegsEvent(KEY_OPEN_APPLE, key_up));
mask = STICKY_OPEN_APPLE;
} else if (key_id == KEY_CLOSED_APPLE) {
mEventQueue.add(new KegsView.KeyKegsEvent(KEY_CLOSED_APPLE, key_up));
mEventQueue.add(new Event.KeyKegsEvent(KEY_CLOSED_APPLE, key_up));
mask = STICKY_CLOSED_APPLE;
}
if (key_up) {
@ -129,8 +134,8 @@ class KegsKeyboard {
}
public void keyDownUp(int key_id) {
mEventQueue.add(new KegsView.KeyKegsEvent(key_id, false)); // key down
mEventQueue.add(new KegsView.KeyKegsEvent(key_id, true)); // key up
mEventQueue.add(new Event.KeyKegsEvent(key_id, false)); // key down
mEventQueue.add(new Event.KeyKegsEvent(key_id, true)); // key up
resetStickyKeys();
}
}

View File

@ -5,31 +5,41 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.AsyncTask;
import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.PopupMenu;
import android.widget.ToggleButton;
public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
private static final String FRAGMENT_ROM = "rom";
private static final String FRAGMENT_DOWNLOAD = "download";
private static final String FRAGMENT_ERROR = "error";
private static final String FRAGMENT_SPEED = "speed";
private KegsView mKegsView;
protected KegsView mKegsView;
private KegsTouch mKegsTouch;
private KegsKeyboard mKegsKeyboard;
private TouchJoystick mJoystick;
private PopupMenu mSettingsMenu;
private boolean mModeMouse = true;
private View.OnClickListener mButtonClick = new View.OnClickListener() {
public void onClick(View v) {
// Log.e("kegs", "button clicked");
int click_id = v.getId();
final int click_id = v.getId();
int key_id = -1;
boolean sticky = false;
if (click_id == R.id.key_escape) {
@ -70,6 +80,52 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
}
};
private PopupMenu.OnMenuItemClickListener mSettingsClick = new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
final int item_id = item.getItemId();
if (item_id == R.id.input_mouse) {
mModeMouse = !mModeMouse;
return true;
} else if (item_id == R.id.input_keyboard) {
// There doesn't seem to be a reliable way to determine the current state, so we have to just toggle it.
InputMethodManager imm = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.toggleSoftInput(0, 0);
}
return true;
} else if (item_id == R.id.input_controls) {
final int vis = areControlsVisible() ? View.GONE : View.VISIBLE;
findViewById(R.id.b1).setVisibility(vis);
findViewById(R.id.b2).setVisibility(vis);
return true;
} else if (item_id == R.id.emulation_speed) {
new SpeedFragment().show(getFragmentManager(), FRAGMENT_SPEED);
return true;
}
return false;
}
};
private boolean areControlsVisible() {
return findViewById(R.id.b1).getVisibility() == View.VISIBLE;
}
// Adjust items to say "Use Joystick" vs "Use Mouse", etc.
private void updateSettingsMenu() {
final Menu m = mSettingsMenu.getMenu();
MenuItem item;
item = m.findItem(R.id.input_mouse);
item.setTitle(mModeMouse ? R.string.input_joystick : R.string.input_mouse);
item = m.findItem(R.id.input_controls);
item.setTitle(areControlsVisible() ? R.string.input_controls_hide : R.string.input_controls_show);
}
public void showPopup(View v) {
updateSettingsMenu();
mSettingsMenu.show();
}
public void onStickyReset() {
((ToggleButton)findViewById(R.id.key_control)).setChecked(false);
((ToggleButton)findViewById(R.id.key_open_apple)).setChecked(false);
@ -134,13 +190,7 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
// TODO: verify that this is all OK, and even Return works immediately.
return executeKeyEvent(event) || super.dispatchKeyEvent(event);
// return super.dispatchKeyEvent(event) || executeKeyEvent(event);
}
public boolean executeKeyEvent(KeyEvent event) {
return mKegsKeyboard.keyEvent(event);
return mKegsKeyboard.keyEvent(event) || super.dispatchKeyEvent(event);
}
// @Override
@ -160,18 +210,29 @@ public class KegsMain extends Activity implements KegsKeyboard.StickyReset {
mKegsTouch = new KegsTouch(mKegsView.getEventQueue());
final GestureDetector inputDetect = new GestureDetector(this, mKegsTouch);
mJoystick = new TouchJoystick(mKegsView.getEventQueue());
final View mainView = findViewById(R.id.mainview);
mainView.setClickable(true);
mainView.setLongClickable(true);
mainView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent event) {
return inputDetect.onTouchEvent(event);
// TODO: consider using two listeners and setOnTouchListener them
if (mModeMouse) {
return inputDetect.onTouchEvent(event);
} else {
return mJoystick.onTouchEvent(event);
}
}
});
mKegsKeyboard = new KegsKeyboard(mKegsView.getEventQueue());
mKegsKeyboard.setOnStickyReset(this);
mSettingsMenu = new PopupMenu(this, findViewById(R.id.key_settings));
mSettingsMenu.inflate(R.menu.options);
mSettingsMenu.setOnMenuItemClickListener(mSettingsClick);
findViewById(R.id.key_escape).setOnClickListener(mButtonClick);
findViewById(R.id.key_return).setOnClickListener(mButtonClick);
findViewById(R.id.key_f4).setOnClickListener(mButtonClick);

View File

@ -25,12 +25,7 @@ class KegsTouch extends GestureDetector.SimpleOnGestureListener {
float distanceX, float distanceY) {
int changeX = (int)distanceX * -1;
int changeY = (int)distanceY * -1;
mEventQueue.add(
new KegsView.MouseKegsEvent(changeX, changeY, mButton1, 1));
// if (mButton1 == 1 && e2.getAction() == MotionEvent.ACTION_UP) {
// mButton1 = 0;
// mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
// }
mEventQueue.add(new Event.MouseKegsEvent(changeX, changeY, mButton1, 1));
return true;
}
@ -39,29 +34,30 @@ class KegsTouch extends GestureDetector.SimpleOnGestureListener {
// press mouse button down
// Log.e("kegs", "onlongpress");
mButton1 = 1;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
}
// TODO: replace this with onSingleTapUp and alter onDoubleTap
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// press mouse button down, then up
mButton1 = 1;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mButton1 = 0;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
return true;
}
@Override
public boolean onDoubleTap(MotionEvent e) {
mButton1 = 1;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mButton1 = 0;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mButton1 = 1;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
mButton1 = 0;
mEventQueue.add(new KegsView.MouseKegsEvent(0, 0, mButton1, 1));
mEventQueue.add(new Event.MouseKegsEvent(0, 0, mButton1, 1));
return true;
}
}

View File

@ -23,32 +23,8 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
private boolean mPaused = false;
private boolean mReady = false;
static class KegsEvent {}
protected ConcurrentLinkedQueue<Event.KegsEvent> mEventQueue = new ConcurrentLinkedQueue<Event.KegsEvent>();
static class KeyKegsEvent extends KegsEvent {
public KeyKegsEvent(int key_id, boolean up) {
this.key_id = key_id;
this.up = up;
}
public int key_id;
public boolean up;
}
static class MouseKegsEvent extends KegsEvent {
public MouseKegsEvent(int x, int y, int buttons, int buttons_valid) {
this.x = x;
this.y = y;
this.buttons = buttons;
this.buttons_valid = buttons_valid;
}
public int x;
public int y;
public int buttons;
public int buttons_valid;
}
protected ConcurrentLinkedQueue<KegsEvent> mEventQueue = new ConcurrentLinkedQueue<KegsEvent>();
class KegsThread extends Thread {
private Bitmap mBitmap;
private Canvas mCanvas;
@ -57,7 +33,6 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
private final ReentrantLock mSurfaceLock = new ReentrantLock();
private final ReentrantLock mPauseLock = new ReentrantLock();
private final Rect mRect = new Rect(0, 0, mA2Width, mA2Height);
// private final Rect mDestRect = new Rect(0, 0, 800, 600);
public KegsThread(SurfaceHolder surfaceHolder, Context context) {
mSurfaceHolder = surfaceHolder;
@ -68,15 +43,17 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
}
// Called by surfaceCreated also!
public void updateScreen() {
protected void updateScreen() {
mSurfaceLock.lock();
mCanvas = mSurfaceHolder.lockCanvas(); // Use mRect ?
try {
if(mCanvas != null) {
// Scaling tests: save/scale/restore, or drawBitmap into a destination rect.
// mCanvas.save();
mCanvas.drawARGB(255, 0, 0, 0);
// mCanvas.scale(1.8f, 1.8f);
mCanvas.drawBitmap(mBitmap, 0, 0, null);
// scaling tests:
// mCanvas.drawBitmap(mBitmap, mRect, mDestRect, null);
// mCanvas.restore();
// Doesn't work well, but consider eliminating the border instead, for phones.
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
mCanvas = null;
@ -94,7 +71,7 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
}
}
public native void mainLoop(Bitmap b, ConcurrentLinkedQueue q);
private native void mainLoop(Bitmap b, ConcurrentLinkedQueue q);
@Override
public void run() {
@ -102,7 +79,8 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
// For TESTING:
// while (true) {
// try {
// Thread.sleep(100);
// Thread.sleep(500);
// checkForPause();
// } catch (InterruptedException e) {}
// }
}
@ -151,6 +129,15 @@ class KegsView extends SurfaceView implements SurfaceHolder.Callback {
thread = new KegsThread(holder, context);
setFocusable(true);
setFocusableInTouchMode(true);
requestFocus();
}
public void setEmulationSpeed(int speed) {
// Speed matches g_limit_speed inside KEGS.
// Instead of a separate "control" event, key ids with bit 8 high are
// special events. See android_driver.c:x_key_special()
mEventQueue.add(new Event.KeyKegsEvent(speed + 0x80, true));
}
public KegsThread getThread() {

View File

@ -0,0 +1,30 @@
package com.froop.app.kegs;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import android.util.Log;
public class SpeedFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// TODO: Show the current speed and use setSingleChoiceItems
builder.setTitle(R.string.speed_title);
builder.setItems(R.array.speed_choices,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int item) {
dismiss();
// Adjust item number to match g_limit_speed.
item++;
if (item > 3) {
item = 0;
}
((KegsMain)getActivity()).mKegsView.setEmulationSpeed(item);
}
});
return builder.create();
}
}

View File

@ -0,0 +1,173 @@
package com.froop.app.kegs;
import android.util.Log;
import android.view.MotionEvent;
import java.util.concurrent.ConcurrentLinkedQueue;
// Change touchscreen events into an absolute joystick position and
// a joystick button. Handles up to two fingers down. The first one that moves
// becomes a joystick, the second acts as a button. You can also fire a button
// by tapping a single finger.
// Quick and dirty.
class TouchJoystick {
private ConcurrentLinkedQueue mEventQueue;
private int mTouchSlop;
public int mMotionPointer = -1; // active pointer Id
private int mButton1 = 0; // buttons pressed? lower two bits.
private MotionEvent trackA; // original A down event
private MotionEvent trackB; // original B down event
private int trackAIndex = -1; // index into trackA that pointerA is in
private int trackBIndex = -1; // index into trackB that pointerB is in
private int pointerA = -1; // pointer Id in 'A' slot
private int pointerB = -1; // pointer Id in 'B' slot
public TouchJoystick(ConcurrentLinkedQueue q) {
mEventQueue = q;
mTouchSlop = android.view.ViewConfiguration.getTouchSlop();
// TODO: Avoid deprecated interface, read docs on replacement:
// final ViewConfiguration configuration = ViewConfiguration.get(context);
// mTouchSlop = configuration.getScaledTouchSlop();
}
private void reset_tracks(boolean resetA, boolean resetB) {
if (resetA && trackA != null) {
trackA.recycle();
trackA = null;
trackAIndex = -1;
pointerA = -1;
}
if (resetB && trackB != null) {
trackB.recycle();
trackB = null;
trackBIndex = -1;
pointerB = -1;
}
}
private boolean isPastSlop(MotionEvent e1, MotionEvent e2,
int e1index, int e2index) {
// Log.w("kegs", "SLOP " + " " + e1 + " " + e2);
if (Math.abs(e2.getX(e2index) - e1.getX(e1index)) >= mTouchSlop) {
return true;
} else if (Math.abs(e2.getY(e2index) - e1.getY(e1index)) >= mTouchSlop) {
return true;
} else {
return false;
}
}
public boolean onTouchEvent(MotionEvent e) {
final int action = e.getActionMasked();
final int pointerIndex = e.getActionIndex();
final int pointerId = e.getPointerId(pointerIndex);
// Log.w("kegs", "touch " + action + " " + pointerId + " ##" + pointerA + "## " + trackA + " ##" + pointerB + "## " + trackB);
if (action == MotionEvent.ACTION_DOWN ||
action == MotionEvent.ACTION_POINTER_DOWN) {
if (trackA != null && trackB != null) {
// We are already tracking two fingers.
return false;
}
if (action == MotionEvent.ACTION_POINTER_DOWN ||
e.getPointerCount() > 1) {
// we have two fingers down now, so might as well press the button.
mButton1 = 1;
mEventQueue.add(new Event.JoystickKegsEvent(0xFFFF, 0xFFFF, mButton1));
}
// start tracking it so that we can test it for SLOP
if (trackA == null) {
trackA = MotionEvent.obtain(e);
trackAIndex = pointerIndex;
pointerA = pointerId;
} else {
if (trackB != null) {
trackB.recycle();
}
trackB = MotionEvent.obtain(e);
trackBIndex = pointerIndex;
pointerB = pointerId;
}
} else if (action == MotionEvent.ACTION_MOVE) {
if (mMotionPointer == -1) {
// No primary pointer is set, see if one has moved enough to be primary.
if (pointerId == pointerA && isPastSlop(trackA, e, trackAIndex, pointerIndex)) {
mMotionPointer = pointerId;
if (trackB == null) {
// In case the secondary pointer is now primary.
mButton1 = 0;
mEventQueue.add(new Event.JoystickKegsEvent(0xFFFF, 0xFFFF, mButton1));
}
} else if (pointerId == pointerB && isPastSlop(trackB, e, trackBIndex, pointerIndex)) {
mMotionPointer = pointerId;
if (trackA == null) {
// In case the secondary pointer is now primary.
mButton1 = 0;
mEventQueue.add(new Event.JoystickKegsEvent(0xFFFF, 0xFFFF, mButton1));
}
}
}
if (pointerId == mMotionPointer) {
// SEND JOYSTICK MOVEMENT based on track{A,B}
if (pointerId == pointerA) {
send_position(trackA, e, trackAIndex, pointerIndex);
} else if (pointerId == pointerB) {
send_position(trackB, e, trackBIndex, pointerIndex);
} else {
// probably an additional finger was down that we had latched onto, ignore it.
// Log.e("kegs", "movement for pointerId " + pointerId + " is unknown!");
}
}
} else if (action == MotionEvent.ACTION_POINTER_UP
|| action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_CANCEL) {
if (pointerId == mMotionPointer) {
// recenter joystick
// 0, 0 would be center, but it's "too perfect"
mEventQueue.add(new Event.JoystickKegsEvent(61, -33, mButton1));
mMotionPointer = -1;
} else {
if (mMotionPointer == -1) {
// No active movement, assume this click/release should be a button press/release.
// TODO it probably shouldn't be sent if they had their finger down for more than 500ms or so.
mButton1 = 1;
mEventQueue.add(new Event.JoystickKegsEvent(0xFFFF, 0xFFFF, mButton1));
}
// SEND JOYSTICK BUTTON UP
mButton1 = 0;
mEventQueue.add(new Event.JoystickKegsEvent(0xFFFF, 0xFFFF, mButton1));
}
reset_tracks(pointerId == pointerA, pointerId == pointerB);
}
return true;
}
private void send_position(MotionEvent e1, MotionEvent e2,
int e1index, int e2index) {
// Send absolute position, -32767 to 32767
// TODO Still trying to find a good translation from distance to axis value.
// Consider looking at DPI and at pressure changes.
// (Would like to capture just 'rolling the thumb')
// Examples of things that didn't work so well...
// (log8 - 1.3) * 42000 ; (log10 - 1.0) * 45000 ; (log5 - 1.7) * 50000
// final float x1 = e2.getX(e2index) - e1.getX(e1index);
// final int x2 = (int)(((Math.log(Math.abs(x1))/logVal) - 1.7) * 50000);
// final int x3 = Math.max(0, Math.min(32767, x2)) * (x1 < 0 ? -1 : 1);
// Currently using linear over 18 pixels. (32768/18)
final float x1 = e2.getX(e2index) - e1.getX(e1index);
final int x3 = (int)(x1 * 1820);
final float y1 = e2.getY(e2index) - e1.getY(e1index);
final int y3 = (int)(y1 * 1820);
// Log.w("kegs", "joystick " + x3 + " " + y3 + " " + mButton1);
mEventQueue.add(new Event.JoystickKegsEvent(x3, y3, mButton1));
}
}