[FIXED] LWJGL in a canvas - Captures keyboard input even when unfocused on JRE7

Started by terryhau, October 06, 2011, 14:59:18

Previous topic - Next topic

terryhau

I have a working LWJGL canvas by using Display.setParent(canvas). After the canvas is focused the first time, it starts grabbing keyboard events, but never lets go. If I click on a text box after, I can not type into it. Actually, anything needing the keyboard will not work, for example, accelerators/hotkeys for menu items and command buttons.

Anyone know of a workaround? Or am I doing something wrong?

Edit: I have found that the problem only happens when running JRE7.

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

public class Window
{
    private JFrame frame;
    private Canvas glCanvas = new Canvas();
    private final JPanel panel1 = new JPanel();
    private final JPanel panel2 = new JPanel();
    private final JTextPane textPane = new JTextPane();

    /**
     * Launch the application.
     */
    public static void main(String[] args)
    {
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                try
                {
                    Window window = new Window();
                    window.frame.setVisible(true);
                    Display.create();
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        });
        
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                GL11.glClearColor(1f, 0f, 0f, 1f);
                GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
                Display.update();
                
                EventQueue.invokeLater(this);
            }
        });
    }
    
    /**
     * Create the application.
     */
    public Window()
    {
        initialize();
    }
    
    /**
     * Initialize the contents of the frame.
     */
    private void initialize()
    {
        frame = new JFrame();
        frame.addWindowListener(new FrameWindowListener());
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new GridLayout(0, 2, 0, 0));
        frame.getContentPane().add(panel1);
        panel1.setLayout(null);
        textPane.setBounds(10, 5, 124, 20);
        
        panel1.add(textPane);
        frame.getContentPane().add(panel2);
        panel2.setLayout(new BorderLayout(0, 0));
        
        glCanvas.setIgnoreRepaint(true);
        panel2.add(glCanvas);
        try { Display.setParent(glCanvas); }
        catch(LWJGLException e) { e.printStackTrace(); }
    }
    
    private class FrameWindowListener extends WindowAdapter
    {
        @Override
        public void windowClosing(WindowEvent e)
        {
            Display.destroy();
        }
    }
}

kappa

Which OS are you on? and which version LWJGL are you using?

terryhau

Sorry

LWJGL 2.7.1
Windows 7 64-bit.

I have tried someone else's LWJGL canvas based app, and it has the same problem.

kappa

Are you able to reproduce the problem when running this? I've tried it on Windows XP and it seems to work fine without capturing keyboard input when clicking elsewhere.

There was a similar issue like the above when running on Linux (which is fixed for the 2.8 release) but not seen or heard about it happening on Windows, maybe an issue with Windows 7?

terryhau

I'm not sure how I can test the problem in that applet, since the whole applet is just one canvas, and there's no other components to click on to 'unfocus' the opengl canvas.
The browser still responds to key presses if that's what you mean.

My program is not an applet. Basically what i have is a Swing window, with a canvas (Display.parent set to the canvas) and a textbox. After clicking in the canvas, I can't type in the textbox, or use the keyboard for anything other than lwjgl input.

Also I'm running JRE 7 64-bit.

Matzon

does this also reproduce the error for you?

1min hackjob - works on my win7 64-bit, 32bit vm

/* 
 * Copyright (c) 2011 LWJGL Project
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are 
 * met:
 * 
 * * Redistributions of source code must retain the above copyright 
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'LWJGL' nor the names of 
 *   its contributors may be used to endorse or promote products derived 
 *   from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.lwjgl.test.input;

import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;

import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.GridLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;

/**
 * <p>
 * </p>
 * @author Brian Matzon <brian@matzon.dk>
 */
public class SwingInputTest {
    private JFrame frame;
    private final JPanel panel1 = new JPanel();
    private final JPanel panel2 = new JPanel();
    private final JTextPane textPane = new JTextPane();
    
	/** The Canvas where the LWJGL Display is added */
	Canvas display_parent;

	/** Thread which runs the main game loop */
	Thread gameThread;

	/** is the game loop running */
	boolean running;
    
    
	public SwingInputTest() {
	}
	
	/**
	 * Once the Canvas is created its add notify method will call this method to
	 * start the LWJGL Display and game loop in another thread.
	 */
	public void startLWJGL() {
		gameThread = new Thread() {
			public void run() {
				running = true;
				try {
					Display.setParent(display_parent);
					//Display.setVSyncEnabled(true);
					Display.create();
					initGL();
				} catch (LWJGLException e) {
					e.printStackTrace();
				}
				gameLoop();
			}
		};
		gameThread.start();
	}


	protected void gameLoop() {
		while(running) {
			System.out.println("--GAMELOOP--");
			
			
			Display.update();
			
			if(Keyboard.isKeyDown(Keyboard.KEY_SPACE)) {
				glClearColor(1f, 0, 0, 0);
				System.out.println("Pigs in space!");
			} else {
				glClearColor(1f, 1f, 1f, 0f);
			}
			glClear(GL_COLOR_BUFFER_BIT);
			
			Display.sync(10);
		}
		Display.destroy();
	}

	protected void initGL() {
	}

	/**
	 * Tell game loop to stop running, after which the LWJGL Display will be destoryed.
	 * The main thread will wait for the Display.destroy() to complete
	 */
	private void stopLWJGL() {
		running = false;
		try {
			gameThread.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	
	private void execute() {
		frame.setVisible(true);
	}

	private void initialize() {
		frame = new JFrame();
		frame.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				frame.remove(display_parent);
				frame.dispose();
			}
		});
        frame.setBounds(100, 100, 450, 300);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new GridLayout(0, 2, 0, 0));
        frame.getContentPane().add(panel1);
        panel1.setLayout(null);
        textPane.setBounds(10, 5, 124, 20);
        
        panel1.add(textPane);
        frame.getContentPane().add(panel2);
        panel2.setLayout(new BorderLayout(0, 0));

        display_parent = new Canvas() {
			public void addNotify() {
				super.addNotify();
				startLWJGL();
			}
			public void removeNotify() {
				stopLWJGL();
				super.removeNotify();
			}
		};
		display_parent.setFocusable(true);
		display_parent.requestFocus();
		display_parent.setIgnoreRepaint(true);
        panel2.add(display_parent);
	}
	
	public static void main(String[] args) {
		SwingInputTest sit = new SwingInputTest();
		sit.initialize();
		sit.execute();
	}	
}

terryhau

Yes, your test reproduces the error for me.
Once I click on the opengl canvas, I can't type in the textbox.

Did you try with JRE6 or JRE7?
I'll try 32-bit VM to see if that's the cause.

Matzon

well, you have to click back in the text box before you can type in it again ?  ::)

terryhau

Yes of course I am doing that. You have to click on the canvas once initially for lwjgl to start capturing keyboard input. Then you click in the textbox. I even get the typing cursor appear showing the text box is focused, but still can't type.

Did you try with JRE6 or JRE7?

I just tested this on a 32bit machine running JRE7 32-bit. The problem still exists.

terryhau

I just tested this on JRE6 (64-bit) and it works fine, So the problem is with JRE7.

After seeing it work properly on JRE6, I noticed some other problems with it on JRE7.
In JRE7, When you click on on the canvas, it doesn't actually get focus because the cursor continues to blink on the textbox (however lwjgl still begins to capture keyboard input).
If I add a focus listener to the canvas, the focusGained and focusLost are never fired. I think it's because the canvas doesn't recognize that it's gaining/losing focus, lwjgl doesn't release the keyboard.

Problem still exists in 2.8.0 nightly.

jediTofu

I read that in JRE7 they edited awt/swing (heavyweight/leightweight components) to work more seamlessly together, so that's probably causing something weird now with LWJGL.
cool story, bro


terryhau

Yes that looks like the same issue.
So I take it, it's up to the Java people to fix it?

belzebub

Is there any update on this one? Anybody that found a workaround yet?

belzebub

This is my current very ugly workaround: adding a global AWT event listener to grab focus events (as the normal textfield events are eaten by awt), then just disabling and enabling everything from toplevel (my container and swing are at the toplevel). To prevent a loop (focus->hide->show->new focus->hide->show->new focus) I kept a boolean to see if you are in "restoring" state.

mainFrame.addWindowFocusListener(new WindowFocusListener() {

         @Override
         public void windowGainedFocus(WindowEvent arg0) {
            log.info("window gained focus");
            SwingUtilities.invokeLater( new Runnable() {
               public void run() {                   
                  if(!inRestoringState){
                     inRestoringState = true;
                     mainFrame.setVisible(false);
                     mainFrame.setVisible(true);
                  } else {
                     inRestoringState = false;
                  }
                 }
               } );
            log.info("refreshed");
         }

         @Override
         public void windowLostFocus(WindowEvent arg0) {
         }
            
         });