Hello everyone,
I am trying to develop a pong clone, using pure Java 2D (i.e. no additional libraries) and Swing/AWT.
Everything works fine for now apart from the following:
When I minimize the game's frame (a JFrame and no full screen exclusive mode yet), and leave it minimized for, say, 20 minutes, after restoring the frame it looks like the JPanel inside the JFrame does not receive key events through KeyListener.
I have also tried to use Frame as the top most window and Canvas instead of a JPanel, but I get the same behaviour.
The 'erroneous' (?) behaviour exists only in linux (specifically Linux Mint 8 with XFCE).
The KeyListener (which is added to the panel/canvas) adds key events to a queue (implemented with LinkedList) and my game loop retrieves the first key event in queue and processes it (if exists) in each game update cycle.
I added a FocusListener, a WindowFocusListener and a WindowListener to the frame so as when it gets the focus or gets activated (or deiconified), I transfer the focus to the panel/canvas (with requestFocusInWindow()).
I also added a FocusListener on the panel/canvas to see if it does gets the focus.
What I have seen as an 'erroneous' behaviour is that, after the frame has been minimized for 20 minutes and then restored, the focus seems to be transferred to the panel/canvas but the KeyListener does not seem to get any key events.
I would really appreciate if you can help me to solve this problem.
Also, I would really like not to use any game engine as of now.
Here is some code that might help you understand a little bit more of how my game works.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| public class Pong extends Frame implements FocusListener, WindowListener, WindowFocusListener { private final PongPanel pp; public Pong() { addFocusListener(this); addWindowListener(this); addWindowFocusListener(this); pp = new PongPanel(640, 480, 300, 5); setBounds(0, 0, 640, 480); setResizable(false); setLocationRelativeTo(null); add(pp); pack(); setVisible(true); pp.requestFocusInWindow(); } @Override public void focusGained(FocusEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { pp.requestFocusInWindow(); } }); }
@Override public void focusLost(FocusEvent e) {}
public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { new Pong(); } }); }
@Override public void windowGainedFocus(WindowEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { pp.requestFocusInWindow(); } }); }
@Override public void windowLostFocus(WindowEvent e) {}
@Override public void windowActivated(WindowEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { pp.requestFocusInWindow(); } }); }
@Override public void windowClosed(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {}
@Override public void windowDeiconified(WindowEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { pp.requestFocusInWindow(); } }); }
@Override public void windowIconified(WindowEvent e) {}
@Override public void windowOpened(WindowEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { pp.requestFocusInWindow(); } }); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class PongPanel extends BasePanel implements FocusListener { public PongPanel(int width, int height, int updatesPerSecond, int maxFrameSkip) { super(width, height, updatesPerSecond, maxFrameSkip); addFocusListener(this); }
@Override public void focusGained(FocusEvent e) { System.out.println("I have the focus"); }
@Override public void focusLost(FocusEvent e) { System.out.println("I lost the focus"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| public class BasePanel extends Canvas implements KeyListener, Runnable { protected final int MAX_FRAMESKIP; protected final int PHEIGHT; protected final int PWIDTH; protected final int SKIP_TICKS; protected final int TICKS_PER_SECOND;
protected boolean running; protected int loops; protected LinkedList<KeyEvent> keyEvents = new LinkedList<KeyEvent>(); protected long nextGameTick = nextGameTick(); protected Thread animator; public BasePanel(int width, int height, int updatesPerSecond, int maxFrameSkips) { super(); TICKS_PER_SECOND = updatesPerSecond; SKIP_TICKS = 1000 / TICKS_PER_SECOND; MAX_FRAMESKIP = maxFrameSkips; PWIDTH = width; PHEIGHT = height; super.setPreferredSize(new Dimension(PWIDTH, PHEIGHT)); super.setBounds(0, 0, PWIDTH, PHEIGHT); super.setIgnoreRepaint(true); super.setFocusable(true); super.addKeyListener(this); } public void addNotify() { super.addNotify();
if (animator == null) { animator = new Thread(this); } startAnimator(); } private void baseRender(float interpolation) {} private void baseUpdate() {} @Override public void keyPressed(final KeyEvent e) { EventQueue.invokeLater(new Runnable() { public void run() { keyEvents.add(e); } }); }
@Override public void keyReleased(KeyEvent e) {}
@Override public void keyTyped(KeyEvent e) {} public static final long nextGameTick() { return System.nanoTime() / 1000000L; } protected void processKeyEvents() {} protected void render(float interpolation) {} public void run() { while (running) { loops = 0; while (nextGameTick() > nextGameTick && loops < MAX_FRAMESKIP) { if (keyEvents.size() > 0) { processKeyEvents(); } baseUpdate(); } baseRender(0.0f);
Thread.yield(); } } private void startAnimator() { running = true; animator.start(); } protected void update() {} } |
Kind Regards,
Charalampos