Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Made it possible to set a custom gamepad mapping via the settings #2486

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions ugs-core/src/resources/MessagesBundle_en_US.properties
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ mainWindow.swing.arrowMovementEnabled = Enable Keyboard Movement
mainWindow.swing.baudLabel = Baud\:
mainWindow.swing.browseButton = Browse
mainWindow.swing.cancelButton = Cancel
mainWindow.swing.okButton = OK
mainWindow.swing.commandLabel = Command\:
mainWindow.swing.connectionPanel = Connection
mainWindow.swing.controlContextTabbedPane.commands = Commands
Expand Down Expand Up @@ -711,6 +712,11 @@ platform.plugin.joystick.action.analogFeed = Analog feed override
platform.plugin.joystick.action.analogSpindle = Analog spindle override
platform.plugin.joystick.reverseAxis = Reverse
platform.plugin.joystick.axisThreshold = Zero threshold (%)
platform.plugin.joystick.axisThreshold.description = You will need to increase the zero threshold if the analog controls are constantly reporting movement when in idle position.
platform.plugin.joystick.customMappings = Custom mappings
platform.plugin.joystick.customMappings.title = Change custom controller mappings
platform.plugin.joystick.connectedTo = Connected to
platform.plugin.joystick.notConnected = Joystick not connected
platform.plugin.cloud.s3Id = AWS Access Key ID
platform.plugin.cloud.s3Secret = AWS Secret Access Key
platform.plugin.cloud.open = Open cloud file
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
Copyright 2024 Will Winder

This file is part of Universal Gcode Sender (UGS).

UGS is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

UGS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with UGS. If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.ugs.nbp.joystick;

import com.willwinder.ugs.nbp.joystick.service.JoystickService;
import com.willwinder.universalgcodesender.i18n.Localization;
import net.miginfocom.swing.MigLayout;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;

/**
* @author Joacim Breiler
*/
public class CustomMappingsDialog extends JDialog {

private final transient JoystickService joystickService;
private JButton cancelButton;
private JButton okButton;
private JTextArea textArea;

public CustomMappingsDialog(Component parent, JoystickService joystickService) {
super(SwingUtilities.getWindowAncestor(parent), ModalityType.APPLICATION_MODAL);
this.joystickService = joystickService;
setTitle(Localization.getString("platform.plugin.joystick.customMappings.title"));
setPreferredSize(new Dimension(600, 300));
setLayout(new MigLayout("fill, insets 5", "", ""));
setResizable(true);

createComponents();
addEventListeners();

pack();
setLocationRelativeTo(SwingUtilities.getWindowAncestor(parent));
}

private void addEventListeners() {
cancelButton.addActionListener(event -> onCancel());
okButton.addActionListener(event -> onOk());
}

private void createComponents() {
setLayout(new BorderLayout());

textArea = new JTextArea(Settings.getCustomMapping());
add(new JScrollPane(textArea), BorderLayout.CENTER);

JPanel buttonPanel = new JPanel(new MigLayout("insets 5", "[center, grow]"));
cancelButton = new JButton(Localization.getString("mainWindow.swing.cancelButton"));
buttonPanel.add(cancelButton);

okButton = new JButton(Localization.getString("mainWindow.swing.okButton"));
buttonPanel.add(okButton);
add(buttonPanel, BorderLayout.SOUTH);
getRootPane().setDefaultButton(okButton);
}

private void onCancel() {
setVisible(false);
dispose();
}

private void onOk() {
Settings.setCustomMapping(textArea.getText());
joystickService.destroy();
joystickService.initialize();
setVisible(false);
dispose();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2020 Will Winder
Copyright 2020-2024 Will Winder

This file is part of Universal Gcode Sender (UGS).

Expand All @@ -23,16 +23,26 @@ This file is part of Universal Gcode Sender (UGS).
import com.willwinder.ugs.nbp.joystick.service.JoystickService;
import com.willwinder.ugs.nbp.joystick.service.JoystickServiceListener;
import com.willwinder.ugs.nbp.joystick.ui.BindActionButton;
import com.willwinder.ugs.nbp.joystick.ui.JoystickOptionsActivateRow;
import com.willwinder.ugs.nbp.joystick.ui.ReverseAxisCheckBox;
import com.willwinder.ugs.nbp.joystick.ui.StatusLabel;
import com.willwinder.ugs.nbp.lib.lookup.CentralLookup;
import com.willwinder.ugs.nbp.lib.options.AbstractOptionsPanel;
import com.willwinder.universalgcodesender.i18n.Localization;
import net.miginfocom.swing.MigLayout;

import javax.swing.*;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import java.awt.*;
import java.util.Arrays;
import java.util.EnumMap;
import java.util.List;
Expand All @@ -43,88 +53,91 @@ public class JoystickOptionsPanel extends AbstractOptionsPanel implements Joysti

private final transient JoystickService joystickService;
private final Map<JoystickControl, StatusLabel> statusLabelMap = new EnumMap<>(JoystickControl.class);
private JPanel panel;
private JCheckBox activeCheckbox;
private JPanel leftPanel;
private JPanel rightPanel;
private JSpinner thresholdSpinner;

JoystickOptionsPanel(JoystickOptionsPanelController controller) {
super(controller);
joystickService = CentralLookup.getDefault().lookup(JoystickService.class);
joystickService.addListener(this);

setLayout(new BorderLayout());
clear();
setLayout(new MigLayout("fill"));
removeAll();
}

@Override
public void load() {
if (panel != null) {
this.remove(panel);
}
joystickService.setActivateActionDispatcher(false);

panel = new JPanel(new MigLayout("inset 5"));

activeCheckbox = new JCheckBox(Localization.getString("platform.plugin.joystick.activate"), Settings.isActive());
panel.add(activeCheckbox, "wrap, spanx");

panel.add(new JSeparator(SwingConstants.HORIZONTAL), "wrap, spanx");
panel.add(new JLabel(Localization.getString("platform.plugin.joystick.buttonControls")), "wrap, spanx, hmin 24");

for (JoystickControl joystickControl : JoystickControl.getDigitalControls()) {
String name = Localization.getString(joystickControl.getLocalization());
StatusLabel label = new StatusLabel(name);
statusLabelMap.put(joystickControl, label);
panel.add(label, "wmin 100, hmin 24");
panel.add(new BindActionButton(joystickService, joystickControl), "wmin 150, hmin 24, wrap");
}
JoystickOptionsActivateRow activateRow;
removeAll();
activateRow = new JoystickOptionsActivateRow(joystickService);
activateRow.addActionListener(event -> {
leftPanel.setVisible(activateRow.isActive());
rightPanel.setVisible(activateRow.isActive());
});
add(activateRow, "growx, spanx, wrap, gapbottom 10");

createLeftPanel();
createRightPanel();
SwingUtilities.invokeLater(changer::changed);
}

panel.add(new JSeparator(SwingConstants.HORIZONTAL), "wrap, spanx");
panel.add(new JLabel(Localization.getString("platform.plugin.joystick.analogControls")), "wrap, spanx, hmin 24");
private void createRightPanel() {

panel.add(new JLabel(Localization.getString("platform.plugin.joystick.axisThreshold")), "wmin 100, hmin 24");
thresholdSpinner = new JSpinner(new SpinnerNumberModel(Settings.getAxisThreshold() * 100, 0, 100, 1));
thresholdSpinner.addChangeListener(this::onThresholdChange);
panel.add(thresholdSpinner, "wmin 150, hmin 24, wrap");
rightPanel = new JPanel(new MigLayout("fillx"));
rightPanel.setVisible(Settings.isActive());
rightPanel.setBorder(new CompoundBorder(new TitledBorder(Localization.getString("platform.plugin.joystick.analogControls")), new EmptyBorder(5, 5, 5, 5)));

for (JoystickControl joystickControl : JoystickControl.getAnalogControls()) {
String name = Localization.getString(joystickControl.getLocalization());
StatusLabel label = new StatusLabel(name);
statusLabelMap.put(joystickControl, label);
panel.add(label, "wmin 100, hmin 24");

rightPanel.add(label, "wmin 150, hmin 24");

JCheckBox reverseAxis = null;
String wrap = ", wrap";
if (REVERSIBLE_CONTROLS.contains(joystickControl)) {
reverseAxis = new ReverseAxisCheckBox(joystickService, joystickControl);
wrap = "";
}
panel.add(new BindActionButton(joystickService, joystickControl), "wmin 150, hmin 24" + wrap);
rightPanel.add(new BindActionButton(joystickService, joystickControl), "w 150:150:150, hmin 24" + wrap);
if (reverseAxis != null) {
panel.add(reverseAxis, "wrap");
rightPanel.add(reverseAxis, "wrap");
}
}

add(panel, BorderLayout.CENTER);
SwingUtilities.invokeLater(changer::changed);
rightPanel.add(new JSeparator(SwingConstants.HORIZONTAL), "hmin 24, gaptop 20, growx, wrap, spanx");
rightPanel.add(new JLabel("<html><body><p style='width: 320px;'>" + Localization.getString("platform.plugin.joystick.axisThreshold.description") + "</p></body></html>"), "spanx, grow, wrap, gapbottom 10");

rightPanel.add(new JLabel(Localization.getString("platform.plugin.joystick.axisThreshold")), "wmin 100, hmin 24");
thresholdSpinner = new JSpinner(new SpinnerNumberModel(Settings.getAxisThreshold() * 100, 0, 100, 1));
thresholdSpinner.addChangeListener(this::onThresholdChange);
rightPanel.add(thresholdSpinner, "wmin 150, hmin 24, wrap");

add(rightPanel, "grow");
}

private void createLeftPanel() {
leftPanel = new JPanel(new MigLayout("fillx"));
leftPanel.setVisible(Settings.isActive());
leftPanel.setBorder(new TitledBorder(Localization.getString("platform.plugin.joystick.buttonControls")));

for (JoystickControl joystickControl : JoystickControl.getDigitalControls()) {
String name = Localization.getString(joystickControl.getLocalization());
StatusLabel label = new StatusLabel(name);
statusLabelMap.put(joystickControl, label);
leftPanel.add(label, "wmin 150, hmin 24");
leftPanel.add(new BindActionButton(joystickService, joystickControl), "w 150:150:150, hmin 24, wrap");
}
add(leftPanel, "grow, gapright 10");
}

private void onThresholdChange(ChangeEvent changeEvent) {
Settings.setAxisThreshold(((Double)thresholdSpinner.getValue()).intValue() / 100f);
Settings.setAxisThreshold(((Double) thresholdSpinner.getValue()).intValue() / 100f);
}

@Override
public void store() {
joystickService.setActivateActionDispatcher(true);

if (activeCheckbox.isSelected()) {
joystickService.initialize();
} else {
joystickService.destroy();
}

Settings.setActive(activeCheckbox.isSelected());
}

@Override
Expand All @@ -141,14 +154,19 @@ public void cancel() {
public void onUpdate(JoystickState state) {
for (JoystickControl control : JoystickControl.getDigitalControls()) {
StatusLabel label = statusLabelMap.get(control);
boolean isPressed = state.getButton(control);
label.setActive(isPressed);
if (label != null) {
boolean isPressed = state.getButton(control);
label.setActive(isPressed);
}
}

for (JoystickControl control : JoystickControl.getAnalogControls()) {
StatusLabel label = statusLabelMap.get(control);
float value = state.getAxis(control);
label.setActive(value != 0);
if (label != null) {
label.setActive(value != 0);
label.setAnalogValue(value);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,18 @@ This file is part of Universal Gcode Sender (UGS).
public class Settings {
public static final String SETTINGS_ACTIVE = "active";
public static final String SETTINGS_VERSION = "version";
public static final String SETTINGS_CUSTOM_MAPPING = "customMapping";

private static Preferences preferences = NbPreferences.forModule(JoystickService.class);
public static final String DEFAULT_CUSTOM_MAPPING = "# Custom UGS controllers\n" +
"03000000412300003780000000000000,Arduino Micro,a:b0,b:b1,x:b2,y:b3,back:b4,guide:b5,start:b6,leftstick:b7,rightstick:b8,leftshoulder:b9,rightshoulder:b10,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,-leftx:a0,+leftx:a1,-lefty:a2,+lefty:a3,-rightx:a4,+rightx:a5,-righty:a6,-righty:a7,lefttrigger:b11,righttrigger:b12,platform:Windows,\n" +
"03000000412300003e00000000000000,Arduino Due,platform:Windows,a:b10,b:b8,x:b9,y:b11,guide:b12,leftshoulder:b0,rightshoulder:b1,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,\n" +
"03000000072000004512000000000000,JOYSTICK FZ,platform:Windows,guide:b0,leftstick:b4,rightstick:b1,leftshoulder:b2,rightshoulder:b3,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a1,rightx:a5,righty:a4~,\n" +
"78696e70757401000000000000000000,XInput Controller,platform:Windows,a:b0,b:b1,x:b2,y:b3,back:b6,start:b7,guide:b10,leftshoulder:b4,rightshoulder:b5,leftstick:b8,rightstick:b9,leftx:a0,lefty:a1,rightx:a3,righty:a4,lefttrigger:a2,righttrigger:a5,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,\n" +
"03000000786901006e70000000000000,XInput Controller,platform:Windows,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,\n" +
"030000006d04000015c2000000000000,Logitech Extreme 3D Pro M/N: J-UK17 P/N: 863225-1000,a:b11,b:b10,x:b8,y:b9,back:b6,guide:b7,start:b5,leftstick:b2,rightstick:-a1,leftshoulder:b4,rightshoulder:b0,dpup:h0.1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,leftx:a0,lefty:a2,platform:Windows,\n" +
"03000000790000000600000000000000,G-Shark GS-GP702,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b3,y:b0,platform:Windows,\n";

private static final Preferences preferences = NbPreferences.forModule(JoystickService.class);

protected Settings() {
}
Expand Down Expand Up @@ -101,7 +111,7 @@ public static String getActionMapping(JoystickControl joystickControl) {
* Sets if the axis values should be inverted
*
* @param joystickControl the axis we wish change settings for
* @param reversed if the axis should be inverted
* @param reversed if the axis should be inverted
*/
public static void setReverseAxis(JoystickControl joystickControl, boolean reversed) {
preferences.putBoolean(joystickControl.name() + "_reverse", reversed);
Expand Down Expand Up @@ -135,4 +145,22 @@ public static float getAxisThreshold() {
public static void setAxisThreshold(float threshold) {
preferences.putFloat("axisThreshold", threshold);
}

/**
* Gets the custom mapping in the gamecontrollerdb-format (https://github.com/gabomdq/SDL_GameControllerDB)
*
* @return the custom mapping
*/
public static String getCustomMapping() {
return preferences.get(SETTINGS_CUSTOM_MAPPING, DEFAULT_CUSTOM_MAPPING);
}

/**
* Sets the custom mapping in the gamecontrollerdb-format (https://github.com/gabomdq/SDL_GameControllerDB)
*
* @param customMapping the custom mapping
*/
public static void setCustomMapping(String customMapping) {
preferences.put(SETTINGS_CUSTOM_MAPPING, customMapping);
}
}
Loading
Loading