mirror of
https://github.com/jamessanford/kegs.git
synced 2025-01-09 20:30:59 +00:00
First pass at Joystick. Add a settings menu.
This commit is contained in:
parent
f0648382c8
commit
ad994b8232
@ -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">
|
||||
|
||||
|
BIN
res/drawable-xhdpi/settings.png
Normal file
BIN
res/drawable-xhdpi/settings.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 956 B |
@ -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
11
res/menu/options.xml
Normal 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>
|
@ -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>
|
||||
|
41
src/com/froop/app/kegs/Event.java
Normal file
41
src/com/froop/app/kegs/Event.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
30
src/com/froop/app/kegs/SpeedFragment.java
Normal file
30
src/com/froop/app/kegs/SpeedFragment.java
Normal 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();
|
||||
}
|
||||
}
|
173
src/com/froop/app/kegs/TouchJoystick.java
Normal file
173
src/com/froop/app/kegs/TouchJoystick.java
Normal 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));
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user