mirror of
https://github.com/brunodev85/winlator.git
synced 2026-10-20 22:11:47 +03:00
Update app
This commit is contained in:
1 parent
440956199e
commit
e6dd7a5976
45 files changed
+1168
-415
No files matched your search
+2
-2
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId 'com.winlator'
|
applicationId 'com.winlator'
|
||||||
minSdkVersion 26
|
minSdkVersion 26
|
||||||
targetSdkVersion 28
|
targetSdkVersion 28
|
||||||
versionCode 9
|
versionCode 10
|
||||||
versionName "3.2"
|
versionName "4.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
|
|
||||||
<activity android:name="com.winlator.ExternalControllerBindingsActivity"
|
<activity android:name="com.winlator.ExternalControllerBindingsActivity"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:exported="false" />
|
android:exported="false"
|
||||||
|
android:screenOrientation="sensor" />
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
{"name" : "BOX64_DYNAREC", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
{"name" : "BOX64_DYNAREC_SAFEFLAGS", "values" : ["0", "1", "2"], "defaultValue" : "2"},
|
{"name" : "BOX64_DYNAREC_SAFEFLAGS", "values" : ["0", "1", "2"], "defaultValue" : "2"},
|
||||||
{"name" : "BOX64_DYNAREC_FASTNAN", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
{"name" : "BOX64_DYNAREC_FASTNAN", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
{"name" : "BOX64_DYNAREC_FASTROUND", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
{"name" : "BOX64_DYNAREC_FASTROUND", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
|
|||||||
BIN
Binary file not shown.
@@ -1,4 +1,5 @@
|
|||||||
[
|
[
|
||||||
|
{"name" : "BOX86_DYNAREC", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
{"name" : "BOX86_DYNAREC_SAFEFLAGS", "values" : ["0", "1", "2"], "defaultValue" : "2"},
|
{"name" : "BOX86_DYNAREC_SAFEFLAGS", "values" : ["0", "1", "2"], "defaultValue" : "2"},
|
||||||
{"name" : "BOX86_DYNAREC_FASTNAN", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
{"name" : "BOX86_DYNAREC_FASTNAN", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
{"name" : "BOX86_DYNAREC_FASTROUND", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
{"name" : "BOX86_DYNAREC_FASTROUND", "values" : ["0", "1"], "toggleSwitch" : true, "defaultValue" : "1"},
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
{"id":3,"name":"Virtual Gamepad","cursorSpeed":1,"elements":[{"type":"D_PAD","shape":"CIRCLE","bindings":["GAMEPAD_DPAD_UP","GAMEPAD_DPAD_RIGHT","GAMEPAD_DPAD_DOWN","GAMEPAD_DPAD_LEFT"],"scale":0.85,"x":0.21568627655506134,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_X","NONE","NONE","NONE"],"scale":1,"x":0.8133170008659363,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_Y","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.6000000238418579,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_A","NONE","NONE","NONE"],"scale":1,"x":0.8721405267715454,"y":0.8666666746139526,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_B","NONE","NONE","NONE"],"scale":1,"x":0.9309640526771545,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R2","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_R1","NONE","NONE","NONE"],"scale":1,"x":0.9215686321258545,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L1","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.42222222685813904,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"RECT","bindings":["GAMEPAD_BUTTON_L2","NONE","NONE","NONE"],"scale":1,"x":0.0784313753247261,"y":0.2888889014720917,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_START","NONE","NONE","NONE"],"scale":0.85,"x":0.538807213306427,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"ROUND_RECT","bindings":["GAMEPAD_BUTTON_SELECT","NONE","NONE","NONE"],"scale":0.85,"x":0.46078431606292725,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0},{"type":"STICK","shape":"CIRCLE","bindings":["GAMEPAD_LEFT_THUMB_UP","GAMEPAD_LEFT_THUMB_RIGHT","GAMEPAD_LEFT_THUMB_DOWN","GAMEPAD_LEFT_THUMB_LEFT"],"scale":1,"x":0.10784313827753067,"y":0.7333333492279053,"toggleSwitch":false,"text":"","iconId":0},{"type":"STICK","shape":"CIRCLE","bindings":["GAMEPAD_RIGHT_THUMB_UP","GAMEPAD_RIGHT_THUMB_RIGHT","GAMEPAD_RIGHT_THUMB_DOWN","GAMEPAD_RIGHT_THUMB_LEFT"],"scale":1,"x":0.7843137383460999,"y":0.35555556416511536,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_L3","NONE","NONE","NONE"],"scale":0.85,"x":0.21568627655506134,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0},{"type":"BUTTON","shape":"CIRCLE","bindings":["GAMEPAD_BUTTON_R3","NONE","NONE","NONE"],"scale":0.85,"x":0.7843137383460999,"y":0.9111111164093018,"toggleSwitch":false,"text":"","iconId":0}]}
|
||||||
Binary file not shown.
@@ -58,7 +58,6 @@ public class ContainerDetailFragment extends Fragment {
|
|||||||
private PreloaderDialog preloaderDialog;
|
private PreloaderDialog preloaderDialog;
|
||||||
private JSONArray gpuNames;
|
private JSONArray gpuNames;
|
||||||
private Callback<String> openDirectoryCallback;
|
private Callback<String> openDirectoryCallback;
|
||||||
private final String[] defaultDLLOverrides = {"d3d8", "d3d9", "d3d10", "d3d10_1", "d3d10core", "d3d11", "d3d12", "d3d12core", "ddraw", "dxgi", "wined3d"};
|
|
||||||
|
|
||||||
public ContainerDetailFragment() {
|
public ContainerDetailFragment() {
|
||||||
this(0);
|
this(0);
|
||||||
@@ -276,9 +275,6 @@ public class ContainerDetailFragment extends Fragment {
|
|||||||
|
|
||||||
registryEditor.setStringValue("Software\\Wine\\Direct3D", "shader_backend", "glsl");
|
registryEditor.setStringValue("Software\\Wine\\Direct3D", "shader_backend", "glsl");
|
||||||
registryEditor.setStringValue("Software\\Wine\\Direct3D", "UseGLSL", "enabled");
|
registryEditor.setStringValue("Software\\Wine\\Direct3D", "UseGLSL", "enabled");
|
||||||
|
|
||||||
final String dllOverridesKey = "Software\\Wine\\DllOverrides";
|
|
||||||
for (String name : defaultDLLOverrides) registryEditor.setStringValue(dllOverridesKey, name, "native,builtin");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import com.winlator.core.AppUtils;
|
|||||||
import com.winlator.core.FileUtils;
|
import com.winlator.core.FileUtils;
|
||||||
import com.winlator.core.UnitUtils;
|
import com.winlator.core.UnitUtils;
|
||||||
import com.winlator.widget.InputControlsView;
|
import com.winlator.widget.InputControlsView;
|
||||||
|
import com.winlator.widget.NumberPicker;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
@@ -117,6 +118,14 @@ public class ControlsEditorActivity extends AppCompatActivity implements View.On
|
|||||||
inputControlsView.invalidate();
|
inputControlsView.invalidate();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NumberPicker npColumns = view.findViewById(R.id.NPColumns);
|
||||||
|
npColumns.setValue(element.getBindingCount());
|
||||||
|
npColumns.setOnValueChangeListener((numberPicker, value) -> {
|
||||||
|
element.setBindingCount(value);
|
||||||
|
profile.save();
|
||||||
|
inputControlsView.invalidate();
|
||||||
|
});
|
||||||
|
|
||||||
final TextView tvScale = view.findViewById(R.id.TVScale);
|
final TextView tvScale = view.findViewById(R.id.TVScale);
|
||||||
SeekBar sbScale = view.findViewById(R.id.SBScale);
|
SeekBar sbScale = view.findViewById(R.id.SBScale);
|
||||||
sbScale.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
sbScale.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||||
@@ -229,7 +238,19 @@ public class ControlsEditorActivity extends AppCompatActivity implements View.On
|
|||||||
final Spinner sBinding = view.findViewById(R.id.SBinding);
|
final Spinner sBinding = view.findViewById(R.id.SBinding);
|
||||||
|
|
||||||
Runnable update = () -> {
|
Runnable update = () -> {
|
||||||
String[] bindingEntries = sBindingType.getSelectedItemPosition() == 0 ? Binding.keyboardBindingLabels() : Binding.mouseBindingLabels();
|
String[] bindingEntries = null;
|
||||||
|
switch (sBindingType.getSelectedItemPosition()) {
|
||||||
|
case 0:
|
||||||
|
bindingEntries = Binding.keyboardBindingLabels();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
bindingEntries = Binding.mouseBindingLabels();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
bindingEntries = Binding.gamepadBindingLabels();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
sBinding.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, bindingEntries));
|
sBinding.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_spinner_dropdown_item, bindingEntries));
|
||||||
AppUtils.setSpinnerSelectionFromValue(sBinding, element.getBindingAt(index).toString());
|
AppUtils.setSpinnerSelectionFromValue(sBinding, element.getBindingAt(index).toString());
|
||||||
};
|
};
|
||||||
@@ -243,13 +264,34 @@ public class ControlsEditorActivity extends AppCompatActivity implements View.On
|
|||||||
@Override
|
@Override
|
||||||
public void onNothingSelected(AdapterView<?> parent) {}
|
public void onNothingSelected(AdapterView<?> parent) {}
|
||||||
});
|
});
|
||||||
sBindingType.setSelection(element.getBindingAt(index).isKeyboard() ? 0 : 1, false);
|
|
||||||
|
Binding selectedBinding = element.getBindingAt(index);
|
||||||
|
if (selectedBinding.isKeyboard()) {
|
||||||
|
sBindingType.setSelection(0, false);
|
||||||
|
}
|
||||||
|
else if (selectedBinding.isMouse()) {
|
||||||
|
sBindingType.setSelection(1, false);
|
||||||
|
}
|
||||||
|
else if (selectedBinding.isGamepad()) {
|
||||||
|
sBindingType.setSelection(2, false);
|
||||||
|
}
|
||||||
|
|
||||||
sBinding.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
sBinding.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
boolean isKeyboard = sBindingType.getSelectedItemPosition() == 0;
|
Binding binding = Binding.NONE;
|
||||||
Binding binding = isKeyboard ? Binding.keyboardBindingValues()[position] : Binding.mouseBindingValues()[position];
|
switch (sBindingType.getSelectedItemPosition()) {
|
||||||
|
case 0:
|
||||||
|
binding = Binding.keyboardBindingValues()[position];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
binding = Binding.mouseBindingValues()[position];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
binding = Binding.gamepadBindingValues()[position];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (binding != element.getBindingAt(index)) {
|
if (binding != element.getBindingAt(index)) {
|
||||||
element.setBindingAt(index, binding);
|
element.setBindingAt(index, binding);
|
||||||
profile.save();
|
profile.save();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.InputDevice;
|
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -76,6 +75,7 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private void updateControllerBinding(int keyCode, Binding binding) {
|
private void updateControllerBinding(int keyCode, Binding binding) {
|
||||||
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) return;
|
if (keyCode == KeyEvent.KEYCODE_UNKNOWN) return;
|
||||||
|
|
||||||
ExternalControllerBinding controllerBinding = controller.getControllerBinding(keyCode);
|
ExternalControllerBinding controllerBinding = controller.getControllerBinding(keyCode);
|
||||||
int position;
|
int position;
|
||||||
if (controllerBinding == null) {
|
if (controllerBinding == null) {
|
||||||
@@ -93,28 +93,29 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity {
|
|||||||
recyclerView.scrollToPosition(position);
|
recyclerView.scrollToPosition(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processJoystickInput(MotionEvent event, int historyPos) {
|
private void processJoystickInput() {
|
||||||
int keyCode = KeyEvent.KEYCODE_UNKNOWN;
|
int keyCode = KeyEvent.KEYCODE_UNKNOWN;
|
||||||
byte sign;
|
|
||||||
Binding binding = Binding.NONE;
|
Binding binding = Binding.NONE;
|
||||||
|
|
||||||
final int[] axes = {MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y};
|
final int[] axes = {MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y};
|
||||||
for (int axis : axes) {
|
final float[] values = {controller.state.thumbLX, controller.state.thumbLY, controller.state.thumbRX, controller.state.thumbRY, controller.state.getDPadX(), controller.state.getDPadY()};
|
||||||
if ((sign = Mathf.sign(ExternalController.getCenteredAxis(event, axis, historyPos))) != 0) {
|
|
||||||
if (axis == MotionEvent.AXIS_X || axis == MotionEvent.AXIS_Z) {
|
byte sign;
|
||||||
|
for (int i = 0; i < axes.length; i++) {
|
||||||
|
if ((sign = Mathf.sign(values[i])) != 0) {
|
||||||
|
if (axes[i] == MotionEvent.AXIS_X || axes[i] == MotionEvent.AXIS_Z) {
|
||||||
binding = sign > 0 ? Binding.MOUSE_MOVE_RIGHT : Binding.MOUSE_MOVE_LEFT;
|
binding = sign > 0 ? Binding.MOUSE_MOVE_RIGHT : Binding.MOUSE_MOVE_LEFT;
|
||||||
}
|
}
|
||||||
else if (axis == MotionEvent.AXIS_Y || axis == MotionEvent.AXIS_RZ) {
|
else if (axes[i] == MotionEvent.AXIS_Y || axes[i] == MotionEvent.AXIS_RZ) {
|
||||||
binding = sign > 0 ? Binding.MOUSE_MOVE_DOWN : Binding.MOUSE_MOVE_UP;
|
binding = sign > 0 ? Binding.MOUSE_MOVE_DOWN : Binding.MOUSE_MOVE_UP;
|
||||||
}
|
}
|
||||||
else if (axis == MotionEvent.AXIS_HAT_X) {
|
else if (axes[i] == MotionEvent.AXIS_HAT_X) {
|
||||||
binding = sign > 0 ? Binding.KEY_D : Binding.KEY_A;
|
binding = sign > 0 ? Binding.KEY_D : Binding.KEY_A;
|
||||||
}
|
}
|
||||||
else if (axis == MotionEvent.AXIS_HAT_Y) {
|
else if (axes[i] == MotionEvent.AXIS_HAT_Y) {
|
||||||
binding = sign > 0 ? Binding.KEY_S : Binding.KEY_W;
|
binding = sign > 0 ? Binding.KEY_S : Binding.KEY_W;
|
||||||
}
|
}
|
||||||
|
|
||||||
keyCode = ExternalControllerBinding.getKeyCodeForAxis(axis, sign);
|
keyCode = ExternalControllerBinding.getKeyCodeForAxis(axes[i], sign);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -122,41 +123,24 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity {
|
|||||||
updateControllerBinding(keyCode, binding);
|
updateControllerBinding(keyCode, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processTriggerButton(MotionEvent event) {
|
|
||||||
if (event.getAxisValue(MotionEvent.AXIS_LTRIGGER) == 1 || event.getAxisValue(MotionEvent.AXIS_BRAKE) == 1) {
|
|
||||||
updateControllerBinding(ExternalControllerBinding.AXIS_LTRIGGER, Binding.NONE);
|
|
||||||
}
|
|
||||||
if (event.getAxisValue(MotionEvent.AXIS_RTRIGGER) == 1 || event.getAxisValue(MotionEvent.AXIS_GAS) == 1) {
|
|
||||||
updateControllerBinding(ExternalControllerBinding.AXIS_RTRIGGER, Binding.NONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||||
if (event.getDeviceId() == controller.getDeviceId()) {
|
if (event.getDeviceId() == controller.getDeviceId() && controller.updateStateFromMotionEvent(event)) {
|
||||||
if (ExternalController.isDPadDevice(event)) {
|
if (controller.state.isPressed(ExternalController.IDX_BUTTON_L2)) updateControllerBinding(KeyEvent.KEYCODE_BUTTON_L2, Binding.NONE);
|
||||||
processTriggerButton(event);
|
if (controller.state.isPressed(ExternalController.IDX_BUTTON_R2)) updateControllerBinding(KeyEvent.KEYCODE_BUTTON_R2, Binding.NONE);
|
||||||
processJoystickInput(event, -1);
|
processJoystickInput();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else if (ExternalController.isJoystickDevice(event)) {
|
|
||||||
int historySize = event.getHistorySize();
|
|
||||||
for (int i = 0; i < historySize; i++) processJoystickInput(event, i);
|
|
||||||
processJoystickInput(event, -1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.onGenericMotionEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
||||||
if ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD &&
|
|
||||||
event.getDeviceId() == controller.getDeviceId() && event.getRepeatCount() == 0) {
|
|
||||||
updateControllerBinding(keyCode, Binding.NONE);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else return super.onKeyDown(keyCode, event);
|
return super.dispatchGenericMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
|
if (event.getDeviceId() == controller.getDeviceId() && event.getRepeatCount() == 0) {
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN) updateControllerBinding(event.getKeyCode(), Binding.NONE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return super.dispatchKeyEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -208,7 +192,19 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity {
|
|||||||
final Context $this = ExternalControllerBindingsActivity.this;
|
final Context $this = ExternalControllerBindingsActivity.this;
|
||||||
|
|
||||||
Runnable update = () -> {
|
Runnable update = () -> {
|
||||||
String[] bindingEntries = holder.bindingType.getSelectedItemPosition() == 0 ? Binding.keyboardBindingLabels() : Binding.mouseBindingLabels();
|
String[] bindingEntries = null;
|
||||||
|
switch (holder.bindingType.getSelectedItemPosition()) {
|
||||||
|
case 0:
|
||||||
|
bindingEntries = Binding.keyboardBindingLabels();
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
bindingEntries = Binding.mouseBindingLabels();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
bindingEntries = Binding.gamepadBindingLabels();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
holder.binding.setAdapter(new ArrayAdapter<>($this, android.R.layout.simple_spinner_dropdown_item, bindingEntries));
|
holder.binding.setAdapter(new ArrayAdapter<>($this, android.R.layout.simple_spinner_dropdown_item, bindingEntries));
|
||||||
AppUtils.setSpinnerSelectionFromValue(holder.binding, item.getBinding().toString());
|
AppUtils.setSpinnerSelectionFromValue(holder.binding, item.getBinding().toString());
|
||||||
};
|
};
|
||||||
@@ -227,8 +223,19 @@ public class ExternalControllerBindingsActivity extends AppCompatActivity {
|
|||||||
holder.binding.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
holder.binding.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
boolean isKeyboard = holder.bindingType.getSelectedItemPosition() == 0;
|
Binding binding = Binding.NONE;
|
||||||
Binding binding = isKeyboard ? Binding.keyboardBindingValues()[position] : Binding.mouseBindingValues()[position];
|
switch (holder.bindingType.getSelectedItemPosition()) {
|
||||||
|
case 0:
|
||||||
|
binding = Binding.keyboardBindingValues()[position];
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
binding = Binding.mouseBindingValues()[position];
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
binding = Binding.gamepadBindingValues()[position];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (binding != item.getBinding()) {
|
if (binding != item.getBinding()) {
|
||||||
item.setBinding(binding);
|
item.setBinding(binding);
|
||||||
profile.save();
|
profile.save();
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ import androidx.fragment.app.Fragment;
|
|||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
|
||||||
import com.google.android.material.navigation.NavigationView;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
|
import com.winlator.contentdialog.ContentDialog;
|
||||||
import com.winlator.core.Callback;
|
import com.winlator.core.Callback;
|
||||||
import com.winlator.core.OBBImageInstaller;
|
import com.winlator.core.OBBImageInstaller;
|
||||||
import com.winlator.core.PreloaderDialog;
|
import com.winlator.core.PreloaderDialog;
|
||||||
import com.winlator.contentdialog.ContentDialog;
|
|
||||||
import com.winlator.xenvironment.ImageFs;
|
import com.winlator.xenvironment.ImageFs;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ import java.util.concurrent.Executors;
|
|||||||
|
|
||||||
public class SettingsFragment extends Fragment {
|
public class SettingsFragment extends Fragment {
|
||||||
public static final String DEFAULT_BOX86_VERSION = "0.3.0";
|
public static final String DEFAULT_BOX86_VERSION = "0.3.0";
|
||||||
public static final String DEFAULT_BOX64_VERSION = "0.2.5";
|
public static final String DEFAULT_BOX64_VERSION = "0.2.6";
|
||||||
private Callback<Uri> selectWineFileCallback;
|
private Callback<Uri> selectWineFileCallback;
|
||||||
private PreloaderDialog preloaderDialog;
|
private PreloaderDialog preloaderDialog;
|
||||||
private SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
@@ -96,17 +96,27 @@ public class SettingsFragment extends Fragment {
|
|||||||
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
preferences = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
|
||||||
final Spinner sBox86Version = view.findViewById(R.id.SBox86Version);
|
final Spinner sBox86Version = view.findViewById(R.id.SBox86Version);
|
||||||
AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, preferences.getString("box86_version", DEFAULT_BOX86_VERSION));
|
String box86Version = preferences.getString("box86_version", DEFAULT_BOX86_VERSION);
|
||||||
|
if (!AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, box86Version)) {
|
||||||
|
AppUtils.setSpinnerSelectionFromIdentifier(sBox86Version, DEFAULT_BOX86_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
final Spinner sBox64Version = view.findViewById(R.id.SBox64Version);
|
final Spinner sBox64Version = view.findViewById(R.id.SBox64Version);
|
||||||
AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, preferences.getString("box64_version", DEFAULT_BOX64_VERSION));
|
String box64Version = preferences.getString("box64_version", DEFAULT_BOX64_VERSION);
|
||||||
|
if (!AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, box64Version)) {
|
||||||
|
AppUtils.setSpinnerSelectionFromIdentifier(sBox64Version, DEFAULT_BOX64_VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
final Spinner sBox86Preset = view.findViewById(R.id.SBox86Preset);
|
final Spinner sBox86Preset = view.findViewById(R.id.SBox86Preset);
|
||||||
final Spinner sBox64Preset = view.findViewById(R.id.SBox64Preset);
|
final Spinner sBox64Preset = view.findViewById(R.id.SBox64Preset);
|
||||||
loadBox86_64PresetSpinners(view, sBox86Preset, sBox64Preset);
|
loadBox86_64PresetSpinners(view, sBox86Preset, sBox64Preset);
|
||||||
|
|
||||||
final Spinner sTurnipVersion = view.findViewById(R.id.STurnipVersion);
|
final Spinner sTurnipVersion = view.findViewById(R.id.STurnipVersion);
|
||||||
AppUtils.setSpinnerSelectionFromIdentifier(sTurnipVersion, preferences.getString("turnip_version", getDefaultTurnipVersion(context)));
|
String defaultTurnipVersion = getDefaultTurnipVersion(context);
|
||||||
|
String turnipVersion = preferences.getString("turnip_version", defaultTurnipVersion);
|
||||||
|
if (!AppUtils.setSpinnerSelectionFromIdentifier(sTurnipVersion, turnipVersion)) {
|
||||||
|
AppUtils.setSpinnerSelectionFromIdentifier(sTurnipVersion, defaultTurnipVersion);
|
||||||
|
}
|
||||||
|
|
||||||
final CheckBox cbUseDRI3 = view.findViewById(R.id.CBUseDRI3);
|
final CheckBox cbUseDRI3 = view.findViewById(R.id.CBUseDRI3);
|
||||||
cbUseDRI3.setChecked(preferences.getBoolean("use_dri3", true));
|
cbUseDRI3.setChecked(preferences.getBoolean("use_dri3", true));
|
||||||
@@ -351,4 +361,14 @@ public class SettingsFragment extends Fragment {
|
|||||||
});
|
});
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void resetBox86_64Version(AppCompatActivity activity) {
|
||||||
|
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
SharedPreferences.Editor editor = preferences.edit();
|
||||||
|
editor.putString("box86_version", DEFAULT_BOX86_VERSION);
|
||||||
|
editor.putString("box64_version", DEFAULT_BOX64_VERSION);
|
||||||
|
editor.remove("current_box86_version");
|
||||||
|
editor.remove("current_box64_version");
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import android.content.SharedPreferences;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.ArrayAdapter;
|
||||||
import android.widget.CheckBox;
|
import android.widget.CheckBox;
|
||||||
@@ -27,6 +28,7 @@ import com.winlator.core.AppUtils;
|
|||||||
import com.winlator.core.CursorLocker;
|
import com.winlator.core.CursorLocker;
|
||||||
import com.winlator.core.EnvVars;
|
import com.winlator.core.EnvVars;
|
||||||
import com.winlator.core.FileUtils;
|
import com.winlator.core.FileUtils;
|
||||||
|
import com.winlator.core.OBBImageInstaller;
|
||||||
import com.winlator.core.OnExtractFileListener;
|
import com.winlator.core.OnExtractFileListener;
|
||||||
import com.winlator.core.PreloaderDialog;
|
import com.winlator.core.PreloaderDialog;
|
||||||
import com.winlator.core.ProcessHelper;
|
import com.winlator.core.ProcessHelper;
|
||||||
@@ -89,7 +91,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
private boolean firstTimeBoot = false;
|
private boolean firstTimeBoot = false;
|
||||||
private SharedPreferences preferences;
|
private SharedPreferences preferences;
|
||||||
private OnExtractFileListener onExtractFileListener;
|
private OnExtractFileListener onExtractFileListener;
|
||||||
private final WinHandler winHandler = new WinHandler();
|
private final WinHandler winHandler = new WinHandler(this);
|
||||||
private float globalCursorSpeed = 1.0f;
|
private float globalCursorSpeed = 1.0f;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,6 +138,9 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
graphicsDriver = shortcut.getExtra("graphicsDriver", container.getGraphicsDriver());
|
graphicsDriver = shortcut.getExtra("graphicsDriver", container.getGraphicsDriver());
|
||||||
audioDriver = shortcut.getExtra("audioDriver", container.getAudioDriver());
|
audioDriver = shortcut.getExtra("audioDriver", container.getAudioDriver());
|
||||||
screenSize = shortcut.getExtra("screenSize", container.getScreenSize());
|
screenSize = shortcut.getExtra("screenSize", container.getScreenSize());
|
||||||
|
|
||||||
|
String dinputMapperType = shortcut.getExtra("dinputMapperType");
|
||||||
|
if (!dinputMapperType.isEmpty()) winHandler.setDInputMapperType(Byte.parseByte(dinputMapperType));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!wineInfo.isWin64()) {
|
if (!wineInfo.isWin64()) {
|
||||||
@@ -199,6 +204,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
|
winHandler.stop();
|
||||||
if (environment != null) environment.stopEnvironmentComponents();
|
if (environment != null) environment.stopEnvironmentComponents();
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
@@ -243,6 +249,7 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void exit() {
|
private void exit() {
|
||||||
|
winHandler.stop();
|
||||||
if (environment != null) environment.stopEnvironmentComponents();
|
if (environment != null) environment.stopEnvironmentComponents();
|
||||||
AppUtils.restartApplication(this);
|
AppUtils.restartApplication(this);
|
||||||
}
|
}
|
||||||
@@ -257,9 +264,10 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
File pulseaudioDir = new File(getFilesDir(), "pulseaudio");
|
File pulseaudioDir = new File(getFilesDir(), "pulseaudio");
|
||||||
TarZstdUtils.extract(this, "patches.tzst", rootDir, onExtractFileListener);
|
TarZstdUtils.extract(this, "patches.tzst", rootDir, onExtractFileListener);
|
||||||
TarZstdUtils.extract(this, "pulseaudio.tzst", pulseaudioDir);
|
TarZstdUtils.extract(this, "pulseaudio.tzst", pulseaudioDir);
|
||||||
WineUtils.applyRegistryKeyTweaks(this);
|
WineUtils.applyRegistryTweaks(this);
|
||||||
container.putExtra("appVersion", appVersion);
|
container.putExtra("appVersion", appVersion);
|
||||||
container.putExtra("imgVersion", imgVersion);
|
container.putExtra("imgVersion", imgVersion);
|
||||||
|
SettingsFragment.resetBox86_64Version(this);
|
||||||
containerDataChanged = true;
|
containerDataChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,9 +296,11 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
envVars.put("MESA_DEBUG", "silent");
|
envVars.put("MESA_DEBUG", "silent");
|
||||||
envVars.put("MESA_NO_ERROR", "1");
|
envVars.put("MESA_NO_ERROR", "1");
|
||||||
envVars.put("WINEPREFIX", ImageFs.WINEPREFIX);
|
envVars.put("WINEPREFIX", ImageFs.WINEPREFIX);
|
||||||
|
envVars.put("WINEESYNC", "1");
|
||||||
if (MainActivity.DEBUG_LEVEL <= 1) envVars.put("WINEDEBUG", "-all");
|
if (MainActivity.DEBUG_LEVEL <= 1) envVars.put("WINEDEBUG", "-all");
|
||||||
|
|
||||||
String rootPath = imageFs.getRootDir().getPath();
|
String rootPath = imageFs.getRootDir().getPath();
|
||||||
|
FileUtils.clear(imageFs.getTmpDir());
|
||||||
|
|
||||||
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
|
GuestProgramLauncherComponent guestProgramLauncherComponent = new GuestProgramLauncherComponent();
|
||||||
|
|
||||||
@@ -399,10 +409,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
};
|
};
|
||||||
loadProfileSpinner.run();
|
loadProfileSpinner.run();
|
||||||
|
|
||||||
final GLRenderer renderer = xServerView.getRenderer();
|
|
||||||
final CheckBox cbCursorVisible = dialog.findViewById(R.id.CBCursorVisible);
|
|
||||||
cbCursorVisible.setChecked(renderer.isCursorVisible());
|
|
||||||
|
|
||||||
final CheckBox cbLockCursor = dialog.findViewById(R.id.CBLockCursor);
|
final CheckBox cbLockCursor = dialog.findViewById(R.id.CBLockCursor);
|
||||||
cbLockCursor.setChecked(xServer.cursorLocker.getState() == CursorLocker.State.LOCKED);
|
cbLockCursor.setChecked(xServer.cursorLocker.getState() == CursorLocker.State.LOCKED);
|
||||||
|
|
||||||
@@ -423,7 +429,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
});
|
});
|
||||||
|
|
||||||
dialog.setOnConfirmCallback(() -> {
|
dialog.setOnConfirmCallback(() -> {
|
||||||
renderer.setCursorVisible(cbCursorVisible.isChecked());
|
|
||||||
xServer.cursorLocker.setState(cbLockCursor.isChecked() ? CursorLocker.State.LOCKED : CursorLocker.State.CONFINED);
|
xServer.cursorLocker.setState(cbLockCursor.isChecked() ? CursorLocker.State.LOCKED : CursorLocker.State.CONFINED);
|
||||||
inputControlsView.setShowTouchscreenControls(cbShowTouchscreenControls.isChecked());
|
inputControlsView.setShowTouchscreenControls(cbShowTouchscreenControls.isChecked());
|
||||||
int position = sProfile.getSelectedItemPosition();
|
int position = sProfile.getSelectedItemPosition();
|
||||||
@@ -510,12 +515,21 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||||
|
return !winHandler.onGenericMotionEvent(event) && super.dispatchGenericMotionEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||||
return (!inputControlsView.onKeyEvent(event) && xServer.keyboard.onKeyEvent(event)) ||
|
return (!inputControlsView.onKeyEvent(event) && !winHandler.onKeyEvent(event) && xServer.keyboard.onKeyEvent(event)) ||
|
||||||
(!ExternalController.isGameController(event.getDevice()) && super.dispatchKeyEvent(event));
|
(!ExternalController.isGameController(event.getDevice()) && super.dispatchKeyEvent(event));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InputControlsView getInputControlsView() {
|
||||||
|
return inputControlsView;
|
||||||
|
}
|
||||||
|
|
||||||
private void generateWineprefix() {
|
private void generateWineprefix() {
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
@@ -731,7 +745,6 @@ public class XServerDisplayActivity extends AppCompatActivity implements Navigat
|
|||||||
|
|
||||||
String args = "";
|
String args = "";
|
||||||
if (shortcut != null) {
|
if (shortcut != null) {
|
||||||
if (shortcut.getExtra("singleCPU", "0").equals("1")) args += "/affinity "+ProcessHelper.getSingleCPUAffinityMask()+" ";
|
|
||||||
String execArgs = shortcut.getExtra("execArgs");
|
String execArgs = shortcut.getExtra("execArgs");
|
||||||
execArgs = !execArgs.isEmpty() ? " "+execArgs : "";
|
execArgs = !execArgs.isEmpty() ? " "+execArgs : "";
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class Container {
|
|||||||
public static final String DEFAULT_GRAPHICS_DRIVER = "turnip-zink";
|
public static final String DEFAULT_GRAPHICS_DRIVER = "turnip-zink";
|
||||||
public static final String DEFAULT_AUDIO_DRIVER = "alsa";
|
public static final String DEFAULT_AUDIO_DRIVER = "alsa";
|
||||||
public static final String DEFAULT_DXWRAPPER = "original-wined3d";
|
public static final String DEFAULT_DXWRAPPER = "original-wined3d";
|
||||||
public static final String DEFAULT_DXCOMPONENTS = "direct3d=0,directsound=0,directmusic=0,directshow=0,directplay=0";
|
public static final String DEFAULT_DXCOMPONENTS = "direct3d=1,directsound=1,directmusic=0,directshow=0,directplay=0";
|
||||||
public static final String DEFAULT_DRIVES = "D:"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
public static final String DEFAULT_DRIVES = "D:"+Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
public static final byte MAX_DRIVE_LETTERS = 8;
|
public static final byte MAX_DRIVE_LETTERS = 8;
|
||||||
public final int id;
|
public final int id;
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package com.winlator.contentdialog;
|
package com.winlator.contentdialog;
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -24,6 +23,7 @@ import com.winlator.core.AppUtils;
|
|||||||
import com.winlator.core.ArrayUtils;
|
import com.winlator.core.ArrayUtils;
|
||||||
import com.winlator.core.EnvVars;
|
import com.winlator.core.EnvVars;
|
||||||
import com.winlator.core.StringUtils;
|
import com.winlator.core.StringUtils;
|
||||||
|
import com.winlator.winhandler.WinHandler;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@@ -75,9 +75,6 @@ public class ShortcutSettingsDialog extends ContentDialog {
|
|||||||
sAudioDriver.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, audioDriverEntries));
|
sAudioDriver.setAdapter(new ArrayAdapter<>(context, android.R.layout.simple_spinner_dropdown_item, audioDriverEntries));
|
||||||
AppUtils.setSpinnerSelectionFromIdentifier(sAudioDriver, shortcut.getExtra("audioDriver"));
|
AppUtils.setSpinnerSelectionFromIdentifier(sAudioDriver, shortcut.getExtra("audioDriver"));
|
||||||
|
|
||||||
final CheckBox cbSingleCPU = findViewById(R.id.CBSingleCPU);
|
|
||||||
cbSingleCPU.setChecked(shortcut.getExtra("singleCPU", "0").equals("1"));
|
|
||||||
|
|
||||||
final CheckBox cbForceFullscreen = findViewById(R.id.CBForceFullscreen);
|
final CheckBox cbForceFullscreen = findViewById(R.id.CBForceFullscreen);
|
||||||
cbForceFullscreen.setChecked(shortcut.getExtra("forceFullscreen", "0").equals("1"));
|
cbForceFullscreen.setChecked(shortcut.getExtra("forceFullscreen", "0").equals("1"));
|
||||||
|
|
||||||
@@ -87,6 +84,9 @@ public class ShortcutSettingsDialog extends ContentDialog {
|
|||||||
final Spinner sBox64Preset = findViewById(R.id.SBox64Preset);
|
final Spinner sBox64Preset = findViewById(R.id.SBox64Preset);
|
||||||
Box86_64PresetManager.loadSpinner("box64", sBox64Preset, shortcut.getExtra("box64Preset", shortcut.container.getBox64Preset()));
|
Box86_64PresetManager.loadSpinner("box64", sBox64Preset, shortcut.getExtra("box64Preset", shortcut.container.getBox64Preset()));
|
||||||
|
|
||||||
|
final Spinner sDInputMapperType = findViewById(R.id.SDInputMapperType);
|
||||||
|
sDInputMapperType.setSelection(Byte.parseByte(shortcut.getExtra("dinputMapperType", String.valueOf(WinHandler.DINPUT_MAPPER_TYPE_XINPUT))));
|
||||||
|
|
||||||
createDXComponentsTab();
|
createDXComponentsTab();
|
||||||
createEnvVarsTab();
|
createEnvVarsTab();
|
||||||
|
|
||||||
@@ -118,7 +118,6 @@ public class ShortcutSettingsDialog extends ContentDialog {
|
|||||||
shortcut.putExtra("dxwrapper", sDXWrapper.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()) : null);
|
shortcut.putExtra("dxwrapper", sDXWrapper.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sDXWrapper.getSelectedItem()) : null);
|
||||||
shortcut.putExtra("audioDriver", sAudioDriver.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sAudioDriver.getSelectedItem()) : null);
|
shortcut.putExtra("audioDriver", sAudioDriver.getSelectedItemPosition() > 0 ? StringUtils.parseIdentifier(sAudioDriver.getSelectedItem()) : null);
|
||||||
shortcut.putExtra("forceFullscreen", cbForceFullscreen.isChecked() ? "1" : null);
|
shortcut.putExtra("forceFullscreen", cbForceFullscreen.isChecked() ? "1" : null);
|
||||||
shortcut.putExtra("singleCPU", cbSingleCPU.isChecked() ? "1" : null);
|
|
||||||
shortcut.putExtra("dxcomponents", getDXComponents());
|
shortcut.putExtra("dxcomponents", getDXComponents());
|
||||||
shortcut.putExtra("envVars", getEnvVars());
|
shortcut.putExtra("envVars", getEnvVars());
|
||||||
|
|
||||||
@@ -127,6 +126,8 @@ public class ShortcutSettingsDialog extends ContentDialog {
|
|||||||
shortcut.putExtra("box86Preset", !box86Preset.equals(shortcut.container.getBox86Preset()) ? box86Preset : null);
|
shortcut.putExtra("box86Preset", !box86Preset.equals(shortcut.container.getBox86Preset()) ? box86Preset : null);
|
||||||
shortcut.putExtra("box64Preset", !box64Preset.equals(shortcut.container.getBox64Preset()) ? box64Preset : null);
|
shortcut.putExtra("box64Preset", !box64Preset.equals(shortcut.container.getBox64Preset()) ? box64Preset : null);
|
||||||
|
|
||||||
|
int dinputMapperType = sDInputMapperType.getSelectedItemPosition();
|
||||||
|
shortcut.putExtra("dinputMapperType", dinputMapperType != WinHandler.DINPUT_MAPPER_TYPE_XINPUT ? String.valueOf(dinputMapperType) : null);
|
||||||
shortcut.saveData();
|
shortcut.saveData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,14 +2,12 @@ package com.winlator.core;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
|
||||||
import androidx.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import com.winlator.MainActivity;
|
import com.winlator.MainActivity;
|
||||||
import com.winlator.R;
|
import com.winlator.R;
|
||||||
|
import com.winlator.SettingsFragment;
|
||||||
import com.winlator.contentdialog.ContentDialog;
|
import com.winlator.contentdialog.ContentDialog;
|
||||||
import com.winlator.xenvironment.ImageFs;
|
import com.winlator.xenvironment.ImageFs;
|
||||||
|
|
||||||
@@ -39,11 +37,7 @@ public abstract class OBBImageInstaller {
|
|||||||
ImageFs imageFs = ImageFs.find(activity);
|
ImageFs imageFs = ImageFs.find(activity);
|
||||||
final File rootDir = imageFs.getRootDir();
|
final File rootDir = imageFs.getRootDir();
|
||||||
|
|
||||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
|
SettingsFragment.resetBox86_64Version(activity);
|
||||||
SharedPreferences.Editor editor = preferences.edit();
|
|
||||||
editor.remove("current_box86_version");
|
|
||||||
editor.remove("current_box64_version");
|
|
||||||
editor.apply();
|
|
||||||
|
|
||||||
if (foundVersion <= 0) foundVersion = imageFs.isValid() ? imageFs.getVersion() + 1 : 1;
|
if (foundVersion <= 0) foundVersion = imageFs.isValid() ? imageFs.getVersion() + 1 : 1;
|
||||||
final int finalVersion = foundVersion;
|
final int finalVersion = foundVersion;
|
||||||
|
|||||||
@@ -131,12 +131,6 @@ public abstract class ProcessHelper {
|
|||||||
return result.toArray(new String[0]);
|
return result.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getSingleCPUAffinityMask() {
|
|
||||||
int numProcessors = (byte)Runtime.getRuntime().availableProcessors();
|
|
||||||
int affinityMask = (int)Math.pow(2, numProcessors-1);
|
|
||||||
return Integer.toHexString(affinityMask);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String getAffinityMask(String cpuList) {
|
public static String getAffinityMask(String cpuList) {
|
||||||
String[] values = cpuList.split(",");
|
String[] values = cpuList.split(",");
|
||||||
int affinityMask = 0;
|
int affinityMask = 0;
|
||||||
|
|||||||
@@ -11,14 +11,45 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
public abstract class TarXZUtils {
|
public abstract class TarXZUtils {
|
||||||
|
public static boolean extract(Context context, String assetFile, File destination) {
|
||||||
|
try {
|
||||||
|
return extract(context.getAssets().open(assetFile), destination);
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean extract(File source, File destination) {
|
||||||
|
if (source == null || !source.isFile()) return false;
|
||||||
|
try {
|
||||||
|
return extract(new BufferedInputStream(new FileInputStream(source), StreamUtils.BUFFER_SIZE), destination);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean extract(Context context, Uri source, File destination) {
|
public static boolean extract(Context context, Uri source, File destination) {
|
||||||
if (source == null) return false;
|
if (source == null) return false;
|
||||||
try (InputStream inStream = new XZCompressorInputStream(new BufferedInputStream(context.getContentResolver().openInputStream(source), StreamUtils.BUFFER_SIZE));
|
try {
|
||||||
|
return extract(context.getContentResolver().openInputStream(source), destination);
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean extract(InputStream source, File destination) {
|
||||||
|
if (source == null) return false;
|
||||||
|
try (InputStream inStream = new XZCompressorInputStream(source);
|
||||||
ArchiveInputStream tar = new TarArchiveInputStream(inStream)) {
|
ArchiveInputStream tar = new TarArchiveInputStream(inStream)) {
|
||||||
TarArchiveEntry entry;
|
TarArchiveEntry entry;
|
||||||
while ((entry = (TarArchiveEntry)tar.getNextEntry()) != null) {
|
while ((entry = (TarArchiveEntry)tar.getNextEntry()) != null) {
|
||||||
|
|||||||
@@ -83,17 +83,20 @@ public abstract class WineUtils {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File wineBin = new File(binDir, "wine");
|
File wineBin32 = new File(binDir, "wine");
|
||||||
if (!wineBin.isFile()) {
|
File wineBin64 = new File(binDir, "wine64");
|
||||||
|
|
||||||
|
if (!wineBin32.isFile()) {
|
||||||
callback.call(null);
|
callback.call(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String arch = (new File(binDir, "wine64")).isFile() ? "x86_64" : "x86";
|
final String arch = wineBin64.isFile() ? "x86_64" : "x86";
|
||||||
|
|
||||||
ImageFs imageFs = ImageFs.find(context);
|
ImageFs imageFs = ImageFs.find(context);
|
||||||
File rootDir = ImageFs.find(context).getRootDir();
|
File rootDir = ImageFs.find(context).getRootDir();
|
||||||
String wineBinRelPath = FileUtils.toRelativePath(rootDir.getPath(), wineBin.getPath());
|
String wineBinAbsPath = arch.equals("x86_64") ? wineBin64.getPath() : wineBin32.getPath();
|
||||||
|
String wineBinRelPath = FileUtils.toRelativePath(rootDir.getPath(), wineBinAbsPath);
|
||||||
final String winePath = wineDir.getPath();
|
final String winePath = wineDir.getPath();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -140,14 +143,26 @@ public abstract class WineUtils {
|
|||||||
return wineInfos;
|
return wineInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void applyRegistryKeyTweaks(Context context) {
|
public static void applyRegistryTweaks(Context context) {
|
||||||
File rootDir = ImageFs.find(context).getRootDir();
|
File rootDir = ImageFs.find(context).getRootDir();
|
||||||
File systemRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/system.reg");
|
File systemRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/system.reg");
|
||||||
|
File userRegFile = new File(rootDir, ImageFs.WINEPREFIX+"/user.reg");
|
||||||
|
|
||||||
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
|
try (WineRegistryEditor registryEditor = new WineRegistryEditor(systemRegFile)) {
|
||||||
registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile");
|
registryEditor.setStringValue("Software\\Classes\\.reg", null, "REGfile");
|
||||||
registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg");
|
registryEditor.setStringValue("Software\\Classes\\.reg", "Content Type", "application/reg");
|
||||||
registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\"");
|
registryEditor.setStringValue("Software\\Classes\\REGfile\\Shell\\Open\\command", null, "C:\\windows\\regedit.exe /C \"%1\"");
|
||||||
|
|
||||||
|
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableHidraw", 1);
|
||||||
|
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "DisableInput", 1);
|
||||||
|
registryEditor.setDwordValue("System\\CurrentControlSet\\Services\\winebus", "Enable SDL", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] dllOverrides = {"d3d8", "d3d9", "d3d10", "d3d10_1", "d3d10core", "d3d11", "d3d12", "d3d12core", "ddraw", "dxgi", "wined3d", "dinput", "dinput8", "xinput1_1", "xinput1_2", "xinput1_3", "xinput1_4", "xinput9_1_0", "xinputuap"};
|
||||||
|
final String dllOverridesKey = "Software\\Wine\\DllOverrides";
|
||||||
|
|
||||||
|
try (WineRegistryEditor registryEditor = new WineRegistryEditor(userRegFile)) {
|
||||||
|
for (String name : dllOverrides) registryEditor.setStringValue(dllOverridesKey, name, "native,builtin");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import com.winlator.xserver.XKeycode;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public enum Binding {
|
public enum Binding {
|
||||||
NONE, MOUSE_LEFT_BUTTON, MOUSE_MIDDLE_BUTTON, MOUSE_RIGHT_BUTTON, MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_ENTER, KEY_ESC, KEY_BKSP, KEY_DEL, KEY_TAB, KEY_SPACE, KEY_CTRL_L, KEY_CTRL_R, KEY_SHIFT_L, KEY_SHIFT_R, KEY_ALT_L, KEY_ALT_R, KEY_HOME, KEY_PRTSCN, KEY_CAPS_LOCK, KEY_NUM_LOCK, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_BRACKET_LEFT, KEY_BRACKET_RIGHT, KEY_BACKSLASH, KEY_SLASH, KEY_SEMICOLON, KEY_COMMA, KEY_PERIOD, KEY_APOSTROPHE, KEY_KP_ADD, KEY_MINUS, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9;
|
NONE, MOUSE_LEFT_BUTTON, MOUSE_MIDDLE_BUTTON, MOUSE_RIGHT_BUTTON, MOUSE_MOVE_LEFT, MOUSE_MOVE_RIGHT, MOUSE_MOVE_UP, MOUSE_MOVE_DOWN, MOUSE_SCROLL_UP, MOUSE_SCROLL_DOWN, KEY_UP, KEY_RIGHT, KEY_DOWN, KEY_LEFT, KEY_ENTER, KEY_ESC, KEY_BKSP, KEY_DEL, KEY_TAB, KEY_SPACE, KEY_CTRL_L, KEY_CTRL_R, KEY_SHIFT_L, KEY_SHIFT_R, KEY_ALT_L, KEY_ALT_R, KEY_HOME, KEY_PRTSCN, KEY_CAPS_LOCK, KEY_NUM_LOCK, KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_BRACKET_LEFT, KEY_BRACKET_RIGHT, KEY_BACKSLASH, KEY_SLASH, KEY_SEMICOLON, KEY_COMMA, KEY_PERIOD, KEY_APOSTROPHE, KEY_KP_ADD, KEY_MINUS, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_KP_0, KEY_KP_1, KEY_KP_2, KEY_KP_3, KEY_KP_4, KEY_KP_5, KEY_KP_6, KEY_KP_7, KEY_KP_8, KEY_KP_9, GAMEPAD_BUTTON_A, GAMEPAD_BUTTON_B, GAMEPAD_BUTTON_X, GAMEPAD_BUTTON_Y, GAMEPAD_BUTTON_L1, GAMEPAD_BUTTON_R1, GAMEPAD_BUTTON_SELECT, GAMEPAD_BUTTON_START, GAMEPAD_BUTTON_L3, GAMEPAD_BUTTON_R3, GAMEPAD_BUTTON_L2, GAMEPAD_BUTTON_R2, GAMEPAD_LEFT_THUMB_UP, GAMEPAD_LEFT_THUMB_RIGHT, GAMEPAD_LEFT_THUMB_DOWN, GAMEPAD_LEFT_THUMB_LEFT, GAMEPAD_RIGHT_THUMB_UP, GAMEPAD_RIGHT_THUMB_RIGHT, GAMEPAD_RIGHT_THUMB_DOWN, GAMEPAD_RIGHT_THUMB_LEFT, GAMEPAD_DPAD_UP, GAMEPAD_DPAD_RIGHT, GAMEPAD_DPAD_DOWN, GAMEPAD_DPAD_LEFT;
|
||||||
public final XKeycode keycode;
|
public final XKeycode keycode;
|
||||||
|
|
||||||
Binding() {
|
Binding() {
|
||||||
@@ -59,7 +59,7 @@ public enum Binding {
|
|||||||
case KEY_KP_ADD:
|
case KEY_KP_ADD:
|
||||||
return "+";
|
return "+";
|
||||||
default:
|
default:
|
||||||
return super.toString().replaceAll("^(MOUSE_)|(KEY_)", "").replace("KP_", "NUMPAD_").replace("_", " ");
|
return super.toString().replaceAll("^(MOUSE_)|(KEY_)|(GAMEPAD_)", "").replace("KP_", "NUMPAD_").replace("_", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,6 +101,10 @@ public enum Binding {
|
|||||||
return name().startsWith("KEY_") || this == NONE;
|
return name().startsWith("KEY_") || this == NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isGamepad() {
|
||||||
|
return name().startsWith("GAMEPAD_");
|
||||||
|
}
|
||||||
|
|
||||||
public static String[] mouseBindingLabels() {
|
public static String[] mouseBindingLabels() {
|
||||||
ArrayList<String> names = new ArrayList<>();
|
ArrayList<String> names = new ArrayList<>();
|
||||||
for (Binding binding : values()) if (binding.isMouse()) names.add(binding.toString());
|
for (Binding binding : values()) if (binding.isMouse()) names.add(binding.toString());
|
||||||
@@ -113,6 +117,12 @@ public enum Binding {
|
|||||||
return labels.toArray(new String[0]);
|
return labels.toArray(new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String[] gamepadBindingLabels() {
|
||||||
|
ArrayList<String> names = new ArrayList<>();
|
||||||
|
for (Binding binding : values()) if (binding.isGamepad()) names.add(binding.toString());
|
||||||
|
return names.toArray(new String[0]);
|
||||||
|
}
|
||||||
|
|
||||||
public static Binding[] mouseBindingValues() {
|
public static Binding[] mouseBindingValues() {
|
||||||
ArrayList<Binding> labels = new ArrayList<>();
|
ArrayList<Binding> labels = new ArrayList<>();
|
||||||
for (Binding binding : values()) if (binding.isMouse()) labels.add(binding);
|
for (Binding binding : values()) if (binding.isMouse()) labels.add(binding);
|
||||||
@@ -124,4 +134,10 @@ public enum Binding {
|
|||||||
for (Binding binding : values()) if (binding.isKeyboard()) values.add(binding);
|
for (Binding binding : values()) if (binding.isKeyboard()) values.add(binding);
|
||||||
return values.toArray(new Binding[0]);
|
return values.toArray(new Binding[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Binding[] gamepadBindingValues() {
|
||||||
|
ArrayList<Binding> labels = new ArrayList<>();
|
||||||
|
for (Binding binding : values()) if (binding.isGamepad()) labels.add(binding);
|
||||||
|
return labels.toArray(new Binding[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ import android.graphics.Path;
|
|||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
|
|
||||||
import androidx.annotation.IntRange;
|
|
||||||
import androidx.core.graphics.ColorUtils;
|
import androidx.core.graphics.ColorUtils;
|
||||||
|
|
||||||
import com.winlator.math.Mathf;
|
import com.winlator.math.Mathf;
|
||||||
@@ -17,7 +16,12 @@ import org.json.JSONArray;
|
|||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class ControlElement {
|
public class ControlElement {
|
||||||
|
public static final float STICK_DEAD_ZONE = 0.15f;
|
||||||
|
public static final float DPAD_DEAD_ZONE = 0.3f;
|
||||||
|
public static final float STICK_SENSITIVITY = 3.5f;
|
||||||
public enum Type {
|
public enum Type {
|
||||||
BUTTON, D_PAD, RANGE_BUTTON, STICK;
|
BUTTON, D_PAD, RANGE_BUTTON, STICK;
|
||||||
|
|
||||||
@@ -56,7 +60,7 @@ public class ControlElement {
|
|||||||
private final InputControlsView inputControlsView;
|
private final InputControlsView inputControlsView;
|
||||||
private Type type = Type.BUTTON;
|
private Type type = Type.BUTTON;
|
||||||
private Shape shape = Shape.CIRCLE;
|
private Shape shape = Shape.CIRCLE;
|
||||||
private final Binding[] bindings = {Binding.NONE, Binding.NONE, Binding.NONE, Binding.NONE};
|
private Binding[] bindings = {Binding.NONE, Binding.NONE, Binding.NONE, Binding.NONE};
|
||||||
private float scale = 1.0f;
|
private float scale = 1.0f;
|
||||||
private short x;
|
private short x;
|
||||||
private short y;
|
private short y;
|
||||||
@@ -64,7 +68,7 @@ public class ControlElement {
|
|||||||
private boolean toggleSwitch = false;
|
private boolean toggleSwitch = false;
|
||||||
private int currentPointerId = -1;
|
private int currentPointerId = -1;
|
||||||
private final Rect boundingBox = new Rect();
|
private final Rect boundingBox = new Rect();
|
||||||
private final boolean[] states = new boolean[4];
|
private boolean[] states = new boolean[4];
|
||||||
private boolean boundingBoxNeedsUpdate = true;
|
private boolean boundingBoxNeedsUpdate = true;
|
||||||
private String text = "";
|
private String text = "";
|
||||||
private byte iconId;
|
private byte iconId;
|
||||||
@@ -79,7 +83,7 @@ public class ControlElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void reset() {
|
private void reset() {
|
||||||
bindings[0] = bindings[1] = bindings[2] = bindings[3] = Binding.NONE;
|
Arrays.fill(bindings, Binding.NONE);
|
||||||
if (type == Type.D_PAD || type == Type.STICK) {
|
if (type == Type.D_PAD || type == Type.STICK) {
|
||||||
bindings[0] = Binding.KEY_W;
|
bindings[0] = Binding.KEY_W;
|
||||||
bindings[1] = Binding.KEY_D;
|
bindings[1] = Binding.KEY_D;
|
||||||
@@ -102,6 +106,17 @@ public class ControlElement {
|
|||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBindingCount() {
|
||||||
|
return bindings.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBindingCount(int bindingCount) {
|
||||||
|
bindings = new Binding[bindingCount];
|
||||||
|
Arrays.fill(bindings, Binding.NONE);
|
||||||
|
states = new boolean[bindingCount];
|
||||||
|
boundingBoxNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
public Shape getShape() {
|
public Shape getShape() {
|
||||||
return shape;
|
return shape;
|
||||||
}
|
}
|
||||||
@@ -136,12 +151,19 @@ public class ControlElement {
|
|||||||
this.toggleSwitch = toggleSwitch;
|
this.toggleSwitch = toggleSwitch;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Binding getBindingAt(@IntRange(from = 0, to = 4) int index) {
|
public Binding getBindingAt(int index) {
|
||||||
return bindings[index];
|
return index < bindings.length ? bindings[index] : Binding.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBindingAt(@IntRange(from = 0, to = 4) int index, Binding binding) {
|
public void setBindingAt(int index, Binding binding) {
|
||||||
this.bindings[index] = binding;
|
if (index >= bindings.length) {
|
||||||
|
int oldLength = bindings.length;
|
||||||
|
bindings = Arrays.copyOf(bindings, index+1);
|
||||||
|
Arrays.fill(bindings, oldLength-1, bindings.length, Binding.NONE);
|
||||||
|
states = new boolean[bindings.length];
|
||||||
|
boundingBoxNeedsUpdate = true;
|
||||||
|
}
|
||||||
|
bindings[index] = binding;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getScale() {
|
public float getScale() {
|
||||||
@@ -223,24 +245,27 @@ public class ControlElement {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case D_PAD:
|
case D_PAD: {
|
||||||
halfWidth = snappingSize * 7;
|
halfWidth = snappingSize * 7;
|
||||||
halfHeight = snappingSize * 7;
|
halfHeight = snappingSize * 7;
|
||||||
break;
|
break;
|
||||||
case STICK:
|
}
|
||||||
|
case STICK: {
|
||||||
halfWidth = snappingSize * 6;
|
halfWidth = snappingSize * 6;
|
||||||
halfHeight = snappingSize * 6;
|
halfHeight = snappingSize * 6;
|
||||||
break;
|
break;
|
||||||
case RANGE_BUTTON:
|
}
|
||||||
if (orientation == 0) {
|
case RANGE_BUTTON: {
|
||||||
halfWidth = snappingSize * 12;
|
halfWidth = snappingSize * ((bindings.length * 4 + 8) / 2);
|
||||||
halfHeight = (int)(snappingSize * 1.5f);
|
halfHeight = snappingSize * 2;
|
||||||
}
|
|
||||||
else {
|
if (orientation == 1) {
|
||||||
halfWidth = (int)(snappingSize * 1.5f);
|
int tmp = halfWidth;
|
||||||
halfHeight = snappingSize * 12;
|
halfWidth = halfHeight;
|
||||||
|
halfHeight = tmp;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
halfWidth *= scale;
|
halfWidth *= scale;
|
||||||
@@ -256,7 +281,7 @@ public class ControlElement {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Binding binding = getBindingAt(0);
|
Binding binding = getBindingAt(0);
|
||||||
String text = binding.toString().replace("NUMPAD ", "NP");
|
String text = binding.toString().replace("NUMPAD ", "NP").replace("BUTTON ", "");
|
||||||
if (text.length() > 7) {
|
if (text.length() > 7) {
|
||||||
String[] parts = text.split(" ");
|
String[] parts = text.split(" ");
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
@@ -326,7 +351,8 @@ public class ControlElement {
|
|||||||
|
|
||||||
if (iconId > 0) {
|
if (iconId > 0) {
|
||||||
drawIcon(canvas, cx, cy, boundingBox.width(), boundingBox.height(), iconId);
|
drawIcon(canvas, cx, cy, boundingBox.width(), boundingBox.height(), iconId);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
String text = getDisplayText();
|
String text = getDisplayText();
|
||||||
paint.setTextSize(Math.min(getTextSizeForWidth(paint, text, boundingBox.width() - strokeWidth * 2), snappingSize * 2 * scale));
|
paint.setTextSize(Math.min(getTextSizeForWidth(paint, text, boundingBox.width() - strokeWidth * 2), snappingSize * 2 * scale));
|
||||||
paint.setTextAlign(Paint.Align.CENTER);
|
paint.setTextAlign(Paint.Align.CENTER);
|
||||||
@@ -379,9 +405,9 @@ public class ControlElement {
|
|||||||
case RANGE_BUTTON: {
|
case RANGE_BUTTON: {
|
||||||
Range range = getRange();
|
Range range = getRange();
|
||||||
int oldColor = paint.getColor();
|
int oldColor = paint.getColor();
|
||||||
float radius = snappingSize * 0.5f * scale;
|
float minSize = Math.min(boundingBox.width(), boundingBox.height());
|
||||||
|
float radius = minSize * 0.5f;
|
||||||
float elementSize = (float)Math.max(boundingBox.width(), boundingBox.height()) / (bindings.length + 2);
|
float elementSize = (float)Math.max(boundingBox.width(), boundingBox.height()) / (bindings.length + 2);
|
||||||
float iconSize = Math.min(boundingBox.width(), boundingBox.height()) * 1.35f;
|
|
||||||
float minTextSize = snappingSize * 2 * scale;
|
float minTextSize = snappingSize * 2 * scale;
|
||||||
|
|
||||||
if (orientation == 0) {
|
if (orientation == 0) {
|
||||||
@@ -391,7 +417,7 @@ public class ControlElement {
|
|||||||
|
|
||||||
float startX = boundingBox.left;
|
float startX = boundingBox.left;
|
||||||
canvas.drawRoundRect(startX, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, paint);
|
canvas.drawRoundRect(startX, boundingBox.top, boundingBox.right, boundingBox.bottom, radius, radius, paint);
|
||||||
drawIcon(canvas, startX + elementSize * 0.5f, cy, iconSize, iconSize, 8);
|
drawIcon(canvas, startX + elementSize * 0.5f, cy, minSize, minSize, 8);
|
||||||
startX += elementSize;
|
startX += elementSize;
|
||||||
|
|
||||||
for (byte i = 0; i < bindings.length; i++) {
|
for (byte i = 0; i < bindings.length; i++) {
|
||||||
@@ -412,7 +438,7 @@ public class ControlElement {
|
|||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setColor(oldColor);
|
paint.setColor(oldColor);
|
||||||
canvas.drawLine(startX, lineTop, startX, lineBottom, paint);
|
canvas.drawLine(startX, lineTop, startX, lineBottom, paint);
|
||||||
drawIcon(canvas, startX + elementSize * 0.5f, cy, iconSize, iconSize, 10);
|
drawIcon(canvas, startX + elementSize * 0.5f, cy, minSize, minSize, 10);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float lineLeft = boundingBox.left + strokeWidth * 0.5f;
|
float lineLeft = boundingBox.left + strokeWidth * 0.5f;
|
||||||
@@ -421,7 +447,7 @@ public class ControlElement {
|
|||||||
|
|
||||||
float startY = boundingBox.top;
|
float startY = boundingBox.top;
|
||||||
canvas.drawRoundRect(boundingBox.left, startY, boundingBox.right, boundingBox.bottom, radius, radius, paint);
|
canvas.drawRoundRect(boundingBox.left, startY, boundingBox.right, boundingBox.bottom, radius, radius, paint);
|
||||||
drawIcon(canvas, cx, startY + elementSize * 0.5f, iconSize, iconSize, 9);
|
drawIcon(canvas, cx, startY + elementSize * 0.5f, minSize, minSize, 9);
|
||||||
startY += elementSize;
|
startY += elementSize;
|
||||||
|
|
||||||
for (byte i = 0; i < bindings.length; i++) {
|
for (byte i = 0; i < bindings.length; i++) {
|
||||||
@@ -442,7 +468,7 @@ public class ControlElement {
|
|||||||
paint.setStyle(Paint.Style.STROKE);
|
paint.setStyle(Paint.Style.STROKE);
|
||||||
paint.setColor(oldColor);
|
paint.setColor(oldColor);
|
||||||
canvas.drawLine(lineLeft, startY, lineRight, startY, paint);
|
canvas.drawLine(lineLeft, startY, lineRight, startY, paint);
|
||||||
drawIcon(canvas, cx, startY + elementSize * 0.5f, iconSize, iconSize, 11);
|
drawIcon(canvas, cx, startY + elementSize * 0.5f, minSize, minSize, 11);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -536,7 +562,7 @@ public class ControlElement {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindings[0] = bindings[1] = bindings[2] = bindings[3] = Binding.NONE;
|
Arrays.fill(bindings, Binding.NONE);
|
||||||
bindings[currentElementIndex-1] = binding;
|
bindings[currentElementIndex-1] = binding;
|
||||||
states[currentElementIndex-1] = true;
|
states[currentElementIndex-1] = true;
|
||||||
inputControlsView.handleInputEvent(binding, true);
|
inputControlsView.handleInputEvent(binding, true);
|
||||||
@@ -551,62 +577,51 @@ public class ControlElement {
|
|||||||
public boolean handleTouchMove(int pointerId, float x, float y) {
|
public boolean handleTouchMove(int pointerId, float x, float y) {
|
||||||
if (pointerId == currentPointerId && (type == ControlElement.Type.D_PAD || type == Type.STICK)) {
|
if (pointerId == currentPointerId && (type == ControlElement.Type.D_PAD || type == Type.STICK)) {
|
||||||
Rect boundingBox = getBoundingBox();
|
Rect boundingBox = getBoundingBox();
|
||||||
x -= boundingBox.left;
|
|
||||||
y -= boundingBox.top;
|
|
||||||
|
|
||||||
float radius = boundingBox.width() * 0.5f;
|
float radius = boundingBox.width() * 0.5f;
|
||||||
float distance = Mathf.lengthSq(radius - x, radius - y);
|
float localX = x - boundingBox.left;
|
||||||
|
float localY = y - boundingBox.top;
|
||||||
|
float offsetX = localX - radius;
|
||||||
|
float offsetY = localY - radius;
|
||||||
|
|
||||||
|
float distance = Mathf.lengthSq(radius - localX, radius - localY);
|
||||||
if (distance > radius * radius) {
|
if (distance > radius * radius) {
|
||||||
float angle = (float)Math.atan2(y - radius, x - radius);
|
float angle = (float)Math.atan2(offsetY, offsetX);
|
||||||
x = (float)((Math.cos(angle) * radius) + radius);
|
offsetX = (float)(Math.cos(angle) * radius);
|
||||||
y = (float)((Math.sin(angle) * radius) + radius);
|
offsetY = (float)(Math.sin(angle) * radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
float invWidth = 1.0f / boundingBox.width();
|
float deltaX = Mathf.clamp(offsetX / radius, -1, 1);
|
||||||
float deltaX = Mathf.clamp(-1 + 2 * ((radius - x) + radius) * invWidth, -1, 1) * -1;
|
float deltaY = Mathf.clamp(offsetY / radius, -1, 1);
|
||||||
float deltaY = Mathf.clamp(-1 + 2 * ((radius - y) + radius) * invWidth, -1, 1) * -1;
|
|
||||||
|
|
||||||
if (type == Type.STICK) {
|
if (type == Type.STICK) {
|
||||||
if (thumbstickPosition == null) thumbstickPosition = new PointF();
|
if (thumbstickPosition == null) thumbstickPosition = new PointF();
|
||||||
thumbstickPosition.x = boundingBox.left + deltaX * radius + radius;
|
thumbstickPosition.x = boundingBox.left + deltaX * radius + radius;
|
||||||
thumbstickPosition.y = boundingBox.top + deltaY * radius + radius;
|
thumbstickPosition.y = boundingBox.top + deltaY * radius + radius;
|
||||||
|
|
||||||
boolean[] states = {
|
|
||||||
deltaY <= -InputControlsView.JOYSTICK_DEAD_ZONE,
|
|
||||||
deltaX >= InputControlsView.JOYSTICK_DEAD_ZONE,
|
|
||||||
deltaY >= InputControlsView.JOYSTICK_DEAD_ZONE,
|
|
||||||
deltaX <= -InputControlsView.JOYSTICK_DEAD_ZONE
|
|
||||||
};
|
|
||||||
|
|
||||||
for (byte i = 0; i < 4; i++) {
|
for (byte i = 0; i < 4; i++) {
|
||||||
float value = i == 1 || i == 3 ? deltaX : deltaY;
|
float value = i == 1 || i == 3 ? deltaX : deltaY;
|
||||||
if (states[i]) {
|
value = Mathf.clamp(Math.max(0, Math.abs(value) - STICK_DEAD_ZONE) * Mathf.sign(value) * STICK_SENSITIVITY, -1, 1);
|
||||||
inputControlsView.handleInputEvent(getBindingAt(i), true, value);
|
inputControlsView.handleInputEvent(getBindingAt(i), true, value);
|
||||||
this.states[i] = true;
|
this.states[i] = true;
|
||||||
}
|
|
||||||
else {
|
|
||||||
inputControlsView.handleInputEvent(getBindingAt(i), false, value);
|
|
||||||
this.states[i] = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inputControlsView.invalidate();
|
inputControlsView.invalidate();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
boolean[] states = {
|
final boolean[] states = {deltaY <= -DPAD_DEAD_ZONE, deltaX >= DPAD_DEAD_ZONE, deltaY >= DPAD_DEAD_ZONE, deltaX <= -DPAD_DEAD_ZONE};
|
||||||
deltaY <= -InputControlsView.DPAD_DEAD_ZONE,
|
|
||||||
deltaX >= InputControlsView.DPAD_DEAD_ZONE,
|
|
||||||
deltaY >= InputControlsView.DPAD_DEAD_ZONE,
|
|
||||||
deltaX <= -InputControlsView.DPAD_DEAD_ZONE
|
|
||||||
};
|
|
||||||
|
|
||||||
for (byte i = 0; i < 4; i++) {
|
for (byte i = 0; i < 4; i++) {
|
||||||
if (states[i] && !this.states[i]) {
|
Binding binding = getBindingAt(i);
|
||||||
inputControlsView.handleInputEvent(getBindingAt(i), true);
|
if (binding.isGamepad()) {
|
||||||
|
inputControlsView.handleInputEvent(binding, states[i]);
|
||||||
|
this.states[i] = states[i];
|
||||||
|
}
|
||||||
|
else if (states[i] && !this.states[i]) {
|
||||||
|
inputControlsView.handleInputEvent(binding, true);
|
||||||
this.states[i] = true;
|
this.states[i] = true;
|
||||||
}
|
}
|
||||||
else if (!states[i] && this.states[i]) {
|
else if (!states[i] && this.states[i]) {
|
||||||
inputControlsView.handleInputEvent(getBindingAt(i), false);
|
inputControlsView.handleInputEvent(binding, false);
|
||||||
this.states[i] = false;
|
this.states[i] = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -627,7 +642,7 @@ public class ControlElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (type == Type.RANGE_BUTTON || type == Type.D_PAD || type == Type.STICK) {
|
else if (type == Type.RANGE_BUTTON || type == Type.D_PAD || type == Type.STICK) {
|
||||||
for (byte i = 0; i < 4; i++) {
|
for (byte i = 0; i < states.length; i++) {
|
||||||
if (states[i]) inputControlsView.handleInputEvent(getBindingAt(i), false);
|
if (states[i]) inputControlsView.handleInputEvent(getBindingAt(i), false);
|
||||||
states[i] = false;
|
states[i] = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ public class ControlsProfile implements Comparable<ControlsProfile> {
|
|||||||
private final List<ControlElement> immutableElements = Collections.unmodifiableList(elements);
|
private final List<ControlElement> immutableElements = Collections.unmodifiableList(elements);
|
||||||
private boolean elementsLoaded = false;
|
private boolean elementsLoaded = false;
|
||||||
private boolean controllersLoaded = false;
|
private boolean controllersLoaded = false;
|
||||||
|
private boolean virtualGamepad = false;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
private GamepadState gamepadState;
|
||||||
|
|
||||||
public ControlsProfile(Context context, int id) {
|
public ControlsProfile(Context context, int id) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -49,9 +51,19 @@ public class ControlsProfile implements Comparable<ControlsProfile> {
|
|||||||
this.cursorSpeed = cursorSpeed;
|
this.cursorSpeed = cursorSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVirtualGamepad() {
|
||||||
|
return virtualGamepad;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GamepadState getGamepadState() {
|
||||||
|
if (gamepadState == null) gamepadState = new GamepadState();
|
||||||
|
return gamepadState;
|
||||||
|
}
|
||||||
|
|
||||||
public ExternalController addController(String id) {
|
public ExternalController addController(String id) {
|
||||||
ExternalController controller = getController(id);
|
ExternalController controller = getController(id);
|
||||||
if (controller == null) controllers.add(controller = ExternalController.getController(id));
|
if (controller == null) controllers.add(controller = ExternalController.getController(id));
|
||||||
|
controllersLoaded = true;
|
||||||
return controller;
|
return controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +107,7 @@ public class ControlsProfile implements Comparable<ControlsProfile> {
|
|||||||
data.put("id", id);
|
data.put("id", id);
|
||||||
data.put("name", name);
|
data.put("name", name);
|
||||||
data.put("cursorSpeed", Float.valueOf(cursorSpeed));
|
data.put("cursorSpeed", Float.valueOf(cursorSpeed));
|
||||||
|
if (virtualGamepad) data.put("virtualGamepad", true);
|
||||||
|
|
||||||
JSONArray elementsJSONArray = new JSONArray();
|
JSONArray elementsJSONArray = new JSONArray();
|
||||||
if (!elementsLoaded && file.isFile()) {
|
if (!elementsLoaded && file.isFile()) {
|
||||||
@@ -183,8 +196,9 @@ public class ControlsProfile implements Comparable<ControlsProfile> {
|
|||||||
public void loadElements(InputControlsView inputControlsView) {
|
public void loadElements(InputControlsView inputControlsView) {
|
||||||
elements.clear();
|
elements.clear();
|
||||||
elementsLoaded = false;
|
elementsLoaded = false;
|
||||||
|
virtualGamepad = false;
|
||||||
|
|
||||||
File file = getProfileFile(inputControlsView.getContext(), id);
|
File file = getProfileFile(context, id);
|
||||||
if (!file.isFile()) return;
|
if (!file.isFile()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -204,10 +218,15 @@ public class ControlsProfile implements Comparable<ControlsProfile> {
|
|||||||
if (elementJSONObject.has("range")) element.setRange(ControlElement.Range.valueOf(elementJSONObject.getString("range")));
|
if (elementJSONObject.has("range")) element.setRange(ControlElement.Range.valueOf(elementJSONObject.getString("range")));
|
||||||
if (elementJSONObject.has("orientation")) element.setOrientation((byte)elementJSONObject.getInt("orientation"));
|
if (elementJSONObject.has("orientation")) element.setOrientation((byte)elementJSONObject.getInt("orientation"));
|
||||||
|
|
||||||
|
boolean hasGamepadBinding = true;
|
||||||
JSONArray bindingsJSONArray = elementJSONObject.getJSONArray("bindings");
|
JSONArray bindingsJSONArray = elementJSONObject.getJSONArray("bindings");
|
||||||
for (int j = 0; j < bindingsJSONArray.length(); j++) {
|
for (int j = 0; j < bindingsJSONArray.length(); j++) {
|
||||||
|
Binding binding = Binding.fromString(bindingsJSONArray.getString(j));
|
||||||
element.setBindingAt(j, Binding.fromString(bindingsJSONArray.getString(j)));
|
element.setBindingAt(j, Binding.fromString(bindingsJSONArray.getString(j)));
|
||||||
|
if (!binding.isGamepad()) hasGamepadBinding = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!virtualGamepad && hasGamepadBinding) virtualGamepad = true;
|
||||||
elements.add(element);
|
elements.add(element);
|
||||||
}
|
}
|
||||||
elementsLoaded = true;
|
elementsLoaded = true;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.winlator.inputcontrols;
|
package com.winlator.inputcontrols;
|
||||||
|
|
||||||
import android.view.InputDevice;
|
import android.view.InputDevice;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
@@ -12,10 +13,23 @@ import org.json.JSONObject;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ExternalController {
|
public class ExternalController {
|
||||||
|
public static final byte IDX_BUTTON_A = 0;
|
||||||
|
public static final byte IDX_BUTTON_B = 1;
|
||||||
|
public static final byte IDX_BUTTON_X = 2;
|
||||||
|
public static final byte IDX_BUTTON_Y = 3;
|
||||||
|
public static final byte IDX_BUTTON_L1 = 4;
|
||||||
|
public static final byte IDX_BUTTON_R1 = 5;
|
||||||
|
public static final byte IDX_BUTTON_SELECT = 6;
|
||||||
|
public static final byte IDX_BUTTON_START = 7;
|
||||||
|
public static final byte IDX_BUTTON_L3 = 8;
|
||||||
|
public static final byte IDX_BUTTON_R3 = 9;
|
||||||
|
public static final byte IDX_BUTTON_L2 = 10;
|
||||||
|
public static final byte IDX_BUTTON_R2 = 11;
|
||||||
private String name;
|
private String name;
|
||||||
private String id;
|
private String id;
|
||||||
private int deviceId = -1;
|
private int deviceId = -1;
|
||||||
private final ArrayList<ExternalControllerBinding> controllerBindings = new ArrayList<>();
|
private final ArrayList<ExternalControllerBinding> controllerBindings = new ArrayList<>();
|
||||||
|
public final GamepadState state = new GamepadState();
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
@@ -104,11 +118,70 @@ public class ExternalController {
|
|||||||
return obj instanceof ExternalController ? ((ExternalController)obj).id.equals(this.id) : super.equals(obj);
|
return obj instanceof ExternalController ? ((ExternalController)obj).id.equals(this.id) : super.equals(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processJoystickInput(MotionEvent event, int historyPos) {
|
||||||
|
state.thumbLX = getCenteredAxis(event, MotionEvent.AXIS_X, historyPos);
|
||||||
|
state.thumbLY = getCenteredAxis(event, MotionEvent.AXIS_Y, historyPos);
|
||||||
|
state.thumbRX = getCenteredAxis(event, MotionEvent.AXIS_Z, historyPos);
|
||||||
|
state.thumbRY = getCenteredAxis(event, MotionEvent.AXIS_RZ, historyPos);
|
||||||
|
|
||||||
|
if (historyPos == -1) {
|
||||||
|
float axisX = getCenteredAxis(event, MotionEvent.AXIS_HAT_X, historyPos);
|
||||||
|
float axisY = getCenteredAxis(event, MotionEvent.AXIS_HAT_Y, historyPos);
|
||||||
|
|
||||||
|
state.dpad[0] = axisY == -1.0f && Math.abs(state.thumbLY) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
state.dpad[1] = axisX == 1.0f && Math.abs(state.thumbLX) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
state.dpad[2] = axisY == 1.0f && Math.abs(state.thumbLY) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
state.dpad[3] = axisX == -1.0f && Math.abs(state.thumbLX) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void processTriggerButton(MotionEvent event) {
|
||||||
|
state.setPressed(IDX_BUTTON_L2, event.getAxisValue(MotionEvent.AXIS_LTRIGGER) == 1.0f || event.getAxisValue(MotionEvent.AXIS_BRAKE) == 1.0f);
|
||||||
|
state.setPressed(IDX_BUTTON_R2, event.getAxisValue(MotionEvent.AXIS_RTRIGGER) == 1.0f || event.getAxisValue(MotionEvent.AXIS_GAS) == 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateStateFromMotionEvent(MotionEvent event) {
|
||||||
|
if (isJoystickDevice(event)) {
|
||||||
|
processTriggerButton(event);
|
||||||
|
int historySize = event.getHistorySize();
|
||||||
|
for (int i = 0; i < historySize; i++) processJoystickInput(event, i);
|
||||||
|
processJoystickInput(event, -1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateStateFromKeyEvent(KeyEvent event) {
|
||||||
|
boolean pressed = event.getAction() == KeyEvent.ACTION_DOWN;
|
||||||
|
int keyCode = event.getKeyCode();
|
||||||
|
int buttonIdx = getButtonIdxByKeyCode(keyCode);
|
||||||
|
if (buttonIdx != -1) {
|
||||||
|
state.setPressed(buttonIdx, pressed);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_DPAD_UP:
|
||||||
|
state.dpad[0] = pressed && Math.abs(state.thumbLY) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||||
|
state.dpad[1] = pressed && Math.abs(state.thumbLX) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||||
|
state.dpad[2] = pressed && Math.abs(state.thumbLY) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
return true;
|
||||||
|
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||||
|
state.dpad[3] = pressed && Math.abs(state.thumbLX) < ControlElement.STICK_DEAD_ZONE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public static ArrayList<ExternalController> getControllers() {
|
public static ArrayList<ExternalController> getControllers() {
|
||||||
ArrayList<ExternalController> controllers = new ArrayList<>();
|
ArrayList<ExternalController> controllers = new ArrayList<>();
|
||||||
for (int deviceId : InputDevice.getDeviceIds()) {
|
for (int deviceId : InputDevice.getDeviceIds()) {
|
||||||
InputDevice device = InputDevice.getDevice(deviceId);
|
InputDevice device = InputDevice.getDevice(deviceId);
|
||||||
if (device != null && !device.isVirtual() && isGameController(device)) {
|
if (isGameController(device)) {
|
||||||
ExternalController controller = new ExternalController();
|
ExternalController controller = new ExternalController();
|
||||||
controller.setId(device.getDescriptor());
|
controller.setId(device.getDescriptor());
|
||||||
controller.setName(device.getName());
|
controller.setName(device.getName());
|
||||||
@@ -123,19 +196,42 @@ public class ExternalController {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ExternalController getController(int deviceId) {
|
||||||
|
for (int deviceId2 : InputDevice.getDeviceIds()) {
|
||||||
|
if (deviceId2 == deviceId || deviceId == 0) {
|
||||||
|
InputDevice device = InputDevice.getDevice(deviceId2);
|
||||||
|
if (isGameController(device)) {
|
||||||
|
ExternalController controller = new ExternalController();
|
||||||
|
controller.setId(device.getDescriptor());
|
||||||
|
controller.setName(device.getName());
|
||||||
|
controller.deviceId = deviceId2;
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isGameController(InputDevice device) {
|
public static boolean isGameController(InputDevice device) {
|
||||||
|
if (device == null) return false;
|
||||||
int sources = device.getSources();
|
int sources = device.getSources();
|
||||||
return (sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
|
return !device.isVirtual() && ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
|
||||||
(sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK;
|
(sources & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float getCenteredAxis(MotionEvent event, int axis, int historyPos) {
|
public static float getCenteredAxis(MotionEvent event, int axis, int historyPos) {
|
||||||
InputDevice device = event.getDevice();
|
if (axis == MotionEvent.AXIS_HAT_X || axis == MotionEvent.AXIS_HAT_Y) {
|
||||||
InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource());
|
float value = event.getAxisValue(axis);
|
||||||
if (range != null) {
|
if (Math.abs(value) == 1.0f) return value;
|
||||||
float flat = range.getFlat();
|
}
|
||||||
float value = historyPos < 0 ? event.getAxisValue(axis) : event.getHistoricalAxisValue(axis, historyPos);
|
else {
|
||||||
if (Math.abs(value) > flat) return value;
|
InputDevice device = event.getDevice();
|
||||||
|
InputDevice.MotionRange range = device.getMotionRange(axis, event.getSource());
|
||||||
|
if (range != null) {
|
||||||
|
float flat = range.getFlat();
|
||||||
|
float value = historyPos < 0 ? event.getAxisValue(axis) : event.getHistoricalAxisValue(axis, historyPos);
|
||||||
|
if (Math.abs(value) > flat) return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -144,7 +240,65 @@ public class ExternalController {
|
|||||||
return (event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE;
|
return (event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK && event.getAction() == MotionEvent.ACTION_MOVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDPadDevice(MotionEvent event) {
|
public static int getButtonIdxByKeyCode(int keyCode) {
|
||||||
return (event.getSource() & InputDevice.SOURCE_DPAD) != InputDevice.SOURCE_DPAD && event.getAction() == MotionEvent.ACTION_MOVE;
|
switch (keyCode) {
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_A:
|
||||||
|
return IDX_BUTTON_A;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_B:
|
||||||
|
return IDX_BUTTON_B;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_X:
|
||||||
|
return IDX_BUTTON_X;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||||
|
return IDX_BUTTON_Y;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||||
|
return IDX_BUTTON_L1;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||||
|
return IDX_BUTTON_R1;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||||
|
return IDX_BUTTON_SELECT;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_START:
|
||||||
|
return IDX_BUTTON_START;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||||
|
return IDX_BUTTON_L3;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||||
|
return IDX_BUTTON_R3;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||||
|
return IDX_BUTTON_L2;
|
||||||
|
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||||
|
return IDX_BUTTON_R2;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getButtonIdxByName(String name) {
|
||||||
|
switch (name) {
|
||||||
|
case "A":
|
||||||
|
return IDX_BUTTON_A;
|
||||||
|
case "B":
|
||||||
|
return IDX_BUTTON_B;
|
||||||
|
case "X":
|
||||||
|
return IDX_BUTTON_X;
|
||||||
|
case "Y":
|
||||||
|
return IDX_BUTTON_Y;
|
||||||
|
case "L1":
|
||||||
|
return IDX_BUTTON_L1;
|
||||||
|
case "R1":
|
||||||
|
return IDX_BUTTON_R1;
|
||||||
|
case "SELECT":
|
||||||
|
return IDX_BUTTON_SELECT;
|
||||||
|
case "START":
|
||||||
|
return IDX_BUTTON_START;
|
||||||
|
case "L3":
|
||||||
|
return IDX_BUTTON_L3;
|
||||||
|
case "R3":
|
||||||
|
return IDX_BUTTON_R3;
|
||||||
|
case "L2":
|
||||||
|
return IDX_BUTTON_L2;
|
||||||
|
case "R2":
|
||||||
|
return IDX_BUTTON_R2;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,8 +17,6 @@ public class ExternalControllerBinding {
|
|||||||
public static final byte AXIS_Z_POSITIVE = -6;
|
public static final byte AXIS_Z_POSITIVE = -6;
|
||||||
public static final byte AXIS_RZ_NEGATIVE = -7;
|
public static final byte AXIS_RZ_NEGATIVE = -7;
|
||||||
public static final byte AXIS_RZ_POSITIVE = -8;
|
public static final byte AXIS_RZ_POSITIVE = -8;
|
||||||
public static final byte AXIS_LTRIGGER = -9;
|
|
||||||
public static final byte AXIS_RTRIGGER = -10;
|
|
||||||
private short keyCode;
|
private short keyCode;
|
||||||
private Binding binding = Binding.NONE;
|
private Binding binding = Binding.NONE;
|
||||||
|
|
||||||
@@ -70,10 +68,6 @@ public class ExternalControllerBinding {
|
|||||||
return "AXIS RZ-";
|
return "AXIS RZ-";
|
||||||
case AXIS_RZ_POSITIVE:
|
case AXIS_RZ_POSITIVE:
|
||||||
return "AXIS RZ+";
|
return "AXIS RZ+";
|
||||||
case AXIS_LTRIGGER:
|
|
||||||
return "L TRIGGER";
|
|
||||||
case AXIS_RTRIGGER:
|
|
||||||
return "R TRIGGER";
|
|
||||||
default:
|
default:
|
||||||
return KeyEvent.keyCodeToString(keyCode).replace("KEYCODE_", "").replace("_", " ");
|
return KeyEvent.keyCodeToString(keyCode).replace("KEYCODE_", "").replace("_", " ");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package com.winlator.inputcontrols;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
|
||||||
|
public class GamepadState {
|
||||||
|
public float thumbLX = 0;
|
||||||
|
public float thumbLY = 0;
|
||||||
|
public float thumbRX = 0;
|
||||||
|
public float thumbRY = 0;
|
||||||
|
public final boolean[] dpad = new boolean[4];
|
||||||
|
public short buttons = 0;
|
||||||
|
|
||||||
|
public byte getPovHat() {
|
||||||
|
byte povHat = -1;
|
||||||
|
if (dpad[0] && dpad[1]) povHat = 1;
|
||||||
|
else if (dpad[1] && dpad[2]) povHat = 3;
|
||||||
|
else if (dpad[2] && dpad[3]) povHat = 5;
|
||||||
|
else if (dpad[3] && dpad[0]) povHat = 7;
|
||||||
|
else if (dpad[0]) povHat = 0;
|
||||||
|
else if (dpad[1]) povHat = 2;
|
||||||
|
else if (dpad[2]) povHat = 4;
|
||||||
|
else if (dpad[3]) povHat = 6;
|
||||||
|
return povHat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeTo(ByteBuffer buffer) {
|
||||||
|
buffer.putShort(buttons);
|
||||||
|
buffer.put(getPovHat());
|
||||||
|
buffer.putShort((short)(thumbLX * Short.MAX_VALUE));
|
||||||
|
buffer.putShort((short)(thumbLY * Short.MAX_VALUE));
|
||||||
|
buffer.putShort((short)(thumbRX * Short.MAX_VALUE));
|
||||||
|
buffer.putShort((short)(thumbRY * Short.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] toByteArray() {
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(11).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
writeTo(buffer);
|
||||||
|
return buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPressed(int buttonIdx, boolean pressed) {
|
||||||
|
int flag = 1<<buttonIdx;
|
||||||
|
if (pressed) {
|
||||||
|
buttons |= flag;
|
||||||
|
}
|
||||||
|
else buttons &= ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPressed(int buttonIdx) {
|
||||||
|
return (buttons & (1<<buttonIdx)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getDPadX() {
|
||||||
|
return (byte)(dpad[1] ? 1 : (dpad[3] ? -1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getDPadY() {
|
||||||
|
return (byte)(dpad[0] ? -1 : (dpad[2] ? 1 : 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,6 +23,7 @@ import com.winlator.inputcontrols.ControlElement;
|
|||||||
import com.winlator.inputcontrols.ControlsProfile;
|
import com.winlator.inputcontrols.ControlsProfile;
|
||||||
import com.winlator.inputcontrols.ExternalController;
|
import com.winlator.inputcontrols.ExternalController;
|
||||||
import com.winlator.inputcontrols.ExternalControllerBinding;
|
import com.winlator.inputcontrols.ExternalControllerBinding;
|
||||||
|
import com.winlator.inputcontrols.GamepadState;
|
||||||
import com.winlator.math.Mathf;
|
import com.winlator.math.Mathf;
|
||||||
import com.winlator.xserver.Pointer;
|
import com.winlator.xserver.Pointer;
|
||||||
import com.winlator.xserver.XServer;
|
import com.winlator.xserver.XServer;
|
||||||
@@ -34,8 +35,6 @@ import java.util.TimerTask;
|
|||||||
|
|
||||||
public class InputControlsView extends View {
|
public class InputControlsView extends View {
|
||||||
public static final float DEFAULT_OVERLAY_OPACITY = 0.4f;
|
public static final float DEFAULT_OVERLAY_OPACITY = 0.4f;
|
||||||
public static final float JOYSTICK_DEAD_ZONE = 0.15f;
|
|
||||||
public static final float DPAD_DEAD_ZONE = 0.3f;
|
|
||||||
private boolean editMode = false;
|
private boolean editMode = false;
|
||||||
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
private final Path path = new Path();
|
private final Path path = new Path();
|
||||||
@@ -241,6 +240,10 @@ public class InputControlsView extends View {
|
|||||||
this.touchpadView = touchpadView;
|
this.touchpadView = touchpadView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public XServer getXServer() {
|
||||||
|
return xServer;
|
||||||
|
}
|
||||||
|
|
||||||
public void setXServer(XServer xServer) {
|
public void setXServer(XServer xServer) {
|
||||||
this.xServer = xServer;
|
this.xServer = xServer;
|
||||||
createMouseMoveTimer();
|
createMouseMoveTimer();
|
||||||
@@ -267,58 +270,41 @@ public class InputControlsView extends View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processJoystickInput(MotionEvent event, ExternalController controller, int historyPos) {
|
private void processJoystickInput(ExternalController controller) {
|
||||||
ExternalControllerBinding controllerBinding;
|
ExternalControllerBinding controllerBinding;
|
||||||
final int[] axes = {MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y};
|
final int[] axes = {MotionEvent.AXIS_X, MotionEvent.AXIS_Y, MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, MotionEvent.AXIS_HAT_X, MotionEvent.AXIS_HAT_Y};
|
||||||
for (int axis : axes) {
|
final float[] values = {controller.state.thumbLX, controller.state.thumbLY, controller.state.thumbRX, controller.state.thumbRY, controller.state.getDPadX(), controller.state.getDPadY()};
|
||||||
float value = ExternalController.getCenteredAxis(event, axis, historyPos);
|
|
||||||
if (Math.abs(value) > JOYSTICK_DEAD_ZONE) {
|
for (byte i = 0; i < axes.length; i++) {
|
||||||
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axis, Mathf.sign(value)));
|
if (Math.abs(values[i]) > ControlElement.STICK_DEAD_ZONE) {
|
||||||
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), true, value);
|
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axes[i], Mathf.sign(values[i])));
|
||||||
|
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), true, values[i]);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axis, (byte) 1));
|
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axes[i], (byte) 1));
|
||||||
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), false, value);
|
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), false, values[i]);
|
||||||
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axis, (byte)-1));
|
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.getKeyCodeForAxis(axes[i], (byte)-1));
|
||||||
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), false, value);
|
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), false, values[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processTriggerButton(MotionEvent event, ExternalController controller) {
|
|
||||||
ExternalControllerBinding controllerBinding;
|
|
||||||
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.AXIS_LTRIGGER);
|
|
||||||
if (controllerBinding != null) {
|
|
||||||
boolean pressed = event.getAxisValue(MotionEvent.AXIS_LTRIGGER) == 1 || event.getAxisValue(MotionEvent.AXIS_BRAKE) == 1;
|
|
||||||
handleInputEvent(controllerBinding.getBinding(), pressed);
|
|
||||||
}
|
|
||||||
|
|
||||||
controllerBinding = controller.getControllerBinding(ExternalControllerBinding.AXIS_RTRIGGER);
|
|
||||||
if (controllerBinding != null) {
|
|
||||||
boolean pressed = event.getAxisValue(MotionEvent.AXIS_RTRIGGER) == 1 || event.getAxisValue(MotionEvent.AXIS_GAS) == 1;
|
|
||||||
handleInputEvent(controllerBinding.getBinding(), pressed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||||
if (!editMode && profile != null) {
|
if (!editMode && profile != null) {
|
||||||
ExternalController controller = profile.getController(event.getDeviceId());
|
ExternalController controller = profile.getController(event.getDeviceId());
|
||||||
if (controller != null) {
|
if (controller != null && controller.updateStateFromMotionEvent(event)) {
|
||||||
if (ExternalController.isDPadDevice(event)) {
|
ExternalControllerBinding controllerBinding;
|
||||||
processTriggerButton(event, controller);
|
controllerBinding = controller.getControllerBinding(KeyEvent.KEYCODE_BUTTON_L2);
|
||||||
processJoystickInput(event, controller, -1);
|
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), controller.state.isPressed(ExternalController.IDX_BUTTON_L2));
|
||||||
return true;
|
|
||||||
}
|
controllerBinding = controller.getControllerBinding(KeyEvent.KEYCODE_BUTTON_R2);
|
||||||
else if (ExternalController.isJoystickDevice(event)) {
|
if (controllerBinding != null) handleInputEvent(controllerBinding.getBinding(), controller.state.isPressed(ExternalController.IDX_BUTTON_R2));
|
||||||
int historySize = event.getHistorySize();
|
|
||||||
for (int i = 0; i < historySize; i++) processJoystickInput(event, controller, i);
|
processJoystickInput(controller);
|
||||||
processJoystickInput(event, controller, -1);
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onGenericMotionEvent(event);
|
return super.onGenericMotionEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -395,9 +381,6 @@ public class InputControlsView extends View {
|
|||||||
}
|
}
|
||||||
case MotionEvent.ACTION_UP:
|
case MotionEvent.ACTION_UP:
|
||||||
case MotionEvent.ACTION_POINTER_UP:
|
case MotionEvent.ACTION_POINTER_UP:
|
||||||
for (ControlElement element : profile.getElements()) if (element.handleTouchUp(pointerId)) handled = true;
|
|
||||||
if (!handled) touchpadView.onTouchEvent(event);
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
case MotionEvent.ACTION_CANCEL:
|
||||||
for (ControlElement element : profile.getElements()) if (element.handleTouchUp(pointerId)) handled = true;
|
for (ControlElement element : profile.getElements()) if (element.handleTouchUp(pointerId)) handled = true;
|
||||||
if (!handled) touchpadView.onTouchEvent(event);
|
if (!handled) touchpadView.onTouchEvent(event);
|
||||||
@@ -433,36 +416,54 @@ public class InputControlsView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void handleInputEvent(Binding binding, boolean isActionDown, float offset) {
|
public void handleInputEvent(Binding binding, boolean isActionDown, float offset) {
|
||||||
if (isActionDown) {
|
if (binding.isGamepad()) {
|
||||||
if (binding == Binding.MOUSE_MOVE_LEFT || binding == Binding.MOUSE_MOVE_RIGHT) {
|
GamepadState state = profile.getGamepadState();
|
||||||
mouseMoveOffset.x = offset != 0 ? offset : (binding == Binding.MOUSE_MOVE_LEFT ? -1 : 1);
|
|
||||||
createMouseMoveTimer();
|
int buttonIdx = binding.ordinal() - Binding.GAMEPAD_BUTTON_A.ordinal();
|
||||||
|
if (buttonIdx <= 11) {
|
||||||
|
state.setPressed(buttonIdx, isActionDown);
|
||||||
|
if (xServer != null) xServer.getWinHandler().saveGamepadState(state);
|
||||||
}
|
}
|
||||||
else if (binding == Binding.MOUSE_MOVE_DOWN || binding == Binding.MOUSE_MOVE_UP) {
|
else if (binding == Binding.GAMEPAD_LEFT_THUMB_UP || binding == Binding.GAMEPAD_LEFT_THUMB_DOWN) {
|
||||||
mouseMoveOffset.y = offset != 0 ? offset : (binding == Binding.MOUSE_MOVE_UP ? -1 : 1);
|
state.thumbLY = isActionDown ? offset : 0;
|
||||||
createMouseMoveTimer();
|
|
||||||
}
|
}
|
||||||
else {
|
else if (binding == Binding.GAMEPAD_LEFT_THUMB_LEFT || binding == Binding.GAMEPAD_LEFT_THUMB_RIGHT) {
|
||||||
Pointer.Button pointerButton = binding.getPointerButton();
|
state.thumbLX = isActionDown ? offset : 0;
|
||||||
if (pointerButton != null) {
|
}
|
||||||
xServer.injectPointerButtonPress(pointerButton);
|
else if (binding == Binding.GAMEPAD_RIGHT_THUMB_UP || binding == Binding.GAMEPAD_RIGHT_THUMB_DOWN) {
|
||||||
}
|
state.thumbRY = isActionDown ? offset : 0;
|
||||||
else xServer.injectKeyPress(binding.keycode);
|
}
|
||||||
|
else if (binding == Binding.GAMEPAD_RIGHT_THUMB_LEFT || binding == Binding.GAMEPAD_RIGHT_THUMB_RIGHT) {
|
||||||
|
state.thumbRX = isActionDown ? offset : 0;
|
||||||
|
}
|
||||||
|
else if (binding == Binding.GAMEPAD_DPAD_UP || binding == Binding.GAMEPAD_DPAD_RIGHT ||
|
||||||
|
binding == Binding.GAMEPAD_DPAD_DOWN || binding == Binding.GAMEPAD_DPAD_LEFT) {
|
||||||
|
state.dpad[binding.ordinal() - Binding.GAMEPAD_DPAD_UP.ordinal()] = isActionDown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (binding == Binding.MOUSE_MOVE_LEFT || binding == Binding.MOUSE_MOVE_RIGHT) {
|
if (binding == Binding.MOUSE_MOVE_LEFT || binding == Binding.MOUSE_MOVE_RIGHT) {
|
||||||
if (Math.abs(offset) < InputControlsView.JOYSTICK_DEAD_ZONE) mouseMoveOffset.x = 0;
|
mouseMoveOffset.x = isActionDown ? offset : 0;
|
||||||
|
createMouseMoveTimer();
|
||||||
}
|
}
|
||||||
else if (binding == Binding.MOUSE_MOVE_UP || binding == Binding.MOUSE_MOVE_DOWN) {
|
else if (binding == Binding.MOUSE_MOVE_DOWN || binding == Binding.MOUSE_MOVE_UP) {
|
||||||
if (Math.abs(offset) < InputControlsView.JOYSTICK_DEAD_ZONE) mouseMoveOffset.y = 0;
|
mouseMoveOffset.y = isActionDown ? offset : 0;
|
||||||
|
createMouseMoveTimer();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Pointer.Button pointerButton = binding.getPointerButton();
|
Pointer.Button pointerButton = binding.getPointerButton();
|
||||||
if (pointerButton != null) {
|
if (isActionDown) {
|
||||||
xServer.injectPointerButtonRelease(pointerButton);
|
if (pointerButton != null) {
|
||||||
|
xServer.injectPointerButtonPress(pointerButton);
|
||||||
|
}
|
||||||
|
else xServer.injectKeyPress(binding.keycode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (pointerButton != null) {
|
||||||
|
xServer.injectPointerButtonRelease(pointerButton);
|
||||||
|
}
|
||||||
|
else xServer.injectKeyRelease(binding.keycode);
|
||||||
}
|
}
|
||||||
else xServer.injectKeyRelease(binding.keycode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
package com.winlator.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import com.winlator.R;
|
||||||
|
import com.winlator.math.Mathf;
|
||||||
|
|
||||||
|
public class NumberPicker extends FrameLayout implements View.OnTouchListener {
|
||||||
|
private int value = 0;
|
||||||
|
private int minValue = 0;
|
||||||
|
private int maxValue = 100;
|
||||||
|
private int step = 1;
|
||||||
|
private final EditText editText;
|
||||||
|
private OnValueChangeListener onValueChangeListener;
|
||||||
|
|
||||||
|
public interface OnValueChangeListener {
|
||||||
|
void onValueChange(NumberPicker numberPicker, int value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberPicker(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberPicker(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberPicker(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
|
||||||
|
LayoutInflater.from(context).inflate(R.layout.number_picker, this, true);
|
||||||
|
editText = findViewById(R.id.EditText);
|
||||||
|
findViewById(R.id.BTDecrement).setOnTouchListener(this);
|
||||||
|
findViewById(R.id.BTIncrement).setOnTouchListener(this);
|
||||||
|
|
||||||
|
if (attrs != null) {
|
||||||
|
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.NumberPicker);
|
||||||
|
minValue = ta.getInt(R.styleable.NumberPicker_minValue, minValue);
|
||||||
|
maxValue = ta.getInt(R.styleable.NumberPicker_maxValue, maxValue);
|
||||||
|
setStep(ta.getInt(R.styleable.NumberPicker_step, step));
|
||||||
|
int value = ta.getInt(R.styleable.NumberPicker_value, 0);
|
||||||
|
ta.recycle();
|
||||||
|
setValue(value);
|
||||||
|
}
|
||||||
|
else setStep(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(int value) {
|
||||||
|
this.value = Mathf.clamp(value, minValue, maxValue);
|
||||||
|
editText.setText(String.valueOf(this.value));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMinValue() {
|
||||||
|
return minValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinValue(int minValue) {
|
||||||
|
this.minValue = minValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getMaxValue() {
|
||||||
|
return maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMaxValue(int maxValue) {
|
||||||
|
this.maxValue = maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increment() {
|
||||||
|
setValue(value+step);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decrement() {
|
||||||
|
setValue(value-step);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStep() {
|
||||||
|
return step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStep(int step) {
|
||||||
|
this.step = step;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OnValueChangeListener getOnValueChangeListener() {
|
||||||
|
return onValueChangeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) {
|
||||||
|
this.onValueChangeListener = onValueChangeListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onButtonClick(View v) {
|
||||||
|
if (!isEnabled()) return;
|
||||||
|
|
||||||
|
int id = v.getId();
|
||||||
|
if (id == R.id.BTIncrement) {
|
||||||
|
increment();
|
||||||
|
}
|
||||||
|
else if (id == R.id.BTDecrement) {
|
||||||
|
decrement();
|
||||||
|
}
|
||||||
|
if (onValueChangeListener != null) {
|
||||||
|
onValueChangeListener.onValueChange(NumberPicker.this, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onTouch(final View v, MotionEvent event) {
|
||||||
|
int action = event.getAction();
|
||||||
|
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
|
||||||
|
onButtonClick(v);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
package com.winlator.winhandler;
|
||||||
|
|
||||||
|
public interface OnGetProcessInfoListener {
|
||||||
|
void onGetProcessInfo(int index, int count, ProcessInfo processInfo);
|
||||||
|
}
|
||||||
@@ -2,9 +2,14 @@ package com.winlator.winhandler;
|
|||||||
|
|
||||||
abstract class RequestCodes {
|
abstract class RequestCodes {
|
||||||
public static final byte EXIT = 0;
|
public static final byte EXIT = 0;
|
||||||
public static final byte EXEC = 1;
|
public static final byte INIT = 1;
|
||||||
public static final byte KILL_PROCESS = 2;
|
public static final byte EXEC = 2;
|
||||||
public static final byte GET_PROCESSES = 3;
|
public static final byte KILL_PROCESS = 3;
|
||||||
public static final byte SET_PROCESS_AFFINITY = 4;
|
public static final byte LIST_PROCESSES = 4;
|
||||||
public static final byte MOUSE_EVENT = 5;
|
public static final byte GET_PROCESS = 5;
|
||||||
}
|
public static final byte SET_PROCESS_AFFINITY = 6;
|
||||||
|
public static final byte MOUSE_EVENT = 7;
|
||||||
|
public static final byte GET_GAMEPAD = 8;
|
||||||
|
public static final byte GET_GAMEPAD_STATE = 9;
|
||||||
|
public static final byte RELEASE_GAMEPAD = 10;
|
||||||
|
}
|
||||||
@@ -19,10 +19,11 @@ import com.winlator.contentdialog.ContentDialog;
|
|||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
public class TaskManagerDialog extends ContentDialog {
|
public class TaskManagerDialog extends ContentDialog implements OnGetProcessInfoListener {
|
||||||
private final XServerDisplayActivity activity;
|
private final XServerDisplayActivity activity;
|
||||||
private final LayoutInflater inflater;
|
private final LayoutInflater inflater;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
public TaskManagerDialog(XServerDisplayActivity activity) {
|
public TaskManagerDialog(XServerDisplayActivity activity) {
|
||||||
super(activity, R.layout.task_manager_dialog);
|
super(activity, R.layout.task_manager_dialog);
|
||||||
@@ -43,32 +44,17 @@ public class TaskManagerDialog extends ContentDialog {
|
|||||||
timer.cancel();
|
timer.cancel();
|
||||||
timer = null;
|
timer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activity.getWinHandler().setOnGetProcessInfoListener(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
inflater = LayoutInflater.from(activity);
|
inflater = LayoutInflater.from(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update() {
|
private void update() {
|
||||||
WinHandler winHandler = activity.getWinHandler();
|
synchronized (lock) {
|
||||||
winHandler.getProcesses((processes) -> activity.runOnUiThread(() -> {
|
activity.getWinHandler().listProcesses();
|
||||||
final LinearLayout container = findViewById(R.id.LLProcessList);
|
}
|
||||||
container.removeAllViews();
|
|
||||||
setBottomBarText(activity.getString(R.string.processes)+": "+processes.size());
|
|
||||||
|
|
||||||
if (!processes.isEmpty()) {
|
|
||||||
findViewById(R.id.TVEmptyText).setVisibility(View.GONE);
|
|
||||||
|
|
||||||
for (final ProcessInfo processInfo : processes) {
|
|
||||||
View itemView = inflater.inflate(R.layout.process_info_list_item, container, false);
|
|
||||||
((TextView)itemView.findViewById(R.id.TVName)).setText(processInfo.name);
|
|
||||||
((TextView)itemView.findViewById(R.id.TVPID)).setText(String.valueOf(processInfo.pid));
|
|
||||||
((TextView)itemView.findViewById(R.id.TVMemoryUsage)).setText(processInfo.getFormattedMemoryUsage());
|
|
||||||
itemView.findViewById(R.id.BTMenu).setOnClickListener((v) -> showListItemMenu(v, processInfo));
|
|
||||||
container.addView(itemView);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else findViewById(R.id.TVEmptyText).setVisibility(View.VISIBLE);
|
|
||||||
}));
|
|
||||||
|
|
||||||
LinearLayout tableHead = findViewById(R.id.LLTableHead);
|
LinearLayout tableHead = findViewById(R.id.LLTableHead);
|
||||||
TextView tvMemoryInfo = (TextView)tableHead.getChildAt(2);
|
TextView tvMemoryInfo = (TextView)tableHead.getChildAt(2);
|
||||||
@@ -120,13 +106,45 @@ public class TaskManagerDialog extends ContentDialog {
|
|||||||
@Override
|
@Override
|
||||||
public void show() {
|
public void show() {
|
||||||
update();
|
update();
|
||||||
|
activity.getWinHandler().setOnGetProcessInfoListener(this);
|
||||||
|
|
||||||
timer = new Timer();
|
timer = new Timer();
|
||||||
timer.schedule(new TimerTask() {
|
timer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
update();
|
activity.runOnUiThread(TaskManagerDialog.this::update);
|
||||||
}
|
}
|
||||||
}, 0, 1000);
|
}, 0, 1000);
|
||||||
super.show();
|
super.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onGetProcessInfo(int index, int numProcesses, ProcessInfo processInfo) {
|
||||||
|
activity.runOnUiThread(() -> {
|
||||||
|
synchronized (lock) {
|
||||||
|
final LinearLayout container = findViewById(R.id.LLProcessList);
|
||||||
|
setBottomBarText(activity.getString(R.string.processes)+": " + numProcesses);
|
||||||
|
|
||||||
|
if (numProcesses == 0) {
|
||||||
|
container.removeAllViews();
|
||||||
|
findViewById(R.id.TVEmptyText).setVisibility(View.VISIBLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
findViewById(R.id.TVEmptyText).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
int childCount = container.getChildCount();
|
||||||
|
View itemView = index < childCount ? container.getChildAt(index) : inflater.inflate(R.layout.process_info_list_item, container, false);
|
||||||
|
((TextView)itemView.findViewById(R.id.TVName)).setText(processInfo.name);
|
||||||
|
((TextView)itemView.findViewById(R.id.TVPID)).setText(String.valueOf(processInfo.pid));
|
||||||
|
((TextView)itemView.findViewById(R.id.TVMemoryUsage)).setText(processInfo.getFormattedMemoryUsage());
|
||||||
|
itemView.findViewById(R.id.BTMenu).setOnClickListener((v) -> showListItemMenu(v, processInfo));
|
||||||
|
if (index >= childCount) container.addView(itemView);
|
||||||
|
|
||||||
|
if (index == numProcesses-1 && childCount > numProcesses) {
|
||||||
|
for (int i = numProcesses; i < childCount; i++) container.removeViewAt(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,55 @@
|
|||||||
package com.winlator.winhandler;
|
package com.winlator.winhandler;
|
||||||
|
|
||||||
import com.winlator.core.Callback;
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import com.winlator.XServerDisplayActivity;
|
||||||
import com.winlator.core.StringUtils;
|
import com.winlator.core.StringUtils;
|
||||||
|
import com.winlator.inputcontrols.ControlsProfile;
|
||||||
|
import com.winlator.inputcontrols.ExternalController;
|
||||||
|
import com.winlator.inputcontrols.GamepadState;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.net.DatagramPacket;
|
||||||
import java.io.OutputStream;
|
import java.net.DatagramSocket;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class WinHandler {
|
public class WinHandler {
|
||||||
private static final short DEFAULT_PORT = 7946;
|
private static final short SERVER_PORT = 7947;
|
||||||
private Socket socket;
|
private static final short CLIENT_PORT = 7946;
|
||||||
private final Object lock = new Object();
|
public static final byte DINPUT_MAPPER_TYPE_STANDARD = 0;
|
||||||
private ByteBuffer data = ByteBuffer.allocate(128).order(ByteOrder.LITTLE_ENDIAN);
|
public static final byte DINPUT_MAPPER_TYPE_XINPUT = 1;
|
||||||
|
private DatagramSocket socket;
|
||||||
|
private final ByteBuffer sendData = ByteBuffer.allocate(64).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
private final ByteBuffer receiveData = ByteBuffer.allocate(64).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
private final DatagramPacket sendPacket = new DatagramPacket(sendData.array(), 64);
|
||||||
|
private final DatagramPacket receivePacket = new DatagramPacket(receiveData.array(), 64);
|
||||||
private final ArrayDeque<Runnable> actions = new ArrayDeque<>();
|
private final ArrayDeque<Runnable> actions = new ArrayDeque<>();
|
||||||
|
private boolean initReceived = false;
|
||||||
|
private boolean running = false;
|
||||||
|
private OnGetProcessInfoListener onGetProcessInfoListener;
|
||||||
|
private ExternalController currentController;
|
||||||
|
private final ArrayDeque<byte[]> gamepadStateQueue = new ArrayDeque<>();
|
||||||
|
private InetAddress localhost;
|
||||||
|
private byte dinputMapperType = DINPUT_MAPPER_TYPE_XINPUT;
|
||||||
|
private final XServerDisplayActivity activity;
|
||||||
|
|
||||||
private boolean sendData() {
|
public WinHandler(XServerDisplayActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean sendPacket(int port) {
|
||||||
try {
|
try {
|
||||||
int size = data.position();
|
int size = sendData.position();
|
||||||
if (size == 0) return true;
|
if (size == 0) return false;
|
||||||
OutputStream outStream = socket.getOutputStream();
|
sendPacket.setAddress(localhost);
|
||||||
outStream.write(data.array(), 0, size);
|
sendPacket.setPort(port);
|
||||||
|
socket.send(sendPacket);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (IOException e) {
|
catch (IOException e) {
|
||||||
@@ -37,158 +57,269 @@ public class WinHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean receiveData(int length) {
|
|
||||||
try {
|
|
||||||
while (length > data.capacity()) {
|
|
||||||
data = ByteBuffer.allocate(data.capacity() * 2).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
}
|
|
||||||
|
|
||||||
data.rewind();
|
|
||||||
InputStream inStream = socket.getInputStream();
|
|
||||||
int bytesRead = inStream.read(data.array(), 0, length);
|
|
||||||
if (bytesRead > 0) {
|
|
||||||
data.limit(length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exec(String command) {
|
public void exec(String command) {
|
||||||
command = command.trim();
|
synchronized (actions) {
|
||||||
if (command.isEmpty()) return;
|
command = command.trim();
|
||||||
String[] cmdList = command.split(" ", 2);
|
if (command.isEmpty()) return;
|
||||||
final String filename = cmdList[0];
|
String[] cmdList = command.split(" ", 2);
|
||||||
final String parameters = cmdList.length > 1 ? cmdList[1] : "";
|
final String filename = cmdList[0];
|
||||||
|
final String parameters = cmdList.length > 1 ? cmdList[1] : "";
|
||||||
|
|
||||||
synchronized (lock) {
|
|
||||||
actions.add(() -> {
|
actions.add(() -> {
|
||||||
byte[] filenameBytes = filename.getBytes();
|
byte[] filenameBytes = filename.getBytes();
|
||||||
byte[] parametersBytes = parameters.getBytes();
|
byte[] parametersBytes = parameters.getBytes();
|
||||||
|
|
||||||
data.rewind();
|
sendData.rewind();
|
||||||
data.put(RequestCodes.EXEC);
|
sendData.put(RequestCodes.EXEC);
|
||||||
data.putInt(filenameBytes.length + parametersBytes.length + 8);
|
sendData.putInt(filenameBytes.length + parametersBytes.length + 8);
|
||||||
data.putInt(filenameBytes.length);
|
sendData.putInt(filenameBytes.length);
|
||||||
data.putInt(parametersBytes.length);
|
sendData.putInt(parametersBytes.length);
|
||||||
data.put(filenameBytes);
|
sendData.put(filenameBytes);
|
||||||
data.put(parametersBytes);
|
sendData.put(parametersBytes);
|
||||||
sendData();
|
sendPacket(CLIENT_PORT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void killProcess(final String processName) {
|
public void killProcess(final String processName) {
|
||||||
synchronized (lock) {
|
synchronized (actions) {
|
||||||
actions.add(() -> {
|
actions.add(() -> {
|
||||||
data.rewind();
|
sendData.rewind();
|
||||||
data.put(RequestCodes.KILL_PROCESS);
|
sendData.put(RequestCodes.KILL_PROCESS);
|
||||||
byte[] bytes = processName.getBytes();
|
byte[] bytes = processName.getBytes();
|
||||||
data.putInt(bytes.length);
|
sendData.putInt(bytes.length);
|
||||||
data.put(bytes);
|
sendData.put(bytes);
|
||||||
sendData();
|
sendPacket(CLIENT_PORT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void getProcesses(final Callback<List<ProcessInfo>> callback) {
|
public void listProcesses() {
|
||||||
synchronized (lock) {
|
synchronized (actions) {
|
||||||
actions.add(() -> {
|
actions.add(() -> {
|
||||||
data.rewind();
|
sendData.rewind();
|
||||||
data.put(RequestCodes.GET_PROCESSES);
|
sendData.put(RequestCodes.LIST_PROCESSES);
|
||||||
data.putInt(0);
|
sendData.putInt(0);
|
||||||
|
|
||||||
if (sendData() && receiveData(5)) {
|
if (!sendPacket(CLIENT_PORT) && onGetProcessInfoListener != null) {
|
||||||
boolean success = data.get() == 1;
|
onGetProcessInfoListener.onGetProcessInfo(0, 0, null);
|
||||||
int responseLength = data.getInt();
|
|
||||||
if (success && receiveData(responseLength)) {
|
|
||||||
ArrayList<ProcessInfo> processes = new ArrayList<>();
|
|
||||||
while (data.position() < data.limit()) {
|
|
||||||
int numBytesBefore = data.position();
|
|
||||||
int pid = data.getInt();
|
|
||||||
long memoryUsage = data.getLong();
|
|
||||||
int affinityMask = data.getInt();
|
|
||||||
|
|
||||||
byte[] bytes = new byte[32];
|
|
||||||
data.get(bytes);
|
|
||||||
String name = StringUtils.fromANSIString(bytes);
|
|
||||||
|
|
||||||
processes.add(new ProcessInfo(pid, name, memoryUsage, affinityMask));
|
|
||||||
|
|
||||||
int bytesRead = data.position() - numBytesBefore;
|
|
||||||
data.position(data.position() + (64 - bytesRead));
|
|
||||||
}
|
|
||||||
callback.call(processes);
|
|
||||||
}
|
|
||||||
else callback.call(Collections.emptyList());
|
|
||||||
}
|
}
|
||||||
else callback.call(Collections.emptyList());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProcessAffinity(final int pid, final int affinityMask) {
|
public void setProcessAffinity(final int pid, final int affinityMask) {
|
||||||
synchronized (lock) {
|
synchronized (actions) {
|
||||||
actions.add(() -> {
|
actions.add(() -> {
|
||||||
data.rewind();
|
sendData.rewind();
|
||||||
data.put(RequestCodes.SET_PROCESS_AFFINITY);
|
sendData.put(RequestCodes.SET_PROCESS_AFFINITY);
|
||||||
data.putInt(8);
|
sendData.putInt(8);
|
||||||
data.putInt(pid);
|
sendData.putInt(pid);
|
||||||
data.putInt(affinityMask);
|
sendData.putInt(affinityMask);
|
||||||
sendData();
|
sendPacket(CLIENT_PORT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mouseEvent(int flags, int dx, int dy, int wheelDelta) {
|
public void mouseEvent(int flags, int dx, int dy, int wheelDelta) {
|
||||||
synchronized (lock) {
|
synchronized (actions) {
|
||||||
if (!isConnected()) return;
|
if (!initReceived) return;
|
||||||
actions.add(() -> {
|
actions.add(() -> {
|
||||||
data.rewind();
|
sendData.rewind();
|
||||||
data.put(RequestCodes.MOUSE_EVENT);
|
sendData.put(RequestCodes.MOUSE_EVENT);
|
||||||
data.putInt(10);
|
sendData.putInt(10);
|
||||||
data.putInt(flags);
|
sendData.putInt(flags);
|
||||||
data.putShort((short)dx);
|
sendData.putShort((short)dx);
|
||||||
data.putShort((short)dy);
|
sendData.putShort((short)dy);
|
||||||
data.putShort((short)wheelDelta);
|
sendData.putShort((short)wheelDelta);
|
||||||
sendData();
|
sendPacket(CLIENT_PORT);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public OnGetProcessInfoListener getOnGetProcessInfoListener() {
|
||||||
return socket != null && !socket.isClosed() && socket.isConnected();
|
return onGetProcessInfoListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void connect() {
|
public void setOnGetProcessInfoListener(OnGetProcessInfoListener onGetProcessInfoListener) {
|
||||||
InetAddress address = null;
|
synchronized (actions) {
|
||||||
try {
|
this.onGetProcessInfoListener = onGetProcessInfoListener;
|
||||||
address = InetAddress.getLocalHost();
|
|
||||||
}
|
}
|
||||||
catch (UnknownHostException e) {}
|
|
||||||
|
|
||||||
try {
|
|
||||||
socket = new Socket(address.getHostAddress(), DEFAULT_PORT);
|
|
||||||
}
|
|
||||||
catch (IOException e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start() {
|
private void startSendThread() {
|
||||||
Executors.newSingleThreadExecutor().execute(() -> {
|
Executors.newSingleThreadExecutor().execute(() -> {
|
||||||
while (true) {
|
while (running) {
|
||||||
synchronized (lock) {
|
synchronized (actions) {
|
||||||
if (!isConnected()) connect();
|
while (initReceived && !actions.isEmpty()) actions.poll().run();
|
||||||
if (isConnected()) while (!actions.isEmpty()) actions.poll().run();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isConnected()) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(100);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
if (socket != null) {
|
||||||
|
socket.close();
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRequest(byte requestCode, final int port) {
|
||||||
|
switch (requestCode) {
|
||||||
|
case RequestCodes.INIT: {
|
||||||
|
initReceived = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RequestCodes.GET_PROCESS: {
|
||||||
|
if (onGetProcessInfoListener == null) return;
|
||||||
|
receiveData.position(receiveData.position() + 4);
|
||||||
|
int numProcesses = receiveData.getShort();
|
||||||
|
int index = receiveData.getShort();
|
||||||
|
int pid = receiveData.getInt();
|
||||||
|
long memoryUsage = receiveData.getLong();
|
||||||
|
int affinityMask = receiveData.getInt();
|
||||||
|
|
||||||
|
byte[] bytes = new byte[32];
|
||||||
|
receiveData.get(bytes);
|
||||||
|
String name = StringUtils.fromANSIString(bytes);
|
||||||
|
|
||||||
|
onGetProcessInfoListener.onGetProcessInfo(index, numProcesses, new ProcessInfo(pid, name, memoryUsage, affinityMask));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RequestCodes.GET_GAMEPAD: {
|
||||||
|
boolean isXInput = receiveData.get() == 1;
|
||||||
|
final ControlsProfile profile = activity.getInputControlsView().getProfile();
|
||||||
|
boolean useVirtualGamepad = profile != null && profile.isVirtualGamepad();
|
||||||
|
|
||||||
|
if (!useVirtualGamepad && (currentController == null || !currentController.isConnected())) {
|
||||||
|
releaseCurrentController();
|
||||||
|
currentController = ExternalController.getController(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
actions.add(() -> {
|
||||||
|
sendData.rewind();
|
||||||
|
sendData.put(RequestCodes.GET_GAMEPAD);
|
||||||
|
|
||||||
|
if (currentController != null || useVirtualGamepad) {
|
||||||
|
sendData.putInt(!useVirtualGamepad ? currentController.getDeviceId() : profile.id);
|
||||||
|
sendData.put(dinputMapperType);
|
||||||
|
byte[] bytes = (useVirtualGamepad ? profile.getName() : currentController.getName()).getBytes();
|
||||||
|
sendData.putInt(bytes.length);
|
||||||
|
sendData.put(bytes);
|
||||||
|
}
|
||||||
|
else sendData.putInt(0);
|
||||||
|
|
||||||
|
sendPacket(port);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RequestCodes.GET_GAMEPAD_STATE: {
|
||||||
|
int gamepadId = receiveData.getInt();
|
||||||
|
final ControlsProfile profile = activity.getInputControlsView().getProfile();
|
||||||
|
boolean useVirtualGamepad = profile != null && profile.isVirtualGamepad();
|
||||||
|
|
||||||
|
if (currentController != null && currentController.getDeviceId() != gamepadId) currentController = null;
|
||||||
|
|
||||||
|
actions.add(() -> {
|
||||||
|
sendData.rewind();
|
||||||
|
sendData.put(RequestCodes.GET_GAMEPAD_STATE);
|
||||||
|
sendData.put((byte)(currentController != null || useVirtualGamepad ? 1 : 0));
|
||||||
|
|
||||||
|
if (currentController != null || useVirtualGamepad) {
|
||||||
|
sendData.putInt(gamepadId);
|
||||||
|
|
||||||
|
synchronized (gamepadStateQueue) {
|
||||||
|
if (gamepadStateQueue.isEmpty()) {
|
||||||
|
if (useVirtualGamepad) {
|
||||||
|
profile.getGamepadState().writeTo(sendData);
|
||||||
|
}
|
||||||
|
else currentController.state.writeTo(sendData);
|
||||||
|
}
|
||||||
|
else sendData.put(gamepadStateQueue.poll());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sendPacket(port);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case RequestCodes.RELEASE_GAMEPAD:
|
||||||
|
releaseCurrentController();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
try {
|
||||||
|
localhost = InetAddress.getLocalHost();
|
||||||
|
}
|
||||||
|
catch (UnknownHostException e) {}
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
startSendThread();
|
||||||
|
Executors.newSingleThreadExecutor().execute(() -> {
|
||||||
|
try {
|
||||||
|
socket = new DatagramSocket(SERVER_PORT);
|
||||||
|
|
||||||
|
while (running) {
|
||||||
|
socket.receive(receivePacket);
|
||||||
|
|
||||||
|
synchronized (actions) {
|
||||||
|
receiveData.rewind();
|
||||||
|
byte requestCode = receiveData.get();
|
||||||
|
handleRequest(requestCode, receivePacket.getPort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (IOException e) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveGamepadState(GamepadState state) {
|
||||||
|
synchronized (gamepadStateQueue) {
|
||||||
|
if (gamepadStateQueue.size() > 20) gamepadStateQueue.removeLast();
|
||||||
|
gamepadStateQueue.add(state.toByteArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void releaseCurrentController() {
|
||||||
|
currentController = null;
|
||||||
|
synchronized (gamepadStateQueue) {
|
||||||
|
gamepadStateQueue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||||
|
boolean handled = false;
|
||||||
|
if (currentController != null && currentController.getDeviceId() == event.getDeviceId()) {
|
||||||
|
handled = currentController.updateStateFromMotionEvent(event);
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onKeyEvent(KeyEvent event) {
|
||||||
|
boolean handled = false;
|
||||||
|
if (currentController != null && currentController.getDeviceId() == event.getDeviceId() && event.getRepeatCount() == 0) {
|
||||||
|
int action = event.getAction();
|
||||||
|
|
||||||
|
if (action == KeyEvent.ACTION_DOWN) {
|
||||||
|
handled = currentController.updateStateFromKeyEvent(event);
|
||||||
|
}
|
||||||
|
else if (action == KeyEvent.ACTION_UP) {
|
||||||
|
handled = currentController.updateStateFromKeyEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handled) saveGamepadState(currentController.state);
|
||||||
|
}
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte getDInputMapperType() {
|
||||||
|
return dinputMapperType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDInputMapperType(byte dinputMapperType) {
|
||||||
|
this.dinputMapperType = dinputMapperType;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +75,10 @@ public class ImageFs {
|
|||||||
return new File(rootDir, "/opt/installed-wine");
|
return new File(rootDir, "/opt/installed-wine");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public File getTmpDir() {
|
||||||
|
return new File(rootDir, "/tmp");
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
|
|||||||
+4
@@ -127,11 +127,15 @@ public class GuestProgramLauncherComponent extends EnvironmentComponent {
|
|||||||
envVars.put("ANDROID_SYSVSHM_SERVER", UnixSocketConfig.SYSVSHM_SERVER_PATH);
|
envVars.put("ANDROID_SYSVSHM_SERVER", UnixSocketConfig.SYSVSHM_SERVER_PATH);
|
||||||
if (this.envVars != null) envVars.putAll(this.envVars);
|
if (this.envVars != null) envVars.putAll(this.envVars);
|
||||||
|
|
||||||
|
File shmDir = new File(rootDir, "/tmp/shm");
|
||||||
|
shmDir.mkdirs();
|
||||||
|
|
||||||
String command = nativeLibraryDir+"/libproot.so";
|
String command = nativeLibraryDir+"/libproot.so";
|
||||||
command += " --kill-on-exit";
|
command += " --kill-on-exit";
|
||||||
command += " --rootfs="+rootDir;
|
command += " --rootfs="+rootDir;
|
||||||
command += " --cwd="+ImageFs.HOME_PATH;
|
command += " --cwd="+ImageFs.HOME_PATH;
|
||||||
command += " --bind=/dev";
|
command += " --bind=/dev";
|
||||||
|
command += " --bind="+shmDir.getAbsolutePath()+":/dev/shm";
|
||||||
command += " --bind=/proc";
|
command += " --bind=/proc";
|
||||||
command += " --bind=/sys";
|
command += " --bind=/sys";
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import android.view.KeyEvent;
|
|||||||
|
|
||||||
import androidx.collection.ArraySet;
|
import androidx.collection.ArraySet;
|
||||||
|
|
||||||
|
import com.winlator.inputcontrols.ExternalController;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class Keyboard {
|
public class Keyboard {
|
||||||
@@ -92,6 +94,8 @@ public class Keyboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean onKeyEvent(KeyEvent event) {
|
public boolean onKeyEvent(KeyEvent event) {
|
||||||
|
if (ExternalController.isGameController(event.getDevice())) return false;
|
||||||
|
|
||||||
int action = event.getAction();
|
int action = event.getAction();
|
||||||
if (action == KeyEvent.ACTION_DOWN || action == KeyEvent.ACTION_UP) {
|
if (action == KeyEvent.ACTION_DOWN || action == KeyEvent.ACTION_UP) {
|
||||||
int keyCode = event.getKeyCode();
|
int keyCode = event.getKeyCode();
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 245 B |
Binary file not shown.
|
After Width: | Height: | Size: 366 B |
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<solid android:color="@color/colorPrimaryDark" />
|
||||||
|
<stroke android:width="1dp" android:color="#303030" />
|
||||||
|
<corners android:radius="4dp" />
|
||||||
|
</shape>
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ScrollView
|
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
@@ -100,6 +100,18 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/vertical" />
|
android:text="@string/vertical" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/columns" />
|
||||||
|
|
||||||
|
<com.winlator.widget.NumberPicker
|
||||||
|
android:id="@+id/NPColumns"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:minValue="3"
|
||||||
|
app:maxValue="8" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -178,7 +190,8 @@
|
|||||||
android:id="@+id/ETCustomText"
|
android:id="@+id/ETCustomText"
|
||||||
android:maxLength="8"
|
android:maxLength="8"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:hint="@string/none" />
|
android:hint="@string/none"
|
||||||
|
android:inputType="textCapCharacters" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -33,12 +33,6 @@
|
|||||||
android:checked="true"
|
android:checked="true"
|
||||||
android:text="@string/show_touchscreen_controls" />
|
android:text="@string/show_touchscreen_controls" />
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/CBCursorVisible"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/cursor_visible" />
|
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/CBLockCursor"
|
android:id="@+id/CBLockCursor"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="42dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:layout_margin="1dp">
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/BTDecrement"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:background="@drawable/number_picker_background"
|
||||||
|
android:padding="7dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/number_picker_decrement"
|
||||||
|
app:tint="#ffffff" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
style="@style/EditText"
|
||||||
|
android:id="@+id/EditText"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:gravity="center"
|
||||||
|
android:focusable="false"
|
||||||
|
android:layout_marginLeft="4dp"
|
||||||
|
android:layout_marginRight="4dp"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:inputType="number" />
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/BTIncrement"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"
|
||||||
|
android:background="@drawable/number_picker_background"
|
||||||
|
android:padding="7dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:src="@drawable/number_picker_increment"
|
||||||
|
app:tint="#ffffff" />
|
||||||
|
</LinearLayout>
|
||||||
@@ -204,11 +204,12 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:min="10"
|
android:min="10"
|
||||||
android:max="200" />
|
android:max="200" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/CBUseDRI3"
|
android:id="@+id/CBUseDRI3"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/use_dri3_extension_experimental"
|
android:text="@string/use_dri3_extension"
|
||||||
android:layout_marginTop="8dp" />
|
android:layout_marginTop="8dp" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,17 @@
|
|||||||
android:layout_marginTop="8dp">
|
android:layout_marginTop="8dp">
|
||||||
|
|
||||||
<LinearLayout style="@style/FieldSet">
|
<LinearLayout style="@style/FieldSet">
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/directinput_mapper_type" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
style="@style/ComboBox"
|
||||||
|
android:id="@+id/SDInputMapperType"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:entries="@array/dinput_mapper_type_entries" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -278,13 +289,6 @@
|
|||||||
android:src="@drawable/icon_menu" />
|
android:src="@drawable/icon_menu" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/CBSingleCPU"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="@string/use_single_cpu"
|
|
||||||
android:layout_marginTop="4dp" />
|
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/CBForceFullscreen"
|
android:id="@+id/CBForceFullscreen"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<string-array name="binding_type_entries">
|
<string-array name="binding_type_entries">
|
||||||
<item>Keyboard</item>
|
<item>Keyboard</item>
|
||||||
<item>Mouse</item>
|
<item>Mouse</item>
|
||||||
|
<item>Gamepad</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="dxwrapper_entries">
|
<string-array name="dxwrapper_entries">
|
||||||
<item>Original WineD3D</item>
|
<item>Original WineD3D</item>
|
||||||
@@ -49,11 +50,15 @@
|
|||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="box64_version_entries">
|
<string-array name="box64_version_entries">
|
||||||
<item>0.2.2 (old)</item>
|
<item>0.2.2 (old)</item>
|
||||||
<item>0.2.5 (new)</item>
|
<item>0.2.6 (new)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
<string-array name="turnip_version_entries">
|
<string-array name="turnip_version_entries">
|
||||||
<item>23.1.6 (a6xx)</item>
|
<item>23.1.6 (a6xx)</item>
|
||||||
<item>23.3.0 (a7xx)</item>
|
<item>23.3.0 (a7xx)</item>
|
||||||
<item>24.0.0 (a7xx)</item>
|
<item>24.0.0 (a7xx)</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
<string-array name="dinput_mapper_type_entries">
|
||||||
|
<item>Standard (Old Gamepads)</item>
|
||||||
|
<item>XInput</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="NumberPicker">
|
||||||
|
<attr name="value" format="integer" />
|
||||||
|
<attr name="minValue" format="integer" />
|
||||||
|
<attr name="maxValue" format="integer" />
|
||||||
|
<attr name="step" format="integer" />
|
||||||
|
<attr name="textSize" format="dimension" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
@@ -62,7 +62,6 @@
|
|||||||
<string name="value">Value</string>
|
<string name="value">Value</string>
|
||||||
<string name="processor_affinity">Processor Affinity</string>
|
<string name="processor_affinity">Processor Affinity</string>
|
||||||
<string name="cursor_speed">Cursor Speed</string>
|
<string name="cursor_speed">Cursor Speed</string>
|
||||||
<string name="cursor_visible">Cursor Visible</string>
|
|
||||||
<string name="lock_cursor">Lock Cursor</string>
|
<string name="lock_cursor">Lock Cursor</string>
|
||||||
<string name="toggle_switch">Toggle Switch</string>
|
<string name="toggle_switch">Toggle Switch</string>
|
||||||
<string name="profile_exported_to">Profile exported to</string>
|
<string name="profile_exported_to">Profile exported to</string>
|
||||||
@@ -144,7 +143,7 @@
|
|||||||
<string name="letter">Letter</string>
|
<string name="letter">Letter</string>
|
||||||
<string name="target_path">Target Path</string>
|
<string name="target_path">Target Path</string>
|
||||||
<string name="xserver">XServer</string>
|
<string name="xserver">XServer</string>
|
||||||
<string name="use_dri3_extension_experimental">Use DRI3 extension (Experimental)</string>
|
<string name="use_dri3_extension">Use DRI3 extension</string>
|
||||||
<string name="width">Width</string>
|
<string name="width">Width</string>
|
||||||
<string name="height">Height</string>
|
<string name="height">Height</string>
|
||||||
<string name="old">old</string>
|
<string name="old">old</string>
|
||||||
@@ -174,7 +173,6 @@
|
|||||||
<string name="performance_unstable">Performance (Unstable)</string>
|
<string name="performance_unstable">Performance (Unstable)</string>
|
||||||
<string name="preset">Preset</string>
|
<string name="preset">Preset</string>
|
||||||
<string name="you_cannot_remove_this_preset">You cannot remove this preset</string>
|
<string name="you_cannot_remove_this_preset">You cannot remove this preset</string>
|
||||||
<string name="use_single_cpu">Use single CPU</string>
|
|
||||||
<string name="dxwrapper_help_content"><![CDATA[
|
<string name="dxwrapper_help_content"><![CDATA[
|
||||||
<i><b>WineD3D</b></i> is a translation layer for DirectX 1-11 to OpenGL.<br /><br />
|
<i><b>WineD3D</b></i> is a translation layer for DirectX 1-11 to OpenGL.<br /><br />
|
||||||
<i><b>DXVK</b></i> is a translation layer for Direct3D 9-11 to Vulkan.<br /><br />
|
<i><b>DXVK</b></i> is a translation layer for Direct3D 9-11 to Vulkan.<br /><br />
|
||||||
@@ -207,4 +205,6 @@
|
|||||||
<string name="box86_64_env_var_help__dynarec_forward">Defines the max allowed forward value when building Block</string>
|
<string name="box86_64_env_var_help__dynarec_forward">Defines the max allowed forward value when building Block</string>
|
||||||
<string name="box86_64_env_var_help__dynarec_callret">Optimization of CALL/RET opcodes</string>
|
<string name="box86_64_env_var_help__dynarec_callret">Optimization of CALL/RET opcodes</string>
|
||||||
<string name="box86_64_env_var_help__dynarec_wait">Defines whether or not Dynarec will wait for the FillBlock to be ready</string>
|
<string name="box86_64_env_var_help__dynarec_wait">Defines whether or not Dynarec will wait for the FillBlock to be ready</string>
|
||||||
|
<string name="columns">Columns</string>
|
||||||
|
<string name="directinput_mapper_type">DirectInput Mapper Type</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in new issue
Block a user