Android: Add new input mapping implementation

This commit is contained in:
JosJuice 2022-01-23 21:47:57 +01:00
parent dd8976f18d
commit 2c529b9db1
31 changed files with 847 additions and 487 deletions

View File

@ -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<Float> 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<InputDevice.MotionRange> 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;
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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())

View File

@ -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()
{

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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<SettingViewHolde
case SettingsItem.TYPE_SUBMENU:
return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this);
case SettingsItem.TYPE_INPUT_BINDING:
return new InputBindingSettingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);
case SettingsItem.TYPE_RUMBLE_BINDING:
return new RumbleBindingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);
case SettingsItem.TYPE_INPUT_MAPPING_CONTROL:
return new InputMappingControlSettingViewHolder(ListItemSettingBinding.inflate(inflater),
this);
case SettingsItem.TYPE_FILE_PICKER:
return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this);
@ -314,9 +308,9 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
mView.loadSubMenu(item.getMenuKey());
}
public void onInputBindingClick(final InputBindingSetting item, final int position)
public void onInputMappingClick(final InputMappingControlSetting item, final int position)
{
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item, this);
final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item);
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
@ -326,13 +320,11 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
dialog.getWindow().setBackgroundDrawable(background);
dialog.setTitle(R.string.input_binding);
dialog.setMessage(String.format(mContext.getString(
item instanceof RumbleBindingSetting ?
R.string.input_rumble_description : R.string.input_binding_description),
dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description),
item.getName()));
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
(dialogInterface, i) -> item.clearValue(getSettings()));
(dialogInterface, i) -> item.clearValue());
dialog.setOnDismissListener(dialog1 ->
{
notifyItemChanged(position);

View File

@ -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;

View File

@ -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<SettingsItem> 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<SettingsItem> 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<SettingsItem> sl, int wiimoteNumber)
{
// TODO
addControllerSettings(sl, EmulatedController.getWiimote(wiimoteNumber));
}
private void addExtensionTypeSettings(ArrayList<SettingsItem> sl, int wiimoteNumber,
int extentionType)
int extensionType)
{
// TODO
addControllerSettings(sl,
EmulatedController.getWiimoteAttachment(wiimoteNumber, extensionType));
}
private void addControllerSettings(ArrayList<SettingsItem> 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()

View File

@ -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;
}
}

View File

@ -11,15 +11,15 @@
<string name="controller_2">GameCube Controller 3</string>
<string name="controller_3">GameCube Controller 4</string>
<string name="wiimote_4">Wii Remote 1</string>
<string name="wiimote_5">Wii Remote 2</string>
<string name="wiimote_6">Wii Remote 3</string>
<string name="wiimote_7">Wii Remote 4</string>
<string name="wiimote_0">Wii Remote 1</string>
<string name="wiimote_1">Wii Remote 2</string>
<string name="wiimote_2">Wii Remote 3</string>
<string name="wiimote_3">Wii Remote 4</string>
<string name="wiimote_extension_4">Wii Remote Extension 1</string>
<string name="wiimote_extension_5">Wii Remote Extension 2</string>
<string name="wiimote_extension_6">Wii Remote Extension 3</string>
<string name="wiimote_extension_7">Wii Remote Extension 4</string>
<string name="wiimote_extension_0">Wii Remote Extension 1</string>
<string name="wiimote_extension_1">Wii Remote Extension 2</string>
<string name="wiimote_extension_2">Wii Remote Extension 3</string>
<string name="wiimote_extension_3">Wii Remote Extension 4</string>
<string name="wiimote_extensions">Extension</string>

View File

@ -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<jclass>(env->NewGlobalRef(control_class));
s_control_pointer = env->GetFieldID(control_class, "mPointer", "J");
s_control_constructor = env->GetMethodID(control_class, "<init>", "(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<jclass>(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, "<init>", "(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<jclass>(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, "<init>", "(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<jclass>(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, "<init>", "(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);
}
}

View File

@ -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

View File

@ -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
)

View File

@ -0,0 +1,51 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/Control.h"
#include <string>
#include <jni.h>
#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<ControllerEmu::Control*>(
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<jlong>(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());
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class Control;
}
jobject ControlToJava(JNIEnv* env, ControllerEmu::Control* control);

View File

@ -0,0 +1,51 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/ControlGroup.h"
#include <jni.h>
#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<ControllerEmu::ControlGroup*>(
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<jlong>(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<jint>(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());
}
}

View File

@ -0,0 +1,13 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
namespace ControllerEmu
{
class ControlGroup;
}
jobject ControlGroupToJava(JNIEnv* env, ControllerEmu::ControlGroup* group);

View File

@ -0,0 +1,54 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "jni/Input/ControlReference.h"
#include <string>
#include <jni.h>
#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<ControlReference*>(
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<jlong>(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<double>();
}
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<std::string> result =
ControlReferenceFromJava(env, obj)->SetExpression(GetJString(env, expr));
return result ? ToJString(env, *result) : nullptr;
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <jni.h>
class ControlReference;
jobject ControlReferenceToJava(JNIEnv* env, ControlReference* control_reference);
ControlReference* ControlReferenceFromJava(JNIEnv* env, jobject control_reference);

View File

@ -0,0 +1,81 @@
// Copyright 2022 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <jni.h>
#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<ControllerEmu::EmulatedController*>(
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<jlong>(controller));
}
extern "C" {
JNIEXPORT jint JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_controlleremu_EmulatedController_getGroupCount(
JNIEnv* env, jobject obj)
{
return static_cast<jint>(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<ControllerEmu::Attachments*>(
Wiimote::GetWiimoteGroup(controller_index, WiimoteEmu::WiimoteGroup::Attachments));
return EmulatedControllerToJava(env, attachments->GetAttachmentList()[attachment_index].get());
}
}

View File

@ -0,0 +1,56 @@
// Copyright 2022 Dolphin Emulator Project
// Licensed under GPLv2+
// Refer to the license.txt file included.
#include <chrono>
#include <string>
#include <vector>
#include <jni.h>
#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<std::string> 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();
}
};