Merge pull request #9624 from JosJuice/input-override

Add new "input override" system for TAS input and Android touch controls
This commit is contained in:
JMC47 2022-10-03 16:25:40 -04:00 committed by GitHub
commit 052c7395fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 1766 additions and 861 deletions

View File

@ -280,9 +280,6 @@ public final class NativeLibrary
public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled, public static native void SetMotionSensorsEnabled(boolean accelerometerEnabled,
boolean gyroscopeEnabled); boolean gyroscopeEnabled);
// Angle is in radians and should be non-negative
public static native double GetInputRadiusAtAngle(int emu_pad_id, int stick, double angle);
/** /**
* Gets the Dolphin version string. * Gets the Dolphin version string.
* *

View File

@ -0,0 +1,84 @@
// SPDX-License-Identifier: GPL-2.0-or-later
package org.dolphinemu.dolphinemu.features.input.model;
public final class InputOverrider
{
public static final class ControlId
{
public static final int GCPAD_A_BUTTON = 0;
public static final int GCPAD_B_BUTTON = 1;
public static final int GCPAD_X_BUTTON = 2;
public static final int GCPAD_Y_BUTTON = 3;
public static final int GCPAD_Z_BUTTON = 4;
public static final int GCPAD_START_BUTTON = 5;
public static final int GCPAD_DPAD_UP = 6;
public static final int GCPAD_DPAD_DOWN = 7;
public static final int GCPAD_DPAD_LEFT = 8;
public static final int GCPAD_DPAD_RIGHT = 9;
public static final int GCPAD_L_DIGITAL = 10;
public static final int GCPAD_R_DIGITAL = 11;
public static final int GCPAD_L_ANALOG = 12;
public static final int GCPAD_R_ANALOG = 13;
public static final int GCPAD_MAIN_STICK_X = 14;
public static final int GCPAD_MAIN_STICK_Y = 15;
public static final int GCPAD_C_STICK_X = 16;
public static final int GCPAD_C_STICK_Y = 17;
public static final int WIIMOTE_A_BUTTON = 18;
public static final int WIIMOTE_B_BUTTON = 19;
public static final int WIIMOTE_ONE_BUTTON = 20;
public static final int WIIMOTE_TWO_BUTTON = 21;
public static final int WIIMOTE_PLUS_BUTTON = 22;
public static final int WIIMOTE_MINUS_BUTTON = 23;
public static final int WIIMOTE_HOME_BUTTON = 24;
public static final int WIIMOTE_DPAD_UP = 25;
public static final int WIIMOTE_DPAD_DOWN = 26;
public static final int WIIMOTE_DPAD_LEFT = 27;
public static final int WIIMOTE_DPAD_RIGHT = 28;
public static final int WIIMOTE_IR_X = 29;
public static final int WIIMOTE_IR_Y = 30;
public static final int NUNCHUK_C_BUTTON = 31;
public static final int NUNCHUK_Z_BUTTON = 32;
public static final int NUNCHUK_STICK_X = 33;
public static final int NUNCHUK_STICK_Y = 34;
public static final int CLASSIC_A_BUTTON = 35;
public static final int CLASSIC_B_BUTTON = 36;
public static final int CLASSIC_X_BUTTON = 37;
public static final int CLASSIC_Y_BUTTON = 38;
public static final int CLASSIC_ZL_BUTTON = 39;
public static final int CLASSIC_ZR_BUTTON = 40;
public static final int CLASSIC_PLUS_BUTTON = 41;
public static final int CLASSIC_MINUS_BUTTON = 42;
public static final int CLASSIC_HOME_BUTTON = 43;
public static final int CLASSIC_DPAD_UP = 44;
public static final int CLASSIC_DPAD_DOWN = 45;
public static final int CLASSIC_DPAD_LEFT = 46;
public static final int CLASSIC_DPAD_RIGHT = 47;
public static final int CLASSIC_L_DIGITAL = 48;
public static final int CLASSIC_R_DIGITAL = 49;
public static final int CLASSIC_L_ANALOG = 50;
public static final int CLASSIC_R_ANALOG = 51;
public static final int CLASSIC_LEFT_STICK_X = 52;
public static final int CLASSIC_LEFT_STICK_Y = 53;
public static final int CLASSIC_RIGHT_STICK_X = 54;
public static final int CLASSIC_RIGHT_STICK_Y = 55;
}
public static native void registerGameCube(int controllerIndex);
public static native void registerWii(int controllerIndex);
public static native void unregisterGameCube(int controllerIndex);
public static native void unregisterWii(int controllerIndex);
public static native void setControlState(int controllerIndex, int control, double state);
public static native void clearControlState(int controllerIndex, int control);
// Angle is in radians and should be non-negative
public static native double getGateRadiusAtAngle(int emuPadId, int stick, double angle);
}

View File

@ -40,8 +40,7 @@ public enum IntSetting implements AbstractIntSetting
InputOverlayPointer.MODE_FOLLOW), InputOverlayPointer.MODE_FOLLOW),
MAIN_DOUBLE_TAP_BUTTON(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS, MAIN_DOUBLE_TAP_BUTTON(Settings.FILE_DOLPHIN, Settings.SECTION_INI_ANDROID_OVERLAY_BUTTONS,
"DoubleTapButton", "DoubleTapButton", NativeLibrary.ButtonType.WIIMOTE_BUTTON_A),
InputOverlayPointer.DOUBLE_TAP_OPTIONS.get(InputOverlayPointer.DOUBLE_TAP_A)),
SYSCONF_LANGUAGE(Settings.FILE_SYSCONF, "IPL", "LNG", 0x01), SYSCONF_LANGUAGE(Settings.FILE_SYSCONF, "IPL", "LNG", 0x01),
SYSCONF_SOUND_MODE(Settings.FILE_SYSCONF, "IPL", "SND", 0x01), SYSCONF_SOUND_MODE(Settings.FILE_SYSCONF, "IPL", "SND", 0x01),

View File

@ -138,6 +138,15 @@ public final class EmulationFragment extends Fragment implements SurfaceHolder.C
super.onPause(); super.onPause();
} }
@Override
public void onDestroy()
{
if (mInputOverlay != null)
mInputOverlay.onDestroy();
super.onDestroy();
}
@Override @Override
public void onDetach() public void onDetach()
{ {

View File

@ -26,9 +26,10 @@ import android.view.View.OnTouchListener;
import android.widget.Toast; import android.widget.Toast;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.NativeLibrary.ButtonState;
import org.dolphinemu.dolphinemu.NativeLibrary.ButtonType; import org.dolphinemu.dolphinemu.NativeLibrary.ButtonType;
import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.input.model.InputOverrider;
import org.dolphinemu.dolphinemu.features.input.model.InputOverrider.ControlId;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
import org.dolphinemu.dolphinemu.features.settings.model.IntSetting; import org.dolphinemu.dolphinemu.features.settings.model.IntSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.Settings;
@ -64,6 +65,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
private Rect mSurfacePosition = null; private Rect mSurfacePosition = null;
private boolean mIsFirstRun = true; private boolean mIsFirstRun = true;
private boolean mGameCubeRegistered = false;
private boolean mWiiRegistered = false;
private boolean mIsInEditMode = false; private boolean mIsInEditMode = false;
private InputOverlayDrawableButton mButtonBeingConfigured; private InputOverlayDrawableButton mButtonBeingConfigured;
private InputOverlayDrawableDpad mDpadBeingConfigured; private InputOverlayDrawableDpad mDpadBeingConfigured;
@ -158,12 +161,26 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
int doubleTapButton = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getIntGlobal(); int doubleTapButton = IntSetting.MAIN_DOUBLE_TAP_BUTTON.getIntGlobal();
if (getConfiguredControllerType() != InputOverlay.OVERLAY_WIIMOTE_CLASSIC && if (getConfiguredControllerType() != InputOverlay.OVERLAY_WIIMOTE_CLASSIC &&
doubleTapButton == InputOverlayPointer.DOUBLE_TAP_CLASSIC_A) doubleTapButton == ButtonType.CLASSIC_BUTTON_A)
{ {
doubleTapButton = InputOverlayPointer.DOUBLE_TAP_A; doubleTapButton = ButtonType.WIIMOTE_BUTTON_A;
} }
overlayPointer = new InputOverlayPointer(mSurfacePosition, doubleTapButton, int doubleTapControl = ControlId.WIIMOTE_A_BUTTON;
switch (doubleTapButton)
{
case ButtonType.WIIMOTE_BUTTON_A:
doubleTapControl = ControlId.WIIMOTE_A_BUTTON;
break;
case ButtonType.WIIMOTE_BUTTON_B:
doubleTapControl = ControlId.WIIMOTE_B_BUTTON;
break;
case ButtonType.WIIMOTE_BUTTON_2:
doubleTapControl = ControlId.WIIMOTE_TWO_BUTTON;
break;
}
overlayPointer = new InputOverlayPointer(mSurfacePosition, doubleTapControl,
IntSetting.MAIN_IR_MODE.getIntGlobal(), IntSetting.MAIN_IR_MODE.getIntGlobal(),
BooleanSetting.MAIN_IR_ALWAYS_RECENTER.getBooleanGlobal()); BooleanSetting.MAIN_IR_ALWAYS_RECENTER.getBooleanGlobal());
} }
@ -218,8 +235,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
button.setPressedState(true); button.setPressedState(true);
button.setTrackId(event.getPointerId(pointerIndex)); button.setTrackId(event.getPointerId(pointerIndex));
pressed = true; pressed = true;
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), InputOverrider.setControlState(0, button.getControl(), 1.0);
ButtonState.PRESSED);
int analogControl = getAnalogControlForTrigger(button.getControl());
if (analogControl >= 0)
InputOverrider.setControlState(0, analogControl, 1.0);
} }
break; break;
case MotionEvent.ACTION_UP: case MotionEvent.ACTION_UP:
@ -228,8 +248,12 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (button.getTrackId() == event.getPointerId(pointerIndex)) if (button.getTrackId() == event.getPointerId(pointerIndex))
{ {
button.setPressedState(false); button.setPressedState(false);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.getId(), InputOverrider.setControlState(0, button.getControl(), 0.0);
ButtonState.RELEASED);
int analogControl = getAnalogControlForTrigger(button.getControl());
if (analogControl >= 0)
InputOverrider.setControlState(0, analogControl, 0.0);
button.setTrackId(-1); button.setTrackId(-1);
} }
break; break;
@ -270,8 +294,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{ {
if (!dpadPressed[i]) if (!dpadPressed[i])
{ {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), InputOverrider.setControlState(0, dpad.getControl(i), 0.0);
ButtonState.RELEASED);
} }
} }
// Press buttons // Press buttons
@ -279,8 +302,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
{ {
if (dpadPressed[i]) if (dpadPressed[i])
{ {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), InputOverrider.setControlState(0, dpad.getControl(i), 1.0);
ButtonState.PRESSED);
} }
} }
setDpadState(dpad, dpadPressed[0], dpadPressed[1], dpadPressed[2], dpadPressed[3]); setDpadState(dpad, dpadPressed[0], dpadPressed[1], dpadPressed[2], dpadPressed[3]);
@ -294,8 +316,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
dpad.setState(InputOverlayDrawableDpad.STATE_DEFAULT); dpad.setState(InputOverlayDrawableDpad.STATE_DEFAULT);
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), InputOverrider.setControlState(0, dpad.getControl(i), 0.0);
ButtonState.RELEASED);
} }
dpad.setTrackId(-1); dpad.setTrackId(-1);
} }
@ -310,27 +331,17 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (joystick.getTrackId() != -1) if (joystick.getTrackId() != -1)
pressed = true; pressed = true;
} }
int[] axisIDs = joystick.getAxisIDs();
float[] axises = joystick.getAxisValues();
for (int i = 0; i < 4; i++) InputOverrider.setControlState(0, joystick.getXControl(), joystick.getX());
{ InputOverrider.setControlState(0, joystick.getYControl(), -joystick.getY());
NativeLibrary.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, axisIDs[i], axises[i]);
}
} }
// No button/joystick pressed, safe to move pointer // No button/joystick pressed, safe to move pointer
if (!pressed && overlayPointer != null) if (!pressed && overlayPointer != null)
{ {
overlayPointer.onTouch(event); overlayPointer.onTouch(event);
float[] axes = overlayPointer.getAxisValues(); InputOverrider.setControlState(0, ControlId.WIIMOTE_IR_X, overlayPointer.getX());
InputOverrider.setControlState(0, ControlId.WIIMOTE_IR_Y, -overlayPointer.getY());
for (int i = 0; i < 4; i++)
{
NativeLibrary
.onGamePadMoveEvent(NativeLibrary.TouchScreenDevice, ButtonType.WIIMOTE_IR_UP + i,
axes[i]);
}
} }
invalidate(); invalidate();
@ -381,7 +392,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (mButtonBeingConfigured == button) if (mButtonBeingConfigured == button)
{ {
// Persist button position by saving new place. // Persist button position by saving new place.
saveControlPosition(mButtonBeingConfigured.getId(), saveControlPosition(mButtonBeingConfigured.getLegacyId(),
mButtonBeingConfigured.getBounds().left, mButtonBeingConfigured.getBounds().left,
mButtonBeingConfigured.getBounds().top, controller, orientation); mButtonBeingConfigured.getBounds().top, controller, orientation);
mButtonBeingConfigured = null; mButtonBeingConfigured = null;
@ -419,7 +430,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (mDpadBeingConfigured == dpad) if (mDpadBeingConfigured == dpad)
{ {
// Persist button position by saving new place. // Persist button position by saving new place.
saveControlPosition(mDpadBeingConfigured.getId(0), saveControlPosition(mDpadBeingConfigured.getLegacyId(),
mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top, mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top,
controller, orientation); controller, orientation);
mDpadBeingConfigured = null; mDpadBeingConfigured = null;
@ -452,7 +463,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
case MotionEvent.ACTION_POINTER_UP: case MotionEvent.ACTION_POINTER_UP:
if (mJoystickBeingConfigured != null) if (mJoystickBeingConfigured != null)
{ {
saveControlPosition(mJoystickBeingConfigured.getId(), saveControlPosition(mJoystickBeingConfigured.getLegacyId(),
mJoystickBeingConfigured.getBounds().left, mJoystickBeingConfigured.getBounds().left,
mJoystickBeingConfigured.getBounds().top, controller, orientation); mJoystickBeingConfigured.getBounds().top, controller, orientation);
mJoystickBeingConfigured = null; mJoystickBeingConfigured = null;
@ -464,6 +475,40 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
return true; return true;
} }
public void onDestroy()
{
unregisterControllers();
}
private void unregisterControllers()
{
if (mGameCubeRegistered)
InputOverrider.unregisterGameCube(0);
if (mWiiRegistered)
InputOverrider.unregisterWii(0);
mGameCubeRegistered = false;
mWiiRegistered = false;
}
private int getAnalogControlForTrigger(int control)
{
switch (control)
{
case ControlId.GCPAD_L_DIGITAL:
return ControlId.GCPAD_L_ANALOG;
case ControlId.GCPAD_R_DIGITAL:
return ControlId.GCPAD_R_ANALOG;
case ControlId.CLASSIC_L_DIGITAL:
return ControlId.CLASSIC_L_ANALOG;
case ControlId.CLASSIC_R_DIGITAL:
return ControlId.CLASSIC_R_ANALOG;
default:
return -1;
}
}
private void setDpadState(InputOverlayDrawableDpad dpad, boolean up, boolean down, boolean left, private void setDpadState(InputOverlayDrawableDpad dpad, boolean up, boolean down, boolean left,
boolean right) boolean right)
{ {
@ -500,61 +545,70 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_0.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_0.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_a, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_a,
R.drawable.gcpad_a_pressed, ButtonType.BUTTON_A, orientation)); R.drawable.gcpad_a_pressed, ButtonType.BUTTON_A, ControlId.GCPAD_A_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_1.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_1.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_b, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_b,
R.drawable.gcpad_b_pressed, ButtonType.BUTTON_B, orientation)); R.drawable.gcpad_b_pressed, ButtonType.BUTTON_B, ControlId.GCPAD_B_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_2.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_2.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_x, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_x,
R.drawable.gcpad_x_pressed, ButtonType.BUTTON_X, orientation)); R.drawable.gcpad_x_pressed, ButtonType.BUTTON_X, ControlId.GCPAD_X_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_3.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_3.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_y, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_y,
R.drawable.gcpad_y_pressed, ButtonType.BUTTON_Y, orientation)); R.drawable.gcpad_y_pressed, ButtonType.BUTTON_Y, ControlId.GCPAD_Y_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_4.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_4.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_z, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_z,
R.drawable.gcpad_z_pressed, ButtonType.BUTTON_Z, orientation)); R.drawable.gcpad_z_pressed, ButtonType.BUTTON_Z, ControlId.GCPAD_Z_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_5.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_5.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_start, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_start,
R.drawable.gcpad_start_pressed, ButtonType.BUTTON_START, orientation)); R.drawable.gcpad_start_pressed, ButtonType.BUTTON_START, ControlId.GCPAD_START_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_6.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_6.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_l, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_l,
R.drawable.gcpad_l_pressed, ButtonType.TRIGGER_L, orientation)); R.drawable.gcpad_l_pressed, ButtonType.TRIGGER_L, ControlId.GCPAD_L_DIGITAL,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_7.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_7.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_r, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.gcpad_r,
R.drawable.gcpad_r_pressed, ButtonType.TRIGGER_R, orientation)); R.drawable.gcpad_r_pressed, ButtonType.TRIGGER_R, ControlId.GCPAD_R_DIGITAL,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_8.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_8.getBooleanGlobal())
{ {
overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad, overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad,
R.drawable.gcwii_dpad_pressed_one_direction, R.drawable.gcwii_dpad_pressed_one_direction,
R.drawable.gcwii_dpad_pressed_two_directions, R.drawable.gcwii_dpad_pressed_two_directions,
ButtonType.BUTTON_UP, ButtonType.BUTTON_DOWN, ButtonType.BUTTON_UP, ControlId.GCPAD_DPAD_UP, ControlId.GCPAD_DPAD_DOWN,
ButtonType.BUTTON_LEFT, ButtonType.BUTTON_RIGHT, orientation)); ControlId.GCPAD_DPAD_LEFT, ControlId.GCPAD_DPAD_RIGHT, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_9.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_9.getBooleanGlobal())
{ {
overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range, overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range,
R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed, ButtonType.STICK_MAIN, R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed, ButtonType.STICK_MAIN,
orientation)); ControlId.GCPAD_MAIN_STICK_X, ControlId.GCPAD_MAIN_STICK_Y, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_10.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_GC_10.getBooleanGlobal())
{ {
overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range, overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range,
R.drawable.gcpad_c, R.drawable.gcpad_c_pressed, ButtonType.STICK_C, orientation)); R.drawable.gcpad_c, R.drawable.gcpad_c_pressed, ButtonType.STICK_C,
ControlId.GCPAD_C_STICK_X, ControlId.GCPAD_C_STICK_Y, orientation));
} }
} }
@ -563,45 +617,52 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_0.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_0.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_a, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_a,
R.drawable.wiimote_a_pressed, ButtonType.WIIMOTE_BUTTON_A, orientation)); R.drawable.wiimote_a_pressed, ButtonType.WIIMOTE_BUTTON_A, ControlId.WIIMOTE_A_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_1.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_1.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_b, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_b,
R.drawable.wiimote_b_pressed, ButtonType.WIIMOTE_BUTTON_B, orientation)); R.drawable.wiimote_b_pressed, ButtonType.WIIMOTE_BUTTON_B, ControlId.WIIMOTE_B_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_2.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_2.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_one, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_one,
R.drawable.wiimote_one_pressed, ButtonType.WIIMOTE_BUTTON_1, orientation)); R.drawable.wiimote_one_pressed, ButtonType.WIIMOTE_BUTTON_1,
ControlId.WIIMOTE_ONE_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_3.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_3.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_two, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_two,
R.drawable.wiimote_two_pressed, ButtonType.WIIMOTE_BUTTON_2, orientation)); R.drawable.wiimote_two_pressed, ButtonType.WIIMOTE_BUTTON_2,
ControlId.WIIMOTE_TWO_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_4.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_4.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_plus, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_plus,
R.drawable.wiimote_plus_pressed, ButtonType.WIIMOTE_BUTTON_PLUS, orientation)); R.drawable.wiimote_plus_pressed, ButtonType.WIIMOTE_BUTTON_PLUS,
ControlId.WIIMOTE_PLUS_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_5.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_5.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_minus, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_minus,
R.drawable.wiimote_minus_pressed, ButtonType.WIIMOTE_BUTTON_MINUS, orientation)); R.drawable.wiimote_minus_pressed, ButtonType.WIIMOTE_BUTTON_MINUS,
ControlId.WIIMOTE_MINUS_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_6.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_6.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_home, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_home,
R.drawable.wiimote_home_pressed, ButtonType.WIIMOTE_BUTTON_HOME, orientation)); R.drawable.wiimote_home_pressed, ButtonType.WIIMOTE_BUTTON_HOME,
ControlId.WIIMOTE_HOME_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_7.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_7.getBooleanGlobal())
{ {
overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad, overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad,
R.drawable.gcwii_dpad_pressed_one_direction, R.drawable.gcwii_dpad_pressed_one_direction,
R.drawable.gcwii_dpad_pressed_two_directions, R.drawable.gcwii_dpad_pressed_two_directions,
ButtonType.WIIMOTE_UP, ButtonType.WIIMOTE_DOWN, ButtonType.WIIMOTE_UP, ControlId.WIIMOTE_DPAD_UP, ControlId.WIIMOTE_DPAD_DOWN,
ButtonType.WIIMOTE_LEFT, ButtonType.WIIMOTE_RIGHT, orientation)); ControlId.WIIMOTE_DPAD_LEFT, ControlId.WIIMOTE_DPAD_RIGHT, orientation));
} }
} }
@ -610,18 +671,21 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_8.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_8.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.nunchuk_c, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.nunchuk_c,
R.drawable.nunchuk_c_pressed, ButtonType.NUNCHUK_BUTTON_C, orientation)); R.drawable.nunchuk_c_pressed, ButtonType.NUNCHUK_BUTTON_C, ControlId.NUNCHUK_C_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_9.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_9.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.nunchuk_z, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.nunchuk_z,
R.drawable.nunchuk_z_pressed, ButtonType.NUNCHUK_BUTTON_Z, orientation)); R.drawable.nunchuk_z_pressed, ButtonType.NUNCHUK_BUTTON_Z, ControlId.NUNCHUK_Z_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_10.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_WII_10.getBooleanGlobal())
{ {
overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range, overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range,
R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed, R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed,
ButtonType.NUNCHUK_STICK, orientation)); ButtonType.NUNCHUK_STICK, ControlId.NUNCHUK_STICK_X, ControlId.NUNCHUK_STICK_Y,
orientation));
} }
} }
@ -630,82 +694,97 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_0.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_0.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_a, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_a,
R.drawable.classic_a_pressed, ButtonType.CLASSIC_BUTTON_A, orientation)); R.drawable.classic_a_pressed, ButtonType.CLASSIC_BUTTON_A, ControlId.CLASSIC_A_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_1.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_1.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_b, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_b,
R.drawable.classic_b_pressed, ButtonType.CLASSIC_BUTTON_B, orientation)); R.drawable.classic_b_pressed, ButtonType.CLASSIC_BUTTON_B, ControlId.CLASSIC_B_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_2.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_2.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_x, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_x,
R.drawable.classic_x_pressed, ButtonType.CLASSIC_BUTTON_X, orientation)); R.drawable.classic_x_pressed, ButtonType.CLASSIC_BUTTON_X, ControlId.CLASSIC_X_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_3.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_3.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_y, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_y,
R.drawable.classic_y_pressed, ButtonType.CLASSIC_BUTTON_Y, orientation)); R.drawable.classic_y_pressed, ButtonType.CLASSIC_BUTTON_Y, ControlId.CLASSIC_Y_BUTTON,
orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_4.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_4.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_plus, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_plus,
R.drawable.wiimote_plus_pressed, ButtonType.CLASSIC_BUTTON_PLUS, orientation)); R.drawable.wiimote_plus_pressed, ButtonType.CLASSIC_BUTTON_PLUS,
ControlId.CLASSIC_PLUS_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_5.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_5.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_minus, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_minus,
R.drawable.wiimote_minus_pressed, ButtonType.CLASSIC_BUTTON_MINUS, orientation)); R.drawable.wiimote_minus_pressed, ButtonType.CLASSIC_BUTTON_MINUS,
ControlId.CLASSIC_MINUS_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_6.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_6.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_home, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.wiimote_home,
R.drawable.wiimote_home_pressed, ButtonType.CLASSIC_BUTTON_HOME, orientation)); R.drawable.wiimote_home_pressed, ButtonType.CLASSIC_BUTTON_HOME,
ControlId.CLASSIC_HOME_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_7.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_7.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_l, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_l,
R.drawable.classic_l_pressed, ButtonType.CLASSIC_TRIGGER_L, orientation)); R.drawable.classic_l_pressed, ButtonType.CLASSIC_TRIGGER_L,
ControlId.CLASSIC_L_DIGITAL, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_8.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_8.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_r, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_r,
R.drawable.classic_r_pressed, ButtonType.CLASSIC_TRIGGER_R, orientation)); R.drawable.classic_r_pressed, ButtonType.CLASSIC_TRIGGER_R,
ControlId.CLASSIC_R_DIGITAL, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_9.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_9.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_zl, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_zl,
R.drawable.classic_zl_pressed, ButtonType.CLASSIC_BUTTON_ZL, orientation)); R.drawable.classic_zl_pressed, ButtonType.CLASSIC_BUTTON_ZL,
ControlId.CLASSIC_ZL_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_10.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_10.getBooleanGlobal())
{ {
overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_zr, overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.classic_zr,
R.drawable.classic_zr_pressed, ButtonType.CLASSIC_BUTTON_ZR, orientation)); R.drawable.classic_zr_pressed, ButtonType.CLASSIC_BUTTON_ZR,
ControlId.CLASSIC_ZR_BUTTON, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_11.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_11.getBooleanGlobal())
{ {
overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad, overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.gcwii_dpad,
R.drawable.gcwii_dpad_pressed_one_direction, R.drawable.gcwii_dpad_pressed_one_direction,
R.drawable.gcwii_dpad_pressed_two_directions, R.drawable.gcwii_dpad_pressed_two_directions,
ButtonType.CLASSIC_DPAD_UP, ButtonType.CLASSIC_DPAD_DOWN, ButtonType.CLASSIC_DPAD_UP, ControlId.CLASSIC_DPAD_UP, ControlId.CLASSIC_DPAD_DOWN,
ButtonType.CLASSIC_DPAD_LEFT, ButtonType.CLASSIC_DPAD_RIGHT, orientation)); ControlId.CLASSIC_DPAD_LEFT, ControlId.CLASSIC_DPAD_RIGHT, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_12.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_12.getBooleanGlobal())
{ {
overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range, overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range,
R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed, R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed,
ButtonType.CLASSIC_STICK_LEFT, orientation)); ButtonType.CLASSIC_STICK_LEFT, ControlId.CLASSIC_LEFT_STICK_X,
ControlId.CLASSIC_LEFT_STICK_Y, orientation));
} }
if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_13.getBooleanGlobal()) if (BooleanSetting.MAIN_BUTTON_TOGGLE_CLASSIC_13.getBooleanGlobal())
{ {
overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range, overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.gcwii_joystick_range,
R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed, R.drawable.gcwii_joystick, R.drawable.gcwii_joystick_pressed,
ButtonType.CLASSIC_STICK_RIGHT, orientation)); ButtonType.CLASSIC_STICK_RIGHT, ControlId.CLASSIC_RIGHT_STICK_X,
ControlId.CLASSIC_RIGHT_STICK_Y, orientation));
} }
} }
public void refreshControls() public void refreshControls()
{ {
unregisterControllers();
// Remove all the overlay buttons from the HashSet. // Remove all the overlay buttons from the HashSet.
overlayButtons.removeAll(overlayButtons); overlayButtons.removeAll(overlayButtons);
overlayDpads.removeAll(overlayDpads); overlayDpads.removeAll(overlayDpads);
@ -734,6 +813,8 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
break; break;
case EMULATED_GAMECUBE_CONTROLLER: case EMULATED_GAMECUBE_CONTROLLER:
InputOverrider.registerGameCube(0);
mGameCubeRegistered = true;
addGameCubeOverlayControls(orientation); addGameCubeOverlayControls(orientation);
break; break;
@ -746,20 +827,28 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
switch (getConfiguredControllerType()) switch (getConfiguredControllerType())
{ {
case OVERLAY_GAMECUBE: case OVERLAY_GAMECUBE:
InputOverrider.registerGameCube(0);
mGameCubeRegistered = true;
addGameCubeOverlayControls(orientation); addGameCubeOverlayControls(orientation);
break; break;
case OVERLAY_WIIMOTE: case OVERLAY_WIIMOTE:
case OVERLAY_WIIMOTE_SIDEWAYS: case OVERLAY_WIIMOTE_SIDEWAYS:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addWiimoteOverlayControls(orientation); addWiimoteOverlayControls(orientation);
break; break;
case OVERLAY_WIIMOTE_NUNCHUK: case OVERLAY_WIIMOTE_NUNCHUK:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addWiimoteOverlayControls(orientation); addWiimoteOverlayControls(orientation);
addNunchukOverlayControls(orientation); addNunchukOverlayControls(orientation);
break; break;
case OVERLAY_WIIMOTE_CLASSIC: case OVERLAY_WIIMOTE_CLASSIC:
InputOverrider.registerWii(0);
mWiiRegistered = true;
addClassicOverlayControls(orientation); addClassicOverlayControls(orientation);
break; break;
@ -768,6 +857,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
} }
} }
} }
mIsFirstRun = false; mIsFirstRun = false;
invalidate(); invalidate();
} }
@ -891,11 +981,12 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param context The current {@link Context}. * @param context The current {@link Context}.
* @param defaultResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Default State). * @param defaultResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Default State).
* @param pressedResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Pressed State). * @param pressedResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Pressed State).
* @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. * @param legacyId Legacy identifier for the button the InputOverlayDrawableButton represents.
* @param control Control identifier for the button the InputOverlayDrawableButton represents.
* @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set. * @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set.
*/ */
private static InputOverlayDrawableButton initializeOverlayButton(Context context, private static InputOverlayDrawableButton initializeOverlayButton(Context context,
int defaultResId, int pressedResId, int buttonId, String orientation) int defaultResId, int pressedResId, int legacyId, int control, String orientation)
{ {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
final Resources res = context.getResources(); final Resources res = context.getResources();
@ -907,7 +998,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Decide scale based on button ID and user preference // Decide scale based on button ID and user preference
float scale; float scale;
switch (buttonId) switch (legacyId)
{ {
case ButtonType.BUTTON_A: case ButtonType.BUTTON_A:
case ButtonType.WIIMOTE_BUTTON_B: case ButtonType.WIIMOTE_BUTTON_B:
@ -961,12 +1052,13 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
final Bitmap pressedStateBitmap = final Bitmap pressedStateBitmap =
resizeBitmap(context, BitmapFactory.decodeResource(res, pressedResId), scale); resizeBitmap(context, BitmapFactory.decodeResource(res, pressedResId), scale);
final InputOverlayDrawableButton overlayDrawable = final InputOverlayDrawableButton overlayDrawable =
new InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId); new InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, legacyId,
control);
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(buttonId, controller, orientation), 0f); int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(buttonId, controller, orientation), 0f); int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
int width = overlayDrawable.getWidth(); int width = overlayDrawable.getWidth();
int height = overlayDrawable.getHeight(); int height = overlayDrawable.getHeight();
@ -989,20 +1081,22 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param defaultResId The {@link Bitmap} resource ID of the default sate. * @param defaultResId The {@link Bitmap} resource ID of the default sate.
* @param pressedOneDirectionResId The {@link Bitmap} resource ID of the pressed sate in one direction. * @param pressedOneDirectionResId The {@link Bitmap} resource ID of the pressed sate in one direction.
* @param pressedTwoDirectionsResId The {@link Bitmap} resource ID of the pressed sate in two directions. * @param pressedTwoDirectionsResId The {@link Bitmap} resource ID of the pressed sate in two directions.
* @param buttonUp Identifier for the up button. * @param legacyId Legacy identifier for the up button.
* @param buttonDown Identifier for the down button. * @param upControl Control identifier for the up button.
* @param buttonLeft Identifier for the left button. * @param downControl Control identifier for the down button.
* @param buttonRight Identifier for the right button. * @param leftControl Control identifier for the left button.
* @param rightControl Control identifier for the right button.
* @return the initialized {@link InputOverlayDrawableDpad} * @return the initialized {@link InputOverlayDrawableDpad}
*/ */
private static InputOverlayDrawableDpad initializeOverlayDpad(Context context, private static InputOverlayDrawableDpad initializeOverlayDpad(Context context,
int defaultResId, int defaultResId,
int pressedOneDirectionResId, int pressedOneDirectionResId,
int pressedTwoDirectionsResId, int pressedTwoDirectionsResId,
int buttonUp, int legacyId,
int buttonDown, int upControl,
int buttonLeft, int downControl,
int buttonRight, int leftControl,
int rightControl,
String orientation) String orientation)
{ {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
@ -1015,7 +1109,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Decide scale based on button ID and user preference // Decide scale based on button ID and user preference
float scale; float scale;
switch (buttonUp) switch (legacyId)
{ {
case ButtonType.BUTTON_UP: case ButtonType.BUTTON_UP:
scale = 0.2375f; scale = 0.2375f;
@ -1046,12 +1140,12 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
final InputOverlayDrawableDpad overlayDrawable = final InputOverlayDrawableDpad overlayDrawable =
new InputOverlayDrawableDpad(res, defaultStateBitmap, new InputOverlayDrawableDpad(res, defaultStateBitmap,
pressedOneDirectionStateBitmap, pressedTwoDirectionsStateBitmap, pressedOneDirectionStateBitmap, pressedTwoDirectionsStateBitmap,
buttonUp, buttonDown, buttonLeft, buttonRight); legacyId, upControl, downControl, leftControl, rightControl);
// The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(buttonUp, controller, orientation), 0f); int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(buttonUp, controller, orientation), 0f); int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
int width = overlayDrawable.getWidth(); int width = overlayDrawable.getWidth();
int height = overlayDrawable.getHeight(); int height = overlayDrawable.getHeight();
@ -1074,11 +1168,14 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
* @param resOuter Resource ID for the outer image of the joystick (the static image that shows the circular bounds). * @param resOuter Resource ID for the outer image of the joystick (the static image that shows the circular bounds).
* @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around).
* @param pressedResInner Resource ID for the pressed inner image of the joystick. * @param pressedResInner Resource ID for the pressed inner image of the joystick.
* @param joystick Identifier for which joystick this is. * @param legacyId Legacy identifier (ButtonType) for which joystick this is.
* @param xControl Control identifier for the X axis.
* @param yControl Control identifier for the Y axis.
* @return the initialized {@link InputOverlayDrawableJoystick}. * @return the initialized {@link InputOverlayDrawableJoystick}.
*/ */
private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context, private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context,
int resOuter, int defaultResInner, int pressedResInner, int joystick, String orientation) int resOuter, int defaultResInner, int pressedResInner, int legacyId, int xControl,
int yControl, String orientation)
{ {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
final Resources res = context.getResources(); final Resources res = context.getResources();
@ -1100,13 +1197,13 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
int drawableX = (int) sPrefs.getFloat(getXKey(joystick, controller, orientation), 0f); int drawableX = (int) sPrefs.getFloat(getXKey(legacyId, controller, orientation), 0f);
int drawableY = (int) sPrefs.getFloat(getYKey(joystick, controller, orientation), 0f); int drawableY = (int) sPrefs.getFloat(getYKey(legacyId, controller, orientation), 0f);
// Decide inner scale based on joystick ID // Decide inner scale based on joystick ID
float innerScale; float innerScale;
if (joystick == ButtonType.STICK_C) if (legacyId == ButtonType.STICK_C)
{ {
innerScale = 1.833f; innerScale = 1.833f;
} }
@ -1124,7 +1221,7 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener
// Send the drawableId to the joystick so it can be referenced when saving control position. // Send the drawableId to the joystick so it can be referenced when saving control position.
final InputOverlayDrawableJoystick overlayDrawable = final InputOverlayDrawableJoystick overlayDrawable =
new InputOverlayDrawableJoystick(res, bitmapOuter, bitmapInnerDefault, new InputOverlayDrawableJoystick(res, bitmapOuter, bitmapInnerDefault,
bitmapInnerPressed, outerRect, innerRect, joystick); bitmapInnerPressed, outerRect, innerRect, legacyId, xControl, yControl);
// Need to set the image's position // Need to set the image's position
overlayDrawable.setPosition(drawableX, drawableY); overlayDrawable.setPosition(drawableX, drawableY);

View File

@ -18,8 +18,9 @@ import android.view.MotionEvent;
*/ */
public final class InputOverlayDrawableButton public final class InputOverlayDrawableButton
{ {
// The ID identifying what type of button this Drawable represents. // The legacy ID identifying what type of button this Drawable represents.
private int mButtonType; private int mLegacyId;
private int mControl;
private int mTrackId; private int mTrackId;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
@ -35,28 +36,33 @@ public final class InputOverlayDrawableButton
* @param res {@link Resources} instance. * @param res {@link Resources} instance.
* @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable. * @param defaultStateBitmap {@link Bitmap} to use with the default state Drawable.
* @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable. * @param pressedStateBitmap {@link Bitmap} to use with the pressed state Drawable.
* @param buttonType Identifier for this type of button. * @param legacyId Legacy identifier (ButtonType) for this type of button.
* @param control Control ID for this type of button.
*/ */
public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap, public InputOverlayDrawableButton(Resources res, Bitmap defaultStateBitmap,
Bitmap pressedStateBitmap, int buttonType) Bitmap pressedStateBitmap, int legacyId, int control)
{ {
mTrackId = -1; mTrackId = -1;
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap); mPressedStateBitmap = new BitmapDrawable(res, pressedStateBitmap);
mButtonType = buttonType; mLegacyId = legacyId;
mControl = control;
mWidth = mDefaultStateBitmap.getIntrinsicWidth(); mWidth = mDefaultStateBitmap.getIntrinsicWidth();
mHeight = mDefaultStateBitmap.getIntrinsicHeight(); mHeight = mDefaultStateBitmap.getIntrinsicHeight();
} }
/** /**
* Gets this InputOverlayDrawableButton's button ID. * Gets this InputOverlayDrawableButton's legacy button ID.
*
* @return this InputOverlayDrawableButton's button ID.
*/ */
public int getId() public int getLegacyId()
{ {
return mButtonType; return mLegacyId;
}
public int getControl()
{
return mControl;
} }
public void setTrackId(int trackId) public void setTrackId(int trackId)

View File

@ -18,8 +18,9 @@ import android.view.MotionEvent;
*/ */
public final class InputOverlayDrawableDpad public final class InputOverlayDrawableDpad
{ {
// The ID identifying what type of button this Drawable represents. // The legacy ID identifying what type of button this Drawable represents.
private int[] mButtonType = new int[4]; private int mLegacyId;
private int[] mControls = new int[4];
private int mTrackId; private int mTrackId;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
@ -47,17 +48,15 @@ public final class InputOverlayDrawableDpad
* @param defaultStateBitmap {@link Bitmap} of the default state. * @param defaultStateBitmap {@link Bitmap} of the default state.
* @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction. * @param pressedOneDirectionStateBitmap {@link Bitmap} of the pressed state in one direction.
* @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction. * @param pressedTwoDirectionsStateBitmap {@link Bitmap} of the pressed state in two direction.
* @param buttonUp Identifier for the up button. * @param legacyId Legacy identifier (ButtonType) for the up button.
* @param buttonDown Identifier for the down button. * @param upControl Control identifier for the up button.
* @param buttonLeft Identifier for the left button. * @param downControl Control identifier for the down button.
* @param buttonRight Identifier for the right button. * @param leftControl Control identifier for the left button.
* @param rightControl Control identifier for the right button.
*/ */
public InputOverlayDrawableDpad(Resources res, public InputOverlayDrawableDpad(Resources res, Bitmap defaultStateBitmap,
Bitmap defaultStateBitmap, Bitmap pressedOneDirectionStateBitmap, Bitmap pressedTwoDirectionsStateBitmap,
Bitmap pressedOneDirectionStateBitmap, int legacyId, int upControl, int downControl, int leftControl, int rightControl)
Bitmap pressedTwoDirectionsStateBitmap,
int buttonUp, int buttonDown,
int buttonLeft, int buttonRight)
{ {
mTrackId = -1; mTrackId = -1;
mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap); mDefaultStateBitmap = new BitmapDrawable(res, defaultStateBitmap);
@ -67,10 +66,11 @@ public final class InputOverlayDrawableDpad
mWidth = mDefaultStateBitmap.getIntrinsicWidth(); mWidth = mDefaultStateBitmap.getIntrinsicWidth();
mHeight = mDefaultStateBitmap.getIntrinsicHeight(); mHeight = mDefaultStateBitmap.getIntrinsicHeight();
mButtonType[0] = buttonUp; mLegacyId = legacyId;
mButtonType[1] = buttonDown; mControls[0] = upControl;
mButtonType[2] = buttonLeft; mControls[1] = downControl;
mButtonType[3] = buttonRight; mControls[2] = leftControl;
mControls[3] = rightControl;
} }
public void draw(Canvas canvas) public void draw(Canvas canvas)
@ -127,14 +127,17 @@ public final class InputOverlayDrawableDpad
} }
} }
/** public int getLegacyId()
* Gets one of the InputOverlayDrawableDpad's button IDs.
*
* @return the requested InputOverlayDrawableDpad's button ID.
*/
public int getId(int direction)
{ {
return mButtonType[direction]; return mLegacyId;
}
/**
* Gets one of the InputOverlayDrawableDpad's control IDs.
*/
public int getControl(int direction)
{
return mControls[direction];
} }
public void setTrackId(int trackId) public void setTrackId(int trackId)

View File

@ -12,7 +12,7 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.view.MotionEvent; import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.features.input.model.InputOverrider;
import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
/** /**
@ -21,10 +21,12 @@ import org.dolphinemu.dolphinemu.features.settings.model.BooleanSetting;
*/ */
public final class InputOverlayDrawableJoystick public final class InputOverlayDrawableJoystick
{ {
private final int[] axisIDs = {0, 0, 0, 0}; private float mCurrentX = 0.0f;
private final float[] axises = {0f, 0f}; private float mCurrentY = 0.0f;
private int trackId = -1; private int trackId = -1;
private final int mJoystickType; private final int mJoystickLegacyId;
private final int mJoystickXControl;
private final int mJoystickYControl;
private int mControlPositionX, mControlPositionY; private int mControlPositionX, mControlPositionY;
private int mPreviousTouchX, mPreviousTouchY; private int mPreviousTouchX, mPreviousTouchY;
private final int mWidth; private final int mWidth;
@ -47,16 +49,17 @@ public final class InputOverlayDrawableJoystick
* @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick. * @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick.
* @param rectOuter {@link Rect} which represents the outer joystick bounds. * @param rectOuter {@link Rect} which represents the outer joystick bounds.
* @param rectInner {@link Rect} which represents the inner joystick bounds. * @param rectInner {@link Rect} which represents the inner joystick bounds.
* @param joystick Identifier for which joystick this is. * @param legacyId Legacy identifier (ButtonType) for which joystick this is.
* @param xControl The control which the x value of the joystick will be written to.
* @param yControl The control which the y value of the joystick will be written to.
*/ */
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bitmapInnerDefault, public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bitmapInnerDefault,
Bitmap bitmapInnerPressed, Rect rectOuter, Rect rectInner, int joystick) Bitmap bitmapInnerPressed, Rect rectOuter, Rect rectInner, int legacyId, int xControl,
int yControl)
{ {
axisIDs[0] = joystick + 1; mJoystickLegacyId = legacyId;
axisIDs[1] = joystick + 2; mJoystickXControl = xControl;
axisIDs[2] = joystick + 3; mJoystickYControl = yControl;
axisIDs[3] = joystick + 4;
mJoystickType = joystick;
mOuterBitmap = new BitmapDrawable(res, bitmapOuter); mOuterBitmap = new BitmapDrawable(res, bitmapOuter);
mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault);
@ -76,13 +79,13 @@ public final class InputOverlayDrawableJoystick
} }
/** /**
* Gets this InputOverlayDrawableJoystick's button ID. * Gets this InputOverlayDrawableJoystick's legacy ID.
* *
* @return this InputOverlayDrawableJoystick's button ID. * @return this InputOverlayDrawableJoystick's legacy ID.
*/ */
public int getId() public int getLegacyId()
{ {
return mJoystickType; return mJoystickLegacyId;
} }
public void draw(Canvas canvas) public void draw(Canvas canvas)
@ -125,7 +128,7 @@ public final class InputOverlayDrawableJoystick
{ {
pressed = true; pressed = true;
mPressedState = false; mPressedState = false;
axises[0] = axises[1] = 0.0f; mCurrentX = mCurrentY = 0.0f;
mOuterBitmap.setAlpha(mOpacity); mOuterBitmap.setAlpha(mOpacity);
mBoundsBoxBitmap.setAlpha(0); mBoundsBoxBitmap.setAlpha(0);
setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
@ -153,10 +156,8 @@ public final class InputOverlayDrawableJoystick
maxX -= getVirtBounds().centerX(); maxX -= getVirtBounds().centerX();
touchY -= getVirtBounds().centerY(); touchY -= getVirtBounds().centerY();
maxY -= getVirtBounds().centerY(); maxY -= getVirtBounds().centerY();
final float AxisX = touchX / maxX; mCurrentX = touchX / maxX;
final float AxisY = touchY / maxY; mCurrentY = touchY / maxY;
axises[0] = AxisY;
axises[1] = AxisX;
SetInnerBounds(); SetInnerBounds();
} }
@ -193,36 +194,40 @@ public final class InputOverlayDrawableJoystick
} }
} }
public float getX()
public float[] getAxisValues()
{ {
float[] joyaxises = {0f, 0f, 0f, 0f}; return mCurrentX;
joyaxises[1] = Math.min(axises[0], 1.0f);
joyaxises[0] = Math.min(axises[0], 0.0f);
joyaxises[3] = Math.min(axises[1], 1.0f);
joyaxises[2] = Math.min(axises[1], 0.0f);
return joyaxises;
} }
public int[] getAxisIDs() public float getY()
{ {
return axisIDs; return mCurrentY;
}
public int getXControl()
{
return mJoystickXControl;
}
public int getYControl()
{
return mJoystickYControl;
} }
private void SetInnerBounds() private void SetInnerBounds()
{ {
double y = axises[0]; double x = mCurrentX;
double x = axises[1]; double y = mCurrentY;
double angle = Math.atan2(y, x) + Math.PI + Math.PI; double angle = Math.atan2(y, x) + Math.PI + Math.PI;
double radius = Math.hypot(y, x); double radius = Math.hypot(y, x);
double maxRadius = NativeLibrary.GetInputRadiusAtAngle(0, mJoystickType, angle); double maxRadius = InputOverrider.getGateRadiusAtAngle(0, mJoystickXControl, angle);
if (radius > maxRadius) if (radius > maxRadius)
{ {
y = maxRadius * Math.sin(angle); y = maxRadius * Math.sin(angle);
x = maxRadius * Math.cos(angle); x = maxRadius * Math.cos(angle);
axises[0] = (float) y; mCurrentY = (float) y;
axises[1] = (float) x; mCurrentX = (float) x;
} }
int pixelX = getVirtBounds().centerX() + (int) (x * (getVirtBounds().width() / 2)); int pixelX = getVirtBounds().centerX() + (int) (x * (getVirtBounds().width() / 2));

View File

@ -7,22 +7,20 @@ import android.os.Handler;
import android.view.MotionEvent; import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.input.model.InputOverrider;
import java.util.ArrayList; import java.util.ArrayList;
public class InputOverlayPointer 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;
public static final int MODE_DISABLED = 0; public static final int MODE_DISABLED = 0;
public static final int MODE_FOLLOW = 1; public static final int MODE_FOLLOW = 1;
public static final int MODE_DRAG = 2; public static final int MODE_DRAG = 2;
private final float[] axes = {0f, 0f}; private float mCurrentX = 0.0f;
private final float[] oldaxes = {0f, 0f}; private float mCurrentY = 0.0f;
private float mOldX = 0.0f;
private float mOldY = 0.0f;
private float mGameCenterX; private float mGameCenterX;
private float mGameCenterY; private float mGameCenterY;
@ -36,7 +34,7 @@ public class InputOverlayPointer
private boolean mRecenter; private boolean mRecenter;
private boolean doubleTap = false; private boolean doubleTap = false;
private int doubleTapButton; private int mDoubleTapControl;
private int trackId = -1; private int trackId = -1;
public static ArrayList<Integer> DOUBLE_TAP_OPTIONS = new ArrayList<>(); public static ArrayList<Integer> DOUBLE_TAP_OPTIONS = new ArrayList<>();
@ -49,9 +47,9 @@ public class InputOverlayPointer
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A); DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A);
} }
public InputOverlayPointer(Rect surfacePosition, int button, int mode, boolean recenter) public InputOverlayPointer(Rect surfacePosition, int doubleTapControl, int mode, boolean recenter)
{ {
doubleTapButton = button; mDoubleTapControl = doubleTapControl;
mMode = mode; mMode = mode;
mRecenter = recenter; mRecenter = recenter;
@ -112,15 +110,15 @@ public class InputOverlayPointer
if (mMode == MODE_FOLLOW) if (mMode == MODE_FOLLOW)
{ {
axes[0] = (event.getY(event.findPointerIndex(trackId)) - mGameCenterY) * mGameHeightHalfInv; mCurrentX = (event.getX(event.findPointerIndex(trackId)) - mGameCenterX) * mGameWidthHalfInv;
axes[1] = (event.getX(event.findPointerIndex(trackId)) - mGameCenterX) * mGameWidthHalfInv; mCurrentY = (event.getY(event.findPointerIndex(trackId)) - mGameCenterY) * mGameHeightHalfInv;
} }
else if (mMode == MODE_DRAG) else if (mMode == MODE_DRAG)
{ {
axes[0] = oldaxes[0] + mCurrentX = mOldX +
(event.getY(event.findPointerIndex(trackId)) - mTouchStartY) * mGameHeightHalfInv;
axes[1] = oldaxes[1] +
(event.getX(event.findPointerIndex(trackId)) - mTouchStartX) * mGameWidthHalfInv; (event.getX(event.findPointerIndex(trackId)) - mTouchStartX) * mGameWidthHalfInv;
mCurrentY = mOldY +
(event.getY(event.findPointerIndex(trackId)) - mTouchStartY) * mGameHeightHalfInv;
} }
} }
@ -130,11 +128,9 @@ public class InputOverlayPointer
{ {
if (doubleTap) if (doubleTap)
{ {
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, InputOverrider.setControlState(0, mDoubleTapControl, 1.0);
doubleTapButton, NativeLibrary.ButtonState.PRESSED); new Handler().postDelayed(() -> InputOverrider.setControlState(0, mDoubleTapControl, 0.0),
new Handler() 50);
.postDelayed(() -> NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice,
doubleTapButton, NativeLibrary.ButtonState.RELEASED), 50);
} }
else else
{ {
@ -146,23 +142,23 @@ public class InputOverlayPointer
private void updateOldAxes() private void updateOldAxes()
{ {
oldaxes[0] = axes[0]; mOldX = mCurrentX;
oldaxes[1] = axes[1]; mOldY = mCurrentY;
} }
private void reset() private void reset()
{ {
axes[0] = axes[1] = oldaxes[0] = oldaxes[1] = 0f; mCurrentX = mCurrentY = mOldX = mOldY = 0.0f;
} }
public float[] getAxisValues() public float getX()
{ {
float[] iraxes = {0f, 0f, 0f, 0f}; return mCurrentX;
iraxes[1] = axes[0]; }
iraxes[0] = axes[0];
iraxes[3] = axes[1]; public float getY()
iraxes[2] = axes[1]; {
return iraxes; return mCurrentY;
} }
public void setMode(int mode) public void setMode(int mode)

View File

@ -10,6 +10,7 @@ add_library(main SHARED
GameList/GameFile.cpp GameList/GameFile.cpp
GameList/GameFile.h GameList/GameFile.h
GameList/GameFileCache.cpp GameList/GameFileCache.cpp
Input/InputOverrider.cpp
IniFile.cpp IniFile.cpp
MainAndroid.cpp MainAndroid.cpp
RiivolutionPatches.cpp RiivolutionPatches.cpp

View File

@ -0,0 +1,62 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <jni.h>
#include "InputCommon/ControllerInterface/Touch/InputOverrider.h"
extern "C" {
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_registerGameCube(
JNIEnv*, jclass, int controller_index)
{
ciface::Touch::RegisterGameCubeInputOverrider(controller_index);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_registerWii(JNIEnv*, jclass,
int controller_index)
{
ciface::Touch::RegisterWiiInputOverrider(controller_index);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_unregisterGameCube(
JNIEnv*, jclass, int controller_index)
{
ciface::Touch::UnregisterGameCubeInputOverrider(controller_index);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_unregisterWii(
JNIEnv*, jclass, int controller_index)
{
ciface::Touch::UnregisterWiiInputOverrider(controller_index);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_setControlState(
JNIEnv*, jclass, int controller_index, int control, double state)
{
ciface::Touch::SetControlState(controller_index, static_cast<ciface::Touch::ControlID>(control),
state);
}
JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_clearControlState(
JNIEnv*, jclass, int controller_index, int control)
{
ciface::Touch::ClearControlState(controller_index,
static_cast<ciface::Touch::ControlID>(control));
}
JNIEXPORT double JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_InputOverrider_getGateRadiusAtAngle(
JNIEnv*, jclass, int controller_index, int stick, double angle)
{
const auto casted_stick = static_cast<ciface::Touch::ControlID>(stick);
return ciface::Touch::GetGateRadiusAtAngle(controller_index, casted_stick, angle);
}
};

View File

@ -302,13 +302,6 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetMotionSen
ciface::Android::SetMotionSensorsEnabled(accelerometer_enabled, gyroscope_enabled); ciface::Android::SetMotionSensorsEnabled(accelerometer_enabled, gyroscope_enabled);
} }
JNIEXPORT double JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetInputRadiusAtAngle(
JNIEnv*, jclass, int emu_pad_id, int stick, double angle)
{
const auto casted_stick = static_cast<ButtonManager::ButtonType>(stick);
return ButtonManager::GetInputRadiusAtAngle(emu_pad_id, casted_stick, angle);
}
JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env, JNIEXPORT jstring JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_GetVersionString(JNIEnv* env,
jclass) jclass)
{ {

View File

@ -36,62 +36,47 @@ static const u16 trigger_bitmasks[] = {
static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT, static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT,
PAD_BUTTON_RIGHT}; PAD_BUTTON_RIGHT};
static const char* const named_buttons[] = {"A", "B", "X", "Y", "Z", "Start"};
static const char* const named_triggers[] = {
// i18n: The left trigger button (labeled L on real controllers)
_trans("L"),
// i18n: The right trigger button (labeled R on real controllers)
_trans("R"),
// i18n: The left trigger button (labeled L on real controllers) used as an analog input
_trans("L-Analog"),
// i18n: The right trigger button (labeled R on real controllers) used as an analog input
_trans("R-Analog")};
GCPad::GCPad(const unsigned int index) : m_index(index) GCPad::GCPad(const unsigned int index) : m_index(index)
{ {
// buttons // buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); groups.emplace_back(m_buttons = new ControllerEmu::Buttons(BUTTONS_GROUP));
for (const char* named_button : named_buttons) for (const char* named_button : {A_BUTTON, B_BUTTON, X_BUTTON, Y_BUTTON, Z_BUTTON})
{ {
const bool is_start = named_button == std::string("Start"); m_buttons->AddInput(ControllerEmu::DoNotTranslate, named_button);
const ControllerEmu::Translatability translate =
is_start ? ControllerEmu::Translate : ControllerEmu::DoNotTranslate;
// i18n: The START/PAUSE button on GameCube controllers
std::string ui_name = is_start ? _trans("START") : named_button;
m_buttons->AddInput(translate, named_button, std::move(ui_name));
} }
// i18n: The START/PAUSE button on GameCube controllers
m_buttons->AddInput(ControllerEmu::Translate, START_BUTTON, _trans("START"));
// sticks // sticks
groups.emplace_back(m_main_stick = new ControllerEmu::OctagonAnalogStick( groups.emplace_back(m_main_stick = new ControllerEmu::OctagonAnalogStick(
"Main Stick", _trans("Control Stick"), MAIN_STICK_GATE_RADIUS)); MAIN_STICK_GROUP, _trans("Control Stick"), MAIN_STICK_GATE_RADIUS));
groups.emplace_back(m_c_stick = new ControllerEmu::OctagonAnalogStick( groups.emplace_back(m_c_stick = new ControllerEmu::OctagonAnalogStick(
"C-Stick", _trans("C Stick"), C_STICK_GATE_RADIUS)); C_STICK_GROUP, _trans("C Stick"), C_STICK_GATE_RADIUS));
// triggers // triggers
groups.emplace_back(m_triggers = new ControllerEmu::MixedTriggers(_trans("Triggers"))); groups.emplace_back(m_triggers = new ControllerEmu::MixedTriggers(TRIGGERS_GROUP));
for (const char* named_trigger : named_triggers) for (const char* named_trigger : {L_DIGITAL, R_DIGITAL, L_ANALOG, R_ANALOG})
{ {
m_triggers->AddInput(ControllerEmu::Translate, named_trigger); m_triggers->AddInput(ControllerEmu::Translate, named_trigger);
} }
// rumble // rumble
groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(_trans("Rumble"))); groups.emplace_back(m_rumble = new ControllerEmu::ControlGroup(RUMBLE_GROUP));
m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor")); m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor"));
// Microphone // Microphone
groups.emplace_back(m_mic = new ControllerEmu::Buttons(_trans("Microphone"))); groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP));
m_mic->AddInput(ControllerEmu::Translate, _trans("Button")); m_mic->AddInput(ControllerEmu::Translate, _trans("Button"));
// dpad // dpad
groups.emplace_back(m_dpad = new ControllerEmu::Buttons(_trans("D-Pad"))); groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP));
for (const char* named_direction : named_directions) for (const char* named_direction : named_directions)
{ {
m_dpad->AddInput(ControllerEmu::Translate, named_direction); m_dpad->AddInput(ControllerEmu::Translate, named_direction);
} }
// options // options
groups.emplace_back(m_options = new ControllerEmu::ControlGroup(_trans("Options"))); groups.emplace_back(m_options = new ControllerEmu::ControlGroup(OPTIONS_GROUP));
m_options->AddSetting( m_options->AddSetting(
&m_always_connected_setting, &m_always_connected_setting,
// i18n: Treat a controller as always being connected regardless of what // i18n: Treat a controller as always being connected regardless of what
@ -138,14 +123,15 @@ GCPadStatus GCPad::GetInput() const
const auto lock = GetStateLock(); const auto lock = GetStateLock();
GCPadStatus pad = {}; GCPadStatus pad = {};
if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected())) if (!(m_always_connected_setting.GetValue() || IsDefaultDeviceConnected() ||
m_input_override_function))
{ {
pad.isConnected = false; pad.isConnected = false;
return pad; return pad;
} }
// buttons // buttons
m_buttons->GetState(&pad.button, button_bitmasks); m_buttons->GetState(&pad.button, button_bitmasks, m_input_override_function);
// set analog A/B analog to full or w/e, prolly not needed // set analog A/B analog to full or w/e, prolly not needed
if (pad.button & PAD_BUTTON_A) if (pad.button & PAD_BUTTON_A)
@ -154,20 +140,20 @@ GCPadStatus GCPad::GetInput() const
pad.analogB = 0xFF; pad.analogB = 0xFF;
// dpad // dpad
m_dpad->GetState(&pad.button, dpad_bitmasks); m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function);
// sticks // sticks
const auto main_stick_state = m_main_stick->GetState(); const auto main_stick_state = m_main_stick->GetState(m_input_override_function);
pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1); pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1);
pad.stickY = MapFloat<u8>(main_stick_state.y, GCPadStatus::MAIN_STICK_CENTER_Y, 1); pad.stickY = MapFloat<u8>(main_stick_state.y, GCPadStatus::MAIN_STICK_CENTER_Y, 1);
const auto c_stick_state = m_c_stick->GetState(); const auto c_stick_state = m_c_stick->GetState(m_input_override_function);
pad.substickX = MapFloat<u8>(c_stick_state.x, GCPadStatus::C_STICK_CENTER_X, 1); pad.substickX = MapFloat<u8>(c_stick_state.x, GCPadStatus::C_STICK_CENTER_X, 1);
pad.substickY = MapFloat<u8>(c_stick_state.y, GCPadStatus::C_STICK_CENTER_Y, 1); pad.substickY = MapFloat<u8>(c_stick_state.y, GCPadStatus::C_STICK_CENTER_Y, 1);
// triggers // triggers
std::array<ControlState, 2> triggers; std::array<ControlState, 2> triggers;
m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data()); m_triggers->GetState(&pad.button, trigger_bitmasks, triggers.data(), m_input_override_function);
pad.triggerLeft = MapFloat<u8>(triggers[0], 0); pad.triggerLeft = MapFloat<u8>(triggers[0], 0);
pad.triggerRight = MapFloat<u8>(triggers[1], 0); pad.triggerRight = MapFloat<u8>(triggers[1], 0);

View File

@ -5,6 +5,8 @@
#include <string> #include <string>
#include "Common/Common.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h" #include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
@ -49,6 +51,31 @@ public:
static constexpr ControlState MAIN_STICK_GATE_RADIUS = 0.7937125; static constexpr ControlState MAIN_STICK_GATE_RADIUS = 0.7937125;
static constexpr ControlState C_STICK_GATE_RADIUS = 0.7221375; static constexpr ControlState C_STICK_GATE_RADIUS = 0.7221375;
static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
static constexpr const char* MAIN_STICK_GROUP = "Main Stick";
static constexpr const char* C_STICK_GROUP = "C-Stick";
static constexpr const char* DPAD_GROUP = _trans("D-Pad");
static constexpr const char* TRIGGERS_GROUP = _trans("Triggers");
static constexpr const char* RUMBLE_GROUP = _trans("Rumble");
static constexpr const char* MIC_GROUP = _trans("Microphone");
static constexpr const char* OPTIONS_GROUP = _trans("Options");
static constexpr const char* A_BUTTON = "A";
static constexpr const char* B_BUTTON = "B";
static constexpr const char* X_BUTTON = "X";
static constexpr const char* Y_BUTTON = "Y";
static constexpr const char* Z_BUTTON = "Z";
static constexpr const char* START_BUTTON = "Start";
// i18n: The left trigger button (labeled L on real controllers)
static constexpr const char* L_DIGITAL = _trans("L");
// i18n: The right trigger button (labeled R on real controllers)
static constexpr const char* R_DIGITAL = _trans("R");
// i18n: The left trigger button (labeled L on real controllers) used as an analog input
static constexpr const char* L_ANALOG = _trans("L-Analog");
// i18n: The right trigger button (labeled R on real controllers) used as an analog input
static constexpr const char* R_ANALOG = _trans("R-Analog");
private: private:
ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_buttons;
ControllerEmu::AnalogStick* m_main_stick; ControllerEmu::AnalogStick* m_main_stick;

View File

@ -119,8 +119,6 @@ int CSIDevice_GCController::RunBuffer(u8* buffer, int request_length)
void CSIDevice_GCController::HandleMoviePadStatus(int device_number, GCPadStatus* pad_status) void CSIDevice_GCController::HandleMoviePadStatus(int device_number, GCPadStatus* pad_status)
{ {
Movie::CallGCInputManip(pad_status, device_number);
Movie::SetPolledDevice(); Movie::SetPolledDevice();
if (NetPlay_GetInput(device_number, pad_status)) if (NetPlay_GetInput(device_number, pad_status))
{ {

View File

@ -5,6 +5,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <optional>
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
#include "Core/Config/SYSCONFSettings.h" #include "Core/Config/SYSCONFSettings.h"
@ -221,9 +222,10 @@ WiimoteCommon::AccelData ConvertAccelData(const Common::Vec3& accel, u16 zero_g,
u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))}); u16(std::clamp(std::lround(scaled_accel.z + zero_g), 0l, MAX_VALUE))});
} }
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed) void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed)
{ {
const auto cursor = ir_group->GetState(true); const auto cursor = ir_group->GetState(true, override_func);
if (!cursor.IsVisible()) if (!cursor.IsVisible())
{ {

View File

@ -15,6 +15,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h" #include "InputCommon/ControllerEmu/ControlGroup/IMUCursor.h"
#include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h" #include "InputCommon/ControllerEmu/ControlGroup/IMUGyroscope.h"
#include "InputCommon/ControllerEmu/ControlGroup/Tilt.h" #include "InputCommon/ControllerEmu/ControlGroup/Tilt.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
namespace WiimoteEmu namespace WiimoteEmu
{ {
@ -81,7 +82,8 @@ void ApproachAngleWithAccel(RotationalState* state, const Common::Vec3& target,
void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_group, float time_elapsed); void EmulateShake(PositionalState* state, ControllerEmu::Shake* shake_group, float time_elapsed);
void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed); void EmulateTilt(RotationalState* state, ControllerEmu::Tilt* tilt_group, float time_elapsed);
void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed); void EmulateSwing(MotionState* state, ControllerEmu::Force* swing_group, float time_elapsed);
void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group, float time_elapsed); void EmulatePoint(MotionState* state, ControllerEmu::Cursor* ir_group,
const ControllerEmu::InputOverrideFunction& override_func, float time_elapsed);
void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_group, void EmulateIMUCursor(IMUCursorState* state, ControllerEmu::IMUCursor* imu_ir_group,
ControllerEmu::IMUAccelerometer* imu_accelerometer_group, ControllerEmu::IMUAccelerometer* imu_accelerometer_group,
ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed); ControllerEmu::IMUGyroscope* imu_gyroscope_group, float time_elapsed);

View File

@ -39,34 +39,11 @@ constexpr std::array<u16, 9> classic_button_bitmasks{{
Classic::BUTTON_HOME, Classic::BUTTON_HOME,
}}; }};
constexpr std::array<std::string_view, 9> classic_button_names{{
"A",
"B",
"X",
"Y",
"ZL",
"ZR",
"-",
"+",
"Home",
}};
constexpr std::array<u16, 2> classic_trigger_bitmasks{{ constexpr std::array<u16, 2> classic_trigger_bitmasks{{
Classic::TRIGGER_L, Classic::TRIGGER_L,
Classic::TRIGGER_R, Classic::TRIGGER_R,
}}; }};
constexpr std::array<const char*, 4> classic_trigger_names{{
// i18n: The left trigger button (labeled L on real controllers)
_trans("L"),
// i18n: The right trigger button (labeled R on real controllers)
_trans("R"),
// i18n: The left trigger button (labeled L on real controllers) used as an analog input
_trans("L-Analog"),
// i18n: The right trigger button (labeled R on real controllers) used as an analog input
_trans("R-Analog"),
}};
constexpr std::array<u16, 4> classic_dpad_bitmasks{{ constexpr std::array<u16, 4> classic_dpad_bitmasks{{
Classic::PAD_UP, Classic::PAD_UP,
Classic::PAD_DOWN, Classic::PAD_DOWN,
@ -77,30 +54,30 @@ constexpr std::array<u16, 4> classic_dpad_bitmasks{{
Classic::Classic() : Extension1stParty("Classic", _trans("Classic Controller")) Classic::Classic() : Extension1stParty("Classic", _trans("Classic Controller"))
{ {
// buttons // buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); groups.emplace_back(m_buttons = new ControllerEmu::Buttons(BUTTONS_GROUP));
for (auto& button_name : classic_button_names) for (auto& button_name :
{A_BUTTON, B_BUTTON, X_BUTTON, Y_BUTTON, ZL_BUTTON, ZR_BUTTON, MINUS_BUTTON, PLUS_BUTTON})
{ {
std::string_view ui_name = (button_name == "Home") ? "HOME" : button_name; m_buttons->AddInput(ControllerEmu::DoNotTranslate, button_name);
m_buttons->AddInput(ControllerEmu::DoNotTranslate, std::string(button_name),
std::string(ui_name));
} }
m_buttons->AddInput(ControllerEmu::DoNotTranslate, HOME_BUTTON, "HOME");
// sticks // sticks
constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / CAL_STICK_RANGE; constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / CAL_STICK_RADIUS;
groups.emplace_back(m_left_stick = groups.emplace_back(m_left_stick =
new ControllerEmu::OctagonAnalogStick(_trans("Left Stick"), gate_radius)); new ControllerEmu::OctagonAnalogStick(LEFT_STICK_GROUP, gate_radius));
groups.emplace_back( groups.emplace_back(m_right_stick =
m_right_stick = new ControllerEmu::OctagonAnalogStick(_trans("Right Stick"), gate_radius)); new ControllerEmu::OctagonAnalogStick(RIGHT_STICK_GROUP, gate_radius));
// triggers // triggers
groups.emplace_back(m_triggers = new ControllerEmu::MixedTriggers(_trans("Triggers"))); groups.emplace_back(m_triggers = new ControllerEmu::MixedTriggers(TRIGGERS_GROUP));
for (const char* trigger_name : classic_trigger_names) for (const char* trigger_name : {L_DIGITAL, R_DIGITAL, L_ANALOG, R_ANALOG})
{ {
m_triggers->AddInput(ControllerEmu::Translate, trigger_name); m_triggers->AddInput(ControllerEmu::Translate, trigger_name);
} }
// dpad // dpad
groups.emplace_back(m_dpad = new ControllerEmu::Buttons(_trans("D-Pad"))); groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP));
for (const char* named_direction : named_directions) for (const char* named_direction : named_directions)
{ {
m_dpad->AddInput(ControllerEmu::Translate, named_direction); m_dpad->AddInput(ControllerEmu::Translate, named_direction);
@ -113,20 +90,22 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// left stick // left stick
{ {
const ControllerEmu::AnalogStick::StateData left_stick_state = m_left_stick->GetState(); const ControllerEmu::AnalogStick::StateData left_stick_state =
m_left_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.x * LEFT_STICK_RADIUS)); const u8 x = MapFloat<u8>(left_stick_state.x, LEFT_STICK_CENTER, 0, LEFT_STICK_RANGE);
const u8 y = static_cast<u8>(LEFT_STICK_CENTER + (left_stick_state.y * LEFT_STICK_RADIUS)); const u8 y = MapFloat<u8>(left_stick_state.y, LEFT_STICK_CENTER, 0, LEFT_STICK_RANGE);
classic_data.SetLeftStick({x, y}); classic_data.SetLeftStick({x, y});
} }
// right stick // right stick
{ {
const ControllerEmu::AnalogStick::StateData right_stick_data = m_right_stick->GetState(); const ControllerEmu::AnalogStick::StateData right_stick_data =
m_right_stick->GetState(m_input_override_function);
const u8 x = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.x * RIGHT_STICK_RADIUS)); const u8 x = MapFloat<u8>(right_stick_data.x, RIGHT_STICK_CENTER, 0, RIGHT_STICK_RANGE);
const u8 y = static_cast<u8>(RIGHT_STICK_CENTER + (right_stick_data.y * RIGHT_STICK_RADIUS)); const u8 y = MapFloat<u8>(right_stick_data.y, RIGHT_STICK_CENTER, 0, RIGHT_STICK_RANGE);
classic_data.SetRightStick({x, y}); classic_data.SetRightStick({x, y});
} }
@ -135,19 +114,20 @@ void Classic::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// triggers // triggers
{ {
ControlState trigs[2] = {0, 0}; ControlState triggers[2] = {0, 0};
m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), trigs); m_triggers->GetState(&buttons, classic_trigger_bitmasks.data(), triggers,
m_input_override_function);
const u8 lt = static_cast<u8>(trigs[0] * TRIGGER_RANGE); const u8 lt = MapFloat<u8>(triggers[0], 0, 0, TRIGGER_RANGE);
const u8 rt = static_cast<u8>(trigs[1] * TRIGGER_RANGE); const u8 rt = MapFloat<u8>(triggers[1], 0, 0, TRIGGER_RANGE);
classic_data.SetLeftTrigger(lt); classic_data.SetLeftTrigger(lt);
classic_data.SetRightTrigger(rt); classic_data.SetRightTrigger(rt);
} }
// buttons and dpad // buttons and dpad
m_buttons->GetState(&buttons, classic_button_bitmasks.data()); m_buttons->GetState(&buttons, classic_button_bitmasks.data(), m_input_override_function);
m_dpad->GetState(&buttons, classic_dpad_bitmasks.data()); m_dpad->GetState(&buttons, classic_dpad_bitmasks.data(), m_input_override_function);
classic_data.SetButtons(buttons); classic_data.SetButtons(buttons);

View File

@ -205,16 +205,42 @@ public:
static constexpr u8 STICK_GATE_RADIUS = 0x61; static constexpr u8 STICK_GATE_RADIUS = 0x61;
static constexpr u8 CAL_STICK_CENTER = 0x80; static constexpr u8 CAL_STICK_CENTER = 0x80;
static constexpr u8 CAL_STICK_RANGE = 0x7f; static constexpr u8 CAL_STICK_RADIUS = 0x7f;
static constexpr u8 CAL_STICK_RANGE = 0xff;
static constexpr u8 LEFT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - LEFT_STICK_BITS); static constexpr u8 LEFT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - LEFT_STICK_BITS);
static constexpr u8 LEFT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - LEFT_STICK_BITS); static constexpr u8 LEFT_STICK_RANGE = CAL_STICK_RANGE >> (CAL_STICK_BITS - LEFT_STICK_BITS);
static constexpr u8 RIGHT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - RIGHT_STICK_BITS); static constexpr u8 RIGHT_STICK_CENTER = CAL_STICK_CENTER >> (CAL_STICK_BITS - RIGHT_STICK_BITS);
static constexpr u8 RIGHT_STICK_RADIUS = CAL_STICK_RANGE >> (CAL_STICK_BITS - RIGHT_STICK_BITS); static constexpr u8 RIGHT_STICK_RANGE = CAL_STICK_RANGE >> (CAL_STICK_BITS - RIGHT_STICK_BITS);
static constexpr u8 TRIGGER_RANGE = 0x1F; static constexpr u8 TRIGGER_RANGE = 0x1F;
static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
static constexpr const char* LEFT_STICK_GROUP = _trans("Left Stick");
static constexpr const char* RIGHT_STICK_GROUP = _trans("Right Stick");
static constexpr const char* TRIGGERS_GROUP = _trans("Triggers");
static constexpr const char* DPAD_GROUP = _trans("D-Pad");
static constexpr const char* A_BUTTON = "A";
static constexpr const char* B_BUTTON = "B";
static constexpr const char* X_BUTTON = "X";
static constexpr const char* Y_BUTTON = "Y";
static constexpr const char* ZL_BUTTON = "ZL";
static constexpr const char* ZR_BUTTON = "ZR";
static constexpr const char* MINUS_BUTTON = "-";
static constexpr const char* PLUS_BUTTON = "+";
static constexpr const char* HOME_BUTTON = "Home";
// i18n: The left trigger button (labeled L on real controllers)
static constexpr const char* L_DIGITAL = _trans("L");
// i18n: The right trigger button (labeled R on real controllers)
static constexpr const char* R_DIGITAL = _trans("R");
// i18n: The left trigger button (labeled L on real controllers) used as an analog input
static constexpr const char* L_ANALOG = _trans("L-Analog");
// i18n: The right trigger button (labeled R on real controllers) used as an analog input
static constexpr const char* R_ANALOG = _trans("R-Analog");
private: private:
ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_buttons;
ControllerEmu::MixedTriggers* m_triggers; ControllerEmu::MixedTriggers* m_triggers;

View File

@ -44,12 +44,12 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
// the "Drawsome" game expects you to go "off screen" a bit to access some menu items. // the "Drawsome" game expects you to go "off screen" a bit to access some menu items.
constexpr u16 MIN_Y = 0x15ff + 0x100; constexpr u16 MIN_Y = 0x15ff + 0x100;
constexpr u16 MAX_Y = 0x00; constexpr u16 MAX_Y = 0x00;
constexpr double CENTER_X = (MAX_X + MIN_X) / 2.0; constexpr u16 CENTER_X = (MAX_X + MIN_X + 1) / 2;
constexpr double CENTER_Y = (MAX_Y + MIN_Y) / 2.0; constexpr u16 CENTER_Y = (MAX_Y + MIN_Y + 1) / 2;
const auto stylus_state = m_stylus->GetState(); const auto stylus_state = m_stylus->GetState(m_input_override_function);
const auto stylus_x = u16(std::lround(CENTER_X + stylus_state.x * (MAX_X - CENTER_X))); const u16 stylus_x = MapFloat<u16>(stylus_state.x, CENTER_X, MIN_X, MAX_X);
const auto stylus_y = u16(std::lround(CENTER_Y + stylus_state.y * (MAX_Y - CENTER_Y))); const u16 stylus_y = MapFloat<u16>(-stylus_state.y, CENTER_Y, MAX_Y, MIN_Y);
tablet_data.stylus_x1 = u8(stylus_x); tablet_data.stylus_x1 = u8(stylus_x);
tablet_data.stylus_x2 = u8(stylus_x >> 8); tablet_data.stylus_x2 = u8(stylus_x >> 8);
@ -74,8 +74,8 @@ void DrawsomeTablet::BuildDesiredExtensionState(DesiredExtensionState* target_st
// Pressure (0 - 0x7ff): // Pressure (0 - 0x7ff):
constexpr u16 MAX_PRESSURE = 0x7ff; constexpr u16 MAX_PRESSURE = 0x7ff;
const auto touch_state = m_touch->GetState(); const auto touch_state = m_touch->GetState(m_input_override_function);
const auto pressure = u16(std::lround(touch_state.data[0] * MAX_PRESSURE)); const u16 pressure = MapFloat<u16>(touch_state.data[0], 0, 0, MAX_PRESSURE);
tablet_data.pressure1 = u8(pressure); tablet_data.pressure1 = u8(pressure);
tablet_data.pressure2 = u8(pressure >> 8); tablet_data.pressure2 = u8(pressure >> 8);

View File

@ -84,17 +84,20 @@ void Drums::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DesiredState& state = target_state->data.emplace<DesiredState>(); DesiredState& state = target_state->data.emplace<DesiredState>();
{ {
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
state.stick_x = MapFloat<u8>(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
state.stick_y = MapFloat<u8>(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
state.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX); state.stick_x = MapFloat(stick_state.x, STICK_CENTER, STICK_MIN, STICK_MAX);
state.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX); state.stick_y = MapFloat(stick_state.y, STICK_CENTER, STICK_MIN, STICK_MAX);
} }
state.buttons = 0; state.buttons = 0;
m_buttons->GetState(&state.buttons, drum_button_bitmasks.data()); m_buttons->GetState(&state.buttons, drum_button_bitmasks.data(), m_input_override_function);
state.drum_pads = 0; state.drum_pads = 0;
m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data()); m_pads->GetState(&state.drum_pads, drum_pad_bitmasks.data(), m_input_override_function);
state.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100)); state.softness = u8(7 - std::lround(m_hit_strength_setting.GetValue() * 7 / 100));
} }

View File

@ -101,17 +101,19 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick // stick
{ {
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
guitar_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER); guitar_data.sx = MapFloat<u8>(stick_state.x, STICK_CENTER, 0, STICK_RANGE);
guitar_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER); guitar_data.sy = MapFloat<u8>(stick_state.y, STICK_CENTER, 0, STICK_RANGE);
} }
// slider bar // slider bar
if (m_slider_bar->controls[0]->control_ref->BoundCount() && if (m_slider_bar->controls[0]->control_ref->BoundCount() &&
m_slider_bar->controls[1]->control_ref->BoundCount()) m_slider_bar->controls[1]->control_ref->BoundCount())
{ {
const ControllerEmu::Slider::StateData slider_data = m_slider_bar->GetState(); const ControllerEmu::Slider::StateData slider_data =
m_slider_bar->GetState(m_input_override_function);
guitar_data.sb = s_slider_bar_control_codes.lower_bound(slider_data.value)->second; guitar_data.sb = s_slider_bar_control_codes.lower_bound(slider_data.value)->second;
} }
@ -122,17 +124,18 @@ void Guitar::BuildDesiredExtensionState(DesiredExtensionState* target_state)
} }
// whammy bar // whammy bar
const ControllerEmu::Triggers::StateData whammy_state = m_whammy->GetState(); const ControllerEmu::Triggers::StateData whammy_state =
guitar_data.whammy = static_cast<u8>(whammy_state.data[0] * 0x1F); m_whammy->GetState(m_input_override_function);
guitar_data.whammy = MapFloat<u8>(whammy_state.data[0], 0, 0, 0x1F);
// buttons // buttons
m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data()); m_buttons->GetState(&guitar_data.bt, guitar_button_bitmasks.data(), m_input_override_function);
// frets // frets
m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data()); m_frets->GetState(&guitar_data.bt, guitar_fret_bitmasks.data(), m_input_override_function);
// strum // strum
m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data()); m_strum->GetState(&guitar_data.bt, guitar_strum_bitmasks.data(), m_input_override_function);
// flip button bits // flip button bits
guitar_data.bt ^= 0xFFFF; guitar_data.bt ^= 0xFFFF;

View File

@ -69,6 +69,7 @@ public:
static const u8 STICK_CENTER = 0x20; static const u8 STICK_CENTER = 0x20;
static const u8 STICK_RADIUS = 0x1f; static const u8 STICK_RADIUS = 0x1f;
static const u8 STICK_RANGE = 0x3f;
// TODO: Test real hardware. Is this accurate? // TODO: Test real hardware. Is this accurate?
static const u8 STICK_GATE_RADIUS = 0x16; static const u8 STICK_GATE_RADIUS = 0x16;

View File

@ -37,14 +37,13 @@ constexpr std::array<u8, 2> nunchuk_button_bitmasks{{
Nunchuk::Nunchuk() : Extension1stParty(_trans("Nunchuk")) Nunchuk::Nunchuk() : Extension1stParty(_trans("Nunchuk"))
{ {
// buttons // buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); groups.emplace_back(m_buttons = new ControllerEmu::Buttons(BUTTONS_GROUP));
m_buttons->AddInput(ControllerEmu::DoNotTranslate, "C"); m_buttons->AddInput(ControllerEmu::DoNotTranslate, C_BUTTON);
m_buttons->AddInput(ControllerEmu::DoNotTranslate, "Z"); m_buttons->AddInput(ControllerEmu::DoNotTranslate, Z_BUTTON);
// stick // stick
constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / STICK_RADIUS; constexpr auto gate_radius = ControlState(STICK_GATE_RADIUS) / STICK_RADIUS;
groups.emplace_back(m_stick = groups.emplace_back(m_stick = new ControllerEmu::OctagonAnalogStick(STICK_GROUP, gate_radius));
new ControllerEmu::OctagonAnalogStick(_trans("Stick"), gate_radius));
// swing // swing
groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing")));
@ -59,7 +58,7 @@ Nunchuk::Nunchuk() : Extension1stParty(_trans("Nunchuk"))
// accelerometer // accelerometer
groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer(
"IMUAccelerometer", _trans("Accelerometer"))); ACCELEROMETER_GROUP, _trans("Accelerometer")));
} }
void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state) void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
@ -67,16 +66,20 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
DataFormat nc_data = {}; DataFormat nc_data = {};
// stick // stick
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); bool override_occurred = false;
nc_data.jx = u8(STICK_CENTER + stick_state.x * STICK_RADIUS); const ControllerEmu::AnalogStick::StateData stick_state =
nc_data.jy = u8(STICK_CENTER + stick_state.y * STICK_RADIUS); m_stick->GetState(m_input_override_function, &override_occurred);
nc_data.jx = MapFloat<u8>(stick_state.x, STICK_CENTER, 0, STICK_RANGE);
nc_data.jy = MapFloat<u8>(stick_state.y, STICK_CENTER, 0, STICK_RANGE);
if (!override_occurred)
{
// Some terribly coded games check whether to move with a check like // Some terribly coded games check whether to move with a check like
// //
// if (x != 0 && y != 0) // if (x != 0 && y != 0)
// do_movement(x, y); // do_movement(x, y);
// //
// With keyboard controls, these games break if you simply hit // With keyboard controls, these games break if you simply hit one
// of the axes. Adjust this if you're hitting one of the axes so that // of the axes. Adjust this if you're hitting one of the axes so that
// we slightly tweak the other axis. // we slightly tweak the other axis.
if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER) if (nc_data.jx != STICK_CENTER || nc_data.jy != STICK_CENTER)
@ -86,10 +89,11 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
if (nc_data.jy == STICK_CENTER) if (nc_data.jy == STICK_CENTER)
++nc_data.jy; ++nc_data.jy;
} }
}
// buttons // buttons
u8 buttons = 0; u8 buttons = 0;
m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data()); m_buttons->GetState(&buttons, nunchuk_button_bitmasks.data(), m_input_override_function);
nc_data.SetButtons(buttons); nc_data.SetButtons(buttons);
// Acceleration data: // Acceleration data:
@ -108,6 +112,8 @@ void Nunchuk::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// shake // shake
accel += m_shake_state.acceleration; accel += m_shake_state.acceleration;
accel = Wiimote::OverrideVec3(m_imu_accelerometer, accel, m_input_override_function);
// Calibration values are 8-bit but we want 10-bit precision, so << 2. // Calibration values are 8-bit but we want 10-bit precision, so << 2.
const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2); const auto acc = ConvertAccelData(accel, ACCEL_ZERO_G << 2, ACCEL_ONE_G << 2);
nc_data.SetAccel(acc.value); nc_data.SetAccel(acc.value);

View File

@ -5,6 +5,8 @@
#include <array> #include <array>
#include "Common/Common.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/Dynamics.h" #include "Core/HW/WiimoteEmu/Dynamics.h"
#include "Core/HW/WiimoteEmu/Extension/Extension.h" #include "Core/HW/WiimoteEmu/Extension/Extension.h"
@ -156,6 +158,8 @@ public:
ControllerEmu::ControlGroup* GetGroup(NunchukGroup group); ControllerEmu::ControlGroup* GetGroup(NunchukGroup group);
void LoadDefaults(const ControllerInterface& ciface) override;
static constexpr u8 BUTTON_C = 0x02; static constexpr u8 BUTTON_C = 0x02;
static constexpr u8 BUTTON_Z = 0x01; static constexpr u8 BUTTON_Z = 0x01;
@ -166,8 +170,14 @@ public:
static constexpr u8 STICK_CENTER = 0x80; static constexpr u8 STICK_CENTER = 0x80;
static constexpr u8 STICK_RADIUS = 0x7F; static constexpr u8 STICK_RADIUS = 0x7F;
static constexpr u8 STICK_RANGE = 0xFF;
void LoadDefaults(const ControllerInterface& ciface) override; static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
static constexpr const char* STICK_GROUP = _trans("Stick");
static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer";
static constexpr const char* C_BUTTON = "C";
static constexpr const char* Z_BUTTON = "Z";
private: private:
ControllerEmu::Tilt* m_tilt; ControllerEmu::Tilt* m_tilt;

View File

@ -64,8 +64,9 @@ void Shinkansen::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// guesses). // guesses).
const u8 brake_values[] = {0, 53, 79, 105, 132, 159, 187, 217, 250}; const u8 brake_values[] = {0, 53, 79, 105, 132, 159, 187, 217, 250};
const u8 power_values[] = {255, 229, 208, 189, 170, 153, 135, 118, 101, 85, 68, 51, 35, 17}; const u8 power_values[] = {255, 229, 208, 189, 170, 153, 135, 118, 101, 85, 68, 51, 35, 17};
state.brake = brake_values[size_t(analog[0] * (sizeof(brake_values) - 1))]; // Not casting from size_t would trigger a static assert in MapFloat due to its use of llround
state.power = power_values[size_t(analog[1] * (sizeof(power_values) - 1))]; state.brake = brake_values[MapFloat(analog[0], 0, 0, static_cast<int>(sizeof(brake_values) - 1))];
state.power = power_values[MapFloat(analog[1], 0, 0, static_cast<int>(sizeof(power_values) - 1))];
// Note: This currently assumes a little-endian host. // Note: This currently assumes a little-endian host.
const u16 button_bitmasks[] = { const u16 button_bitmasks[] = {

View File

@ -54,8 +54,8 @@ void TaTaCon::BuildDesiredExtensionState(DesiredExtensionState* target_state)
{ {
DataFormat tatacon_data = {}; DataFormat tatacon_data = {};
m_center->GetState(&tatacon_data.state, center_bitmasks.data()); m_center->GetState(&tatacon_data.state, center_bitmasks.data(), m_input_override_function);
m_rim->GetState(&tatacon_data.state, rim_bitmasks.data()); m_rim->GetState(&tatacon_data.state, rim_bitmasks.data(), m_input_override_function);
// Flip button bits. // Flip button bits.
tatacon_data.state ^= 0xff; tatacon_data.state ^= 0xff;

View File

@ -86,16 +86,17 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// stick // stick
{ {
const ControllerEmu::AnalogStick::StateData stick_state = m_stick->GetState(); const ControllerEmu::AnalogStick::StateData stick_state =
m_stick->GetState(m_input_override_function);
tt_data.sx = static_cast<u8>((stick_state.x * STICK_RADIUS) + STICK_CENTER); tt_data.sx = MapFloat<u8>(stick_state.x, STICK_CENTER, 0, STICK_RANGE);
tt_data.sy = static_cast<u8>((stick_state.y * STICK_RADIUS) + STICK_CENTER); tt_data.sy = MapFloat<u8>(stick_state.y, STICK_CENTER, 0, STICK_RANGE);
} }
// left table // left table
{ {
const ControllerEmu::Slider::StateData lt = m_left_table->GetState(); const ControllerEmu::Slider::StateData lt = m_left_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(lt.value * TABLE_RANGE); const s8 tt = MapFloat<u8>(lt.value, 0, 0, TABLE_RANGE);
tt_data.ltable1 = tt; tt_data.ltable1 = tt;
tt_data.ltable2 = tt >> 5; tt_data.ltable2 = tt >> 5;
@ -103,8 +104,8 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// right table // right table
{ {
const ControllerEmu::Slider::StateData rt = m_right_table->GetState(); const ControllerEmu::Slider::StateData rt = m_right_table->GetState(m_input_override_function);
const s8 tt = static_cast<s8>(rt.value * TABLE_RANGE); const s8 tt = MapFloat<u8>(rt.value, 0, 0, TABLE_RANGE);
tt_data.rtable1 = tt; tt_data.rtable1 = tt;
tt_data.rtable2 = tt >> 1; tt_data.rtable2 = tt >> 1;
@ -114,8 +115,8 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// effect dial // effect dial
{ {
const auto dial_state = m_effect_dial->GetState(); const auto dial_state = m_effect_dial->GetState(m_input_override_function);
const u8 dial = static_cast<u8>(dial_state.value * EFFECT_DIAL_RANGE) + EFFECT_DIAL_CENTER; const u8 dial = MapFloat<u8>(dial_state.value, EFFECT_DIAL_CENTER, 0, EFFECT_DIAL_RANGE);
tt_data.dial1 = dial; tt_data.dial1 = dial;
tt_data.dial2 = dial >> 3; tt_data.dial2 = dial >> 3;
@ -123,13 +124,13 @@ void Turntable::BuildDesiredExtensionState(DesiredExtensionState* target_state)
// crossfade slider // crossfade slider
{ {
const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState(); const ControllerEmu::Slider::StateData cfs = m_crossfade->GetState(m_input_override_function);
tt_data.slider = static_cast<u8>((cfs.value * CROSSFADE_RANGE) + CROSSFADE_CENTER); tt_data.slider = MapFloat<u8>(cfs.value, CROSSFADE_CENTER, 0, CROSSFADE_RANGE);
} }
// buttons // buttons
m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data()); m_buttons->GetState(&tt_data.bt, turntable_button_bitmasks.data(), m_input_override_function);
// flip button bits :/ // flip button bits :/
tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED | tt_data.bt ^= (BUTTON_L_GREEN | BUTTON_L_RED | BUTTON_L_BLUE | BUTTON_R_GREEN | BUTTON_R_RED |

View File

@ -78,6 +78,7 @@ public:
static constexpr int STICK_BIT_COUNT = 6; static constexpr int STICK_BIT_COUNT = 6;
static constexpr u8 STICK_CENTER = (1 << STICK_BIT_COUNT) / 2; static constexpr u8 STICK_CENTER = (1 << STICK_BIT_COUNT) / 2;
static constexpr u8 STICK_RADIUS = STICK_CENTER - 1; static constexpr u8 STICK_RADIUS = STICK_CENTER - 1;
static constexpr u8 STICK_RANGE = (1 << STICK_BIT_COUNT) - 1;
// TODO: Test real hardware. Is this accurate? // TODO: Test real hardware. Is this accurate?
static constexpr u8 STICK_GATE_RADIUS = 0x16; static constexpr u8 STICK_GATE_RADIUS = 0x16;
@ -86,11 +87,11 @@ public:
static constexpr int EFFECT_DIAL_BIT_COUNT = 5; static constexpr int EFFECT_DIAL_BIT_COUNT = 5;
static constexpr u8 EFFECT_DIAL_CENTER = (1 << EFFECT_DIAL_BIT_COUNT) / 2; static constexpr u8 EFFECT_DIAL_CENTER = (1 << EFFECT_DIAL_BIT_COUNT) / 2;
static constexpr u8 EFFECT_DIAL_RANGE = EFFECT_DIAL_CENTER - 1; static constexpr u8 EFFECT_DIAL_RANGE = (1 << EFFECT_DIAL_BIT_COUNT) - 1;
static constexpr int CROSSFADE_BIT_COUNT = 4; static constexpr int CROSSFADE_BIT_COUNT = 4;
static constexpr u8 CROSSFADE_CENTER = (1 << CROSSFADE_BIT_COUNT) / 2; static constexpr u8 CROSSFADE_CENTER = (1 << CROSSFADE_BIT_COUNT) / 2;
static constexpr u8 CROSSFADE_RANGE = CROSSFADE_CENTER - 1; static constexpr u8 CROSSFADE_RANGE = (1 << CROSSFADE_BIT_COUNT) - 1;
private: private:
ControllerEmu::Buttons* m_buttons; ControllerEmu::Buttons* m_buttons;

View File

@ -5,11 +5,13 @@
#include <algorithm> #include <algorithm>
#include <memory> #include <memory>
#include <optional>
#include <string_view> #include <string_view>
#include <fmt/format.h> #include <fmt/format.h>
#include "Common/Assert.h" #include "Common/Assert.h"
#include "Common/Common.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Config.h" #include "Common/Config/Config.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
@ -63,10 +65,6 @@ static const u16 dpad_bitmasks[] = {Wiimote::PAD_UP, Wiimote::PAD_DOWN, Wiimote:
static const u16 dpad_sideways_bitmasks[] = {Wiimote::PAD_RIGHT, Wiimote::PAD_LEFT, Wiimote::PAD_UP, static const u16 dpad_sideways_bitmasks[] = {Wiimote::PAD_RIGHT, Wiimote::PAD_LEFT, Wiimote::PAD_UP,
Wiimote::PAD_DOWN}; Wiimote::PAD_DOWN};
constexpr std::array<std::string_view, 7> named_buttons{
"A", "B", "1", "2", "-", "+", "Home",
};
void Wiimote::Reset() void Wiimote::Reset()
{ {
const bool want_determinism = Core::WantsDeterminism(); const bool want_determinism = Core::WantsDeterminism();
@ -211,24 +209,23 @@ void Wiimote::Reset()
Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(index) Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(index)
{ {
// Buttons // Buttons
groups.emplace_back(m_buttons = new ControllerEmu::Buttons(_trans("Buttons"))); groups.emplace_back(m_buttons = new ControllerEmu::Buttons(BUTTONS_GROUP));
for (auto& named_button : named_buttons) for (auto& named_button : {A_BUTTON, B_BUTTON, ONE_BUTTON, TWO_BUTTON, MINUS_BUTTON, PLUS_BUTTON})
{ {
std::string_view ui_name = (named_button == "Home") ? "HOME" : named_button; m_buttons->AddInput(ControllerEmu::DoNotTranslate, named_button);
m_buttons->AddInput(ControllerEmu::DoNotTranslate, std::string(named_button),
std::string(ui_name));
} }
m_buttons->AddInput(ControllerEmu::DoNotTranslate, HOME_BUTTON, "HOME");
// Pointing (IR) // Pointing (IR)
// i18n: "Point" refers to the action of pointing a Wii Remote. // i18n: "Point" refers to the action of pointing a Wii Remote.
groups.emplace_back(m_ir = new ControllerEmu::Cursor("IR", _trans("Point"))); groups.emplace_back(m_ir = new ControllerEmu::Cursor(IR_GROUP, _trans("Point")));
groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing"))); groups.emplace_back(m_swing = new ControllerEmu::Force(_trans("Swing")));
groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt"))); groups.emplace_back(m_tilt = new ControllerEmu::Tilt(_trans("Tilt")));
groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake"))); groups.emplace_back(m_shake = new ControllerEmu::Shake(_trans("Shake")));
groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer( groups.emplace_back(m_imu_accelerometer = new ControllerEmu::IMUAccelerometer(
"IMUAccelerometer", _trans("Accelerometer"))); ACCELEROMETER_GROUP, _trans("Accelerometer")));
groups.emplace_back(m_imu_gyroscope = groups.emplace_back(m_imu_gyroscope =
new ControllerEmu::IMUGyroscope("IMUGyroscope", _trans("Gyroscope"))); new ControllerEmu::IMUGyroscope(GYROSCOPE_GROUP, _trans("Gyroscope")));
groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point"))); groups.emplace_back(m_imu_ir = new ControllerEmu::IMUCursor("IMUIR", _trans("Point")));
const auto fov_default = const auto fov_default =
@ -272,7 +269,7 @@ Wiimote::Wiimote(const unsigned int index) : m_index(index), m_bt_device_index(i
m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor")); m_rumble->AddOutput(ControllerEmu::Translate, _trans("Motor"));
// D-Pad // D-Pad
groups.emplace_back(m_dpad = new ControllerEmu::Buttons(_trans("D-Pad"))); groups.emplace_back(m_dpad = new ControllerEmu::Buttons(DPAD_GROUP));
for (const char* named_direction : named_directions) for (const char* named_direction : named_directions)
{ {
m_dpad->AddInput(ControllerEmu::Translate, named_direction); m_dpad->AddInput(ControllerEmu::Translate, named_direction);
@ -458,9 +455,10 @@ void Wiimote::BuildDesiredWiimoteState(DesiredWiimoteState* target_state)
// Fetch pressed buttons from user input. // Fetch pressed buttons from user input.
target_state->buttons.hex = 0; target_state->buttons.hex = 0;
m_buttons->GetState(&target_state->buttons.hex, button_bitmasks); m_buttons->GetState(&target_state->buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&target_state->buttons.hex, m_dpad->GetState(&target_state->buttons.hex,
IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks); IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
// Calculate accelerometer state. // Calculate accelerometer state.
// Calibration values are 8-bit but we want 10-bit precision, so << 2. // Calibration values are 8-bit but we want 10-bit precision, so << 2.
@ -628,9 +626,6 @@ void Wiimote::SendDataReport(const DesiredWiimoteState& target_state)
std::fill_n(ext_data, ext_size, u8(0xff)); std::fill_n(ext_data, ext_size, u8(0xff));
} }
} }
Movie::CallWiiInputManip(rpt_builder, m_bt_device_index, m_active_extension,
GetExtensionEncryptionKey());
} }
Movie::CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension, Movie::CheckWiimoteStatus(m_bt_device_index, rpt_builder, m_active_extension,
@ -651,8 +646,9 @@ ButtonData Wiimote::GetCurrentlyPressedButtons()
const auto lock = GetStateLock(); const auto lock = GetStateLock();
ButtonData buttons{}; ButtonData buttons{};
m_buttons->GetState(&buttons.hex, button_bitmasks); m_buttons->GetState(&buttons.hex, button_bitmasks, m_input_override_function);
m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks); m_dpad->GetState(&buttons.hex, IsSideways() ? dpad_sideways_bitmasks : dpad_bitmasks,
m_input_override_function);
return buttons; return buttons;
} }
@ -789,7 +785,7 @@ void Wiimote::StepDynamics()
{ {
EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ); EmulateSwing(&m_swing_state, m_swing, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ); EmulateTilt(&m_tilt_state, m_tilt, 1.f / ::Wiimote::UPDATE_FREQ);
EmulatePoint(&m_point_state, m_ir, 1.f / ::Wiimote::UPDATE_FREQ); EmulatePoint(&m_point_state, m_ir, m_input_override_function, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ); EmulateShake(&m_shake_state, m_shake, 1.f / ::Wiimote::UPDATE_FREQ);
EmulateIMUCursor(&m_imu_cursor_state, m_imu_ir, m_imu_accelerometer, m_imu_gyroscope, EmulateIMUCursor(&m_imu_cursor_state, m_imu_ir, m_imu_accelerometer, m_imu_gyroscope,
1.f / ::Wiimote::UPDATE_FREQ); 1.f / ::Wiimote::UPDATE_FREQ);
@ -831,20 +827,87 @@ Common::Quaternion Wiimote::GetOrientation() const
Common::Quaternion::RotateX(float(MathUtil::TAU / 4 * IsUpright())); Common::Quaternion::RotateX(float(MathUtil::TAU / 4 * IsUpright()));
} }
std::optional<Common::Vec3> Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const
{
bool has_value = optional_vec.has_value();
Common::Vec3 vec = has_value ? *optional_vec : Common::Vec3{};
if (m_input_override_function)
{
if (const std::optional<ControlState> x_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
has_value = true;
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
has_value = true;
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = m_input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
has_value = true;
vec.z = *z_override;
}
}
return has_value ? std::make_optional(vec) : std::nullopt;
}
Common::Vec3 Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const
{
return OverrideVec3(control_group, vec, m_input_override_function);
}
Common::Vec3
Wiimote::OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function)
{
if (input_override_function)
{
if (const std::optional<ControlState> x_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE, vec.x))
{
vec.x = *x_override;
}
if (const std::optional<ControlState> y_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE, vec.y))
{
vec.y = *y_override;
}
if (const std::optional<ControlState> z_override = input_override_function(
control_group->name, ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE, vec.z))
{
vec.z = *z_override;
}
}
return vec;
}
Common::Vec3 Wiimote::GetTotalAcceleration() const Common::Vec3 Wiimote::GetTotalAcceleration() const
{ {
if (const auto accel = m_imu_accelerometer->GetState()) const Common::Vec3 default_accel = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION));
return GetAcceleration(*accel); const Common::Vec3 accel = m_imu_accelerometer->GetState().value_or(default_accel);
return GetAcceleration(); return OverrideVec3(m_imu_accelerometer, GetAcceleration(accel));
} }
Common::Vec3 Wiimote::GetTotalAngularVelocity() const Common::Vec3 Wiimote::GetTotalAngularVelocity() const
{ {
if (const auto ang_vel = m_imu_gyroscope->GetState()) const Common::Vec3 default_ang_vel = {};
return GetAngularVelocity(*ang_vel); const Common::Vec3 ang_vel = m_imu_gyroscope->GetState().value_or(default_ang_vel);
return GetAngularVelocity(); return OverrideVec3(m_imu_gyroscope, GetAngularVelocity(ang_vel));
} }
Common::Matrix44 Wiimote::GetTotalTransformation() const Common::Matrix44 Wiimote::GetTotalTransformation() const

View File

@ -5,8 +5,11 @@
#include <array> #include <array>
#include <numeric> #include <numeric>
#include <optional>
#include <string> #include <string>
#include "Common/Common.h"
#include "Core/HW/WiimoteCommon/WiimoteReport.h" #include "Core/HW/WiimoteCommon/WiimoteReport.h"
#include "Core/HW/WiimoteEmu/Camera.h" #include "Core/HW/WiimoteEmu/Camera.h"
@ -112,6 +115,20 @@ public:
static constexpr u16 BUTTON_MINUS = 0x1000; static constexpr u16 BUTTON_MINUS = 0x1000;
static constexpr u16 BUTTON_HOME = 0x8000; static constexpr u16 BUTTON_HOME = 0x8000;
static constexpr const char* BUTTONS_GROUP = _trans("Buttons");
static constexpr const char* DPAD_GROUP = _trans("D-Pad");
static constexpr const char* ACCELEROMETER_GROUP = "IMUAccelerometer";
static constexpr const char* GYROSCOPE_GROUP = "IMUGyroscope";
static constexpr const char* IR_GROUP = "IR";
static constexpr const char* A_BUTTON = "A";
static constexpr const char* B_BUTTON = "B";
static constexpr const char* ONE_BUTTON = "1";
static constexpr const char* TWO_BUTTON = "2";
static constexpr const char* MINUS_BUTTON = "-";
static constexpr const char* PLUS_BUTTON = "+";
static constexpr const char* HOME_BUTTON = "Home";
explicit Wiimote(unsigned int index); explicit Wiimote(unsigned int index);
~Wiimote(); ~Wiimote();
@ -146,6 +163,10 @@ public:
// Active extension number is exposed for TAS. // Active extension number is exposed for TAS.
ExtensionNumber GetActiveExtensionNumber() const; ExtensionNumber GetActiveExtensionNumber() const;
static Common::Vec3
OverrideVec3(const ControllerEmu::ControlGroup* control_group, Common::Vec3 vec,
const ControllerEmu::InputOverrideFunction& input_override_function);
private: private:
// Used only for error generation: // Used only for error generation:
static constexpr u8 EEPROM_I2C_ADDR = 0x50; static constexpr u8 EEPROM_I2C_ADDR = 0x50;
@ -161,11 +182,10 @@ private:
void BuildDesiredWiimoteState(DesiredWiimoteState* target_state); void BuildDesiredWiimoteState(DesiredWiimoteState* target_state);
// Returns simulated accelerometer data in m/s^2. // Returns simulated accelerometer data in m/s^2.
Common::Vec3 GetAcceleration( Common::Vec3 GetAcceleration(Common::Vec3 extra_acceleration) const;
Common::Vec3 extra_acceleration = Common::Vec3(0, 0, float(GRAVITY_ACCELERATION))) const;
// Returns simulated gyroscope data in radians/s. // Returns simulated gyroscope data in radians/s.
Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity = {}) const; Common::Vec3 GetAngularVelocity(Common::Vec3 extra_angular_velocity) const;
// Returns the transformation of the world around the wiimote. // Returns the transformation of the world around the wiimote.
// Used for simulating camera data and for rotating acceleration data. // Used for simulating camera data and for rotating acceleration data.
@ -176,6 +196,10 @@ private:
// Returns the world rotation from the effects of sideways/upright settings. // Returns the world rotation from the effects of sideways/upright settings.
Common::Quaternion GetOrientation() const; Common::Quaternion GetOrientation() const;
std::optional<Common::Vec3> OverrideVec3(const ControllerEmu::ControlGroup* control_group,
std::optional<Common::Vec3> optional_vec) const;
Common::Vec3 OverrideVec3(const ControllerEmu::ControlGroup* control_group,
Common::Vec3 vec) const;
Common::Vec3 GetTotalAcceleration() const; Common::Vec3 GetTotalAcceleration() const;
Common::Vec3 GetTotalAngularVelocity() const; Common::Vec3 GetTotalAngularVelocity() const;
Common::Matrix44 GetTotalTransformation() const; Common::Matrix44 GetTotalTransformation() const;

View File

@ -122,9 +122,6 @@ static bool s_bPolled = false;
static std::mutex s_input_display_lock; static std::mutex s_input_display_lock;
static std::string s_InputDisplay[8]; static std::string s_InputDisplay[8];
static GCManipFunction s_gc_manip_func;
static WiiManipFunction s_wii_manip_func;
static std::string s_current_file_name; static std::string s_current_file_name;
static void GetSettings(); static void GetSettings();
@ -1426,28 +1423,6 @@ void SaveRecording(const std::string& filename)
Core::DisplayMessage(fmt::format("Failed to save {}", filename), 2000); Core::DisplayMessage(fmt::format("Failed to save {}", filename), 2000);
} }
void SetGCInputManip(GCManipFunction func)
{
s_gc_manip_func = std::move(func);
}
void SetWiiInputManip(WiiManipFunction func)
{
s_wii_manip_func = std::move(func);
}
// NOTE: CPU Thread
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID)
{
if (s_gc_manip_func)
s_gc_manip_func(PadStatus, controllerID);
}
// NOTE: CPU Thread
void CallWiiInputManip(DataReportBuilder& rpt, int controllerID, int ext, const EncryptionKey& key)
{
if (s_wii_manip_func)
s_wii_manip_func(rpt, controllerID, ext, key);
}
// NOTE: GPU Thread // NOTE: GPU Thread
void SetGraphicsConfig() void SetGraphicsConfig()
{ {

View File

@ -5,7 +5,6 @@
#include <array> #include <array>
#include <cstring> #include <cstring>
#include <functional>
#include <optional> #include <optional>
#include <string> #include <string>
#include <string_view> #include <string_view>
@ -200,14 +199,4 @@ std::string GetInputDisplay();
std::string GetRTCDisplay(); std::string GetRTCDisplay();
std::string GetRerecords(); std::string GetRerecords();
// Done this way to avoid mixing of core and gui code
using GCManipFunction = std::function<void(GCPadStatus*, int)>;
using WiiManipFunction = std::function<void(WiimoteCommon::DataReportBuilder&, int, int,
const WiimoteEmu::EncryptionKey&)>;
void SetGCInputManip(GCManipFunction);
void SetWiiInputManip(WiiManipFunction);
void CallGCInputManip(GCPadStatus* PadStatus, int controllerID);
void CallWiiInputManip(WiimoteCommon::DataReportBuilder& rpt, int controllerID, int ext,
const WiimoteEmu::EncryptionKey& key);
} // namespace Movie } // namespace Movie

View File

@ -403,15 +403,6 @@ void MainWindow::CreateComponents()
m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i); m_wii_tas_input_windows[i] = new WiiTASInputWindow(nullptr, i);
} }
Movie::SetGCInputManip([this](GCPadStatus* pad_status, int controller_id) {
m_gc_tas_input_windows[controller_id]->GetValues(pad_status);
});
Movie::SetWiiInputManip([this](WiimoteCommon::DataReportBuilder& rpt, int controller_id, int ext,
const WiimoteEmu::EncryptionKey& key) {
m_wii_tas_input_windows[controller_id]->GetValues(rpt, ext, key);
});
m_jit_widget = new JITWidget(this); m_jit_widget = new JITWidget(this);
m_log_widget = new LogWidget(this); m_log_widget = new LogWidget(this);
m_log_config_widget = new LogConfigWidget(this); m_log_config_widget = new LogConfigWidget(this);

View File

@ -13,18 +13,25 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/GCPadEmu.h"
#include "DolphinQt/TAS/TASCheckBox.h" #include "DolphinQt/TAS/TASCheckBox.h"
#include "InputCommon/GCPadStatus.h" #include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/InputConfig.h"
GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(parent) GCTASInputWindow::GCTASInputWindow(QWidget* parent, int controller_id)
: TASInputWindow(parent), m_controller_id(controller_id)
{ {
setWindowTitle(tr("GameCube TAS Input %1").arg(num + 1)); setWindowTitle(tr("GameCube TAS Input %1").arg(controller_id + 1));
m_main_stick_box = CreateStickInputs(tr("Main Stick"), m_x_main_stick_value, m_y_main_stick_value, m_main_stick_box = CreateStickInputs(tr("Main Stick"), GCPad::MAIN_STICK_GROUP, &m_overrider,
255, 255, Qt::Key_F, Qt::Key_G); m_x_main_stick_value, m_y_main_stick_value, 1, 1, 255, 255,
m_c_stick_box = CreateStickInputs(tr("C Stick"), m_x_c_stick_value, m_y_c_stick_value, 255, 255, Qt::Key_F, Qt::Key_G);
Qt::Key_H, Qt::Key_J); m_c_stick_box =
CreateStickInputs(tr("C Stick"), GCPad::C_STICK_GROUP, &m_overrider, m_x_c_stick_value,
m_y_c_stick_value, 1, 1, 255, 255, Qt::Key_H, Qt::Key_J);
auto* top_layout = new QHBoxLayout; auto* top_layout = new QHBoxLayout;
top_layout->addWidget(m_main_stick_box); top_layout->addWidget(m_main_stick_box);
@ -33,27 +40,43 @@ GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(pa
m_triggers_box = new QGroupBox(tr("Triggers")); m_triggers_box = new QGroupBox(tr("Triggers"));
auto* l_trigger_layout = auto* l_trigger_layout =
CreateSliderValuePairLayout(tr("Left"), m_l_trigger_value, 0, 255, Qt::Key_N, m_triggers_box); CreateSliderValuePairLayout(tr("Left"), GCPad::TRIGGERS_GROUP, GCPad::L_ANALOG, &m_overrider,
auto* r_trigger_layout = CreateSliderValuePairLayout(tr("Right"), m_r_trigger_value, 0, 255, m_l_trigger_value, 0, 0, 0, 255, Qt::Key_N, m_triggers_box);
Qt::Key_M, m_triggers_box);
auto* r_trigger_layout =
CreateSliderValuePairLayout(tr("Right"), GCPad::TRIGGERS_GROUP, GCPad::R_ANALOG, &m_overrider,
m_r_trigger_value, 0, 0, 0, 255, Qt::Key_M, m_triggers_box);
auto* triggers_layout = new QVBoxLayout; auto* triggers_layout = new QVBoxLayout;
triggers_layout->addLayout(l_trigger_layout); triggers_layout->addLayout(l_trigger_layout);
triggers_layout->addLayout(r_trigger_layout); triggers_layout->addLayout(r_trigger_layout);
m_triggers_box->setLayout(triggers_layout); m_triggers_box->setLayout(triggers_layout);
m_a_button = CreateButton(QStringLiteral("&A")); m_a_button =
m_b_button = CreateButton(QStringLiteral("&B")); CreateButton(QStringLiteral("&A"), GCPad::BUTTONS_GROUP, GCPad::A_BUTTON, &m_overrider);
m_x_button = CreateButton(QStringLiteral("&X")); m_b_button =
m_y_button = CreateButton(QStringLiteral("&Y")); CreateButton(QStringLiteral("&B"), GCPad::BUTTONS_GROUP, GCPad::B_BUTTON, &m_overrider);
m_z_button = CreateButton(QStringLiteral("&Z")); m_x_button =
m_l_button = CreateButton(QStringLiteral("&L")); CreateButton(QStringLiteral("&X"), GCPad::BUTTONS_GROUP, GCPad::X_BUTTON, &m_overrider);
m_r_button = CreateButton(QStringLiteral("&R")); m_y_button =
m_start_button = CreateButton(QStringLiteral("&START")); CreateButton(QStringLiteral("&Y"), GCPad::BUTTONS_GROUP, GCPad::Y_BUTTON, &m_overrider);
m_left_button = CreateButton(QStringLiteral("L&eft")); m_z_button =
m_up_button = CreateButton(QStringLiteral("&Up")); CreateButton(QStringLiteral("&Z"), GCPad::BUTTONS_GROUP, GCPad::Z_BUTTON, &m_overrider);
m_down_button = CreateButton(QStringLiteral("&Down")); m_start_button = CreateButton(QStringLiteral("&START"), GCPad::BUTTONS_GROUP, GCPad::START_BUTTON,
m_right_button = CreateButton(QStringLiteral("R&ight")); &m_overrider);
m_l_button =
CreateButton(QStringLiteral("&L"), GCPad::TRIGGERS_GROUP, GCPad::L_DIGITAL, &m_overrider);
m_r_button =
CreateButton(QStringLiteral("&R"), GCPad::TRIGGERS_GROUP, GCPad::R_DIGITAL, &m_overrider);
m_left_button =
CreateButton(QStringLiteral("L&eft"), GCPad::DPAD_GROUP, DIRECTION_LEFT, &m_overrider);
m_up_button = CreateButton(QStringLiteral("&Up"), GCPad::DPAD_GROUP, DIRECTION_UP, &m_overrider);
m_down_button =
CreateButton(QStringLiteral("&Down"), GCPad::DPAD_GROUP, DIRECTION_DOWN, &m_overrider);
m_right_button =
CreateButton(QStringLiteral("R&ight"), GCPad::DPAD_GROUP, DIRECTION_RIGHT, &m_overrider);
auto* buttons_layout = new QGridLayout; auto* buttons_layout = new QGridLayout;
buttons_layout->addWidget(m_a_button, 0, 0); buttons_layout->addWidget(m_a_button, 0, 0);
@ -84,40 +107,14 @@ GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(pa
setLayout(layout); setLayout(layout);
} }
void GCTASInputWindow::GetValues(GCPadStatus* pad) void GCTASInputWindow::hideEvent(QHideEvent* event)
{ {
if (!isVisible()) Pad::GetConfig()->GetController(m_controller_id)->ClearInputOverrideFunction();
return; }
GetButton<u16>(m_a_button, pad->button, PAD_BUTTON_A); void GCTASInputWindow::showEvent(QShowEvent* event)
GetButton<u16>(m_b_button, pad->button, PAD_BUTTON_B); {
GetButton<u16>(m_x_button, pad->button, PAD_BUTTON_X); Pad::GetConfig()
GetButton<u16>(m_y_button, pad->button, PAD_BUTTON_Y); ->GetController(m_controller_id)
GetButton<u16>(m_z_button, pad->button, PAD_TRIGGER_Z); ->SetInputOverrideFunction(m_overrider.GetInputOverrideFunction());
GetButton<u16>(m_l_button, pad->button, PAD_TRIGGER_L);
GetButton<u16>(m_r_button, pad->button, PAD_TRIGGER_R);
GetButton<u16>(m_left_button, pad->button, PAD_BUTTON_LEFT);
GetButton<u16>(m_up_button, pad->button, PAD_BUTTON_UP);
GetButton<u16>(m_down_button, pad->button, PAD_BUTTON_DOWN);
GetButton<u16>(m_right_button, pad->button, PAD_BUTTON_RIGHT);
GetButton<u16>(m_start_button, pad->button, PAD_BUTTON_START);
if (m_a_button->isChecked())
pad->analogA = 0xFF;
else
pad->analogA = 0x00;
if (m_b_button->isChecked())
pad->analogB = 0xFF;
else
pad->analogB = 0x00;
GetSpinBoxU8(m_l_trigger_value, pad->triggerLeft);
GetSpinBoxU8(m_r_trigger_value, pad->triggerRight);
GetSpinBoxU8(m_x_main_stick_value, pad->stickX);
GetSpinBoxU8(m_y_main_stick_value, pad->stickY);
GetSpinBoxU8(m_x_c_stick_value, pad->substickX);
GetSpinBoxU8(m_y_c_stick_value, pad->substickY);
} }

View File

@ -6,18 +6,25 @@
#include "DolphinQt/TAS/TASInputWindow.h" #include "DolphinQt/TAS/TASInputWindow.h"
class QGroupBox; class QGroupBox;
class QHideEvent;
class QShowEvent;
class QSpinBox; class QSpinBox;
class TASCheckBox; class TASCheckBox;
struct GCPadStatus;
class GCTASInputWindow : public TASInputWindow class GCTASInputWindow : public TASInputWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit GCTASInputWindow(QWidget* parent, int num); explicit GCTASInputWindow(QWidget* parent, int controller_id);
void GetValues(GCPadStatus* pad);
void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
private: private:
int m_controller_id;
InputOverrider m_overrider;
TASCheckBox* m_a_button; TASCheckBox* m_a_button;
TASCheckBox* m_b_button; TASCheckBox* m_b_button;
TASCheckBox* m_x_button; TASCheckBox* m_x_button;

View File

@ -54,8 +54,8 @@ void IRWidget::paintEvent(QPaintEvent* event)
painter.drawLine(PADDING + w / 2, PADDING, PADDING + w / 2, PADDING + h); painter.drawLine(PADDING + w / 2, PADDING, PADDING + w / 2, PADDING + h);
// convert from value space to widget space // convert from value space to widget space
u16 x = PADDING + (w - (m_x * w) / ir_max_x); u16 x = PADDING + ((m_x * w) / ir_max_x);
u16 y = PADDING + ((m_y * h) / ir_max_y); u16 y = PADDING + (h - (m_y * h) / ir_max_y);
painter.drawLine(PADDING + w / 2, PADDING + h / 2, x, y); painter.drawLine(PADDING + w / 2, PADDING + h / 2, x, y);
@ -87,8 +87,8 @@ void IRWidget::handleMouseEvent(QMouseEvent* event)
else else
{ {
// convert from widget space to value space // convert from widget space to value space
int new_x = ir_max_x - (event->pos().x() * ir_max_x) / width(); int new_x = (event->pos().x() * ir_max_x) / width();
int new_y = (event->pos().y() * ir_max_y) / height(); int new_y = ir_max_y - (event->pos().y() * ir_max_y) / height();
m_x = std::max(0, std::min(static_cast<int>(ir_max_x), new_x)); m_x = std::max(0, std::min(static_cast<int>(ir_max_x), new_x));
m_y = std::max(0, std::min(static_cast<int>(ir_max_y), new_y)); m_y = std::max(0, std::min(static_cast<int>(ir_max_y), new_y));

View File

@ -34,5 +34,7 @@ private:
}; };
// Should be part of class but fails to compile on mac os // Should be part of class but fails to compile on mac os
static const u16 ir_min_x = 0;
static const u16 ir_min_y = 0;
static const u16 ir_max_x = 1023; static const u16 ir_max_x = 1023;
static const u16 ir_max_y = 767; static const u16 ir_max_y = 767;

View File

@ -4,6 +4,7 @@
#include "DolphinQt/TAS/TASInputWindow.h" #include "DolphinQt/TAS/TASInputWindow.h"
#include <cmath> #include <cmath>
#include <utility>
#include <QCheckBox> #include <QCheckBox>
#include <QGroupBox> #include <QGroupBox>
@ -23,7 +24,23 @@
#include "DolphinQt/TAS/TASCheckBox.h" #include "DolphinQt/TAS/TASCheckBox.h"
#include "DolphinQt/TAS/TASSlider.h" #include "DolphinQt/TAS/TASSlider.h"
#include "InputCommon/GCPadStatus.h" #include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/StickGate.h"
void InputOverrider::AddFunction(std::string_view group_name, std::string_view control_name,
OverrideFunction function)
{
m_functions.emplace(std::make_pair(group_name, control_name), std::move(function));
}
ControllerEmu::InputOverrideFunction InputOverrider::GetInputOverrideFunction() const
{
return [this](std::string_view group_name, std::string_view control_name,
ControlState controller_state) {
const auto it = m_functions.find(std::make_pair(group_name, control_name));
return it != m_functions.end() ? it->second(controller_state) : std::nullopt;
};
}
TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent) TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent)
{ {
@ -63,13 +80,22 @@ int TASInputWindow::GetTurboReleaseFrames() const
return m_turbo_release_frames->value(); return m_turbo_release_frames->value();
} }
TASCheckBox* TASInputWindow::CreateButton(const QString& name) TASCheckBox* TASInputWindow::CreateButton(const QString& text, std::string_view group_name,
std::string_view control_name, InputOverrider* overrider)
{ {
return new TASCheckBox(name, this); TASCheckBox* checkbox = new TASCheckBox(text, this);
overrider->AddFunction(group_name, control_name, [this, checkbox](ControlState controller_state) {
return GetButton(checkbox, controller_state);
});
return checkbox;
} }
QGroupBox* TASInputWindow::CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value, QGroupBox* TASInputWindow::CreateStickInputs(const QString& text, std::string_view group_name,
u16 max_x, u16 max_y, Qt::Key x_shortcut_key, InputOverrider* overrider, QSpinBox*& x_value,
QSpinBox*& y_value, u16 min_x, u16 min_y, u16 max_x,
u16 max_y, Qt::Key x_shortcut_key,
Qt::Key y_shortcut_key) Qt::Key y_shortcut_key)
{ {
const QKeySequence x_shortcut_key_sequence = QKeySequence(Qt::ALT | x_shortcut_key); const QKeySequence x_shortcut_key_sequence = QKeySequence(Qt::ALT | x_shortcut_key);
@ -77,7 +103,7 @@ QGroupBox* TASInputWindow::CreateStickInputs(QString name, QSpinBox*& x_value, Q
auto* box = auto* box =
new QGroupBox(QStringLiteral("%1 (%2/%3)") new QGroupBox(QStringLiteral("%1 (%2/%3)")
.arg(name, x_shortcut_key_sequence.toString(QKeySequence::NativeText), .arg(text, x_shortcut_key_sequence.toString(QKeySequence::NativeText),
y_shortcut_key_sequence.toString(QKeySequence::NativeText))); y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
const int x_default = static_cast<int>(std::round(max_x / 2.)); const int x_default = static_cast<int>(std::round(max_x / 2.));
@ -112,33 +138,72 @@ QGroupBox* TASInputWindow::CreateStickInputs(QString name, QSpinBox*& x_value, Q
layout->addLayout(visual_layout); layout->addLayout(visual_layout);
box->setLayout(layout); box->setLayout(layout);
overrider->AddFunction(group_name, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
[this, x_value, x_default, min_x, max_x](ControlState controller_state) {
return GetSpinBox(x_value, x_default, min_x, max_x, controller_state);
});
overrider->AddFunction(group_name, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
[this, y_value, y_default, min_y, max_y](ControlState controller_state) {
return GetSpinBox(y_value, y_default, min_y, max_y, controller_state);
});
return box; return box;
} }
QBoxLayout* TASInputWindow::CreateSliderValuePairLayout(QString name, QSpinBox*& value, QBoxLayout* TASInputWindow::CreateSliderValuePairLayout(
int default_, u16 max, Qt::Key shortcut_key, const QString& text, std::string_view group_name, std::string_view control_name,
QWidget* shortcut_widget, bool invert) InputOverrider* overrider, QSpinBox*& value, u16 zero, int default_, u16 min, u16 max,
Qt::Key shortcut_key, QWidget* shortcut_widget, std::optional<ControlState> scale)
{ {
const QKeySequence shortcut_key_sequence = QKeySequence(Qt::ALT | shortcut_key); const QKeySequence shortcut_key_sequence = QKeySequence(Qt::ALT | shortcut_key);
auto* label = new QLabel(QStringLiteral("%1 (%2)").arg( auto* label = new QLabel(QStringLiteral("%1 (%2)").arg(
name, shortcut_key_sequence.toString(QKeySequence::NativeText))); text, shortcut_key_sequence.toString(QKeySequence::NativeText)));
QBoxLayout* layout = new QHBoxLayout; QBoxLayout* layout = new QHBoxLayout;
layout->addWidget(label); layout->addWidget(label);
value = CreateSliderValuePair(layout, default_, max, shortcut_key_sequence, Qt::Horizontal, value = CreateSliderValuePair(group_name, control_name, overrider, layout, zero, default_, min,
shortcut_widget, invert); max, shortcut_key_sequence, Qt::Horizontal, shortcut_widget, scale);
return layout; return layout;
} }
QSpinBox* TASInputWindow::CreateSliderValuePair(
std::string_view group_name, std::string_view control_name, InputOverrider* overrider,
QBoxLayout* layout, u16 zero, int default_, u16 min, u16 max,
QKeySequence shortcut_key_sequence, Qt::Orientation orientation, QWidget* shortcut_widget,
std::optional<ControlState> scale)
{
QSpinBox* value = CreateSliderValuePair(layout, default_, max, shortcut_key_sequence, orientation,
shortcut_widget);
InputOverrider::OverrideFunction func;
if (scale)
{
func = [this, value, zero, scale](ControlState controller_state) {
return GetSpinBox(value, zero, controller_state, *scale);
};
}
else
{
func = [this, value, zero, min, max](ControlState controller_state) {
return GetSpinBox(value, zero, min, max, controller_state);
};
}
overrider->AddFunction(group_name, control_name, std::move(func));
return value;
}
// The shortcut_widget argument needs to specify the container widget that will be hidden/shown. // The shortcut_widget argument needs to specify the container widget that will be hidden/shown.
// This is done to avoid ambigous shortcuts // This is done to avoid ambigous shortcuts
QSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_, u16 max, QSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_, u16 max,
QKeySequence shortcut_key_sequence, QKeySequence shortcut_key_sequence,
Qt::Orientation orientation, Qt::Orientation orientation,
QWidget* shortcut_widget, bool invert) QWidget* shortcut_widget)
{ {
auto* value = new QSpinBox(); auto* value = new QSpinBox();
value->setRange(0, 99999); value->setRange(0, 99999);
@ -151,7 +216,6 @@ QSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_
slider->setRange(0, max); slider->setRange(0, max);
slider->setValue(default_); slider->setValue(default_);
slider->setFocusPolicy(Qt::ClickFocus); slider->setFocusPolicy(Qt::ClickFocus);
slider->setInvertedAppearance(invert);
connect(slider, &QSlider::valueChanged, value, &QSpinBox::setValue); connect(slider, &QSlider::valueChanged, value, &QSpinBox::setValue);
connect(value, qOverload<int>(&QSpinBox::valueChanged), slider, &QSlider::setValue); connect(value, qOverload<int>(&QSpinBox::valueChanged), slider, &QSlider::setValue);
@ -170,10 +234,10 @@ QSpinBox* TASInputWindow::CreateSliderValuePair(QBoxLayout* layout, int default_
return value; return value;
} }
template <typename UX> std::optional<ControlState> TASInputWindow::GetButton(TASCheckBox* checkbox,
void TASInputWindow::GetButton(TASCheckBox* checkbox, UX& buttons, UX mask) ControlState controller_state)
{ {
const bool pressed = (buttons & mask) != 0; const bool pressed = std::llround(controller_state) > 0;
if (m_use_controller->isChecked()) if (m_use_controller->isChecked())
{ {
if (pressed) if (pressed)
@ -188,50 +252,54 @@ void TASInputWindow::GetButton(TASCheckBox* checkbox, UX& buttons, UX mask)
} }
} }
if (checkbox->GetValue()) return checkbox->GetValue() ? 1.0 : 0.0;
buttons |= mask;
else
buttons &= ~mask;
} }
template void TASInputWindow::GetButton<u8>(TASCheckBox* button, u8& pad, u8 mask);
template void TASInputWindow::GetButton<u16>(TASCheckBox* button, u16& pad, u16 mask);
void TASInputWindow::GetSpinBoxU8(QSpinBox* spin, u8& controller_value) std::optional<ControlState> TASInputWindow::GetSpinBox(QSpinBox* spin, u16 zero, u16 min, u16 max,
ControlState controller_state)
{ {
const u16 controller_value =
ControllerEmu::EmulatedController::MapFloat<u16>(controller_state, zero, 0, max);
if (m_use_controller->isChecked()) if (m_use_controller->isChecked())
{ {
if (!m_spinbox_most_recent_values_u8.count(spin) || if (!m_spinbox_most_recent_values.count(spin) ||
m_spinbox_most_recent_values_u8[spin] != controller_value) m_spinbox_most_recent_values[spin] != controller_value)
{ {
QueueOnObjectBlocking(spin, [spin, controller_value] { spin->setValue(controller_value); }); QueueOnObjectBlocking(spin, [spin, controller_value] { spin->setValue(controller_value); });
} }
m_spinbox_most_recent_values_u8[spin] = controller_value; m_spinbox_most_recent_values[spin] = controller_value;
} }
else else
{ {
m_spinbox_most_recent_values_u8.clear(); m_spinbox_most_recent_values.clear();
} }
controller_value = spin->value(); return ControllerEmu::EmulatedController::MapToFloat<ControlState, u16>(spin->value(), zero, min,
max);
} }
void TASInputWindow::GetSpinBoxU16(QSpinBox* spin, u16& controller_value) std::optional<ControlState> TASInputWindow::GetSpinBox(QSpinBox* spin, u16 zero,
ControlState controller_state,
ControlState scale)
{ {
const u16 controller_value = static_cast<u16>(std::llround(controller_state * scale + zero));
if (m_use_controller->isChecked()) if (m_use_controller->isChecked())
{ {
if (!m_spinbox_most_recent_values_u16.count(spin) || if (!m_spinbox_most_recent_values.count(spin) ||
m_spinbox_most_recent_values_u16[spin] != controller_value) m_spinbox_most_recent_values[spin] != controller_value)
{ {
QueueOnObjectBlocking(spin, [spin, controller_value] { spin->setValue(controller_value); }); QueueOnObjectBlocking(spin, [spin, controller_value] { spin->setValue(controller_value); });
} }
m_spinbox_most_recent_values_u16[spin] = controller_value; m_spinbox_most_recent_values[spin] = controller_value;
} }
else else
{ {
m_spinbox_most_recent_values_u16.clear(); m_spinbox_most_recent_values.clear();
} }
controller_value = spin->value(); return (spin->value() - zero) / scale;
} }

View File

@ -3,11 +3,18 @@
#pragma once #pragma once
#include <map>
#include <optional>
#include <string_view>
#include <utility>
#include <QDialog> #include <QDialog>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
struct GCPadStatus; #include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
class QBoxLayout; class QBoxLayout;
class QCheckBox; class QCheckBox;
class QDialog; class QDialog;
@ -16,6 +23,20 @@ class QSpinBox;
class QString; class QString;
class TASCheckBox; class TASCheckBox;
class InputOverrider final
{
public:
using OverrideFunction = std::function<std::optional<ControlState>(ControlState)>;
void AddFunction(std::string_view group_name, std::string_view control_name,
OverrideFunction function);
ControllerEmu::InputOverrideFunction GetInputOverrideFunction() const;
private:
std::map<std::pair<std::string_view, std::string_view>, OverrideFunction> m_functions;
};
class TASInputWindow : public QDialog class TASInputWindow : public QDialog
{ {
Q_OBJECT Q_OBJECT
@ -26,19 +47,25 @@ public:
int GetTurboReleaseFrames() const; int GetTurboReleaseFrames() const;
protected: protected:
TASCheckBox* CreateButton(const QString& name); TASCheckBox* CreateButton(const QString& text, std::string_view group_name,
QGroupBox* CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value, u16 max_x, std::string_view control_name, InputOverrider* overrider);
u16 max_y, Qt::Key x_shortcut_key, Qt::Key y_shortcut_key); QGroupBox* CreateStickInputs(const QString& text, std::string_view group_name,
QBoxLayout* CreateSliderValuePairLayout(QString name, QSpinBox*& value, int default_, u16 max, InputOverrider* overrider, QSpinBox*& x_value, QSpinBox*& y_value,
Qt::Key shortcut_key, QWidget* shortcut_widget, u16 min_x, u16 min_y, u16 max_x, u16 max_y, Qt::Key x_shortcut_key,
bool invert = false); Qt::Key y_shortcut_key);
QBoxLayout* CreateSliderValuePairLayout(const QString& text, std::string_view group_name,
std::string_view control_name, InputOverrider* overrider,
QSpinBox*& value, u16 zero, int default_, u16 min,
u16 max, Qt::Key shortcut_key, QWidget* shortcut_widget,
std::optional<ControlState> scale = {});
QSpinBox* CreateSliderValuePair(std::string_view group_name, std::string_view control_name,
InputOverrider* overrider, QBoxLayout* layout, u16 zero,
int default_, u16 min, u16 max,
QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
QWidget* shortcut_widget, std::optional<ControlState> scale = {});
QSpinBox* CreateSliderValuePair(QBoxLayout* layout, int default_, u16 max, QSpinBox* CreateSliderValuePair(QBoxLayout* layout, int default_, u16 max,
QKeySequence shortcut_key_sequence, Qt::Orientation orientation, QKeySequence shortcut_key_sequence, Qt::Orientation orientation,
QWidget* shortcut_widget, bool invert = false); QWidget* shortcut_widget);
template <typename UX>
void GetButton(TASCheckBox* button, UX& pad, UX mask);
void GetSpinBoxU8(QSpinBox* spin, u8& controller_value);
void GetSpinBoxU16(QSpinBox* spin, u16& controller_value);
QGroupBox* m_settings_box; QGroupBox* m_settings_box;
QCheckBox* m_use_controller; QCheckBox* m_use_controller;
@ -46,7 +73,12 @@ protected:
QSpinBox* m_turbo_release_frames; QSpinBox* m_turbo_release_frames;
private: private:
std::optional<ControlState> GetButton(TASCheckBox* checkbox, ControlState controller_state);
std::optional<ControlState> GetSpinBox(QSpinBox* spin, u16 zero, u16 min, u16 max,
ControlState controller_state);
std::optional<ControlState> GetSpinBox(QSpinBox* spin, u16 zero, ControlState controller_state,
ControlState scale);
std::map<TASCheckBox*, bool> m_checkbox_set_by_controller; std::map<TASCheckBox*, bool> m_checkbox_set_by_controller;
std::map<QSpinBox*, u8> m_spinbox_most_recent_values_u8; std::map<QSpinBox*, u16> m_spinbox_most_recent_values;
std::map<QSpinBox*, u8> m_spinbox_most_recent_values_u16;
}; };

View File

@ -15,18 +15,14 @@
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/MathUtil.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/HW/WiimoteCommon/DataReport.h" #include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/Encryption.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h" #include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Extension.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h" #include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h" #include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "Core/HW/WiimoteEmu/Camera.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "DolphinQt/QtUtils/AspectRatioWidget.h" #include "DolphinQt/QtUtils/AspectRatioWidget.h"
@ -34,6 +30,9 @@
#include "DolphinQt/TAS/IRWidget.h" #include "DolphinQt/TAS/IRWidget.h"
#include "DolphinQt/TAS/TASCheckBox.h" #include "DolphinQt/TAS/TASCheckBox.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/StickGate.h"
#include "InputCommon/InputConfig.h" #include "InputCommon/InputConfig.h"
using namespace WiimoteCommon; using namespace WiimoteCommon;
@ -48,21 +47,25 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
ir_x_shortcut_key_sequence.toString(QKeySequence::NativeText), ir_x_shortcut_key_sequence.toString(QKeySequence::NativeText),
ir_y_shortcut_key_sequence.toString(QKeySequence::NativeText))); ir_y_shortcut_key_sequence.toString(QKeySequence::NativeText)));
const int ir_x_default = static_cast<int>(std::round(ir_max_x / 2.)); const int ir_x_center = static_cast<int>(std::round(ir_max_x / 2.));
const int ir_y_default = static_cast<int>(std::round(ir_max_y / 2.)); const int ir_y_center = static_cast<int>(std::round(ir_max_y / 2.));
auto* x_layout = new QHBoxLayout; auto* x_layout = new QHBoxLayout;
m_ir_x_value = CreateSliderValuePair(x_layout, ir_x_default, ir_max_x, ir_x_shortcut_key_sequence, m_ir_x_value = CreateSliderValuePair(
Qt::Horizontal, m_ir_box, true); WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
&m_wiimote_overrider, x_layout, ir_x_center, ir_x_center, ir_min_x, ir_max_x,
ir_x_shortcut_key_sequence, Qt::Horizontal, m_ir_box);
auto* y_layout = new QVBoxLayout; auto* y_layout = new QVBoxLayout;
m_ir_y_value = CreateSliderValuePair(y_layout, ir_y_default, ir_max_y, ir_y_shortcut_key_sequence, m_ir_y_value = CreateSliderValuePair(
Qt::Vertical, m_ir_box, true); WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
&m_wiimote_overrider, y_layout, ir_y_center, ir_y_center, ir_min_y, ir_max_y,
ir_y_shortcut_key_sequence, Qt::Vertical, m_ir_box);
m_ir_y_value->setMaximumWidth(60); m_ir_y_value->setMaximumWidth(60);
auto* visual = new IRWidget(this); auto* visual = new IRWidget(this);
visual->SetX(ir_x_default); visual->SetX(ir_x_center);
visual->SetY(ir_y_default); visual->SetY(ir_y_center);
connect(m_ir_x_value, qOverload<int>(&QSpinBox::valueChanged), visual, &IRWidget::SetX); connect(m_ir_x_value, qOverload<int>(&QSpinBox::valueChanged), visual, &IRWidget::SetX);
connect(m_ir_y_value, qOverload<int>(&QSpinBox::valueChanged), visual, &IRWidget::SetY); connect(m_ir_y_value, qOverload<int>(&QSpinBox::valueChanged), visual, &IRWidget::SetY);
@ -80,16 +83,19 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
ir_layout->addLayout(visual_layout); ir_layout->addLayout(visual_layout);
m_ir_box->setLayout(ir_layout); m_ir_box->setLayout(ir_layout);
m_nunchuk_stick_box = CreateStickInputs(tr("Nunchuk Stick"), m_nunchuk_stick_x_value, m_nunchuk_stick_box = CreateStickInputs(
m_nunchuk_stick_y_value, 255, 255, Qt::Key_X, Qt::Key_Y); tr("Nunchuk Stick"), WiimoteEmu::Nunchuk::STICK_GROUP, &m_nunchuk_overrider,
m_nunchuk_stick_x_value, m_nunchuk_stick_y_value, 0, 0, 255, 255, Qt::Key_X, Qt::Key_Y);
m_classic_left_stick_box = m_classic_left_stick_box =
CreateStickInputs(tr("Left Stick"), m_classic_left_stick_x_value, CreateStickInputs(tr("Left Stick"), WiimoteEmu::Classic::LEFT_STICK_GROUP,
m_classic_left_stick_y_value, 63, 63, Qt::Key_F, Qt::Key_G); &m_classic_overrider, m_classic_left_stick_x_value,
m_classic_left_stick_y_value, 0, 0, 63, 63, Qt::Key_F, Qt::Key_G);
m_classic_right_stick_box = m_classic_right_stick_box =
CreateStickInputs(tr("Right Stick"), m_classic_right_stick_x_value, CreateStickInputs(tr("Right Stick"), WiimoteEmu::Classic::RIGHT_STICK_GROUP,
m_classic_right_stick_y_value, 31, 31, Qt::Key_Q, Qt::Key_W); &m_classic_overrider, m_classic_right_stick_x_value,
m_classic_right_stick_y_value, 0, 0, 31, 31, Qt::Key_Q, Qt::Key_W);
// Need to enforce the same minimum width because otherwise the different lengths in the labels // Need to enforce the same minimum width because otherwise the different lengths in the labels
// used on the QGroupBox will cause the StickWidgets to have different sizes. // used on the QGroupBox will cause the StickWidgets to have different sizes.
@ -104,18 +110,33 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
top_layout->addWidget(m_classic_left_stick_box); top_layout->addWidget(m_classic_left_stick_box);
top_layout->addWidget(m_classic_right_stick_box); top_layout->addWidget(m_classic_right_stick_box);
constexpr u16 ACCEL_ZERO_G = WiimoteEmu::Wiimote::ACCEL_ZERO_G << 2;
constexpr u16 ACCEL_ONE_G = WiimoteEmu::Wiimote::ACCEL_ONE_G << 2;
constexpr u16 ACCEL_MIN = 0;
constexpr u16 ACCEL_MAX = (1 << 10) - 1;
constexpr double ACCEL_SCALE = (ACCEL_ONE_G - ACCEL_ZERO_G) / MathUtil::GRAVITY_ACCELERATION;
auto* remote_orientation_x_layout = auto* remote_orientation_x_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("X"), m_remote_orientation_x_value, 512, 1023, Qt::Key_Q, CreateSliderValuePairLayout(tr("X"), WiimoteEmu::Wiimote::ACCELEROMETER_GROUP,
m_remote_orientation_box); ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
&m_wiimote_overrider, m_remote_orientation_x_value, ACCEL_ZERO_G,
ACCEL_ZERO_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_Q,
m_remote_orientation_box, ACCEL_SCALE);
auto* remote_orientation_y_layout = auto* remote_orientation_y_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("Y"), m_remote_orientation_y_value, 512, 1023, Qt::Key_W, CreateSliderValuePairLayout(tr("Y"), WiimoteEmu::Wiimote::ACCELEROMETER_GROUP,
m_remote_orientation_box); ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
&m_wiimote_overrider, m_remote_orientation_y_value, ACCEL_ZERO_G,
ACCEL_ZERO_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_W,
m_remote_orientation_box, ACCEL_SCALE);
auto* remote_orientation_z_layout = auto* remote_orientation_z_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("Z"), m_remote_orientation_z_value, 616, 1023, Qt::Key_E, CreateSliderValuePairLayout(tr("Z"), WiimoteEmu::Wiimote::ACCELEROMETER_GROUP,
m_remote_orientation_box); ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE,
&m_wiimote_overrider, m_remote_orientation_z_value, ACCEL_ZERO_G,
ACCEL_ONE_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_E,
m_remote_orientation_box, ACCEL_SCALE);
auto* remote_orientation_layout = new QVBoxLayout; auto* remote_orientation_layout = new QVBoxLayout;
remote_orientation_layout->addLayout(remote_orientation_x_layout); remote_orientation_layout->addLayout(remote_orientation_x_layout);
@ -127,15 +148,24 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
auto* nunchuk_orientation_x_layout = auto* nunchuk_orientation_x_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("X"), m_nunchuk_orientation_x_value, 512, 1023, Qt::Key_I, CreateSliderValuePairLayout(tr("X"), WiimoteEmu::Nunchuk::ACCELEROMETER_GROUP,
ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE,
&m_nunchuk_overrider, m_nunchuk_orientation_x_value, ACCEL_ZERO_G,
ACCEL_ZERO_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_I,
m_nunchuk_orientation_box); m_nunchuk_orientation_box);
auto* nunchuk_orientation_y_layout = auto* nunchuk_orientation_y_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("Y"), m_nunchuk_orientation_y_value, 512, 1023, Qt::Key_O, CreateSliderValuePairLayout(tr("Y"), WiimoteEmu::Nunchuk::ACCELEROMETER_GROUP,
ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE,
&m_nunchuk_overrider, m_nunchuk_orientation_y_value, ACCEL_ZERO_G,
ACCEL_ZERO_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_O,
m_nunchuk_orientation_box); m_nunchuk_orientation_box);
auto* nunchuk_orientation_z_layout = auto* nunchuk_orientation_z_layout =
// i18n: Refers to a 3D axis (used when mapping motion controls) // i18n: Refers to a 3D axis (used when mapping motion controls)
CreateSliderValuePairLayout(tr("Z"), m_nunchuk_orientation_z_value, 512, 1023, Qt::Key_P, CreateSliderValuePairLayout(tr("Z"), WiimoteEmu::Nunchuk::ACCELEROMETER_GROUP,
ControllerEmu::ReshapableInput::Z_INPUT_OVERRIDE,
&m_nunchuk_overrider, m_nunchuk_orientation_z_value, ACCEL_ZERO_G,
ACCEL_ONE_G, ACCEL_MIN, ACCEL_MAX, Qt::Key_P,
m_nunchuk_orientation_box); m_nunchuk_orientation_box);
auto* nunchuk_orientation_layout = new QVBoxLayout; auto* nunchuk_orientation_layout = new QVBoxLayout;
@ -145,29 +175,46 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
m_nunchuk_orientation_box->setLayout(nunchuk_orientation_layout); m_nunchuk_orientation_box->setLayout(nunchuk_orientation_layout);
m_triggers_box = new QGroupBox(tr("Triggers")); m_triggers_box = new QGroupBox(tr("Triggers"));
auto* l_trigger_layout = CreateSliderValuePairLayout(tr("Left"), m_left_trigger_value, 0, 31, auto* l_trigger_layout = CreateSliderValuePairLayout(
Qt::Key_N, m_triggers_box); tr("Left"), WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::L_ANALOG,
auto* r_trigger_layout = CreateSliderValuePairLayout(tr("Right"), m_right_trigger_value, 0, 31, &m_classic_overrider, m_left_trigger_value, 0, 0, 0, 31, Qt::Key_N, m_triggers_box);
Qt::Key_M, m_triggers_box); auto* r_trigger_layout = CreateSliderValuePairLayout(
tr("Right"), WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::R_ANALOG,
&m_classic_overrider, m_right_trigger_value, 0, 0, 0, 31, Qt::Key_M, m_triggers_box);
auto* triggers_layout = new QVBoxLayout; auto* triggers_layout = new QVBoxLayout;
triggers_layout->addLayout(l_trigger_layout); triggers_layout->addLayout(l_trigger_layout);
triggers_layout->addLayout(r_trigger_layout); triggers_layout->addLayout(r_trigger_layout);
m_triggers_box->setLayout(triggers_layout); m_triggers_box->setLayout(triggers_layout);
m_a_button = CreateButton(QStringLiteral("&A")); m_a_button = CreateButton(QStringLiteral("&A"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_b_button = CreateButton(QStringLiteral("&B")); WiimoteEmu::Wiimote::A_BUTTON, &m_wiimote_overrider);
m_1_button = CreateButton(QStringLiteral("&1")); m_b_button = CreateButton(QStringLiteral("&B"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_2_button = CreateButton(QStringLiteral("&2")); WiimoteEmu::Wiimote::B_BUTTON, &m_wiimote_overrider);
m_plus_button = CreateButton(QStringLiteral("&+")); m_1_button = CreateButton(QStringLiteral("&1"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_minus_button = CreateButton(QStringLiteral("&-")); WiimoteEmu::Wiimote::ONE_BUTTON, &m_wiimote_overrider);
m_home_button = CreateButton(QStringLiteral("&HOME")); m_2_button = CreateButton(QStringLiteral("&2"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_left_button = CreateButton(QStringLiteral("&Left")); WiimoteEmu::Wiimote::TWO_BUTTON, &m_wiimote_overrider);
m_up_button = CreateButton(QStringLiteral("&Up")); m_plus_button = CreateButton(QStringLiteral("&+"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_down_button = CreateButton(QStringLiteral("&Down")); WiimoteEmu::Wiimote::PLUS_BUTTON, &m_wiimote_overrider);
m_right_button = CreateButton(QStringLiteral("&Right")); m_minus_button = CreateButton(QStringLiteral("&-"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
m_c_button = CreateButton(QStringLiteral("&C")); WiimoteEmu::Wiimote::MINUS_BUTTON, &m_wiimote_overrider);
m_z_button = CreateButton(QStringLiteral("&Z")); m_home_button = CreateButton(QStringLiteral("&HOME"), WiimoteEmu::Wiimote::BUTTONS_GROUP,
WiimoteEmu::Wiimote::HOME_BUTTON, &m_wiimote_overrider);
m_left_button = CreateButton(QStringLiteral("&Left"), WiimoteEmu::Wiimote::DPAD_GROUP,
DIRECTION_LEFT, &m_wiimote_overrider);
m_up_button = CreateButton(QStringLiteral("&Up"), WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_UP,
&m_wiimote_overrider);
m_down_button = CreateButton(QStringLiteral("&Down"), WiimoteEmu::Wiimote::DPAD_GROUP,
DIRECTION_DOWN, &m_wiimote_overrider);
m_right_button = CreateButton(QStringLiteral("&Right"), WiimoteEmu::Wiimote::DPAD_GROUP,
DIRECTION_RIGHT, &m_wiimote_overrider);
m_c_button = CreateButton(QStringLiteral("&C"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
WiimoteEmu::Nunchuk::C_BUTTON, &m_nunchuk_overrider);
m_z_button = CreateButton(QStringLiteral("&Z"), WiimoteEmu::Nunchuk::BUTTONS_GROUP,
WiimoteEmu::Nunchuk::Z_BUTTON, &m_nunchuk_overrider);
auto* buttons_layout = new QGridLayout; auto* buttons_layout = new QGridLayout;
buttons_layout->addWidget(m_a_button, 0, 0); buttons_layout->addWidget(m_a_button, 0, 0);
@ -196,21 +243,38 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
m_nunchuk_buttons_box = new QGroupBox(tr("Nunchuk Buttons")); m_nunchuk_buttons_box = new QGroupBox(tr("Nunchuk Buttons"));
m_nunchuk_buttons_box->setLayout(nunchuk_buttons_layout); m_nunchuk_buttons_box->setLayout(nunchuk_buttons_layout);
m_classic_a_button = CreateButton(QStringLiteral("&A")); m_classic_a_button = CreateButton(QStringLiteral("&A"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_b_button = CreateButton(QStringLiteral("&B")); WiimoteEmu::Classic::A_BUTTON, &m_classic_overrider);
m_classic_x_button = CreateButton(QStringLiteral("&X")); m_classic_b_button = CreateButton(QStringLiteral("&B"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_y_button = CreateButton(QStringLiteral("&Y")); WiimoteEmu::Classic::B_BUTTON, &m_classic_overrider);
m_classic_l_button = CreateButton(QStringLiteral("&L")); m_classic_x_button = CreateButton(QStringLiteral("&X"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_r_button = CreateButton(QStringLiteral("&R")); WiimoteEmu::Classic::X_BUTTON, &m_classic_overrider);
m_classic_zl_button = CreateButton(QStringLiteral("&ZL")); m_classic_y_button = CreateButton(QStringLiteral("&Y"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_zr_button = CreateButton(QStringLiteral("ZR")); WiimoteEmu::Classic::Y_BUTTON, &m_classic_overrider);
m_classic_plus_button = CreateButton(QStringLiteral("&+")); m_classic_zl_button = CreateButton(QStringLiteral("&ZL"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_minus_button = CreateButton(QStringLiteral("&-")); WiimoteEmu::Classic::ZL_BUTTON, &m_classic_overrider);
m_classic_home_button = CreateButton(QStringLiteral("&HOME")); m_classic_zr_button = CreateButton(QStringLiteral("ZR"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_left_button = CreateButton(QStringLiteral("L&eft")); WiimoteEmu::Classic::ZR_BUTTON, &m_classic_overrider);
m_classic_up_button = CreateButton(QStringLiteral("&Up")); m_classic_plus_button = CreateButton(QStringLiteral("&+"), WiimoteEmu::Classic::BUTTONS_GROUP,
m_classic_down_button = CreateButton(QStringLiteral("&Down")); WiimoteEmu::Classic::PLUS_BUTTON, &m_classic_overrider);
m_classic_right_button = CreateButton(QStringLiteral("R&ight")); m_classic_minus_button = CreateButton(QStringLiteral("&-"), WiimoteEmu::Classic::BUTTONS_GROUP,
WiimoteEmu::Classic::MINUS_BUTTON, &m_classic_overrider);
m_classic_home_button = CreateButton(QStringLiteral("&HOME"), WiimoteEmu::Classic::BUTTONS_GROUP,
WiimoteEmu::Classic::HOME_BUTTON, &m_classic_overrider);
m_classic_l_button = CreateButton(QStringLiteral("&L"), WiimoteEmu::Classic::TRIGGERS_GROUP,
WiimoteEmu::Classic::L_DIGITAL, &m_classic_overrider);
m_classic_r_button = CreateButton(QStringLiteral("&R"), WiimoteEmu::Classic::TRIGGERS_GROUP,
WiimoteEmu::Classic::R_DIGITAL, &m_classic_overrider);
m_classic_left_button = CreateButton(QStringLiteral("L&eft"), WiimoteEmu::Classic::DPAD_GROUP,
DIRECTION_LEFT, &m_classic_overrider);
m_classic_up_button = CreateButton(QStringLiteral("&Up"), WiimoteEmu::Classic::DPAD_GROUP,
DIRECTION_UP, &m_classic_overrider);
m_classic_down_button = CreateButton(QStringLiteral("&Down"), WiimoteEmu::Classic::DPAD_GROUP,
DIRECTION_DOWN, &m_classic_overrider);
m_classic_right_button = CreateButton(QStringLiteral("R&ight"), WiimoteEmu::Classic::DPAD_GROUP,
DIRECTION_RIGHT, &m_classic_overrider);
auto* classic_buttons_layout = new QGridLayout; auto* classic_buttons_layout = new QGridLayout;
classic_buttons_layout->addWidget(m_classic_a_button, 0, 0); classic_buttons_layout->addWidget(m_classic_a_button, 0, 0);
@ -247,11 +311,9 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
setLayout(layout); setLayout(layout);
u8 ext = 0;
if (Core::IsRunning()) if (Core::IsRunning())
{ {
ext = static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(num)) m_active_extension = GetWiimote()->GetActiveExtensionNumber();
->GetActiveExtensionNumber();
} }
else else
{ {
@ -261,16 +323,35 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow(
ini.GetIfExists("Wiimote" + std::to_string(num + 1), "Extension", &extension); ini.GetIfExists("Wiimote" + std::to_string(num + 1), "Extension", &extension);
if (extension == "Nunchuk") if (extension == "Nunchuk")
ext = 1; m_active_extension = WiimoteEmu::ExtensionNumber::NUNCHUK;
if (extension == "Classic") else if (extension == "Classic")
ext = 2; m_active_extension = WiimoteEmu::ExtensionNumber::CLASSIC;
else
m_active_extension = WiimoteEmu::ExtensionNumber::NONE;
} }
UpdateExt(ext); UpdateExt();
} }
void WiiTASInputWindow::UpdateExt(u8 ext) WiimoteEmu::Wiimote* WiiTASInputWindow::GetWiimote()
{ {
if (ext == 1) return static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(m_num));
}
ControllerEmu::Attachments* WiiTASInputWindow::GetAttachments()
{
return static_cast<ControllerEmu::Attachments*>(
GetWiimote()->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments));
}
WiimoteEmu::Extension* WiiTASInputWindow::GetExtension()
{
return static_cast<WiimoteEmu::Extension*>(
GetAttachments()->GetAttachmentList()[m_active_extension].get());
}
void WiiTASInputWindow::UpdateExt()
{
if (m_active_extension == WiimoteEmu::ExtensionNumber::NUNCHUK)
{ {
setWindowTitle(tr("Wii TAS Input %1 - Wii Remote + Nunchuk").arg(m_num + 1)); setWindowTitle(tr("Wii TAS Input %1 - Wii Remote + Nunchuk").arg(m_num + 1));
m_ir_box->show(); m_ir_box->show();
@ -284,7 +365,7 @@ void WiiTASInputWindow::UpdateExt(u8 ext)
m_remote_buttons_box->show(); m_remote_buttons_box->show();
m_classic_buttons_box->hide(); m_classic_buttons_box->hide();
} }
else if (ext == 2) else if (m_active_extension == WiimoteEmu::ExtensionNumber::CLASSIC)
{ {
setWindowTitle(tr("Wii TAS Input %1 - Classic Controller").arg(m_num + 1)); setWindowTitle(tr("Wii TAS Input %1 - Classic Controller").arg(m_num + 1));
m_ir_box->hide(); m_ir_box->hide();
@ -314,183 +395,22 @@ void WiiTASInputWindow::UpdateExt(u8 ext)
} }
} }
void WiiTASInputWindow::GetValues(DataReportBuilder& rpt, int ext, void WiiTASInputWindow::hideEvent(QHideEvent* event)
const WiimoteEmu::EncryptionKey& key)
{ {
if (!isVisible()) GetWiimote()->ClearInputOverrideFunction();
return; GetExtension()->ClearInputOverrideFunction();
}
UpdateExt(ext);
void WiiTASInputWindow::showEvent(QShowEvent* event)
if (m_remote_buttons_box->isVisible() && rpt.HasCore()) {
{ WiimoteEmu::Wiimote* wiimote = GetWiimote();
DataReportBuilder::CoreData core;
rpt.GetCoreData(&core); if (m_active_extension != WiimoteEmu::ExtensionNumber::CLASSIC)
wiimote->SetInputOverrideFunction(m_wiimote_overrider.GetInputOverrideFunction());
using EmuWiimote = WiimoteEmu::Wiimote;
if (m_active_extension == WiimoteEmu::ExtensionNumber::NUNCHUK)
u16& buttons = core.hex; GetExtension()->SetInputOverrideFunction(m_nunchuk_overrider.GetInputOverrideFunction());
GetButton<u16>(m_a_button, buttons, EmuWiimote::BUTTON_A);
GetButton<u16>(m_b_button, buttons, EmuWiimote::BUTTON_B); if (m_active_extension == WiimoteEmu::ExtensionNumber::CLASSIC)
GetButton<u16>(m_1_button, buttons, EmuWiimote::BUTTON_ONE); GetExtension()->SetInputOverrideFunction(m_classic_overrider.GetInputOverrideFunction());
GetButton<u16>(m_2_button, buttons, EmuWiimote::BUTTON_TWO);
GetButton<u16>(m_plus_button, buttons, EmuWiimote::BUTTON_PLUS);
GetButton<u16>(m_minus_button, buttons, EmuWiimote::BUTTON_MINUS);
GetButton<u16>(m_home_button, buttons, EmuWiimote::BUTTON_HOME);
GetButton<u16>(m_left_button, buttons, EmuWiimote::PAD_LEFT);
GetButton<u16>(m_up_button, buttons, EmuWiimote::PAD_UP);
GetButton<u16>(m_down_button, buttons, EmuWiimote::PAD_DOWN);
GetButton<u16>(m_right_button, buttons, EmuWiimote::PAD_RIGHT);
rpt.SetCoreData(core);
}
if (m_remote_orientation_box->isVisible() && rpt.HasAccel())
{
// FYI: Interleaved reports may behave funky as not all data is always available.
AccelData accel;
rpt.GetAccelData(&accel);
GetSpinBoxU16(m_remote_orientation_x_value, accel.value.x);
GetSpinBoxU16(m_remote_orientation_y_value, accel.value.y);
GetSpinBoxU16(m_remote_orientation_z_value, accel.value.z);
rpt.SetAccelData(accel);
}
if (m_ir_box->isVisible() && rpt.HasIR() && !m_use_controller->isChecked())
{
u8* const ir_data = rpt.GetIRDataPtr();
u16 y = m_ir_y_value->value();
std::array<u16, 4> x;
x[0] = m_ir_x_value->value();
x[1] = x[0] + 100;
x[2] = x[0] - 10;
x[3] = x[1] + 10;
// FYI: This check is not entirely foolproof.
// TODO: IR "full" mode not implemented.
u8 mode = WiimoteEmu::CameraLogic::IR_MODE_BASIC;
if (rpt.GetIRDataSize() == sizeof(WiimoteEmu::IRExtended) * 4)
mode = WiimoteEmu::CameraLogic::IR_MODE_EXTENDED;
else if (rpt.GetIRDataSize() == sizeof(WiimoteEmu::IRFull) * 2)
mode = WiimoteEmu::CameraLogic::IR_MODE_FULL;
if (mode == WiimoteEmu::CameraLogic::IR_MODE_BASIC)
{
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRBasic) * 2);
auto* const ir_basic = reinterpret_cast<WiimoteEmu::IRBasic*>(ir_data);
for (int i = 0; i < 2; ++i)
{
if (x[i * 2] < 1024 && y < 768)
{
ir_basic[i].x1 = static_cast<u8>(x[i * 2]);
ir_basic[i].x1hi = x[i * 2] >> 8;
ir_basic[i].y1 = static_cast<u8>(y);
ir_basic[i].y1hi = y >> 8;
}
if (x[i * 2 + 1] < 1024 && y < 768)
{
ir_basic[i].x2 = static_cast<u8>(x[i * 2 + 1]);
ir_basic[i].x2hi = x[i * 2 + 1] >> 8;
ir_basic[i].y2 = static_cast<u8>(y);
ir_basic[i].y2hi = y >> 8;
}
}
}
else
{
// TODO: this code doesnt work, resulting in no IR TAS inputs in e.g. wii sports menu when no
// remote extension is used
memset(ir_data, 0xFF, sizeof(WiimoteEmu::IRExtended) * 4);
auto* const ir_extended = reinterpret_cast<WiimoteEmu::IRExtended*>(ir_data);
for (size_t i = 0; i < x.size(); ++i)
{
if (x[i] < 1024 && y < 768)
{
ir_extended[i].x = static_cast<u8>(x[i]);
ir_extended[i].xhi = x[i] >> 8;
ir_extended[i].y = static_cast<u8>(y);
ir_extended[i].yhi = y >> 8;
ir_extended[i].size = 10;
}
}
}
}
if (rpt.HasExt() && m_nunchuk_stick_box->isVisible())
{
u8* const ext_data = rpt.GetExtDataPtr();
auto& nunchuk = *reinterpret_cast<WiimoteEmu::Nunchuk::DataFormat*>(ext_data);
GetSpinBoxU8(m_nunchuk_stick_x_value, nunchuk.jx);
GetSpinBoxU8(m_nunchuk_stick_y_value, nunchuk.jy);
auto accel = nunchuk.GetAccel().value;
GetSpinBoxU16(m_nunchuk_orientation_x_value, accel.x);
GetSpinBoxU16(m_nunchuk_orientation_y_value, accel.y);
GetSpinBoxU16(m_nunchuk_orientation_z_value, accel.z);
nunchuk.SetAccel(accel);
u8 bt = nunchuk.GetButtons();
GetButton<u8>(m_c_button, bt, WiimoteEmu::Nunchuk::BUTTON_C);
GetButton<u8>(m_z_button, bt, WiimoteEmu::Nunchuk::BUTTON_Z);
nunchuk.SetButtons(bt);
key.Encrypt(reinterpret_cast<u8*>(&nunchuk), 0, sizeof(nunchuk));
}
if (m_classic_left_stick_box->isVisible())
{
u8* const ext_data = rpt.GetExtDataPtr();
auto& cc = *reinterpret_cast<WiimoteEmu::Classic::DataFormat*>(ext_data);
key.Decrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
u16 bt = cc.GetButtons();
GetButton<u16>(m_classic_a_button, bt, WiimoteEmu::Classic::BUTTON_A);
GetButton<u16>(m_classic_b_button, bt, WiimoteEmu::Classic::BUTTON_B);
GetButton<u16>(m_classic_x_button, bt, WiimoteEmu::Classic::BUTTON_X);
GetButton<u16>(m_classic_y_button, bt, WiimoteEmu::Classic::BUTTON_Y);
GetButton<u16>(m_classic_plus_button, bt, WiimoteEmu::Classic::BUTTON_PLUS);
GetButton<u16>(m_classic_minus_button, bt, WiimoteEmu::Classic::BUTTON_MINUS);
GetButton<u16>(m_classic_l_button, bt, WiimoteEmu::Classic::TRIGGER_L);
GetButton<u16>(m_classic_r_button, bt, WiimoteEmu::Classic::TRIGGER_R);
GetButton<u16>(m_classic_zl_button, bt, WiimoteEmu::Classic::BUTTON_ZL);
GetButton<u16>(m_classic_zr_button, bt, WiimoteEmu::Classic::BUTTON_ZR);
GetButton<u16>(m_classic_home_button, bt, WiimoteEmu::Classic::BUTTON_HOME);
GetButton<u16>(m_classic_left_button, bt, WiimoteEmu::Classic::PAD_LEFT);
GetButton<u16>(m_classic_up_button, bt, WiimoteEmu::Classic::PAD_UP);
GetButton<u16>(m_classic_down_button, bt, WiimoteEmu::Classic::PAD_DOWN);
GetButton<u16>(m_classic_right_button, bt, WiimoteEmu::Classic::PAD_RIGHT);
cc.SetButtons(bt);
auto right_stick = cc.GetRightStick().value;
GetSpinBoxU8(m_classic_right_stick_x_value, right_stick.x);
GetSpinBoxU8(m_classic_right_stick_y_value, right_stick.y);
cc.SetRightStick(right_stick);
auto left_stick = cc.GetLeftStick().value;
GetSpinBoxU8(m_classic_left_stick_x_value, left_stick.x);
GetSpinBoxU8(m_classic_left_stick_y_value, left_stick.y);
cc.SetLeftStick(left_stick);
u8 rt = cc.GetRightTrigger().value;
GetSpinBoxU8(m_right_trigger_value, rt);
cc.SetRightTrigger(rt);
u8 lt = cc.GetLeftTrigger().value;
GetSpinBoxU8(m_left_trigger_value, lt);
cc.SetLeftTrigger(lt);
key.Encrypt(reinterpret_cast<u8*>(&cc), 0, sizeof(cc));
}
} }

View File

@ -5,31 +5,48 @@
#include "DolphinQt/TAS/TASInputWindow.h" #include "DolphinQt/TAS/TASInputWindow.h"
namespace WiimoteCommon #include "Core/HW/WiimoteEmu/ExtensionPort.h"
{
class DataReportBuilder; class QGroupBox;
} class QHideEvent;
class QShowEvent;
class QSpinBox;
class TASCheckBox;
namespace WiimoteEmu namespace WiimoteEmu
{ {
class EncryptionKey; class Extension;
} class Wiimote;
} // namespace WiimoteEmu
class QGroupBox; namespace ControllerEmu
class QSpinBox; {
class TASCheckBox; class Attachments;
}
class WiiTASInputWindow : public TASInputWindow class WiiTASInputWindow : public TASInputWindow
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit WiiTASInputWindow(QWidget* parent, int num); explicit WiiTASInputWindow(QWidget* parent, int num);
void GetValues(WiimoteCommon::DataReportBuilder& rpt, int ext,
const WiimoteEmu::EncryptionKey& key); void hideEvent(QHideEvent* event) override;
void showEvent(QShowEvent* event) override;
private: private:
void UpdateExt(u8 ext); WiimoteEmu::Wiimote* GetWiimote();
ControllerEmu::Attachments* GetAttachments();
WiimoteEmu::Extension* GetExtension();
void UpdateExt();
WiimoteEmu::ExtensionNumber m_active_extension;
int m_num; int m_num;
InputOverrider m_wiimote_overrider;
InputOverrider m_nunchuk_overrider;
InputOverrider m_classic_overrider;
TASCheckBox* m_a_button; TASCheckBox* m_a_button;
TASCheckBox* m_b_button; TASCheckBox* m_b_button;
TASCheckBox* m_1_button; TASCheckBox* m_1_button;

View File

@ -140,6 +140,8 @@ elseif(ANDROID)
ControllerInterface/Android/Android.h ControllerInterface/Android/Android.h
ControllerInterface/Touch/ButtonManager.cpp ControllerInterface/Touch/ButtonManager.cpp
ControllerInterface/Touch/ButtonManager.h ControllerInterface/Touch/ButtonManager.h
ControllerInterface/Touch/InputOverrider.cpp
ControllerInterface/Touch/InputOverrider.h
ControllerInterface/Touch/Touchscreen.cpp ControllerInterface/Touch/Touchscreen.cpp
ControllerInterface/Touch/Touchscreen.h ControllerInterface/Touch/Touchscreen.h
) )

View File

@ -4,6 +4,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h" #include "InputCommon/ControllerEmu/ControlGroup/AnalogStick.h"
#include <cmath> #include <cmath>
#include <optional>
#include "Common/Common.h" #include "Common/Common.h"
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
@ -48,6 +49,34 @@ AnalogStick::StateData AnalogStick::GetState() const
return GetReshapableState(true); return GetReshapableState(true);
} }
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func) const
{
bool override_occurred = false;
return GetState(override_func, &override_occurred);
}
AnalogStick::StateData AnalogStick::GetState(const InputOverrideFunction& override_func,
bool* override_occurred) const
{
StateData state = GetState();
if (!override_func)
return state;
if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x))
{
state.x = *x_override;
*override_occurred = true;
}
if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y))
{
state.y = *y_override;
*override_occurred = true;
}
return state;
}
ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const ControlState AnalogStick::GetGateRadiusAtAngle(double ang) const
{ {
return m_stick_gate->GetRadiusAtAngle(ang); return m_stick_gate->GetRadiusAtAngle(ang);

View File

@ -21,6 +21,8 @@ public:
ControlState GetGateRadiusAtAngle(double ang) const override; ControlState GetGateRadiusAtAngle(double ang) const override;
StateData GetState() const; StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
StateData GetState(const InputOverrideFunction& override_func, bool* override_occurred) const;
private: private:
Control* GetModifierInput() const override; Control* GetModifierInput() const override;

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include <cmath>
#include <string> #include <string>
#include "InputCommon/ControlReference/ControlReference.h" #include "InputCommon/ControlReference/ControlReference.h"
@ -24,5 +25,21 @@ public:
for (auto& control : controls) for (auto& control : controls)
*buttons |= *(bitmasks++) * control->GetState<bool>(); *buttons |= *(bitmasks++) * control->GetState<bool>();
} }
template <typename C>
void GetState(C* const buttons, const C* bitmasks,
const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState(buttons, bitmasks);
for (auto& control : controls)
{
ControlState state = control->GetState();
if (std::optional<ControlState> state_override = override_func(name, control->name, state))
state = *state_override;
*buttons |= *(bitmasks++) * (std::lround(state) > 0);
}
}
}; };
} // namespace ControllerEmu } // namespace ControllerEmu

View File

@ -5,14 +5,18 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <functional>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include <string_view>
#include <type_traits> #include <type_traits>
#include <vector> #include <vector>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "InputCommon/ControllerEmu/Control/Control.h" #include "InputCommon/ControllerEmu/Control/Control.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu namespace ControllerEmu
{ {
@ -27,6 +31,9 @@ class NumericSetting;
template <typename T> template <typename T>
class SettingValue; class SettingValue;
using InputOverrideFunction = std::function<std::optional<ControlState>(
const std::string_view group_name, const std::string_view control_name, ControlState state)>;
enum class GroupType enum class GroupType
{ {
Other, Other,

View File

@ -82,15 +82,28 @@ ControlState Cursor::GetGateRadiusAtAngle(double ang) const
Cursor::StateData Cursor::GetState(const bool adjusted) Cursor::StateData Cursor::GetState(const bool adjusted)
{ {
if (!adjusted) const ReshapeData input = GetReshapableState(adjusted);
{ const StateData state = adjusted ? UpdateState(input) : StateData{input.x, input.y};
const auto raw_input = GetReshapableState(false); return state;
}
return {raw_input.x, raw_input.y}; Cursor::StateData Cursor::GetState(const bool adjusted,
} const ControllerEmu::InputOverrideFunction& override_func)
{
StateData state = GetState(adjusted);
if (!override_func)
return state;
const auto input = GetReshapableState(true); if (const std::optional<ControlState> x_override = override_func(name, X_INPUT_OVERRIDE, state.x))
state.x = *x_override;
if (const std::optional<ControlState> y_override = override_func(name, Y_INPUT_OVERRIDE, state.y))
state.y = *y_override;
return state;
}
Cursor::StateData Cursor::UpdateState(Cursor::ReshapeData input)
{
// TODO: Using system time is ugly. // TODO: Using system time is ugly.
// Kill this after state is moved into wiimote rather than this class. // Kill this after state is moved into wiimote rather than this class.
const auto now = Clock::now(); const auto now = Clock::now();

View File

@ -29,6 +29,7 @@ public:
// Modifies the state // Modifies the state
StateData GetState(bool adjusted); StateData GetState(bool adjusted);
StateData GetState(bool adjusted, const ControllerEmu::InputOverrideFunction& override_func);
// Yaw movement in radians. // Yaw movement in radians.
ControlState GetTotalYaw() const; ControlState GetTotalYaw() const;
@ -40,6 +41,8 @@ public:
ControlState GetVerticalOffset() const; ControlState GetVerticalOffset() const;
private: private:
Cursor::StateData UpdateState(Cursor::ReshapeData input);
// This is used to reduce the cursor speed for relative input // This is used to reduce the cursor speed for relative input
// to something that makes sense with the default range. // to something that makes sense with the default range.
static constexpr double STEP_PER_SEC = 0.01 * 200; static constexpr double STEP_PER_SEC = 0.01 * 200;

View File

@ -4,6 +4,7 @@
#include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h" #include "InputCommon/ControllerEmu/ControlGroup/MixedTriggers.h"
#include <algorithm> #include <algorithm>
#include <cmath>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <string> #include <string>
@ -63,6 +64,53 @@ void MixedTriggers::GetState(u16* const digital, const u16* bitmasks, ControlSta
} }
} }
void MixedTriggers::GetState(u16* digital, const u16* bitmasks, ControlState* analog,
const InputOverrideFunction& override_func, bool adjusted) const
{
if (!override_func)
return GetState(digital, bitmasks, analog, adjusted);
const ControlState threshold = GetThreshold();
ControlState deadzone = GetDeadzone();
// Return raw values. (used in UI)
if (!adjusted)
{
deadzone = 0.0;
}
const int trigger_count = int(controls.size() / 2);
for (int i = 0; i != trigger_count; ++i)
{
bool button_bool = false;
const ControlState button_value = ApplyDeadzone(controls[i]->GetState(), deadzone);
ControlState analog_value = ApplyDeadzone(controls[trigger_count + i]->GetState(), deadzone);
// Apply threshold:
if (button_value > threshold)
{
analog_value = 1.0;
button_bool = true;
}
if (const std::optional<ControlState> button_override =
override_func(name, controls[i]->name, static_cast<ControlState>(button_bool)))
{
button_bool = std::lround(*button_override) > 0;
}
if (const std::optional<ControlState> analog_override =
override_func(name, controls[trigger_count + i]->name, analog_value))
{
analog_value = *analog_override;
}
if (button_bool)
*digital |= bitmasks[i];
analog[i] = std::min(analog_value, 1.0);
}
}
ControlState MixedTriggers::GetDeadzone() const ControlState MixedTriggers::GetDeadzone() const
{ {
return m_deadzone_setting.GetValue() / 100; return m_deadzone_setting.GetValue() / 100;

View File

@ -17,6 +17,8 @@ public:
void GetState(u16* digital, const u16* bitmasks, ControlState* analog, void GetState(u16* digital, const u16* bitmasks, ControlState* analog,
bool adjusted = true) const; bool adjusted = true) const;
void GetState(u16* digital, const u16* bitmasks, ControlState* analog,
const InputOverrideFunction& override_func, bool adjusted = true) const;
ControlState GetDeadzone() const; ControlState GetDeadzone() const;
ControlState GetThreshold() const; ControlState GetThreshold() const;

View File

@ -35,4 +35,21 @@ Slider::StateData Slider::GetState() const
return {std::clamp(ApplyDeadzone(state, deadzone), -1.0, 1.0)}; return {std::clamp(ApplyDeadzone(state, deadzone), -1.0, 1.0)};
} }
Slider::StateData Slider::GetState(const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState();
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
ControlState state = controls[1]->GetState() - controls[0]->GetState();
state = ApplyDeadzone(state, deadzone);
if (std::optional<ControlState> state_override = override_func(name, X_INPUT_OVERRIDE, state))
state = *state_override;
return {std::clamp(state, -1.0, 1.0)};
}
} // namespace ControllerEmu } // namespace ControllerEmu

View File

@ -23,6 +23,9 @@ public:
explicit Slider(const std::string& name_); explicit Slider(const std::string& name_);
StateData GetState() const; StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
static constexpr const char* X_INPUT_OVERRIDE = "X";
private: private:
SettingValue<double> m_deadzone_setting; SettingValue<double> m_deadzone_setting;

View File

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cstddef> #include <cstddef>
#include <memory> #include <memory>
#include <optional>
#include <string> #include <string>
#include "Common/Common.h" #include "Common/Common.h"
@ -31,4 +32,25 @@ Triggers::StateData Triggers::GetState() const
return result; return result;
} }
Triggers::StateData Triggers::GetState(const InputOverrideFunction& override_func) const
{
if (!override_func)
return GetState();
const size_t trigger_count = controls.size();
const ControlState deadzone = m_deadzone_setting.GetValue() / 100;
StateData result(trigger_count);
for (size_t i = 0; i < trigger_count; ++i)
{
ControlState state = ApplyDeadzone(controls[i]->GetState(), deadzone);
if (std::optional<ControlState> state_override = override_func(name, controls[i]->name, state))
state = *state_override;
result.data[i] = std::min(state, 1.0);
}
return result;
}
} // namespace ControllerEmu } // namespace ControllerEmu

View File

@ -26,6 +26,7 @@ public:
explicit Triggers(const std::string& name); explicit Triggers(const std::string& name);
StateData GetState() const; StateData GetState() const;
StateData GetState(const InputOverrideFunction& override_func) const;
private: private:
SettingValue<double> m_deadzone_setting; SettingValue<double> m_deadzone_setting;

View File

@ -6,6 +6,7 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <utility>
#include "Common/IniFile.h" #include "Common/IniFile.h"
@ -176,4 +177,15 @@ void EmulatedController::LoadDefaults(const ControllerInterface& ciface)
SetDefaultDevice(default_device_string); SetDefaultDevice(default_device_string);
} }
} }
void EmulatedController::SetInputOverrideFunction(InputOverrideFunction override_func)
{
m_input_override_function = std::move(override_func);
}
void EmulatedController::ClearInputOverrideFunction()
{
m_input_override_function = {};
}
} // namespace ControllerEmu } // namespace ControllerEmu

View File

@ -15,12 +15,18 @@
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Common/MathUtil.h" #include "Common/MathUtil.h"
#include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerInterface/CoreDevice.h" #include "InputCommon/ControllerInterface/CoreDevice.h"
class ControllerInterface; class ControllerInterface;
const char* const named_directions[] = {_trans("Up"), _trans("Down"), _trans("Left"), constexpr const char* DIRECTION_UP = _trans("Up");
_trans("Right")}; constexpr const char* DIRECTION_DOWN = _trans("Down");
constexpr const char* DIRECTION_LEFT = _trans("Left");
constexpr const char* DIRECTION_RIGHT = _trans("Right");
constexpr const char* named_directions[] = {DIRECTION_UP, DIRECTION_DOWN, DIRECTION_LEFT,
DIRECTION_RIGHT};
class ControlReference; class ControlReference;
@ -184,6 +190,9 @@ public:
void SetDefaultDevice(const std::string& device); void SetDefaultDevice(const std::string& device);
void SetDefaultDevice(ciface::Core::DeviceQualifier devq); void SetDefaultDevice(ciface::Core::DeviceQualifier devq);
void SetInputOverrideFunction(InputOverrideFunction override_func);
void ClearInputOverrideFunction();
void UpdateReferences(const ControllerInterface& devi); void UpdateReferences(const ControllerInterface& devi);
void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref); void UpdateSingleControlReference(const ControllerInterface& devi, ControlReference* ref);
@ -200,7 +209,7 @@ public:
std::vector<std::unique_ptr<ControlGroup>> groups; std::vector<std::unique_ptr<ControlGroup>> groups;
// Maps a float from -1.0..+1.0 to an integer of the provided values. // Maps a float from -1.0..+1.0 to an integer in the provided range.
template <typename T, typename F> template <typename T, typename F>
static T MapFloat(F input_value, T zero_value, T neg_1_value = std::numeric_limits<T>::min(), static T MapFloat(F input_value, T zero_value, T neg_1_value = std::numeric_limits<T>::min(),
T pos_1_value = std::numeric_limits<T>::max()) T pos_1_value = std::numeric_limits<T>::max())
@ -223,11 +232,28 @@ public:
return T(std::llround((zero_value - neg_1_value) * input_value + zero_value)); return T(std::llround((zero_value - neg_1_value) * input_value + zero_value));
} }
// The inverse of the function above.
// Maps an integer in the provided range to a float in the range -1.0..1.0.
template <typename F, typename T>
static F MapToFloat(T input_value, T zero_value, T neg_1_value = std::numeric_limits<T>::min(),
T pos_1_value = std::numeric_limits<T>::max())
{
static_assert(std::is_integral<T>(), "T is only sane for int types.");
static_assert(std::is_floating_point<F>(), "F is only sane for float types.");
if (input_value >= zero_value)
return F(input_value - zero_value) / F(pos_1_value - zero_value);
else
return -F(zero_value - input_value) / F(zero_value - neg_1_value);
}
protected: protected:
// TODO: Wiimote attachments actually end up using their parent controller value for this, // TODO: Wiimote attachments actually end up using their parent controller value for this,
// so theirs won't be used (and thus shouldn't even exist). // so theirs won't be used (and thus shouldn't even exist).
ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars; ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars;
InputOverrideFunction m_input_override_function;
void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env); void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env);
private: private:

View File

@ -106,6 +106,10 @@ public:
const ReshapeData& GetCenter() const; const ReshapeData& GetCenter() const;
void SetCenter(ReshapeData center); void SetCenter(ReshapeData center);
static constexpr const char* X_INPUT_OVERRIDE = "X";
static constexpr const char* Y_INPUT_OVERRIDE = "Y";
static constexpr const char* Z_INPUT_OVERRIDE = "Z";
protected: protected:
ReshapeData Reshape(ControlState x, ControlState y, ControlState modifier = 0.0, ReshapeData Reshape(ControlState x, ControlState y, ControlState modifier = 0.0,
ControlState clamp = 1.0) const; ControlState clamp = 1.0) const;

View File

@ -9,23 +9,11 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include "Common/Assert.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Common/StringUtil.h" #include "Common/StringUtil.h"
#include "Common/Thread.h" #include "Common/Thread.h"
#include "Core/Core.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/GCPadEmu.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/StickGate.h"
namespace ButtonManager namespace ButtonManager
{ {
namespace namespace
@ -700,39 +688,6 @@ float GetAxisValue(int pad_id, ButtonType axis)
return value; return value;
} }
double GetInputRadiusAtAngle(int pad_id, ButtonType stick, double angle)
{
// To avoid a crash, don't access controllers before they've been initialized by the boot process
if (!Core::IsRunningAndStarted())
return 0;
ControllerEmu::ControlGroup* group;
switch (stick)
{
case STICK_MAIN:
group = Pad::GetGroup(pad_id, PadGroup::MainStick);
break;
case STICK_C:
group = Pad::GetGroup(pad_id, PadGroup::CStick);
break;
case NUNCHUK_STICK:
group = Wiimote::GetNunchukGroup(pad_id, WiimoteEmu::NunchukGroup::Stick);
break;
case CLASSIC_STICK_LEFT:
group = Wiimote::GetClassicGroup(pad_id, WiimoteEmu::ClassicGroup::LeftStick);
break;
case CLASSIC_STICK_RIGHT:
group = Wiimote::GetClassicGroup(pad_id, WiimoteEmu::ClassicGroup::RightStick);
break;
default:
ASSERT(false);
return 0;
}
return static_cast<ControllerEmu::ReshapableInput*>(group)->GetInputRadiusAtAngle(angle);
}
bool GamepadEvent(const std::string& dev, int button, int action) bool GamepadEvent(const std::string& dev, int button, int action)
{ {
auto it = m_controllers.find(dev); auto it = m_controllers.find(dev);

View File

@ -272,9 +272,6 @@ void Init(const std::string&);
bool GetButtonPressed(int pad_id, ButtonType button); bool GetButtonPressed(int pad_id, ButtonType button);
float GetAxisValue(int pad_id, ButtonType axis); float GetAxisValue(int pad_id, ButtonType axis);
// emu_pad_id is numbered 0 to 3 for both GC pads and Wiimotes
double GetInputRadiusAtAngle(int emu_pad_id, ButtonType stick, double angle);
bool GamepadEvent(const std::string& dev, int button, int action); bool GamepadEvent(const std::string& dev, int button, int action);
void GamepadAxisEvent(const std::string& dev, int axis, float value); void GamepadAxisEvent(const std::string& dev, int axis, float value);

View File

@ -0,0 +1,272 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include "InputCommon/ControllerInterface/Touch/InputOverrider.h"
#include <array>
#include <map>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include "Common/Assert.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/GCPadEmu.h"
#include "Core/HW/Wiimote.h"
#include "Core/HW/WiimoteEmu/Extension/Classic.h"
#include "Core/HW/WiimoteEmu/Extension/Nunchuk.h"
#include "Core/HW/WiimoteEmu/WiimoteEmu.h"
#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h"
#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h"
#include "InputCommon/ControllerEmu/ControllerEmu.h"
#include "InputCommon/ControllerEmu/StickGate.h"
#include "InputCommon/ControllerInterface/CoreDevice.h"
#include "InputCommon/InputConfig.h"
namespace ciface::Touch
{
namespace
{
struct InputState
{
ControlState normal_state = 0;
ControlState override_state = 0;
bool overriding = false;
};
using ControlsMap = std::map<std::pair<std::string_view, std::string_view>, ControlID>;
using StateArray = std::array<InputState, ControlID::NUMBER_OF_CONTROLS>;
std::array<StateArray, 4> s_state_arrays;
const ControlsMap s_gcpad_controls_map = {{
{{GCPad::BUTTONS_GROUP, GCPad::A_BUTTON}, ControlID::GCPAD_A_BUTTON},
{{GCPad::BUTTONS_GROUP, GCPad::B_BUTTON}, ControlID::GCPAD_B_BUTTON},
{{GCPad::BUTTONS_GROUP, GCPad::X_BUTTON}, ControlID::GCPAD_X_BUTTON},
{{GCPad::BUTTONS_GROUP, GCPad::Y_BUTTON}, ControlID::GCPAD_Y_BUTTON},
{{GCPad::BUTTONS_GROUP, GCPad::Z_BUTTON}, ControlID::GCPAD_Z_BUTTON},
{{GCPad::BUTTONS_GROUP, GCPad::START_BUTTON}, ControlID::GCPAD_START_BUTTON},
{{GCPad::DPAD_GROUP, DIRECTION_UP}, ControlID::GCPAD_DPAD_UP},
{{GCPad::DPAD_GROUP, DIRECTION_DOWN}, ControlID::GCPAD_DPAD_DOWN},
{{GCPad::DPAD_GROUP, DIRECTION_LEFT}, ControlID::GCPAD_DPAD_LEFT},
{{GCPad::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::GCPAD_DPAD_RIGHT},
{{GCPad::TRIGGERS_GROUP, GCPad::L_DIGITAL}, ControlID::GCPAD_L_DIGITAL},
{{GCPad::TRIGGERS_GROUP, GCPad::R_DIGITAL}, ControlID::GCPAD_R_DIGITAL},
{{GCPad::TRIGGERS_GROUP, GCPad::L_ANALOG}, ControlID::GCPAD_L_ANALOG},
{{GCPad::TRIGGERS_GROUP, GCPad::R_ANALOG}, ControlID::GCPAD_R_ANALOG},
{{GCPad::MAIN_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::GCPAD_MAIN_STICK_X},
{{GCPad::MAIN_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::GCPAD_MAIN_STICK_Y},
{{GCPad::C_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::GCPAD_C_STICK_X},
{{GCPad::C_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::GCPAD_C_STICK_Y},
}};
const ControlsMap s_wiimote_controls_map = {{
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::A_BUTTON},
ControlID::WIIMOTE_A_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::B_BUTTON},
ControlID::WIIMOTE_B_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::ONE_BUTTON},
ControlID::WIIMOTE_ONE_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::TWO_BUTTON},
ControlID::WIIMOTE_TWO_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::PLUS_BUTTON},
ControlID::WIIMOTE_PLUS_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::MINUS_BUTTON},
ControlID::WIIMOTE_MINUS_BUTTON},
{{WiimoteEmu::Wiimote::BUTTONS_GROUP, WiimoteEmu::Wiimote::HOME_BUTTON},
ControlID::WIIMOTE_HOME_BUTTON},
{{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_UP}, ControlID::WIIMOTE_DPAD_UP},
{{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_DOWN}, ControlID::WIIMOTE_DPAD_DOWN},
{{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_LEFT}, ControlID::WIIMOTE_DPAD_LEFT},
{{WiimoteEmu::Wiimote::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::WIIMOTE_DPAD_RIGHT},
{{WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::WIIMOTE_IR_X},
{{WiimoteEmu::Wiimote::IR_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::WIIMOTE_IR_Y},
}};
const ControlsMap s_nunchuk_controls_map = {{
{{WiimoteEmu::Nunchuk::BUTTONS_GROUP, WiimoteEmu::Nunchuk::C_BUTTON},
ControlID::NUNCHUK_C_BUTTON},
{{WiimoteEmu::Nunchuk::BUTTONS_GROUP, WiimoteEmu::Nunchuk::Z_BUTTON},
ControlID::NUNCHUK_Z_BUTTON},
{{WiimoteEmu::Nunchuk::STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::NUNCHUK_STICK_X},
{{WiimoteEmu::Nunchuk::STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::NUNCHUK_STICK_Y},
}};
const ControlsMap s_classic_controls_map = {{
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::A_BUTTON},
ControlID::CLASSIC_A_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::B_BUTTON},
ControlID::CLASSIC_B_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::X_BUTTON},
ControlID::CLASSIC_X_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::Y_BUTTON},
ControlID::CLASSIC_Y_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::ZL_BUTTON},
ControlID::CLASSIC_ZL_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::ZR_BUTTON},
ControlID::CLASSIC_ZR_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::PLUS_BUTTON},
ControlID::CLASSIC_PLUS_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::MINUS_BUTTON},
ControlID::CLASSIC_MINUS_BUTTON},
{{WiimoteEmu::Classic::BUTTONS_GROUP, WiimoteEmu::Classic::HOME_BUTTON},
ControlID::CLASSIC_HOME_BUTTON},
{{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_UP}, ControlID::CLASSIC_DPAD_UP},
{{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_DOWN}, ControlID::CLASSIC_DPAD_DOWN},
{{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_LEFT}, ControlID::CLASSIC_DPAD_LEFT},
{{WiimoteEmu::Classic::DPAD_GROUP, DIRECTION_RIGHT}, ControlID::CLASSIC_DPAD_RIGHT},
{{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::L_DIGITAL},
ControlID::CLASSIC_L_DIGITAL},
{{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::R_DIGITAL},
ControlID::CLASSIC_R_DIGITAL},
{{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::L_ANALOG},
ControlID::CLASSIC_L_ANALOG},
{{WiimoteEmu::Classic::TRIGGERS_GROUP, WiimoteEmu::Classic::R_ANALOG},
ControlID::CLASSIC_R_ANALOG},
{{WiimoteEmu::Classic::LEFT_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::CLASSIC_LEFT_STICK_X},
{{WiimoteEmu::Classic::LEFT_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::CLASSIC_LEFT_STICK_Y},
{{WiimoteEmu::Classic::RIGHT_STICK_GROUP, ControllerEmu::ReshapableInput::X_INPUT_OVERRIDE},
ControlID::CLASSIC_RIGHT_STICK_X},
{{WiimoteEmu::Classic::RIGHT_STICK_GROUP, ControllerEmu::ReshapableInput::Y_INPUT_OVERRIDE},
ControlID::CLASSIC_RIGHT_STICK_Y},
}};
ControllerEmu::InputOverrideFunction GetInputOverrideFunction(const ControlsMap& controls_map,
size_t i)
{
StateArray& state_array = s_state_arrays[i];
return [&](std::string_view group_name, std::string_view control_name,
ControlState controller_state) -> std::optional<ControlState> {
const auto it = controls_map.find(std::make_pair(group_name, control_name));
if (it == controls_map.end())
return std::nullopt;
const ControlID control = it->second;
InputState& input_state = state_array[control];
if (input_state.normal_state != controller_state)
{
input_state.normal_state = controller_state;
input_state.overriding = false;
}
return input_state.overriding ? std::make_optional(input_state.override_state) : std::nullopt;
};
}
} // namespace
void RegisterGameCubeInputOverrider(int controller_index)
{
Pad::GetConfig()
->GetController(controller_index)
->SetInputOverrideFunction(GetInputOverrideFunction(s_gcpad_controls_map, controller_index));
}
void RegisterWiiInputOverrider(int controller_index)
{
auto* wiimote =
static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(controller_index));
wiimote->SetInputOverrideFunction(
GetInputOverrideFunction(s_wiimote_controls_map, controller_index));
auto& attachments = static_cast<ControllerEmu::Attachments*>(
wiimote->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
->GetAttachmentList();
attachments[WiimoteEmu::ExtensionNumber::NUNCHUK]->SetInputOverrideFunction(
GetInputOverrideFunction(s_nunchuk_controls_map, controller_index));
attachments[WiimoteEmu::ExtensionNumber::CLASSIC]->SetInputOverrideFunction(
GetInputOverrideFunction(s_classic_controls_map, controller_index));
}
void UnregisterGameCubeInputOverrider(int controller_index)
{
Pad::GetConfig()->GetController(controller_index)->ClearInputOverrideFunction();
for (size_t i = ControlID::FIRST_GC_CONTROL; i <= ControlID::LAST_GC_CONTROL; ++i)
s_state_arrays[controller_index][i].overriding = false;
}
void UnregisterWiiInputOverrider(int controller_index)
{
auto* wiimote =
static_cast<WiimoteEmu::Wiimote*>(Wiimote::GetConfig()->GetController(controller_index));
wiimote->ClearInputOverrideFunction();
auto& attachments = static_cast<ControllerEmu::Attachments*>(
wiimote->GetWiimoteGroup(WiimoteEmu::WiimoteGroup::Attachments))
->GetAttachmentList();
attachments[WiimoteEmu::ExtensionNumber::NUNCHUK]->ClearInputOverrideFunction();
attachments[WiimoteEmu::ExtensionNumber::CLASSIC]->ClearInputOverrideFunction();
for (size_t i = ControlID::FIRST_WII_CONTROL; i <= ControlID::LAST_WII_CONTROL; ++i)
s_state_arrays[controller_index][i].overriding = false;
}
void SetControlState(int controller_index, ControlID control, double state)
{
InputState& input_state = s_state_arrays[controller_index][control];
input_state.override_state = state;
input_state.overriding = true;
}
void ClearControlState(int controller_index, ControlID control)
{
InputState& input_state = s_state_arrays[controller_index][control];
input_state.overriding = false;
}
double GetGateRadiusAtAngle(int controller_index, ControlID stick, double angle)
{
ControllerEmu::ControlGroup* group;
switch (stick)
{
case ControlID::GCPAD_MAIN_STICK_X:
case ControlID::GCPAD_MAIN_STICK_Y:
group = Pad::GetGroup(controller_index, PadGroup::MainStick);
break;
case ControlID::GCPAD_C_STICK_X:
case ControlID::GCPAD_C_STICK_Y:
group = Pad::GetGroup(controller_index, PadGroup::CStick);
break;
case ControlID::NUNCHUK_STICK_X:
case ControlID::NUNCHUK_STICK_Y:
group = Wiimote::GetNunchukGroup(controller_index, WiimoteEmu::NunchukGroup::Stick);
break;
case ControlID::CLASSIC_LEFT_STICK_X:
case ControlID::CLASSIC_LEFT_STICK_Y:
group = Wiimote::GetClassicGroup(controller_index, WiimoteEmu::ClassicGroup::LeftStick);
break;
case ControlID::CLASSIC_RIGHT_STICK_X:
case ControlID::CLASSIC_RIGHT_STICK_Y:
group = Wiimote::GetClassicGroup(controller_index, WiimoteEmu::ClassicGroup::RightStick);
break;
default:
ASSERT(false);
return 0;
}
return static_cast<ControllerEmu::ReshapableInput*>(group)->GetGateRadiusAtAngle(angle);
}
} // namespace ciface::Touch

View File

@ -0,0 +1,89 @@
// Copyright 2021 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#pragma once
namespace ciface::Touch
{
enum ControlID
{
GCPAD_A_BUTTON = 0,
GCPAD_B_BUTTON = 1,
GCPAD_X_BUTTON = 2,
GCPAD_Y_BUTTON = 3,
GCPAD_Z_BUTTON = 4,
GCPAD_START_BUTTON = 5,
GCPAD_DPAD_UP = 6,
GCPAD_DPAD_DOWN = 7,
GCPAD_DPAD_LEFT = 8,
GCPAD_DPAD_RIGHT = 9,
GCPAD_L_DIGITAL = 10,
GCPAD_R_DIGITAL = 11,
GCPAD_L_ANALOG = 12,
GCPAD_R_ANALOG = 13,
GCPAD_MAIN_STICK_X = 14,
GCPAD_MAIN_STICK_Y = 15,
GCPAD_C_STICK_X = 16,
GCPAD_C_STICK_Y = 17,
WIIMOTE_A_BUTTON = 18,
WIIMOTE_B_BUTTON = 19,
WIIMOTE_ONE_BUTTON = 20,
WIIMOTE_TWO_BUTTON = 21,
WIIMOTE_PLUS_BUTTON = 22,
WIIMOTE_MINUS_BUTTON = 23,
WIIMOTE_HOME_BUTTON = 24,
WIIMOTE_DPAD_UP = 25,
WIIMOTE_DPAD_DOWN = 26,
WIIMOTE_DPAD_LEFT = 27,
WIIMOTE_DPAD_RIGHT = 28,
WIIMOTE_IR_X = 29,
WIIMOTE_IR_Y = 30,
NUNCHUK_C_BUTTON = 31,
NUNCHUK_Z_BUTTON = 32,
NUNCHUK_STICK_X = 33,
NUNCHUK_STICK_Y = 34,
CLASSIC_A_BUTTON = 35,
CLASSIC_B_BUTTON = 36,
CLASSIC_X_BUTTON = 37,
CLASSIC_Y_BUTTON = 38,
CLASSIC_ZL_BUTTON = 39,
CLASSIC_ZR_BUTTON = 40,
CLASSIC_PLUS_BUTTON = 41,
CLASSIC_MINUS_BUTTON = 42,
CLASSIC_HOME_BUTTON = 43,
CLASSIC_DPAD_UP = 44,
CLASSIC_DPAD_DOWN = 45,
CLASSIC_DPAD_LEFT = 46,
CLASSIC_DPAD_RIGHT = 47,
CLASSIC_L_DIGITAL = 48,
CLASSIC_R_DIGITAL = 49,
CLASSIC_L_ANALOG = 50,
CLASSIC_R_ANALOG = 51,
CLASSIC_LEFT_STICK_X = 52,
CLASSIC_LEFT_STICK_Y = 53,
CLASSIC_RIGHT_STICK_X = 54,
CLASSIC_RIGHT_STICK_Y = 55,
NUMBER_OF_CONTROLS,
FIRST_GC_CONTROL = GCPAD_A_BUTTON,
LAST_GC_CONTROL = GCPAD_C_STICK_Y,
FIRST_WII_CONTROL = WIIMOTE_A_BUTTON,
LAST_WII_CONTROL = CLASSIC_RIGHT_STICK_Y,
};
void RegisterGameCubeInputOverrider(int controller_index);
void RegisterWiiInputOverrider(int controller_index);
void UnregisterGameCubeInputOverrider(int controller_index);
void UnregisterWiiInputOverrider(int controller_index);
void SetControlState(int controller_index, ControlID control, double state);
void ClearControlState(int controller_index, ControlID control);
// Angle is in radians and should be non-negative
double GetGateRadiusAtAngle(int controller_index, ControlID stick, double angle);
} // namespace ciface::Touch