diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java deleted file mode 100644 index a111155181..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.dialogs; - -import android.content.Context; -import android.view.InputDevice; -import android.view.KeyEvent; -import android.view.MotionEvent; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AlertDialog; - -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; -import org.dolphinemu.dolphinemu.utils.Log; - -import java.util.ArrayList; -import java.util.List; - -/** - * {@link AlertDialog} derivative that listens for - * motion events from controllers and joysticks. - */ -public final class MotionAlertDialog extends AlertDialog -{ - // The selected input preference - private final InputBindingSetting setting; - private final ArrayList mPreviousValues = new ArrayList<>(); - private int mPrevDeviceId = 0; - private boolean mWaitingForEvent = true; - private SettingsAdapter mAdapter; - - /** - * Constructor - * - * @param context The current {@link Context}. - * @param setting The Preference to show this dialog for. - */ - public MotionAlertDialog(Context context, InputBindingSetting setting, SettingsAdapter adapter) - { - super(context); - - this.setting = setting; - mAdapter = adapter; - } - - public boolean onKeyEvent(int keyCode, KeyEvent event) - { - Log.debug("[MotionAlertDialog] Received key event: " + event.getAction()); - if (event.getAction() == KeyEvent.ACTION_UP) - { - if (true) - { - setting.onKeyInput(mAdapter.getSettings(), event); - dismiss(); - } - // Even if we ignore the key, we still consume it. Thus return true regardless. - return true; - } - return false; - } - - @Override - public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) - { - // Intended for devices with no touchscreen or mouse - if (keyCode == KeyEvent.KEYCODE_BACK) - { - setting.clearValue(mAdapter.getSettings()); - dismiss(); - return true; - } - return super.onKeyLongPress(keyCode, event); - } - - @Override - public boolean dispatchKeyEvent(KeyEvent event) - { - // Handle this key if we care about it, otherwise pass it down the framework - return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event); - } - - @Override - public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) - { - // Handle this event if we care about it, otherwise pass it down the framework - return onMotionEvent(event) || super.dispatchGenericMotionEvent(event); - } - - private boolean onMotionEvent(MotionEvent event) - { - if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0) - return false; - if (event.getAction() != MotionEvent.ACTION_MOVE) - return false; - - InputDevice input = event.getDevice(); - - List motionRanges = input.getMotionRanges(); - - if (input.getId() != mPrevDeviceId) - { - mPreviousValues.clear(); - } - mPrevDeviceId = input.getId(); - boolean firstEvent = mPreviousValues.isEmpty(); - - int numMovedAxis = 0; - float axisMoveValue = 0.0f; - InputDevice.MotionRange lastMovedRange = null; - char lastMovedDir = '?'; - if (mWaitingForEvent) - { - for (int i = 0; i < motionRanges.size(); i++) - { - InputDevice.MotionRange range = motionRanges.get(i); - int axis = range.getAxis(); - float value = event.getAxisValue(axis); - if (firstEvent) - { - mPreviousValues.add(value); - } - else - { - float previousValue = mPreviousValues.get(i); - - // Only handle the axes that are not neutral (more than 0.5) - // but ignore any axis that has a constant value (e.g. always 1) - if (Math.abs(value) > 0.5f && value != previousValue) - { - // It is common to have multiple axes with the same physical input. For example, - // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. - // To handle this, we ignore an axis motion that's the exact same as a motion - // we already saw. This way, we ignore axes with two names, but catch the case - // where a joystick is moved in two directions. - // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html - if (value != axisMoveValue) - { - axisMoveValue = value; - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = value < 0.0f ? '-' : '+'; - } - } - // Special case for d-pads (axis value jumps between 0 and 1 without any values - // in between). Without this, the user would need to press the d-pad twice - // due to the first press being caught by the "if (firstEvent)" case further up. - else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) - { - numMovedAxis++; - lastMovedRange = range; - lastMovedDir = previousValue < 0.0f ? '-' : '+'; - } - } - - mPreviousValues.set(i, value); - } - - // If only one axis moved, that's the winner. - if (numMovedAxis == 1) - { - mWaitingForEvent = false; - setting.onMotionInput(mAdapter.getSettings(), input, lastMovedRange, lastMovedDir); - dismiss(); - } - } - return true; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java new file mode 100644 index 0000000000..cb136a908d --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model; + +public final class MappingCommon +{ + private MappingCommon() + { + } + + /** + * Waits until the user presses one or more inputs or until a timeout, + * then returns the pressed inputs. + * + * When this is being called, a separate thread must be calling ControllerInterface's + * dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered. + * + * @return The input(s) pressed by the user in the form of an InputCommon expression, + * or an empty string if there were no inputs. + */ + public static native String detectInput(); + + public static native void save(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java new file mode 100644 index 0000000000..6033e17e37 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::Control. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class Control +{ + @Keep + private final long mPointer; + + @Keep + private Control(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native ControlReference getControlReference(); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java new file mode 100644 index 0000000000..ddfce9a737 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::ControlGroup. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlGroup +{ + @Keep + private final long mPointer; + + @Keep + private ControlGroup(long pointer) + { + mPointer = pointer; + } + + public native String getUiName(); + + public native int getControlCount(); + + public native Control getControl(int i); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java new file mode 100644 index 0000000000..fd01cc1705 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; +import androidx.annotation.Nullable; + +/** + * Represents a C++ ControlReference. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class ControlReference +{ + @Keep + private final long mPointer; + + @Keep + private ControlReference(long pointer) + { + mPointer = pointer; + } + + public native double getState(); + + public native String getExpression(); + + /** + * Sets the expression for this control reference. + * + * @param expr The new expression + * @return null on success, a human-readable error on failure + */ + @Nullable + public native String setExpression(String expr); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java new file mode 100644 index 0000000000..b0fbd0fd92 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.controlleremu; + +import androidx.annotation.Keep; + +/** + * Represents a C++ ControllerEmu::EmulatedController. + * + * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed + * in C++ is undefined behavior! + */ +public class EmulatedController +{ + @Keep + private final long mPointer; + + @Keep + private EmulatedController(long pointer) + { + mPointer = pointer; + } + + public native int getGroupCount(); + + public native ControlGroup getGroup(int index); + + public native void updateSingleControlReference(ControlReference controlReference); + + public static native EmulatedController getGcPad(int controllerIndex); + + public static native EmulatedController getWiimote(int controllerIndex); + + public static native EmulatedController getWiimoteAttachment(int controllerIndex, + int attachmentIndex); +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java new file mode 100644 index 0000000000..8888e2430b --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model.view; + +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; +import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; + +public final class InputMappingControlSetting extends SettingsItem +{ + private final ControlReference mControlReference; + private final EmulatedController mController; + + public InputMappingControlSetting(Control control, EmulatedController controller) + { + super(control.getUiName(), ""); + mControlReference = control.getControlReference(); + mController = controller; + } + + public String getValue() + { + return mControlReference.getExpression(); + } + + public void setValue(String expr) + { + mControlReference.setExpression(expr); + mController.updateSingleControlReference(mControlReference); + } + + public void clearValue() + { + setValue(""); + } + + @Override + public int getType() + { + return TYPE_INPUT_MAPPING_CONTROL; + } + + @Override + public AbstractSetting getSetting() + { + return null; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java new file mode 100644 index 0000000000..f36d9bc478 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui; + +import android.app.Activity; +import android.view.InputDevice; +import android.view.KeyEvent; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; + +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; + +/** + * {@link AlertDialog} derivative that listens for + * motion events from controllers and joysticks. + */ +public final class MotionAlertDialog extends AlertDialog +{ + private final Activity mActivity; + private final InputMappingControlSetting mSetting; + private boolean mRunning = false; + + /** + * Constructor + * + * @param activity The current {@link Activity}. + * @param setting The setting to show this dialog for. + */ + public MotionAlertDialog(Activity activity, InputMappingControlSetting setting) + { + super(activity); + + mActivity = activity; + mSetting = setting; + } + + @Override + protected void onStart() + { + super.onStart(); + + mRunning = true; + new Thread(() -> + { + String result = MappingCommon.detectInput(); + mActivity.runOnUiThread(() -> + { + if (mRunning) + { + mSetting.setValue(result); + dismiss(); + } + }); + }).start(); + } + + @Override + protected void onStop() + { + super.onStop(); + mRunning = false; + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) + { + ControllerInterface.dispatchKeyEvent(event); + + if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isLongPress()) + { + // Special case: Let the user cancel by long-pressing Back (intended for non-touch devices) + mSetting.clearValue(); + dismiss(); + } + + return true; + } + + @Override + public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) + { + if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) + { + // Special case: Let the user cancel by touching an on-screen button + return super.dispatchGenericMotionEvent(event); + } + + ControllerInterface.dispatchGenericMotionEvent(event); + return true; + } +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java similarity index 51% rename from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java rename to Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java index b61fcc458b..72ddcb3fd3 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java @@ -1,46 +1,38 @@ // SPDX-License-Identifier: GPL-2.0-or-later -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; +package org.dolphinemu.dolphinemu.features.input.ui.viewholder; -import android.content.Context; -import android.content.SharedPreferences; import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; +import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; -public final class InputBindingSettingViewHolder extends SettingViewHolder +public final class InputMappingControlSettingViewHolder extends SettingViewHolder { - private InputBindingSetting mItem; - - private final Context mContext; + private InputMappingControlSetting mItem; private final ListItemSettingBinding mBinding; - public InputBindingSettingViewHolder(@NonNull ListItemSettingBinding binding, - SettingsAdapter adapter, Context context) + public InputMappingControlSettingViewHolder(@NonNull ListItemSettingBinding binding, + SettingsAdapter adapter) { super(binding.getRoot(), adapter); mBinding = binding; - mContext = context; } @Override public void bind(SettingsItem item) { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (InputBindingSetting) item; + mItem = (InputMappingControlSetting) item; mBinding.textSettingName.setText(mItem.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); + mBinding.textSettingDescription.setText(mItem.getValue()); setStyle(mBinding.textSettingName, mItem); } @@ -54,7 +46,7 @@ public final class InputBindingSettingViewHolder extends SettingViewHolder return; } - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); + getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition()); setStyle(mBinding.textSettingName, mItem); } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java index 7d3d5a5b19..9a94048838 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/Settings.java @@ -8,6 +8,7 @@ import android.widget.Toast; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView; import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile; import org.dolphinemu.dolphinemu.services.GameFileCacheManager; @@ -170,6 +171,8 @@ public class Settings implements Closeable SettingsFile.saveFile(entry.getKey(), entry.getValue(), view); } + MappingCommon.save(); + NativeConfig.save(NativeConfig.LAYER_BASE); if (!NativeLibrary.IsRunning()) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java index 14c5f49021..b65f45837b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/HeaderSetting.java @@ -13,6 +13,11 @@ public class HeaderSetting extends SettingsItem super(context, titleId, descriptionId); } + public HeaderSetting(CharSequence title, CharSequence description) + { + super(title, description); + } + @Override public int getType() { diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java deleted file mode 100644 index 175986d3ba..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/InputBindingSetting.java +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.InputDevice; -import android.view.KeyEvent; - -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; - -public class InputBindingSetting extends SettingsItem -{ - private String mFile; - private String mSection; - private String mKey; - - private String mGameId; - - public InputBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, titleId, 0); - mFile = file; - mSection = section; - mKey = key; - mGameId = gameId; - } - - public String getKey() - { - return mKey; - } - - public String getValue(Settings settings) - { - return settings.getSection(mFile, mSection).getString(mKey, ""); - } - - /** - * Saves the provided key input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param keyEvent KeyEvent of this key press. - */ - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - InputDevice device = keyEvent.getDevice(); - String bindStr = "Device '" + device.getDescriptor() + "'-Button " + keyEvent.getKeyCode(); - String uiString = device.getName() + ": Button " + keyEvent.getKeyCode(); - setValue(settings, bindStr, uiString); - } - - /** - * Saves the provided motion input setting both to the INI file (so native code can use it) and as - * an Android preference (so it persists correctly and is human-readable.) - * - * @param device InputDevice from which the input event originated. - * @param motionRange MotionRange of the movement - * @param axisDir Either '-' or '+' - */ - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - String bindStr = - "Device '" + device.getDescriptor() + "'-Axis " + motionRange.getAxis() + axisDir; - String uiString = device.getName() + ": Axis " + motionRange.getAxis() + axisDir; - setValue(settings, bindStr, uiString); - } - - public void setValue(Settings settings, String bind, String ui) - { - SharedPreferences - preferences = - PreferenceManager.getDefaultSharedPreferences(DolphinApplication.getAppContext()); - SharedPreferences.Editor editor = preferences.edit(); - editor.putString(mKey + mGameId, ui); - editor.apply(); - - settings.getSection(mFile, mSection).setString(mKey, bind); - } - - public void clearValue(Settings settings) - { - setValue(settings, "", ""); - } - - @Override - public int getType() - { - return TYPE_INPUT_BINDING; - } - - public String getGameId() - { - return mGameId; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java deleted file mode 100644 index 6c3e11e17b..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/RumbleBindingSetting.java +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.model.view; - -import android.content.Context; -import android.os.Vibrator; -import android.view.InputDevice; -import android.view.KeyEvent; - -import org.dolphinemu.dolphinemu.DolphinApplication; -import org.dolphinemu.dolphinemu.R; -import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting; -import org.dolphinemu.dolphinemu.features.settings.model.Settings; -import org.dolphinemu.dolphinemu.utils.Rumble; - -public class RumbleBindingSetting extends InputBindingSetting -{ - public RumbleBindingSetting(Context context, String file, String section, String key, int titleId, - String gameId) - { - super(context, file, section, key, titleId, gameId); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onKeyInput(Settings settings, KeyEvent keyEvent) - { - saveRumble(settings, keyEvent.getDevice()); - } - - /** - * Just need the device when saving rumble. - */ - @Override - public void onMotionInput(Settings settings, InputDevice device, - InputDevice.MotionRange motionRange, char axisDir) - { - saveRumble(settings, device); - } - - private void saveRumble(Settings settings, InputDevice device) - { - Vibrator vibrator = device.getVibrator(); - if (vibrator != null && vibrator.hasVibrator()) - { - setValue(settings, device.getDescriptor(), device.getName()); - Rumble.doRumble(vibrator); - } - else - { - setValue(settings, "", - DolphinApplication.getAppContext().getString(R.string.rumble_not_found)); - } - } - - @Override - public int getType() - { - return TYPE_RUMBLE_BINDING; - } - - @Override - public AbstractSetting getSetting() - { - return null; - } -} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java index 7b41c8e3f8..9c7b89c12f 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/SettingsItem.java @@ -21,9 +21,8 @@ public abstract class SettingsItem public static final int TYPE_SINGLE_CHOICE = 2; public static final int TYPE_SLIDER = 3; public static final int TYPE_SUBMENU = 4; - public static final int TYPE_INPUT_BINDING = 5; + public static final int TYPE_INPUT_MAPPING_CONTROL = 5; public static final int TYPE_STRING_SINGLE_CHOICE = 6; - public static final int TYPE_RUMBLE_BINDING = 7; public static final int TYPE_SINGLE_CHOICE_DYNAMIC_DESCRIPTIONS = 8; public static final int TYPE_FILE_PICKER = 9; public static final int TYPE_RUN_RUNNABLE = 10; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java index 6fc30a381b..1d4fe28af9 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/MenuTag.java @@ -31,14 +31,14 @@ public enum MenuTag GCPAD_2("gcpad", 1), GCPAD_3("gcpad", 2), GCPAD_4("gcpad", 3), - WIIMOTE_1("wiimote", 4), - WIIMOTE_2("wiimote", 5), - WIIMOTE_3("wiimote", 6), - WIIMOTE_4("wiimote", 7), - WIIMOTE_EXTENSION_1("wiimote_extension", 4), - WIIMOTE_EXTENSION_2("wiimote_extension", 5), - WIIMOTE_EXTENSION_3("wiimote_extension", 6), - WIIMOTE_EXTENSION_4("wiimote_extension", 7); + WIIMOTE_1("wiimote", 0), + WIIMOTE_2("wiimote", 1), + WIIMOTE_3("wiimote", 2), + WIIMOTE_4("wiimote", 3), + WIIMOTE_EXTENSION_1("wiimote_extension", 0), + WIIMOTE_EXTENSION_2("wiimote_extension", 1), + WIIMOTE_EXTENSION_3("wiimote_extension", 2), + WIIMOTE_EXTENSION_4("wiimote_extension", 3); private String tag; private int subType = -1; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java index a7f028afe3..8fe3fe4602 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.java @@ -37,15 +37,15 @@ import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding; import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding; -import org.dolphinemu.dolphinemu.dialogs.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; +import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.model.Settings; import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker; import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting; import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions; @@ -57,9 +57,7 @@ import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.DateTimeSetting import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputBindingSettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder; -import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder; import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder; @@ -124,13 +122,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter item.clearValue(getSettings())); + (dialogInterface, i) -> item.clearValue()); dialog.setOnDismissListener(dialog1 -> { notifyItemChanged(position); diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java index 00699e3a0c..bb73093656 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragment.java @@ -67,14 +67,14 @@ public final class SettingsFragment extends Fragment implements SettingsFragment titles.put(MenuTag.GCPAD_2, R.string.controller_1); titles.put(MenuTag.GCPAD_3, R.string.controller_2); titles.put(MenuTag.GCPAD_4, R.string.controller_3); - titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_4); - titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_5); - titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_6); - titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_7); - titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_4); - titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_5); - titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_6); - titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_7); + titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0); + titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1); + titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2); + titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3); + titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0); + titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1); + titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2); + titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3); } private FragmentSettingsBinding mBinding; diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java index 9d1a783979..01f4b52b71 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.java @@ -14,6 +14,9 @@ import androidx.appcompat.app.AppCompatActivity; import org.dolphinemu.dolphinemu.NativeLibrary; import org.dolphinemu.dolphinemu.R; import org.dolphinemu.dolphinemu.activities.UserDataActivity; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup; +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; +import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting; import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting; import org.dolphinemu.dolphinemu.features.settings.model.AdHocBooleanSetting; @@ -795,14 +798,14 @@ public final class SettingsFragmentPresenter private void addWiimoteSettings(ArrayList sl) { - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_4, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(4))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_5, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(5))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_6, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(6))); - sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_7, 0, - R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(7))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_1_SOURCE, R.string.wiimote_0, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(0))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_2_SOURCE, R.string.wiimote_1, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(1))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_3_SOURCE, R.string.wiimote_2, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(2))); + sl.add(new SingleChoiceSetting(mContext, IntSetting.WIIMOTE_4_SOURCE, R.string.wiimote_3, 0, + R.array.wiimoteTypeEntries, R.array.wiimoteTypeValues, MenuTag.getWiimoteMenuTag(3))); } private void addGraphicsSettings(ArrayList sl) @@ -1073,7 +1076,7 @@ public final class SettingsFragmentPresenter { if (gcPadType == 6) // Emulated { - // TODO + addControllerSettings(sl, EmulatedController.getGcPad(gcPadNumber)); } else if (gcPadType == 12) // Adapter { @@ -1086,13 +1089,30 @@ public final class SettingsFragmentPresenter private void addWiimoteSubSettings(ArrayList sl, int wiimoteNumber) { - // TODO + addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber)); } private void addExtensionTypeSettings(ArrayList sl, int wiimoteNumber, - int extentionType) + int extensionType) { - // TODO + addControllerSettings(sl, + EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType)); + } + + private void addControllerSettings(ArrayList sl, EmulatedController controller) + { + int groupCount = controller.getGroupCount(); + for (int i = 0; i < groupCount; i++) + { + ControlGroup group = controller.getGroup(i); + sl.add(new HeaderSetting(group.getUiName(), "")); + + int controlCount = group.getControlCount(); + for (int j = 0; j < controlCount; j++) + { + sl.add(new InputMappingControlSetting(group.getControl(j), controller)); + } + } } private static int getLogVerbosityEntries() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java deleted file mode 100644 index 3b7a6b0f75..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/viewholder/RumbleBindingViewHolder.java +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.settings.ui.viewholder; - -import android.content.Context; -import android.content.SharedPreferences; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; - -import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding; -import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting; -import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem; -import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter; - -public class RumbleBindingViewHolder extends SettingViewHolder -{ - private RumbleBindingSetting mItem; - - private final Context mContext; - - private final ListItemSettingBinding mBinding; - - public RumbleBindingViewHolder(@NonNull ListItemSettingBinding binding, SettingsAdapter adapter, - Context context) - { - super(binding.getRoot(), adapter); - mBinding = binding; - mContext = context; - } - - @Override - public void bind(SettingsItem item) - { - SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); - - mItem = (RumbleBindingSetting) item; - - mBinding.textSettingName.setText(item.getName()); - mBinding.textSettingDescription - .setText(sharedPreferences.getString(mItem.getKey() + mItem.getGameId(), "")); - - setStyle(mBinding.textSettingName, mItem); - } - - @Override - public void onClick(View clicked) - { - if (!mItem.isEditable()) - { - showNotRuntimeEditableError(); - return; - } - - getAdapter().onInputBindingClick(mItem, getBindingAdapterPosition()); - - setStyle(mBinding.textSettingName, mItem); - } - - @Nullable @Override - protected SettingsItem getItem() - { - return mItem; - } -} diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml index cca14f13a8..a1c7a32f95 100644 --- a/Source/Android/app/src/main/res/values/strings.xml +++ b/Source/Android/app/src/main/res/values/strings.xml @@ -11,15 +11,15 @@ GameCube Controller 3 GameCube Controller 4 - Wii Remote 1 - Wii Remote 2 - Wii Remote 3 - Wii Remote 4 + Wii Remote 1 + Wii Remote 2 + Wii Remote 3 + Wii Remote 4 - Wii Remote Extension 1 - Wii Remote Extension 2 - Wii Remote Extension 3 - Wii Remote Extension 4 + Wii Remote Extension 1 + Wii Remote Extension 2 + Wii Remote Extension 3 + Wii Remote Extension 4 Extension diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp index 8e43016c62..54d3f765c9 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.cpp +++ b/Source/Android/jni/AndroidCommon/IDCache.cpp @@ -83,6 +83,22 @@ static jfieldID s_riivolution_patches_pointer; static jclass s_wii_update_cb_class; static jmethodID s_wii_update_cb_run; +static jclass s_control_class; +static jfieldID s_control_pointer; +static jmethodID s_control_constructor; + +static jclass s_control_group_class; +static jfieldID s_control_group_pointer; +static jmethodID s_control_group_constructor; + +static jclass s_control_reference_class; +static jfieldID s_control_reference_pointer; +static jmethodID s_control_reference_constructor; + +static jclass s_emulated_controller_class; +static jfieldID s_emulated_controller_pointer; +static jmethodID s_emulated_controller_constructor; + namespace IDCache { JNIEnv* GetEnvForThread() @@ -383,6 +399,66 @@ jmethodID GetWiiUpdateCallbackFunction() return s_wii_update_cb_run; } +jclass GetControlClass() +{ + return s_control_class; +} + +jfieldID GetControlPointer() +{ + return s_control_pointer; +} + +jmethodID GetControlConstructor() +{ + return s_control_constructor; +} + +jclass GetControlGroupClass() +{ + return s_control_group_class; +} + +jfieldID GetControlGroupPointer() +{ + return s_control_group_pointer; +} + +jmethodID GetControlGroupConstructor() +{ + return s_control_group_constructor; +} + +jclass GetControlReferenceClass() +{ + return s_control_reference_class; +} + +jfieldID GetControlReferencePointer() +{ + return s_control_reference_pointer; +} + +jmethodID GetControlReferenceConstructor() +{ + return s_control_reference_constructor; +} + +jclass GetEmulatedControllerClass() +{ + return s_emulated_controller_class; +} + +jfieldID GetEmulatedControllerPointer() +{ + return s_emulated_controller_pointer; +} + +jmethodID GetEmulatedControllerConstructor() +{ + return s_emulated_controller_constructor; +} + } // namespace IDCache extern "C" { @@ -541,6 +617,35 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) s_wii_update_cb_run = env->GetMethodID(s_wii_update_cb_class, "run", "(IIJ)Z"); env->DeleteLocalRef(wii_update_cb_class); + const jclass control_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control"); + s_control_class = reinterpret_cast(env->NewGlobalRef(control_class)); + s_control_pointer = env->GetFieldID(control_class, "mPointer", "J"); + s_control_constructor = env->GetMethodID(control_class, "", "(J)V"); + env->DeleteLocalRef(control_class); + + const jclass control_group_class = + env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup"); + s_control_group_class = reinterpret_cast(env->NewGlobalRef(control_group_class)); + s_control_group_pointer = env->GetFieldID(control_group_class, "mPointer", "J"); + s_control_group_constructor = env->GetMethodID(control_group_class, "", "(J)V"); + env->DeleteLocalRef(control_group_class); + + const jclass control_reference_class = env->FindClass( + "org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference"); + s_control_reference_class = reinterpret_cast(env->NewGlobalRef(control_reference_class)); + s_control_reference_pointer = env->GetFieldID(control_reference_class, "mPointer", "J"); + s_control_reference_constructor = env->GetMethodID(control_reference_class, "", "(J)V"); + env->DeleteLocalRef(control_reference_class); + + const jclass emulated_controller_class = env->FindClass( + "org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController"); + s_emulated_controller_class = + reinterpret_cast(env->NewGlobalRef(emulated_controller_class)); + s_emulated_controller_pointer = env->GetFieldID(emulated_controller_class, "mPointer", "J"); + s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "", "(J)V"); + env->DeleteLocalRef(emulated_controller_class); + return JNI_VERSION; } @@ -568,5 +673,9 @@ JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) env->DeleteGlobalRef(s_graphics_mod_class); env->DeleteGlobalRef(s_riivolution_patches_class); env->DeleteGlobalRef(s_wii_update_cb_class); + env->DeleteGlobalRef(s_control_class); + env->DeleteGlobalRef(s_control_group_class); + env->DeleteGlobalRef(s_control_reference_class); + env->DeleteGlobalRef(s_emulated_controller_class); } } diff --git a/Source/Android/jni/AndroidCommon/IDCache.h b/Source/Android/jni/AndroidCommon/IDCache.h index 79ab19acaa..bd727c12a9 100644 --- a/Source/Android/jni/AndroidCommon/IDCache.h +++ b/Source/Android/jni/AndroidCommon/IDCache.h @@ -82,4 +82,20 @@ jfieldID GetRiivolutionPatchesPointer(); jclass GetWiiUpdateCallbackClass(); jmethodID GetWiiUpdateCallbackFunction(); +jclass GetControlClass(); +jfieldID GetControlPointer(); +jmethodID GetControlConstructor(); + +jclass GetControlGroupClass(); +jfieldID GetControlGroupPointer(); +jmethodID GetControlGroupConstructor(); + +jclass GetControlReferenceClass(); +jfieldID GetControlReferencePointer(); +jmethodID GetControlReferenceConstructor(); + +jclass GetEmulatedControllerClass(); +jfieldID GetEmulatedControllerPointer(); +jmethodID GetEmulatedControllerConstructor(); + } // namespace IDCache diff --git a/Source/Android/jni/CMakeLists.txt b/Source/Android/jni/CMakeLists.txt index 1c419aefcf..575847290e 100644 --- a/Source/Android/jni/CMakeLists.txt +++ b/Source/Android/jni/CMakeLists.txt @@ -10,7 +10,15 @@ add_library(main SHARED GameList/GameFile.cpp GameList/GameFile.h GameList/GameFileCache.cpp + Input/Control.cpp + Input/Control.h + Input/ControlGroup.cpp + Input/ControlGroup.h + Input/ControlReference.cpp + Input/ControlReference.h + Input/EmulatedController.cpp Input/InputOverrider.cpp + Input/MappingCommon.cpp IniFile.cpp MainAndroid.cpp RiivolutionPatches.cpp @@ -23,6 +31,7 @@ PRIVATE androidcommon common core + inputcommon uicommon ) diff --git a/Source/Android/jni/Input/Control.cpp b/Source/Android/jni/Input/Control.cpp new file mode 100644 index 0000000000..3a0496b92e --- /dev/null +++ b/Source/Android/jni/Input/Control.cpp @@ -0,0 +1,51 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/Control.h" + +#include + +#include + +#include "Common/MsgHandler.h" +#include "InputCommon/ControlReference/ControlReference.h" +#include "InputCommon/ControllerEmu/Control/Control.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/ControlReference.h" + +static ControllerEmu::Control* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetControlPointer())); +} + +jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control) +{ + if (!control) + return nullptr; + + return env->NewObject(IDCache::GetControlClass(), IDCache::GetControlConstructor(), + reinterpret_cast(control)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getUiName(JNIEnv* env, + jobject obj) +{ + ControllerEmu::Control* control = GetPointer(env, obj); + std::string ui_name = control->ui_name; + if (control->translate == ControllerEmu::Translatability::Translate) + ui_name = Common::GetStringT(ui_name.c_str()); + return ToJString(env, ui_name); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_Control_getControlReference( + JNIEnv* env, jobject obj) +{ + return ControlReferenceToJava(env, GetPointer(env, obj)->control_ref.get()); +} +} diff --git a/Source/Android/jni/Input/Control.h b/Source/Android/jni/Input/Control.h new file mode 100644 index 0000000000..c27732dd38 --- /dev/null +++ b/Source/Android/jni/Input/Control.h @@ -0,0 +1,13 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class Control; +} + +jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control); diff --git a/Source/Android/jni/Input/ControlGroup.cpp b/Source/Android/jni/Input/ControlGroup.cpp new file mode 100644 index 0000000000..0e56774e84 --- /dev/null +++ b/Source/Android/jni/Input/ControlGroup.cpp @@ -0,0 +1,51 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/ControlGroup.h" + +#include + +#include "Common/MsgHandler.h" +#include "InputCommon/ControllerEmu/ControlGroup/ControlGroup.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/Control.h" + +static ControllerEmu::ControlGroup* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetControlGroupPointer())); +} + +jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group) +{ + if (!group) + return nullptr; + + return env->NewObject(IDCache::GetControlGroupClass(), IDCache::GetControlGroupConstructor(), + reinterpret_cast(group)); +} + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getUiName( + JNIEnv* env, jobject obj) +{ + return ToJString(env, Common::GetStringT(GetPointer(env, obj)->ui_name.c_str())); +} + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControlCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->controls.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlGroup_getControl( + JNIEnv* env, jobject obj, jint i) +{ + return ControlToJava(env, GetPointer(env, obj)->controls[i].get()); +} +} diff --git a/Source/Android/jni/Input/ControlGroup.h b/Source/Android/jni/Input/ControlGroup.h new file mode 100644 index 0000000000..b7ec0e78d1 --- /dev/null +++ b/Source/Android/jni/Input/ControlGroup.h @@ -0,0 +1,13 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +namespace ControllerEmu +{ +class ControlGroup; +} + +jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group); diff --git a/Source/Android/jni/Input/ControlReference.cpp b/Source/Android/jni/Input/ControlReference.cpp new file mode 100644 index 0000000000..8bf9351109 --- /dev/null +++ b/Source/Android/jni/Input/ControlReference.cpp @@ -0,0 +1,54 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "jni/Input/ControlReference.h" + +#include + +#include + +#include "InputCommon/ControlReference/ControlReference.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" + +ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference) +{ + return reinterpret_cast( + env->GetLongField(control_reference, IDCache::GetControlReferencePointer())); +} + +jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference) +{ + if (!control_reference) + return nullptr; + + return env->NewObject(IDCache::GetControlReferenceClass(), + IDCache::GetControlReferenceConstructor(), + reinterpret_cast(control_reference)); +} + +extern "C" { + +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getState( + JNIEnv* env, jobject obj) +{ + return ControlReferenceFromJava(env, obj)->GetState(); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_getExpression( + JNIEnv* env, jobject obj) +{ + return ToJString(env, ControlReferenceFromJava(env, obj)->GetExpression()); +} + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_ControlReference_setExpression( + JNIEnv* env, jobject obj, jstring expr) +{ + const std::optional result = + ControlReferenceFromJava(env, obj)->SetExpression(GetJString(env, expr)); + return result ? ToJString(env, *result) : nullptr; +} +} diff --git a/Source/Android/jni/Input/ControlReference.h b/Source/Android/jni/Input/ControlReference.h new file mode 100644 index 0000000000..c8bfb7041a --- /dev/null +++ b/Source/Android/jni/Input/ControlReference.h @@ -0,0 +1,11 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +class ControlReference; + +jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference); +ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference); diff --git a/Source/Android/jni/Input/EmulatedController.cpp b/Source/Android/jni/Input/EmulatedController.cpp new file mode 100644 index 0000000000..af6ba3b48a --- /dev/null +++ b/Source/Android/jni/Input/EmulatedController.cpp @@ -0,0 +1,81 @@ +// Copyright 2022 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include + +#include "Core/HW/GCPad.h" +#include "Core/HW/Wiimote.h" +#include "Core/HW/WiimoteEmu/WiimoteEmu.h" +#include "InputCommon/ControllerEmu/ControlGroup/Attachments.h" +#include "InputCommon/ControllerEmu/ControllerEmu.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/InputConfig.h" +#include "jni/AndroidCommon/AndroidCommon.h" +#include "jni/AndroidCommon/IDCache.h" +#include "jni/Input/Control.h" +#include "jni/Input/ControlGroup.h" +#include "jni/Input/ControlReference.h" + +static ControllerEmu::EmulatedController* GetPointer(JNIEnv* env, jobject obj) +{ + return reinterpret_cast( + env->GetLongField(obj, IDCache::GetEmulatedControllerPointer())); +} + +static jobject EmulatedControllerToJava(JNIEnv* env, ControllerEmu::EmulatedController* controller) +{ + if (!controller) + return nullptr; + + return env->NewObject(IDCache::GetEmulatedControllerClass(), + IDCache::GetEmulatedControllerConstructor(), + reinterpret_cast(controller)); +} + +extern "C" { + +JNIEXPORT jint JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount( + JNIEnv* env, jobject obj) +{ + return static_cast(GetPointer(env, obj)->groups.size()); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroup( + JNIEnv* env, jobject obj, jint controller_index) +{ + return ControlGroupToJava(env, GetPointer(env, obj)->groups[controller_index].get()); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_updateSingleControlReference( + JNIEnv* env, jobject obj, jobject control_reference) +{ + return GetPointer(env, obj)->UpdateSingleControlReference( + g_controller_interface, ControlReferenceFromJava(env, control_reference)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGcPad( + JNIEnv* env, jclass, jint controller_index) +{ + return EmulatedControllerToJava(env, Pad::GetConfig()->GetController(controller_index)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimote( + JNIEnv* env, jclass, jint controller_index) +{ + return EmulatedControllerToJava(env, Wiimote::GetConfig()->GetController(controller_index)); +} + +JNIEXPORT jobject JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getWiimoteAttachment( + JNIEnv* env, jclass, jint controller_index, jint attachment_index) +{ + auto* attachments = static_cast( + Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Attachments)); + return EmulatedControllerToJava(env, attachments->GetAttachmentList()[attachment_index].get()); +} +} diff --git a/Source/Android/jni/Input/MappingCommon.cpp b/Source/Android/jni/Input/MappingCommon.cpp new file mode 100644 index 0000000000..c02f1c2ae5 --- /dev/null +++ b/Source/Android/jni/Input/MappingCommon.cpp @@ -0,0 +1,56 @@ +// Copyright 2022 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include + +#include "Core/FreeLookManager.h" +#include "Core/HW/GBAPad.h" +#include "Core/HW/GCKeyboard.h" +#include "Core/HW/GCPad.h" +#include "Core/HW/Wiimote.h" +#include "Core/HotkeyManager.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/MappingCommon.h" +#include "jni/AndroidCommon/AndroidCommon.h" + +namespace +{ +constexpr auto INPUT_DETECT_INITIAL_TIME = std::chrono::seconds(3); +constexpr auto INPUT_DETECT_CONFIRMATION_TIME = std::chrono::milliseconds(0); +constexpr auto INPUT_DETECT_MAXIMUM_TIME = std::chrono::seconds(5); +} // namespace + +extern "C" { + +JNIEXPORT jstring JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_detectInput(JNIEnv* env, jclass) +{ + const std::vector device_strings = g_controller_interface.GetAllDeviceStrings(); + const ciface::Core::DeviceQualifier default_device{}; + + auto detections = + g_controller_interface.DetectInput(device_strings, INPUT_DETECT_INITIAL_TIME, + INPUT_DETECT_CONFIRMATION_TIME, INPUT_DETECT_MAXIMUM_TIME); + + ciface::MappingCommon::RemoveSpuriousTriggerCombinations(&detections); + + return ToJString(env, ciface::MappingCommon::BuildExpression(detections, default_device, + ciface::MappingCommon::Quote::On)); +} + +JNIEXPORT void JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_MappingCommon_save(JNIEnv* env, jclass) +{ + Pad::GetConfig()->SaveConfig(); + Pad::GetGBAConfig()->SaveConfig(); + Keyboard::GetConfig()->SaveConfig(); + Wiimote::GetConfig()->SaveConfig(); + HotkeyManagerEmu::GetConfig()->SaveConfig(); + FreeLook::GetInputConfig()->SaveConfig(); +} +};