ANDROID: A tentative handling of handling joystick control

Basically as a virtual mouse
This commit is contained in:
antoniou 2020-10-25 02:29:39 +03:00
parent 9045bceac9
commit 64eb1c0afe
6 changed files with 259 additions and 13 deletions

View File

@ -499,6 +499,24 @@ bool OSystem_Android::getFeatureState(Feature f) {
}
}
// TODO Re-eval if we need this here
Common::HardwareInputSet *OSystem_Android::getHardwareInputSet() {
using namespace Common;
CompositeHardwareInputSet *inputSet = new CompositeHardwareInputSet();
inputSet->addHardwareInputSet(new MouseHardwareInputSet(defaultMouseButtons));
inputSet->addHardwareInputSet(new KeyboardHardwareInputSet(defaultKeys, defaultModifiers));
inputSet->addHardwareInputSet(new JoystickHardwareInputSet(defaultJoystickButtons, defaultJoystickAxes));
return inputSet;
}
// TODO Re-eval if we need this here
Common::KeymapArray OSystem_Android::getGlobalKeymaps() {
Common::KeymapArray globalMaps = BaseBackend::getGlobalKeymaps();
return globalMaps;
}
Common::KeymapperDefaultBindings *OSystem_Android::getKeymapperDefaultBindings() {
Common::KeymapperDefaultBindings *keymapperDefaultBindings = new Common::KeymapperDefaultBindings();

View File

@ -115,6 +115,8 @@ private:
public:
virtual bool pollEvent(Common::Event &event) override;
virtual Common::HardwareInputSet *getHardwareInputSet() override;
virtual Common::KeymapArray getGlobalKeymaps() override;
virtual Common::KeymapperDefaultBindings *getKeymapperDefaultBindings() override;
virtual uint32 getMillis(bool skipRecord = false) override;

View File

@ -902,22 +902,65 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
break;
case JE_JOYSTICK:
e.mouse = dynamic_cast<AndroidGraphicsManager *>(_graphicsManager)->getMousePosition();
switch (arg1) {
// AMOTION_EVENT_ACTION_MOVE is 2 in NDK (https://developer.android.com/ndk/reference/group/input)
case AMOTION_EVENT_ACTION_MOVE:
e.mouse = dynamic_cast<AndroidGraphicsManager *>(_graphicsManager)->getMousePosition();
e.type = Common::EVENT_MOUSEMOVE;
// already multiplied by 100
e.mouse.x += arg2 * _joystick_scale / _eventScaleX;
e.mouse.y += arg3 * _joystick_scale / _eventScaleY;
break;
case AKEY_EVENT_ACTION_DOWN:
e.type = Common::EVENT_KEYDOWN;
break;
case AKEY_EVENT_ACTION_UP:
e.type = Common::EVENT_KEYUP;
break;
default:
LOGE("unhandled jaction on joystick: %d", arg1);
return;
}
if (arg1 != AMOTION_EVENT_ACTION_MOVE) {
switch (arg2) {
case AKEYCODE_BUTTON_1:
case AKEYCODE_BUTTON_2:
switch (arg1) {
case AKEY_EVENT_ACTION_DOWN:
e.type = (arg2 == AKEYCODE_BUTTON_1?
Common::EVENT_LBUTTONDOWN :
Common::EVENT_RBUTTONDOWN);
break;
case AKEY_EVENT_ACTION_UP:
e.type = (arg2 == AKEYCODE_BUTTON_1?
Common::EVENT_LBUTTONUP :
Common::EVENT_RBUTTONUP);
break;
}
e.mouse = dynamic_cast<AndroidGraphicsManager *>(_graphicsManager)->getMousePosition();
break;
case AKEYCODE_BUTTON_3:
e.kbd.keycode = Common::KEYCODE_ESCAPE;
e.kbd.ascii = Common::ASCII_ESCAPE;
break;
case AKEYCODE_BUTTON_4:
e.type = Common::EVENT_MAINMENU;
break;
default:
LOGW("unmapped gamepad key: %d", arg2);
return;
}
}
pushEvent(e);
return;

View File

@ -404,6 +404,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
public void onText(CharSequence p1) {}
// TODO - "Swipe" behavior does not seem to work currently. Should we support it?
public void swipeLeft() {
//Log.d(ScummVM.LOG_TAG, "SHOW KEYBOARD - 001 - swipeLeft");
}
@ -451,7 +452,7 @@ public class ScummVMActivity extends Activity implements OnKeyboardVisibilityLis
_screenKeyboard = builtinKeyboard;
// TODO better to have specific dimensions in dp and not adjusted to parent
// it may resolve the issue of resizing the keyboard wrongly (smaller) when returning to the suspended Activity in low resolution
FrameLayout.LayoutParams sKeyboardLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL | Gravity.FILL_HORIZONTAL);
FrameLayout.LayoutParams sKeyboardLayout = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);
_videoLayout.addView(_screenKeyboard, sKeyboardLayout);
_videoLayout.bringChildToFront(_screenKeyboard);

View File

@ -4,7 +4,6 @@ import android.os.Handler;
import android.os.Message;
import android.content.Context;
//import android.util.Log;
import android.util.Log;
import android.view.KeyEvent;
import android.view.KeyCharacterMap;
import android.view.MotionEvent;
@ -328,6 +327,13 @@ public class ScummVMEventsBase implements
case KeyEvent.KEYCODE_BUTTON_MODE:
type = JE_GAMEPAD;
break;
case KeyEvent.KEYCODE_BUTTON_1:
case KeyEvent.KEYCODE_BUTTON_2:
case KeyEvent.KEYCODE_BUTTON_3:
case KeyEvent.KEYCODE_BUTTON_4:
// These are oddly detected with SOURCE_KEYBOARD for joystick so don't bother checking the e.getSource()
type = JE_JOYSTICK;
break;
default:
if (e.isSystem()) {
type = JE_SYS_KEY;
@ -337,7 +343,7 @@ public class ScummVMEventsBase implements
break;
}
// _scummvm.displayMessageOnOSD("GetKey: " + keyCode + " unic=" + eventUnicodeChar+ " arg3= " + (eventUnicodeChar& KeyCharacterMap.COMBINING_ACCENT_MASK));
//_scummvm.displayMessageOnOSD("GetKey: " + keyCode + " unic=" + eventUnicodeChar+ " arg3= " + (eventUnicodeChar& KeyCharacterMap.COMBINING_ACCENT_MASK));
// look in events.cpp for how this is handled
_scummvm.pushEvent(type,

View File

@ -1,26 +1,202 @@
package org.scummvm.scummvm;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.InputDevice;
import androidx.annotation.NonNull;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
// A class that extends the basic ScummVMEventsBase, supporting Android APIs > HONEYCOMB_MR1 (API 12)
public class ScummVMEventsModern extends ScummVMEventsBase {
private static final int MSG_REPEAT = 3;
private static final int REPEAT_INTERVAL = 20; // ~50 keys per second
private static final int REPEAT_START_DELAY = 40;
public ScummVMEventsModern(Context context, ScummVM scummvm, MouseHelper mouseHelper) {
super(context, scummvm, mouseHelper);
}
@Override
public boolean onGenericMotionEvent(MotionEvent e) {
if ((e.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0) {
_scummvm.pushEvent(JE_JOYSTICK, e.getAction(),
(int)(e.getAxisValue(MotionEvent.AXIS_X)*100),
(int)(e.getAxisValue(MotionEvent.AXIS_Y)*100),
0, 0, 0);
return true;
// Custom handler code (to avoid mem leaks, see warning "This Handler Class Should Be Static Or Leaks Might Occur”) based on:
// https://stackoverflow.com/a/27826094
public static class ScummVMEventsModernHandler extends Handler {
private final WeakReference<ScummVMEventsModern> mListenerReference;
public ScummVMEventsModernHandler(ScummVMEventsModern listener) {
mListenerReference = new WeakReference<>(listener);
}
return false;
@Override
public synchronized void handleMessage(@NonNull Message msg) {
ScummVMEventsModern listener = mListenerReference.get();
if(listener != null) {
switch (msg.what) {
case MSG_REPEAT:
if (listener.repeatMove()) {
Message repeat = Message.obtain(this, MSG_REPEAT);
sendMessageDelayed(repeat, REPEAT_INTERVAL);
}
break;
}
}
}
public void clear() {
this.removeCallbacksAndMessages(null);
}
}
@Override
public void clearEventHandler() {
super.clearEventHandler();
mHandler.clear();
}
private ScummVMEventsModernHandler mHandler = new ScummVMEventsModernHandler(this);
private float repeatingX = 0.0f;
private float repeatingY = 0.0f;
private static float getCenteredAxis(MotionEvent event, InputDevice device, int axis, int historyPos) {
final InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource());
final int actionPointerIndex = event.getActionIndex();
// A joystick at rest does not always report an absolute position of
// (0,0). Use the getFlat() method to determine the range of values
// bounding the joystick axis center.
if (range != null) {
final float flat = range.getFlat();
// if (axis == MotionEvent.AXIS_X
// || axis == MotionEvent.AXIS_HAT_X
// || axis == MotionEvent.AXIS_Z) {
// Log.d(ScummVM.LOG_TAG, "Flat X= " + flat);
// } else {
// Log.d(ScummVM.LOG_TAG, "Flat Y= " + flat);
// }
float axisVal = (historyPos < 0) ? event.getAxisValue( range.getAxis(), actionPointerIndex) : event.getHistoricalAxisValue( range.getAxis(), actionPointerIndex, historyPos);
// Normalize
final float value = (axisVal - range.getMin() ) / range.getRange() * 2.0f - 1.0f;
// Ignore axis values that are within the 'flat' region of the
// joystick axis center.
if (Math.abs(value) > flat) {
return value;
}
}
return 0;
}
private void removeMessages() {
if (mHandler != null) {
mHandler.removeMessages(MSG_REPEAT);
}
}
private boolean repeatMove() {
_scummvm.pushEvent(JE_JOYSTICK, MotionEvent.ACTION_MOVE,
(int) (repeatingX * 100),
(int) (repeatingY * 100),
0, 0, 0);
return true;
}
private void processJoystickInput(MotionEvent event, int historyPos) {
InputDevice inputDevice = event.getDevice();
// Calculate the horizontal distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat axis, or the right control stick.
float x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_X, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - LEFT: x= " +x);
if (x == 0) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_X, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - HAT: x= " +x);
}
if (x == 0) {
x = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Z, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - RIGHT: x= " +x);
}
// Calculate the vertical distance to move by
// using the input value from one of these physical controls:
// the left control stick, hat switch, or the right control stick.
float y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_Y, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - LEFT: y= " +y);
if (y == 0) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_HAT_Y, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - HAT: y= " +y);
}
if (y == 0) {
y = getCenteredAxis(event, inputDevice, MotionEvent.AXIS_RZ, historyPos);
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - RIGHT: y= " +y);
}
// extra filter to stop repetition in order to avoid cases when android does not send onGenericMotionEvent()
// for small x or y (while abs is still larger than range.getflat())
// In such case we would end up with a slow moving "mouse" cursor - so we need this extra filter
if (Math.abs(x * 100) < 20.0f && Math.abs(y * 100) < 20.0f) {
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - pushEvent(): STOPPED: " + (int)(x * 100) + " y= " + (int)(y * 100));
removeMessages();
// do the move anyway, just don't repeat
repeatMove();
repeatingX = 0.0f;
repeatingY = 0.0f;
} else {
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - pushEvent(): x= " + (int)(x * 100) + " y= " + (int)(y * 100));
if (repeatingX != 0.0f || repeatingY != 0.0f) {
// already repeating - just update the movement co-ords
repeatingX = x;
repeatingY = y;
} else {
// start repeating
//removeMessages();
repeatingX = x;
repeatingY = y;
Message msg = mHandler.obtainMessage(MSG_REPEAT);
mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY);
repeatMove();
}
}
}
@Override
public boolean onGenericMotionEvent(MotionEvent event) {
// Check that the event came from a joystick
if (((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK
|| (event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0)) {
int action = event.getActionMasked();
if (action == MotionEvent.ACTION_MOVE) {
// Process all historical movement samples in the batch
final int historySize = event.getHistorySize();
// Process the movements starting from the
// earliest historical position in the batch
for (int i = 0; i < historySize; i++) {
// Process the event at historical position i
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - onGenericMotionEvent(m) hist: ");
processJoystickInput(event, i);
}
// Process the current movement sample in the batch (position -1)
//Log.d(ScummVM.LOG_TAG, "JOYSTICK - onGenericMotionEvent(m): " );
processJoystickInput(event, -1);
return true;
}
}
// this basically returns false since the super just returns false
return super.onGenericMotionEvent(event);
}
}