Java Swing KeyBindings stop working only on Mac

kneedhelp :

I'm working on a game using Swing and I'm using KeyBindings for input from the keyboard.

I'm having issues where the KeyBindings stop responding. It happens every time I run the application, but as far as I can tell, it's not when a certain chain of events occurs. The KeyBindings just stop receiving input from the keyboard. I also use mouse input, which continues to work, so I know it's not related to input in general.

Some things I've tried:

  • made sure my object was not garbage collected
  • looked for a certain reason the issue occurs (ex: after a certain amount of time, a certain key combination pressed), to which I could find none
  • tried to use a KeyListener instead

None of these worked.

I then copied the project (without changing any code) to a windows machine, and after testing, I could not get the problem to occur once. I didn't paste code here because I'm 99% positive this is not related to my code, but related to the operating system it's running on.

Is this a problem with the macOS, or the JDK for Mac, or something else I don't know about?

I'm using JDK version 8 update 112, the computer is running macOS Sierra version 10.12.2.

It seems someone else had the same problem I now do, but they never got an answer.

EDIT

Here's a code example where the problem occurs:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

//an example of the problem where the keyboard stops receiving input randomly
public class ProblemExample extends JPanel {
    private static final long serialVersionUID = 1L;

    private int xPos = 200, yPos = 200;
    private boolean wKey, aKey, sKey, dKey;
    private BufferedImage image; //sprite

    public ProblemExample() {
        JFrame frame = new JFrame();
        frame.add(this);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        //makes the sprite a red square
        image = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
        int[] redPixels = new int[50 * 50];
        for (int i = 0; i < redPixels.length; i++) {
            redPixels[i] = 0xffff0000;
        }
        image.setRGB(0, 0, 50, 50, redPixels, 0, 50);
        initializeKeys();
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(800, 600);
    }

    //sets up Key Bindings
    private void initializeKeys() {
        final String W = "W",
                     A = "A", 
                     S = "S", 
                     D = "D",
                     PRESSED = "PRESSED",
                     RELEASED = "RELEASED";

        InputMap inputMap = this.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
        ActionMap actionMap = this.getActionMap();

        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, false), W + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, false), A + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, false), S + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, false), D + PRESSED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0, true), W + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, 0, true), A + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, 0, true), S + RELEASED);
        inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_D, 0, true), D + RELEASED);

        Action wActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                wKey = true;
            }
        };
        Action aActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                aKey = true;
            }
        };
        Action sActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                sKey = true;
            }
        };
        Action dActionPressed = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dKey = true;
            }
        };
        Action wActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                wKey = false;
            }
        };
        Action aActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                aKey = false;
            }
        };
        Action sActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                sKey = false;
            }
        };
        Action dActionReleased = new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dKey = false;
            }
        };

        actionMap.put(W + PRESSED, wActionPressed);
        actionMap.put(A + PRESSED, aActionPressed);
        actionMap.put(S + PRESSED, sActionPressed);
        actionMap.put(D + PRESSED, dActionPressed);
        actionMap.put(W + RELEASED, wActionReleased);
        actionMap.put(A + RELEASED, aActionReleased);
        actionMap.put(S + RELEASED, sActionReleased);
        actionMap.put(D + RELEASED, dActionReleased);
    }

    public void loop() {
        if (wKey) yPos -= 5;
        if (aKey) xPos -= 5;
        if (sKey) yPos += 5;
        if (dKey) xPos += 5;
        repaint();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, xPos, yPos, null);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                ProblemExample example = new ProblemExample();
                Timer timer = new Timer(60, new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        example.loop();
                    }
                });
                timer.start();
            }
        });
    }

}
Tahir Hussain Mir :

You might think this is a bug in MAC, while it is not Because MAC has the feature of secondary keys.
As you are the user of MAC, I hope you are aware of this feature. This feature might be the the cause of your problem.
The Secondary key feature of MAC : is used where holding down a letter key will display variations of that letter, like holding down “u” to get “ü.” This comes in handy when spelling non-English words.

There’s a simple way to take control and change the behavior of long key presses to accommodate your needs.

Open the Terminal app and write:

defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false

Then restart any open app in which you want this setting to activate.

REVERTING BACK: Just,simply add true in place of false to the previous command like this:

defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool true

UPDATE:
You can speed up the rate of key repeats or decrease the delay before a held key starts repeating, by going to the System Preferences, and make changes under the keyboard header.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=452084&siteId=1