2

(Note: I am aware of this question in which the user would have to enter a Terminal command to fix this issue, but I would prefer a solution in which the solution can be put into the application.)

To explain, I am using KeyBindings in a Java application; however, if one holds a key like a, e, i, o, u, n, s, etc., the diacritic menu OSX uses somehow completely disables key input.

It does not, however, affect mouse input if that is relevant.

Here is some sample code in which the problem can be demonstrated. If you press and hold any of the aforementioned keys on OSX for approximately one second or more, the KeyBindings stop working entirely. (I recommend holding the key down for more just to be sure, though.

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

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.WindowConstants;

public class Test extends JPanel implements ActionListener {
    private static final long serialVersionUID = 1L;

    private static JFrame frame;

    private static boolean up = false, down = false, left = false, right = false;

    private static int x = 275, y = 275;

    public static void main(String[] args) {
        Test t = new Test();
        t.setBounds(0, 0, 1200, 600);
        t.setVisible(true);

        Timer repaintTimer = new Timer(2, t);

        frame = new JFrame();
        frame.setSize(600, 600);

        setUpKeyActions(t);

        frame.add(t);

        Dimension dim = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setLocation((dim.width - frame.getWidth()) / 2, (dim.height - frame.getHeight()) / 2);
        frame.getContentPane().setLayout(null);
        frame.setAlwaysOnTop(true);
        frame.setResizable(false);

        repaintTimer.start();

        frame.setVisible(true);
        frame.requestFocus();
    }

    private static void setUpKeyActions(Test t) {
        int condition = WHEN_IN_FOCUSED_WINDOW;

        new KeyAction(t, condition, KeyEvent.VK_UP, 0, false) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                up = true;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_UP, 0, true) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                up = false;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_LEFT, 0, false) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                left = true;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_LEFT, 0, true) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                left = false;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_RIGHT, 0, false) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                right = true;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_RIGHT, 0, true) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                right = false;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_DOWN, 0, false) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                down = true;
            }

        };

        new KeyAction(t, condition, KeyEvent.VK_DOWN, 0, true) {
            private static final long serialVersionUID = 1L;

            @Override
            public void actionPerformed(ActionEvent e) {
                down = false;
            }

        };

    }

    private static abstract class KeyAction extends AbstractAction {
        private static final long serialVersionUID = 1L;

        KeyAction(JComponent component, int condition, int keyCode, int modifiers, boolean onKeyRelease) {
            InputMap inputMap = component.getInputMap(condition);
            ActionMap actionMap = component.getActionMap();
            KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers, onKeyRelease);
            inputMap.put(keyStroke, keyStroke.toString());
            actionMap.put(keyStroke.toString(), this);
        }

    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        if(up)
            y -= 1;
        if(down)
            y += 1;
        if(right)
            x += 1;
        if(left)
            x -= 1;
        if(x < 0)
            x = 0;
        else if(x > frame.getWidth() - 30)
            x = frame.getWidth() - 30;
        if(y < 0)
            y = 0;
        else if(y > frame.getHeight() - 50)
            y = frame.getHeight() - 50;
        g.drawRect(x, y, 30, 30);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        frame.repaint();
    }

}
Gigi Bayte 2
  • 838
  • 1
  • 8
  • 20
  • I've not been able to replicate the issues (MacOS High Sierra 10.13.5/Java 1.8.0_171), but, you might also consider using something like [this](https://stackoverflow.com/questions/12760311/performing-a-certain-task-after-pressing-ctrl-alt-backspace/12763850#12763850) as a "work around" – MadProgrammer Jun 23 '18 at 23:19
  • @MadProgrammer Thanks! I'll look into that and let you know how it works for me. I'm on High Sierra 10.13.5 and Java 1.8.0_151, so I'll look into the Java version being an issue as well. – Gigi Bayte 2 Jun 24 '18 at 01:01
  • @MadProgrammer Sorry for the late reply. Things have been very busy. Unfortunately, the bug seems to persist even when I use KeyboardFocusManager.addKeyEventDispatcher. – Gigi Bayte 2 Jul 04 '18 at 23:28
  • @MadProgrammer I just updated my JDK to 1.8.0_151 to 1.8.0_172, and that fixed the issue. I guess I'll use org.apache.commons.SystemUtils to check and require 1.9 or above since I don't believe you can specify implementation with that. Thanks for the help! Could you make an answer with the information you have given so you get credit for the answer? – Gigi Bayte 2 Jul 04 '18 at 23:54
  • You can self answer, highlighting the differences in versions as some other people might have the problem – MadProgrammer Jul 04 '18 at 23:55

1 Answers1

1

To answer the question I was having, the issue was the Java version.

I had JDK 1.8.0_151, but apparently this issue was fixed in some iteration up to JDK 1.8.0_172.

So, to make sure this issue does not happen to the users of my application, I will require JDK 1.9 or above using org.apache.commons.lang3.SystemUtils and use the isJavaVersionAtLeast(JavaVersion requiredVersion) method from this answer.

Update:

I actually plan to use the method I got from my question here since it allows me not to have to require JRE 1.9 but instead allows me to make the minimum 1.8.0_172.

Gigi Bayte 2
  • 838
  • 1
  • 8
  • 20