Merge pull request #7500 from zackhow/pointer

Android: Add IR pointer control to touch overlay
This commit is contained in:
JMC47 2019-01-24 20:02:23 -05:00 committed by GitHub
commit ddb1fbf701
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 707 additions and 49 deletions

View File

@ -1,5 +1,10 @@
# RMCE01, RMCJ01, RMCK01, RMCP01 - Mario Kart Wii
[Controls]
IRHeight = 50
IRWidth = 30
IRCenter = 50
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -1,5 +1,10 @@
# RMGE01, RMGJ01, RMGK01, RMGP01 - SUPER MARIO GALAXY
[Controls]
IRHeight = 50
IRWidth = 30
IRCenter = 50
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -1,4 +1,8 @@
# RODE01, RODJ01, RODK01, RODP01 - WarioWare: Smooth Moves
[Controls]
IRHeight = 71
IRWidth = 64
IRCenter = 99
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -1,4 +1,8 @@
# RUUE01, RUUJ01, RUUK01, RUUP01 - Animal Crossing Wii
[Controls]
IRHeight = 50
IRWidth = 30
IRCenter = 50
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -1,4 +1,8 @@
# RZDE01, RZDJ01, RZDK01, RZDP01 - The Legend of Zelda: Twilight Princess [Wii]
[Controls]
IRHeight = 39
IRWidth = 37
IRCenter = 91
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -1,4 +1,8 @@
# SB4E01, SB4J01, SB4P01 - Super Mario Galaxy 2
[Controls]
IRHeight = 50
IRWidth = 30
IRCenter = 50
[Core]
# Values set here will override the main Dolphin settings.

View File

@ -18,6 +18,9 @@ IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Height = 50
IR/Width = 30
IR/Center = 50
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
@ -154,6 +157,9 @@ IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Height = 50
IR/Width = 30
IR/Center = 50
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
@ -290,6 +296,9 @@ IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Height = 50
IR/Width = 30
IR/Center = 50
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`
@ -426,6 +435,9 @@ IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Height = 50
IR/Width = 30
IR/Center = 50
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`

View File

@ -18,6 +18,9 @@ IR/Right = `Axis 115`
IR/Forward = `Axis 116`
IR/Backward = `Axis 117`
IR/Hide = `Button 118`
IR/Height = 50
IR/Width = 30
IR/Center = 50
Swing/Up = `Axis 120`
Swing/Down = `Axis 121`
Swing/Left = `Axis 122`

View File

@ -397,6 +397,8 @@ public final class NativeLibrary
*/
public static native void RefreshWiimotes();
public static native void ReloadWiimoteConfig();
private static boolean alertResult = false;
public static boolean displayAlertMsg(final String caption, final String text,
@ -490,4 +492,19 @@ public final class NativeLibrary
sEmulationActivity.clear();
}
public static void updateTouchPointer()
{
final EmulationActivity emulationActivity = sEmulationActivity.get();
if (emulationActivity == null)
{
Log.warning("[NativeLibrary] EmulationActivity is null.");
}
else
{
emulationActivity.runOnUiThread(emulationActivity::initInputPointer);
}
}
public static native float GetGameAspectRatio();
}

View File

@ -2,6 +2,7 @@ package org.dolphinemu.dolphinemu.activities;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
@ -38,6 +39,8 @@ import org.dolphinemu.dolphinemu.fragments.MenuFragment;
import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment;
import org.dolphinemu.dolphinemu.model.GameFile;
import org.dolphinemu.dolphinemu.services.GameFileCacheService;
import org.dolphinemu.dolphinemu.overlay.InputOverlay;
import org.dolphinemu.dolphinemu.overlay.InputOverlayPointer;
import org.dolphinemu.dolphinemu.ui.main.MainActivity;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
@ -74,12 +77,14 @@ public final class EmulationActivity extends AppCompatActivity
private boolean activityRecreated;
private String mSelectedTitle;
private String mSelectedGameId;
private int mPlatform;
private String[] mPaths;
private boolean backPressedOnce = false;
public static final String EXTRA_SELECTED_GAMES = "SelectedGames";
public static final String EXTRA_SELECTED_TITLE = "SelectedTitle";
public static final String EXTRA_SELECTED_GAMEID = "SelectedGameId";
public static final String EXTRA_PLATFORM = "Platform";
@Retention(SOURCE)
@ -91,7 +96,7 @@ public final class EmulationActivity extends AppCompatActivity
MENU_ACTION_SAVE_SLOT6, MENU_ACTION_LOAD_SLOT1, MENU_ACTION_LOAD_SLOT2,
MENU_ACTION_LOAD_SLOT3, MENU_ACTION_LOAD_SLOT4, MENU_ACTION_LOAD_SLOT5,
MENU_ACTION_LOAD_SLOT6, MENU_ACTION_EXIT, MENU_ACTION_CHANGE_DISC,
MENU_ACTION_RESET_OVERLAY})
MENU_ACTION_RESET_OVERLAY, MENU_SET_IR_SENSITIVITY, MENU_ACTION_CHOOSE_DOUBLETAP})
public @interface MenuAction
{
}
@ -123,6 +128,8 @@ public final class EmulationActivity extends AppCompatActivity
public static final int MENU_ACTION_JOYSTICK_REL_CENTER = 24;
public static final int MENU_ACTION_RUMBLE = 25;
public static final int MENU_ACTION_RESET_OVERLAY = 26;
public static final int MENU_SET_IR_SENSITIVITY = 27;
public static final int MENU_ACTION_CHOOSE_DOUBLETAP = 28;
private static SparseIntArray buttonsActionsMap = new SparseIntArray();
@ -165,6 +172,10 @@ public final class EmulationActivity extends AppCompatActivity
buttonsActionsMap.append(R.id.menu_emulation_rumble, EmulationActivity.MENU_ACTION_RUMBLE);
buttonsActionsMap
.append(R.id.menu_emulation_reset_overlay, EmulationActivity.MENU_ACTION_RESET_OVERLAY);
buttonsActionsMap.append(R.id.menu_emulation_set_ir_sensitivity,
EmulationActivity.MENU_SET_IR_SENSITIVITY);
buttonsActionsMap.append(R.id.menu_emulation_choose_doubletap,
EmulationActivity.MENU_ACTION_CHOOSE_DOUBLETAP);
}
private static String[] scanForSecondDisc(GameFile gameFile)
@ -182,6 +193,7 @@ public final class EmulationActivity extends AppCompatActivity
launcher.putExtra(EXTRA_SELECTED_GAMES, scanForSecondDisc(gameFile));
launcher.putExtra(EXTRA_SELECTED_TITLE, gameFile.getTitle());
launcher.putExtra(EXTRA_SELECTED_GAMEID, gameFile.getGameId());
launcher.putExtra(EXTRA_PLATFORM, gameFile.getPlatform());
activity.startActivityForResult(launcher, MainPresenter.REQUEST_EMULATE_GAME);
}
@ -201,6 +213,7 @@ public final class EmulationActivity extends AppCompatActivity
Intent gameToEmulate = getIntent();
mPaths = gameToEmulate.getStringArrayExtra(EXTRA_SELECTED_GAMES);
mSelectedTitle = gameToEmulate.getStringExtra(EXTRA_SELECTED_TITLE);
mSelectedGameId = gameToEmulate.getStringExtra(EXTRA_SELECTED_GAMEID);
mPlatform = gameToEmulate.getIntExtra(EXTRA_PLATFORM, 0);
activityRecreated = false;
}
@ -294,6 +307,7 @@ public final class EmulationActivity extends AppCompatActivity
}
outState.putStringArray(EXTRA_SELECTED_GAMES, mPaths);
outState.putString(EXTRA_SELECTED_TITLE, mSelectedTitle);
outState.putString(EXTRA_SELECTED_GAMEID, mSelectedGameId);
outState.putInt(EXTRA_PLATFORM, mPlatform);
super.onSaveInstanceState(outState);
}
@ -302,6 +316,7 @@ public final class EmulationActivity extends AppCompatActivity
{
mPaths = savedInstanceState.getStringArray(EXTRA_SELECTED_GAMES);
mSelectedTitle = savedInstanceState.getString(EXTRA_SELECTED_TITLE);
mSelectedGameId = savedInstanceState.getString(EXTRA_SELECTED_GAMEID);
mPlatform = savedInstanceState.getInt(EXTRA_PLATFORM);
}
@ -578,6 +593,14 @@ public final class EmulationActivity extends AppCompatActivity
FileBrowserHelper.openFilePicker(this, REQUEST_CHANGE_DISC);
return;
case MENU_SET_IR_SENSITIVITY:
setIRSensitivity();
return;
case MENU_ACTION_CHOOSE_DOUBLETAP:
chooseDoubleTapButton();
return;
case MENU_ACTION_EXIT:
// ATV menu is built using a fragment, this will pop that fragment before emulation ends.
if (TvUtil.isLeanback(getApplicationContext()))
@ -708,6 +731,39 @@ public final class EmulationActivity extends AppCompatActivity
alertDialog.show();
}
public void chooseDoubleTapButton()
{
final SharedPreferences.Editor editor = mPreferences.edit();
AlertDialog.Builder builder = new AlertDialog.Builder(this);
int currentController =
mPreferences.getInt("wiiController", InputOverlay.OVERLAY_WIIMOTE_NUNCHUCK);
int currentValue = mPreferences.getInt("doubleTapButton",
InputOverlayPointer.DOUBLE_TAP_OPTIONS.get(InputOverlayPointer.DOUBLE_TAP_A));
int buttonList = currentController == InputOverlay.OVERLAY_WIIMOTE_CLASSIC ?
R.array.doubleTapWithClassic : R.array.doubleTap;
if (currentController != InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
currentValue == InputOverlay.OVERLAY_WIIMOTE_CLASSIC)
{
currentValue = InputOverlay.OVERLAY_WIIMOTE;
}
builder.setSingleChoiceItems(buttonList, currentValue, (DialogInterface dialog, int which) ->
editor.putInt("doubleTapButton", InputOverlayPointer.DOUBLE_TAP_OPTIONS.get(which)));
builder.setPositiveButton(getString(R.string.ok), (dialogInterface, i) ->
{
editor.commit();
mEmulationFragment.initInputPointer();
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}
private void adjustScale()
{
LayoutInflater inflater = LayoutInflater.from(this);
@ -785,6 +841,132 @@ public final class EmulationActivity extends AppCompatActivity
}
private void setIRSensitivity()
{
int irHeight = Integer.valueOf(
mPreferences.getString(SettingsFile.KEY_WIIBIND_IR_HEIGHT + mSelectedGameId, "50"));
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.dialog_ir_sensitivity, null);
TextView mTextSliderValueHeight = (TextView) view.findViewById(R.id.text_ir_height);
TextView units = (TextView) view.findViewById(R.id.text_ir_height_units);
SeekBar seekbarHeight = view.findViewById(R.id.seekbar_height);
mTextSliderValueHeight.setText(String.valueOf(irHeight));
units.setText(getString(R.string.height));
seekbarHeight.setMax(100);
seekbarHeight.setProgress(irHeight);
seekbarHeight.setKeyProgressIncrement(5);
seekbarHeight.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mTextSliderValueHeight.setText(String.valueOf(progress));
}
@Override public void onStartTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
@Override public void onStopTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
});
int irWidth = Integer.valueOf(
mPreferences.getString(SettingsFile.KEY_WIIBIND_IR_WIDTH + mSelectedGameId, "50"));
TextView mTextSliderValueWidth = (TextView) view.findViewById(R.id.text_ir_width);
TextView unitsWidth = (TextView) view.findViewById(R.id.text_ir_width_units);
SeekBar seekbarWidth = view.findViewById(R.id.seekbar_width);
mTextSliderValueWidth.setText(String.valueOf(irWidth));
unitsWidth.setText(getString(R.string.width));
seekbarWidth.setMax(100);
seekbarWidth.setProgress(irWidth);
seekbarWidth.setKeyProgressIncrement(5);
seekbarWidth.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mTextSliderValueWidth.setText(String.valueOf(progress));
}
@Override public void onStartTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
@Override public void onStopTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
});
int irCenter = Integer.valueOf(
mPreferences.getString(SettingsFile.KEY_WIIBIND_IR_CENTER + mSelectedGameId, "50"));
TextView mTextSliderValueCenter = (TextView) view.findViewById(R.id.text_ir_center);
TextView unitsCenter = (TextView) view.findViewById(R.id.text_ir_center_units);
SeekBar seekbarCenter = view.findViewById(R.id.seekbar_center);
mTextSliderValueCenter.setText(String.valueOf(irCenter));
unitsCenter.setText(getString(R.string.center));
seekbarCenter.setMax(100);
seekbarCenter.setProgress(irCenter);
seekbarCenter.setKeyProgressIncrement(5);
seekbarCenter.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener()
{
@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mTextSliderValueCenter.setText(String.valueOf(progress));
}
@Override public void onStartTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
@Override public void onStopTrackingTouch(SeekBar seekBar)
{
// Do nothing
}
});
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.emulation_ir_sensitivity));
builder.setView(view);
builder.setPositiveButton(R.string.ok, (dialogInterface, i) ->
{
SettingsFile.saveSingleCustomSetting(mSelectedGameId, Settings.SECTION_CONTROLS,
SettingsFile.KEY_WIIBIND_IR_HEIGHT, mTextSliderValueHeight.getText().toString());
SettingsFile.saveSingleCustomSetting(mSelectedGameId, Settings.SECTION_CONTROLS,
SettingsFile.KEY_WIIBIND_IR_WIDTH, mTextSliderValueWidth.getText().toString());
SettingsFile.saveSingleCustomSetting(mSelectedGameId, Settings.SECTION_CONTROLS,
SettingsFile.KEY_WIIBIND_IR_CENTER, mTextSliderValueCenter.getText().toString());
NativeLibrary.ReloadWiimoteConfig();
SharedPreferences.Editor editor = mPreferences.edit();
editor.putString(SettingsFile.KEY_WIIBIND_IR_HEIGHT + mSelectedGameId,
mTextSliderValueHeight.getText().toString());
editor.putString(SettingsFile.KEY_WIIBIND_IR_WIDTH + mSelectedGameId,
mTextSliderValueWidth.getText().toString());
editor.putString(SettingsFile.KEY_WIIBIND_IR_CENTER + mSelectedGameId,
mTextSliderValueCenter.getText().toString());
editor.apply();
});
builder.setNegativeButton(R.string.cancel, (dialogInterface, i) ->
{
// Do nothing
});
builder.show();
}
private void resetOverlay()
{
new AlertDialog.Builder(this)
@ -882,4 +1064,10 @@ public final class EmulationActivity extends AppCompatActivity
{
return mSettings;
}
public void initInputPointer()
{
if (deviceHasTouchScreen())
mEmulationFragment.initInputPointer();
}
}

View File

@ -151,6 +151,9 @@ public final class SettingsFile
public static final String KEY_WIIBIND_IR_FORWARD = "IRForward_";
public static final String KEY_WIIBIND_IR_BACKWARD = "IRBackward_";
public static final String KEY_WIIBIND_IR_HIDE = "IRHide_";
public static final String KEY_WIIBIND_IR_HEIGHT = "IRHeight";
public static final String KEY_WIIBIND_IR_WIDTH = "IRWidth";
public static final String KEY_WIIBIND_IR_CENTER = "IRCenter";
public static final String KEY_WIIBIND_SWING_UP = "SwingUp_";
public static final String KEY_WIIBIND_SWING_DOWN = "SwingDown_";
public static final String KEY_WIIBIND_SWING_LEFT = "SwingLeft_";

View File

@ -102,14 +102,6 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
surfaceView.getHolder().addCallback(this);
mInputOverlay = contents.findViewById(R.id.surface_input_overlay);
if (mInputOverlay != null)
{
// If the input overlay was previously disabled, then don't show it.
if (!mPreferences.getBoolean("showInputOverlay", true))
{
mInputOverlay.setVisibility(View.GONE);
}
}
Button doneButton = contents.findViewById(R.id.done_control_config);
if (doneButton != null)
@ -199,18 +191,19 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
// If the overlay is currently set to INVISIBLE
if (!mPreferences.getBoolean("showInputOverlay", false))
{
// Set it to VISIBLE
mInputOverlay.setVisibility(View.VISIBLE);
editor.putBoolean("showInputOverlay", true);
}
else
{
// Set it to INVISIBLE
mInputOverlay.setVisibility(View.GONE);
editor.putBoolean("showInputOverlay", false);
}
editor.commit();
mInputOverlay.refreshControls();
}
editor.apply();
public void initInputPointer()
{
mInputOverlay.initTouchPointer();
}
public void refreshInputOverlay()

View File

@ -40,9 +40,16 @@ import java.util.Set;
*/
public final class InputOverlay extends SurfaceView implements OnTouchListener
{
public static final int OVERLAY_GAMECUBE = 0;
public static final int OVERLAY_WIIMOTE = 1;
public static final int OVERLAY_WIIMOTE_SIDEWAYS = 2;
public static final int OVERLAY_WIIMOTE_NUNCHUCK = 3;
public static final int OVERLAY_WIIMOTE_CLASSIC = 4;
private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>();
private final Set<InputOverlayDrawableDpad> overlayDpads = new HashSet<>();
private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>();
private InputOverlayPointer overlayPointer;
private boolean mIsInEditMode = false;
private InputOverlayDrawableButton mButtonBeingConfigured;
@ -85,6 +92,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
mPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
if (!mPreferences.getBoolean("OverlayInitV2", false))
defaultOverlay();
// Load the controls.
refreshControls();
@ -98,6 +106,27 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
requestFocus();
}
public void initTouchPointer()
{
// Refresh before starting the pointer
refreshControls();
if (!EmulationActivity.isGameCubeGame())
{
int doubleTapButton = mPreferences.getInt("doubleTapButton",
InputOverlayPointer.DOUBLE_TAP_OPTIONS.get(InputOverlayPointer.DOUBLE_TAP_A));
if (mPreferences.getInt("wiiController", OVERLAY_WIIMOTE_NUNCHUCK) !=
InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
doubleTapButton == InputOverlayPointer.DOUBLE_TAP_CLASSIC_A)
{
doubleTapButton = InputOverlayPointer.DOUBLE_TAP_A;
}
overlayPointer = new InputOverlayPointer(this.getContext(), doubleTapButton);
}
}
@Override
public void draw(Canvas canvas)
{
@ -128,6 +157,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
}
int pointerIndex = event.getActionIndex();
// Tracks if any button/joystick is pressed down
boolean pressed = false;
for (InputOverlayDrawableButton button : overlayButtons)
{
@ -142,6 +173,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{
button.setPressedState(true);
button.setTrackId(event.getPointerId(pointerIndex));
pressed = true;
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(),
ButtonState.PRESSED);
}
@ -154,6 +186,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
button.setPressedState(false);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(),
ButtonState.RELEASED);
button.setTrackId(-1);
}
break;
}
@ -166,35 +199,47 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
case MotionEvent.ACTION_MOVE:
// Up, Down, Left, Right
boolean[] pressed = {false, false, false, false};
// If a pointer enters the bounds of a button, press that button.
if (dpad.getBounds()
.contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex)))
{
dpad.setTrackId(event.getPointerId(pointerIndex));
pressed = true;
}
case MotionEvent.ACTION_MOVE:
if (dpad.getTrackId() == event.getPointerId(pointerIndex))
{
// Up, Down, Left, Right
boolean[] dpadPressed = {false, false, false, false};
if (dpad.getBounds().top + (dpad.getHeight() / 3) > (int) event.getY(pointerIndex))
pressed[0] = true;
dpadPressed[0] = true;
if (dpad.getBounds().bottom - (dpad.getHeight() / 3) < (int) event.getY(pointerIndex))
pressed[1] = true;
dpadPressed[1] = true;
if (dpad.getBounds().left + (dpad.getWidth() / 3) > (int) event.getX(pointerIndex))
pressed[2] = true;
dpadPressed[2] = true;
if (dpad.getBounds().right - (dpad.getWidth() / 3) < (int) event.getX(pointerIndex))
pressed[3] = true;
dpadPressed[3] = true;
// Release the buttons first, then press
for (int i = 0; i < pressed.length; i++)
if (!pressed[i])
for (int i = 0; i < dpadPressed.length; i++)
{
if (!dpadPressed[i])
{
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i),
ButtonState.RELEASED);
}
}
// Press buttons
for (int i = 0; i < pressed.length; i++)
if (pressed[i])
for (int i = 0; i < dpadPressed.length; i++)
{
if (dpadPressed[i])
{
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i),
ButtonState.PRESSED);
setDpadState(dpad, pressed[0], pressed[1], pressed[2], pressed[3]);
dpad.setTrackId(event.getPointerId(pointerIndex));
}
}
setDpadState(dpad, dpadPressed[0], dpadPressed[1], dpadPressed[2], dpadPressed[3]);
}
break;
case MotionEvent.ACTION_UP:
@ -208,6 +253,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i),
ButtonState.RELEASED);
}
dpad.setTrackId(-1);
}
break;
}
@ -215,7 +261,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
for (InputOverlayDrawableJoystick joystick : overlayJoysticks)
{
joystick.TrackEvent(event);
if (joystick.TrackEvent(event))
{
if (joystick.getTrackId() != -1)
pressed = true;
}
int[] axisIDs = joystick.getAxisIDs();
float[] axises = joystick.getAxisValues();
@ -225,6 +275,18 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
}
}
// No button/joystick pressed, safe to move pointer
if (!pressed && overlayPointer != null)
{
overlayPointer.onTouch(event);
float[] axises = overlayPointer.getAxisValues();
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, ButtonType.WIIMOTE_IR + 2,
axises[0]);
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, ButtonType.WIIMOTE_IR + 4,
axises[1]);
}
invalidate();
return true;
@ -617,21 +679,24 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ?
"-Portrait" : "";
// Add all the enabled overlay items back to the HashSet.
if (EmulationActivity.isGameCubeGame() || mPreferences.getInt("wiiController", 3) == 0)
if (mPreferences.getBoolean("showInputOverlay", true))
{
addGameCubeOverlayControls(orientation);
}
else if (mPreferences.getInt("wiiController", 3) == 4)
{
addClassicOverlayControls(orientation);
}
else
{
addWiimoteOverlayControls(orientation);
if (mPreferences.getInt("wiiController", 3) == 3)
// Add all the enabled overlay items back to the HashSet.
if (EmulationActivity.isGameCubeGame() || mPreferences.getInt("wiiController", 3) == 0)
{
addNunchukOverlayControls(orientation);
addGameCubeOverlayControls(orientation);
}
else if (mPreferences.getInt("wiiController", 3) == 4)
{
addClassicOverlayControls(orientation);
}
else
{
addWiimoteOverlayControls(orientation);
if (mPreferences.getInt("wiiController", 3) == 3)
{
addNunchukOverlayControls(orientation);
}
}
}

View File

@ -41,6 +41,7 @@ public final class InputOverlayDrawableButton
public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap,
Bitmap pressedStateBitmap, int buttonType)
{
mTrackId = -1;
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap);
mButtonType = buttonType;

View File

@ -60,6 +60,7 @@ public final class InputOverlayDrawableDpad
int buttonUp, int buttonDown,
int buttonLeft, int buttonRight)
{
mTrackId = -1;
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
mPressedOneDirectionStateBitmap = new BitmapDrawable(res, pressedOneDirectionStateBitmap);
mPressedTwoDirectionsStateBitmap = new BitmapDrawable(res, pressedTwoDirectionsStateBitmap);

View File

@ -94,10 +94,11 @@ public final class InputOverlayDrawableJoystick
mBoundsBoxBitmap.draw(canvas);
}
public void TrackEvent(MotionEvent event)
public boolean TrackEvent(MotionEvent event)
{
boolean reCenter = mPreferences.getBoolean("joystickRelCenter", true);
int pointerIndex = event.getActionIndex();
boolean pressed = false;
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
@ -105,7 +106,7 @@ public final class InputOverlayDrawableJoystick
case MotionEvent.ACTION_POINTER_DOWN:
if (getBounds().contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex)))
{
mPressedState = true;
mPressedState = pressed = true;
mOuterBitmap.setAlpha(0);
mBoundsBoxBitmap.setAlpha(255);
if (reCenter)
@ -121,6 +122,7 @@ public final class InputOverlayDrawableJoystick
case MotionEvent.ACTION_POINTER_UP:
if (trackId == event.getPointerId(pointerIndex))
{
pressed = true;
mPressedState = false;
axises[0] = axises[1] = 0.0f;
mOuterBitmap.setAlpha(255);
@ -136,7 +138,7 @@ public final class InputOverlayDrawableJoystick
}
if (trackId == -1)
return;
return pressed;
for (int i = 0; i < event.getPointerCount(); i++)
{
@ -158,6 +160,7 @@ public final class InputOverlayDrawableJoystick
SetInnerBounds();
}
}
return pressed;
}
public boolean onConfigureTouch(MotionEvent event)
@ -274,4 +277,9 @@ public final class InputOverlayDrawableJoystick
{
return mHeight;
}
public int getTrackId()
{
return trackId;
}
}

View File

@ -0,0 +1,135 @@
package org.dolphinemu.dolphinemu.overlay;
import android.app.Activity;
import android.content.Context;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.NativeLibrary;
import java.util.ArrayList;
public class InputOverlayPointer
{
public static final int DOUBLE_TAP_A = 0;
public static final int DOUBLE_TAP_B = 1;
public static final int DOUBLE_TAP_2 = 2;
public static final int DOUBLE_TAP_CLASSIC_A = 3;
private final float[] axes = {0f, 0f};
private float maxHeight;
private float maxWidth;
private float aspectAdjusted;
private boolean xAdjusted;
private boolean doubleTap = false;
private int doubleTapButton;
private int trackId = -1;
public static ArrayList<Integer> DOUBLE_TAP_OPTIONS = new ArrayList<>();
static
{
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.WIIMOTE_BUTTON_A);
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.WIIMOTE_BUTTON_B);
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.WIIMOTE_BUTTON_2);
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A);
}
public InputOverlayPointer(Context context, int button)
{
Display display = ((Activity) context).getWindowManager().getDefaultDisplay();
DisplayMetrics outMetrics = new DisplayMetrics();
display.getMetrics(outMetrics);
doubleTapButton = button;
Integer y = outMetrics.heightPixels;
Integer x = outMetrics.widthPixels;
// Adjusting for device's black bars.
Float deviceAR = (float) x / y;
Float gameAR = NativeLibrary.GetGameAspectRatio();
aspectAdjusted = gameAR / deviceAR;
if (gameAR < deviceAR) // Black bars on left/right
{
xAdjusted = true;
Integer gameX = Math.round((float) y * gameAR);
Integer buffer = (x - gameX);
maxWidth = (float) (x - buffer) / 2;
maxHeight = (float) y / 2;
}
else // Bars on top/bottom
{
xAdjusted = false;
Integer gameY = Math.round((float) x * gameAR);
Integer buffer = (y - gameY);
maxWidth = (float) x / 2;
maxHeight = (float) (y - buffer) / 2;
}
}
public boolean onTouch(MotionEvent event)
{
int pointerIndex = event.getActionIndex();
switch (event.getAction() & MotionEvent.ACTION_MASK)
{
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
trackId = event.getPointerId(pointerIndex);
touchPress();
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
if (trackId == event.getPointerId(pointerIndex))
trackId = -1;
break;
}
if (trackId == -1)
return false;
int x = (int) event.getX(event.findPointerIndex(trackId));
int y = (int) event.getY(event.findPointerIndex(trackId));
if (xAdjusted)
{
axes[0] = (y - maxHeight) / maxHeight;
axes[1] = ((x * aspectAdjusted) - maxWidth) / maxWidth;
}
else
{
axes[0] = ((y * aspectAdjusted) - maxHeight) / maxHeight;
axes[1] = (x - maxWidth) / maxWidth;
}
return false;
}
private void touchPress()
{
if (doubleTap)
{
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice,
doubleTapButton, NativeLibrary.ButtonState.PRESSED);
new Handler().postDelayed(() -> NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice,
doubleTapButton, NativeLibrary.ButtonState.RELEASED), 50);
}
else
{
doubleTap = true;
new Handler().postDelayed(() -> doubleTap = false, 300);
}
}
public float[] getAxisValues()
{
float[] ir = {0f, 0f};
ir[0] = axes[0];
ir[1] = axes[1];
return axes;
}
}

View File

@ -0,0 +1,115 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
<SeekBar
android:id="@+id/seekbar_width"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/text_ir_width"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"/>
<TextView
android:id="@+id/text_ir_width"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge"
tools:text="75"/>
<TextView
android:id="@+id/text_ir_width_units"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/text_ir_width"
android:layout_toEndOf="@+id/text_ir_width"
tools:text="%"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
<SeekBar
android:id="@+id/seekbar_height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/text_ir_height"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"/>
<TextView
android:id="@+id/text_ir_height"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge"
tools:text="75"/>
<TextView
android:id="@+id/text_ir_height_units"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/text_ir_height"
android:layout_toEndOf="@+id/text_ir_height"
tools:text="%"/>
</RelativeLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
<SeekBar
android:id="@+id/seekbar_center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/text_ir_center"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginLeft="@dimen/spacing_large"
android:layout_marginRight="@dimen/spacing_large"/>
<TextView
android:id="@+id/text_ir_center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="@dimen/spacing_medlarge"
android:layout_marginTop="@dimen/spacing_medlarge"
tools:text="75"/>
<TextView
android:id="@+id/text_ir_center_units"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/text_ir_center"
android:layout_toEndOf="@+id/text_ir_center"
tools:text="%"/>
</RelativeLayout>
</LinearLayout>

View File

@ -112,7 +112,21 @@
<item
android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/>
<item
android:id="@+id/menu_emulation_ir_group"
android:title="@string/emulation_ir_group"
app:showAsAction="never">
<menu>
<item
android:id="@+id/menu_emulation_set_ir_sensitivity"
android:title="@string/emulation_ir_sensitivity"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_emulation_choose_doubletap"
android:title="@string/emulation_choose_doubletap"
app:showAsAction="ifRoom"/>
</menu>
</item>
<item
android:id="@+id/menu_emulation_reset_overlay"
android:title="@string/emulation_touch_overlay_reset"/>

View File

@ -306,6 +306,19 @@
<item>Right Stick</item>
</string-array>
<string-array name="doubleTap">
<item>Button A</item>
<item>Button B</item>
<item>Button 2</item>
</string-array>
<string-array name="doubleTapWithClassic">
<item>Button A</item>
<item>Button B</item>
<item>Button 2</item>
<item>Classic A</item>
</string-array>
<string-array name="gameSettingsMenusGC">
<item>Core Settings</item>
<item>GFX Settings</item>

View File

@ -298,6 +298,9 @@
<string name="emulation_controller_changed">You may have to reload the game after changing extensions.</string>
<string name="emulation_touch_button_help">Swipe down from the top of the screen to access the menu.</string>
<string name="emulation_touch_overlay_reset">Reset Overlay</string>
<string name="emulation_ir_group">Touch IR Pointer</string>
<string name="emulation_ir_sensitivity">IR Sensitivity</string>
<string name="emulation_choose_doubletap">Double tap button</string>
<!-- GC Adapter Menu-->
<string name="gc_adapter_rumble">Enable Vibration</string>
@ -322,4 +325,10 @@
<string name="homescreen_favorites">Favorites</string>
<string name="select_dir">Select This Directory</string>
<!-- Misc -->
<string name="height">Height</string>
<string name="width">Width</string>
<string name="center">Center</string>
</resources>

View File

@ -12,6 +12,7 @@ static JavaVM* s_java_vm;
static jclass s_native_library_class;
static jmethodID s_display_alert_msg;
static jmethodID s_get_update_touch_pointer;
static jclass s_game_file_class;
static jfieldID s_game_file_pointer;
@ -41,6 +42,11 @@ jmethodID GetDisplayAlertMsg()
return s_display_alert_msg;
}
jmethodID GetUpdateTouchPointer()
{
return s_get_update_touch_pointer;
}
jclass GetAnalyticsClass()
{
return s_analytics_class;
@ -98,6 +104,8 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
s_native_library_class = reinterpret_cast<jclass>(env->NewGlobalRef(native_library_class));
s_display_alert_msg = env->GetStaticMethodID(s_native_library_class, "displayAlertMsg",
"(Ljava/lang/String;Ljava/lang/String;Z)Z");
s_get_update_touch_pointer =
env->GetStaticMethodID(IDCache::GetNativeLibraryClass(), "updateTouchPointer", "()V");
const jclass game_file_class = env->FindClass("org/dolphinemu/dolphinemu/model/GameFile");
s_game_file_class = reinterpret_cast<jclass>(env->NewGlobalRef(game_file_class));

View File

@ -14,6 +14,7 @@ JavaVM* GetJavaVM();
jclass GetNativeLibraryClass();
jmethodID GetDisplayAlertMsg();
jmethodID GetUpdateTouchPointer();
jclass GetAnalyticsClass();
jmethodID GetSendAnalyticsReport();

View File

@ -107,6 +107,11 @@ void Host_UpdateMainFrame()
void Host_RequestRenderWindowSize(int width, int height)
{
// Update touch pointer
JNIEnv* env;
IDCache::GetJavaVM()->AttachCurrentThread(&env, nullptr);
env->CallStaticVoidMethod(IDCache::GetNativeLibraryClass(), IDCache::GetUpdateTouchPointer());
IDCache::GetJavaVM()->DetachCurrentThread();
}
bool Host_UINeedsControllerState()
@ -564,6 +569,13 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SurfaceDestr
s_surf = nullptr;
}
}
JNIEXPORT jfloat JNICALL
Java_org_dolphinemu_dolphinemu_NativeLibrary_GetGameAspectRatio(JNIEnv* env, jobject obj)
{
return g_renderer->CalculateDrawAspectRatio();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimotes(JNIEnv* env,
jobject obj)
{
@ -571,6 +583,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_RefreshWiimo
WiimoteReal::Refresh();
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ReloadWiimoteConfig(JNIEnv* env,
jobject obj)
{
Wiimote::LoadConfig();
}
static void Run(const std::vector<std::string>& paths, bool first_open,
std::optional<std::string> savestate_path = {}, bool delete_savestate = false)
{

View File

@ -30,6 +30,7 @@ private:
{
public:
std::string GetName() const;
bool IsDetectable() override { return false; }
Axis(int padID, ButtonManager::ButtonType index, float neg = 1.0f)
: _padID(padID), _index(index), _neg(neg)
{

View File

@ -33,6 +33,11 @@ bool InputConfig::LoadConfig(bool isGC)
std::string profile[MAX_BBMOTES];
std::string path;
#if defined(ANDROID)
bool use_ir_config = false;
std::string ir_values[3];
#endif
if (SConfig::GetInstance().GetGameID() != "00000000")
{
std::string type;
@ -73,6 +78,18 @@ bool InputConfig::LoadConfig(bool isGC)
}
}
}
#if defined(ANDROID)
// For use on android touchscreen IR pointer
// Check for IR values
if (control_section->Exists("IRWidth") && control_section->Exists("IRHeight") &&
control_section->Exists("IRCenter"))
{
use_ir_config = true;
control_section->Get("IRWidth", &ir_values[0]);
control_section->Get("IRHeight", &ir_values[1]);
control_section->Get("IRCenter", &ir_values[2]);
}
#endif
}
if (inifile.Load(File::GetUserPath(D_CONFIG_IDX) + m_ini_name + ".ini"))
@ -80,6 +97,7 @@ bool InputConfig::LoadConfig(bool isGC)
int n = 0;
for (auto& controller : m_controllers)
{
IniFile::Section config;
// Load settings from ini
if (useProfile[n])
{
@ -91,13 +109,22 @@ bool InputConfig::LoadConfig(bool isGC)
IniFile profile_ini;
profile_ini.Load(profile[n]);
controller->LoadConfig(profile_ini.GetOrCreateSection("Profile"));
config = *profile_ini.GetOrCreateSection("Profile");
}
else
{
controller->LoadConfig(inifile.GetOrCreateSection(controller->GetName()));
config = *inifile.GetOrCreateSection(controller->GetName());
}
#if defined(ANDROID)
// Only set for wii pads
if (!isGC && use_ir_config)
{
config.Set("IR/Width", ir_values[0]);
config.Set("IR/Height", ir_values[1]);
config.Set("IR/Center", ir_values[2]);
}
#endif
controller->LoadConfig(&config);
// Update refs
controller->UpdateReferences(g_controller_interface);