diff --git a/Data/Sys/GameSettings/RMC.ini b/Data/Sys/GameSettings/RMC.ini index 5dcaf1d11b..2f933454a1 100644 --- a/Data/Sys/GameSettings/RMC.ini +++ b/Data/Sys/GameSettings/RMC.ini @@ -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. diff --git a/Data/Sys/GameSettings/RMG.ini b/Data/Sys/GameSettings/RMG.ini index e599c21f3d..ac715ed28b 100644 --- a/Data/Sys/GameSettings/RMG.ini +++ b/Data/Sys/GameSettings/RMG.ini @@ -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. diff --git a/Data/Sys/GameSettings/ROD.ini b/Data/Sys/GameSettings/ROD.ini index 229988d24c..90c26ba2cd 100644 --- a/Data/Sys/GameSettings/ROD.ini +++ b/Data/Sys/GameSettings/ROD.ini @@ -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. diff --git a/Data/Sys/GameSettings/RUU.ini b/Data/Sys/GameSettings/RUU.ini index c9ce0e9055..6a9159d4d6 100644 --- a/Data/Sys/GameSettings/RUU.ini +++ b/Data/Sys/GameSettings/RUU.ini @@ -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. diff --git a/Data/Sys/GameSettings/RZD.ini b/Data/Sys/GameSettings/RZD.ini index bf2a80008f..0ceb74c8ac 100644 --- a/Data/Sys/GameSettings/RZD.ini +++ b/Data/Sys/GameSettings/RZD.ini @@ -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. diff --git a/Data/Sys/GameSettings/SB4.ini b/Data/Sys/GameSettings/SB4.ini index 00cce48a86..2fccf50960 100644 --- a/Data/Sys/GameSettings/SB4.ini +++ b/Data/Sys/GameSettings/SB4.ini @@ -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. diff --git a/Source/Android/app/src/main/assets/WiimoteNew.ini b/Source/Android/app/src/main/assets/WiimoteNew.ini index e8efbaf794..caa82c3268 100644 --- a/Source/Android/app/src/main/assets/WiimoteNew.ini +++ b/Source/Android/app/src/main/assets/WiimoteNew.ini @@ -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` diff --git a/Source/Android/app/src/main/assets/WiimoteProfile.ini b/Source/Android/app/src/main/assets/WiimoteProfile.ini index 6c036c1a37..f31f68346c 100644 --- a/Source/Android/app/src/main/assets/WiimoteProfile.ini +++ b/Source/Android/app/src/main/assets/WiimoteProfile.ini @@ -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` diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java index 6574d22199..442a4e8644 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/NativeLibrary.java @@ -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(); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java index f5309869d5..cf75b127e0 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java @@ -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(); + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java index 0dd027cd80..df51a38e04 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/utils/SettingsFile.java @@ -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_"; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java index dc888d74d4..f817414add 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/fragments/EmulationFragment.java @@ -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() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java index de29e00ed5..c679838d3b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java @@ -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 overlayButtons = new HashSet<>(); private final Set overlayDpads = new HashSet<>(); private final Set 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); + } } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableButton.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableButton.java index 96b4aeca0a..266cd273c4 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableButton.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableButton.java @@ -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; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java index fd60f1fc98..5aa8c4846d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java @@ -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); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableJoystick.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableJoystick.java index a54d01c7cf..55f6347096 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableJoystick.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableJoystick.java @@ -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; + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayPointer.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayPointer.java new file mode 100644 index 0000000000..492cf186eb --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayPointer.java @@ -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 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; + } +} diff --git a/Source/Android/app/src/main/res/layout/dialog_ir_sensitivity.xml b/Source/Android/app/src/main/res/layout/dialog_ir_sensitivity.xml new file mode 100644 index 0000000000..8f60d318e2 --- /dev/null +++ b/Source/Android/app/src/main/res/layout/dialog_ir_sensitivity.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Source/Android/app/src/main/res/menu/menu_emulation_wii.xml b/Source/Android/app/src/main/res/menu/menu_emulation_wii.xml index 5a26d61814..465e582aff 100644 --- a/Source/Android/app/src/main/res/menu/menu_emulation_wii.xml +++ b/Source/Android/app/src/main/res/menu/menu_emulation_wii.xml @@ -112,7 +112,21 @@ - + + + + + + diff --git a/Source/Android/app/src/main/res/values/arrays.xml b/Source/Android/app/src/main/res/values/arrays.xml index 403e27b2b1..503788998a 100644 --- a/Source/Android/app/src/main/res/values/arrays.xml +++ b/Source/Android/app/src/main/res/values/arrays.xml @@ -306,6 +306,19 @@ Right Stick + + Button A + Button B + Button 2 + + + + Button A + Button B + Button 2 + Classic A + + Core Settings GFX Settings diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index 8c2c821aaf..0e13855779 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -298,6 +298,9 @@ You may have to reload the game after changing extensions. Swipe down from the top of the screen to access the menu. Reset Overlay + Touch IR Pointer + IR Sensitivity + Double tap button Enable Vibration @@ -322,4 +325,10 @@ Favorites Select This Directory + + + Height + Width + Center + diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 42a5632139..9841506fab 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -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(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(env->NewGlobalRef(game_file_class)); diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 5b302353ce..26262ac8d7 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -14,6 +14,7 @@ JavaVM* GetJavaVM(); jclass GetNativeLibraryClass(); jmethodID GetDisplayAlertMsg(); +jmethodID GetUpdateTouchPointer(); jclass GetAnalyticsClass(); jmethodID GetSendAnalyticsReport(); diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index c606345abe..686053496e 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -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& paths, bool first_open, std::optional savestate_path = {}, bool delete_savestate = false) { diff --git a/Source/Core/InputCommon/ControllerInterface/Android/Android.h b/Source/Core/InputCommon/ControllerInterface/Android/Android.h index deb23539ce..59919982ef 100644 --- a/Source/Core/InputCommon/ControllerInterface/Android/Android.h +++ b/Source/Core/InputCommon/ControllerInterface/Android/Android.h @@ -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) { diff --git a/Source/Core/InputCommon/InputConfig.cpp b/Source/Core/InputCommon/InputConfig.cpp index 65cfdef086..0a6a82d540 100644 --- a/Source/Core/InputCommon/InputConfig.cpp +++ b/Source/Core/InputCommon/InputConfig.cpp @@ -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);