I have a music Java Swing application that is played by sensing which keyboard key is pressed, and when it is released. It depends on the KeyPressed and KeyReleased KeyEvents. It uses Java 8.
It has been working for years, on Windows, Linux (using open java), and Mac OSX (using Oracle Java).
On Mac OSX 10.12 (Sierra), a new problem appeared, and it continues to be a problem on Mac OSX 10.13 (High Sierra). We have been able to work-around the problem by turning off key-repeat.
In debugging the problem, I discovered that once key-repeat is triggered, KeyPressed events are no longer passed to the application. There are no exceptions thrown. KeyPressed events just stop coming, but KeyReleased events continue to be passed to the application.
When the application is restarted, KeyEvents work as expected (both KeyPressed events, and KeyReleased events are passed to the application). But again, if key-repeat gets triggered, KeyPressed events stop coming to the application.
I wrote a small test program to demonstrate the problem:
public class KeyEventsJDialog extends javax.swing.JDialog {
private JPanel jPanelMain;
private JLabel jLabelError;
private JToggleButton jToggleButton1;
private static final long serialVersionUID = 0 ;
private int lastKeyCode = 0 ;
private boolean isError = false ;
/**
* Auto-generated main method to display this JDialog
*/
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
KeyEventsJDialog inst = new KeyEventsJDialog(frame);
inst.setVisible(true);
inst.jPanelMain.requestFocus() ;
}
});
}
public KeyEventsJDialog(JFrame frame) {
super(frame);
initGUI();
}
private void initGUI() {
try {
{
this.setTitle("Keyboard Events Test");
this.addComponentListener(new ComponentAdapter() {
public void componentHidden(ComponentEvent evt) {
thisComponentHidden(evt);
}
});
this.addWindowListener(new WindowAdapter() {
public void windowClosed(WindowEvent evt) {
thisWindowClosed(evt);
}
});
{
jPanelMain = new JPanel();
getContentPane().add(jPanelMain, BorderLayout.CENTER);
jPanelMain.addKeyListener(new KeyAdapter() {
public void keyTyped(KeyEvent evt) {
jPanelMainKeyTyped(evt);
}
public void keyReleased(KeyEvent evt) {
jPanelMainKeyReleased(evt);
}
public void keyPressed(KeyEvent evt) {
jPanelMainKeyPressed(evt);
}
});
{
jToggleButton1 = new JToggleButton();
jPanelMain.add(jToggleButton1);
jToggleButton1.setText("(undefined)");
}
{
jLabelError = new JLabel();
jPanelMain.add(jLabelError);
jLabelError.setText(" ");
jLabelError.setPreferredSize(new java.awt.Dimension(206, 18));
}
}
}
setSize(400, 300);
} catch (Exception e) {
e.printStackTrace();
}
}
private void jPanelMainKeyPressed(KeyEvent evt)
{
System.out.println("jPanelMain.keyPressed, event="+evt);
lastKeyCode = evt.getExtendedKeyCode() ;
JToggleButton button = jToggleButton1 ;
if (!button.isSelected())
{
button.setText(" ") ;
button.setSelected(true) ;
}
isError = false ;
jLabelError.setText(" ") ;
}
private void jPanelMainKeyReleased(KeyEvent evt)
{
System.out.println("jPanelMain.keyReleased, event="+evt);
lastKeyCode = evt.getExtendedKeyCode() ;
String typedString = "'" + evt.getKeyChar() + "' = " + lastKeyCode ;
JToggleButton button = jToggleButton1 ;
button.setText(new String(typedString)) ;
if ((!button.isSelected()) || isError)
{
isError = true ;
jLabelError.setText(KeyEvent.getKeyText(lastKeyCode) + " KeyPressed event missing") ;
}
else
{
jLabelError.setText(KeyEvent.getKeyText(lastKeyCode)) ;
}
button.setSelected(false) ;
}
private void jPanelMainKeyTyped(KeyEvent evt)
{
System.out.println("jPanelMain.keyTyped, event="+evt);
char typed = evt.getKeyChar() ;
String typedString = "(undefined)" ;
JToggleButton button = jToggleButton1 ;
if (!button.isSelected())
{
isError = true ;
jLabelError.setText("KeyPressed event missing") ;
button.setSelected(true) ;
}
typedString = "'" + typed + "' = " + lastKeyCode ;
button.setText(new String(typedString)) ;
}
private void thisWindowClosed(WindowEvent evt)
{
System.out.println("this.windowClosed, event="+evt);
System.exit(0) ;
}
private void thisComponentHidden(ComponentEvent evt)
{
System.out.println("this.componentHidden, event="+evt);
System.exit(0) ;
}
}
This program has a single toggle button, and when a key is pressed, the toggle button is selected. When the key is released, the toggle button is un-selected. It also monitors KeyTyped events, and indicates what key was used in the button text (and a label field to the right of it).
All events are KeyEvents are traced using System.out.println().
With this tool, you can experiment with the problem.
In experimenting, I noticed a strange thing I also can't explain.
When it gets into the state where KeyPressed events are no longer being sent, if I press and hold the "t" key (long enough to trigger key-repeat), after I let up on the "t" key, KeyPressed events are again passed to the application, until some other key (the "a" key most reliably causes it) is held long enough to trigger key-repeat, and then KeyPressed events stop.
Does anyone have any idea why the KeyPressed events stop coming to the application?
Thank you for responding to this.