Android: Add new input mapping implementation
This commit is contained in:
parent
dd8976f18d
commit
2c529b9db1
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue