GLFW KeyCallback Only certain keys seem to work

Started by Lugen, July 06, 2015, 08:06:27

Previous topic - Next topic

Lugen

So this is a rather odd problem I'm having. I would guess that I'm missing something here.

So I'm currently following this tutorial and I'm getting a strange problem with keyboard input. At first it appeared that no keys where detected but then after pressing randomly I found only a few where triggering the callback and even stranger, pressing these keys would for a brief moment enable a couple of other keys as well.

I'm pointing to the GLFWKeyCallback function through this class.
import org.lwjgl.glfw.GLFW;
import org.lwjgl.glfw.GLFWKeyCallback;

public class Input extends GLFWKeyCallback{

	// Keycode sate list, size set according to available number of keys
	public static boolean[] keys = new boolean[65536];
	
	// Main key callback
	// This functions needs to be wrapped in a class in order to be referenced
	public void invoke(long window, int key, int scancode, int action, int mods) {
		System.out.println(key);
		keys[key] = action != GLFW.GLFW_RELEASE;
	}

	// Key down
	public static boolean isKeyDown(int keycode) {
		return keys[keycode];
	}
}


Then set the callback like this during initialization of the window.
if (glfwInit() == GL_FALSE) {
			return;
		}

		glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

		window = glfwCreateWindow(width, height, "Tutorial", NULL, NULL);
		if (window == NULL) {
			System.err.println("Could not create GLFW window.");
			return;
		}

		ByteBuffer videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
		glfwSetWindowPos(window, (GLFWvidmode.width(videoMode) - width) / 2,
				(GLFWvidmode.height(videoMode) - height) / 2);

		glfwSetKeyCallback(window, new Input());

		glfwMakeContextCurrent(window);
		glfwShowWindow(window);
		GLContext.createFromCurrent();


Then in my update function I simply run glfwPollEvents(). Ups is at 60.

I've also tried this code and it gives the same result.

Any clues whatsoever are very much appreciated!

spasi

The part of your code that polls events and tests for key state is missing. Could you please post a fully working example?

From the above code, I see two issues: a) you're not holding a reference to "new Input()", the program will crash if you don't and b) the boolean[] keys array is too big; you don't need more than GLFW_KEY_LAST+1 elements.

Lugen

Quote from: spasi on July 06, 2015, 08:19:08
The part of your code that polls events and tests for key state is missing. Could you please post a fully working example?

From the above code, I see two issues: a) you're not holding a reference to "new Input()", the program will crash if you don't and b) the boolean[] keys array is too big; you don't need more than GLFW_KEY_LAST+1 elements.

Thanks for the fast reply.
I mentioned the poll test passingly as it's just this code.
private void update() {
		glfwPollEvents();
		level.update();
	}


Is this what you mean by holding a reference to new Input()?
"input" in this case is a private variable in the main class declaration.
input = new Input();
		glfwSetKeyCallback(window, input);


Heck, I'll just post the entire Main-class in case that helps.
package tutorial;

// Imports static methods
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL13.*;
import static org.lwjgl.system.MemoryUtil.*;

import java.nio.ByteBuffer;

import org.lwjgl.glfw.GLFWvidmode;
import org.lwjgl.opengl.GLContext;

import tutorial.graphic.Shader;
import tutorial.input.Input;
import tutorial.level.Level;
import tutorial.math.Mat4;

public class Main implements Runnable {

	private int width = 1280;
	private int height = 720;

	private Thread thread;
	private boolean bRunning = false;

	// There are no objects in C, only identifiers
	private long window;
	private Input input;

	// Level instance
	private Level level;

	public void start() {
		bRunning = true;
		thread = new Thread(this, "Game");
		thread.start();
	}

	// Initiate opengl, needs to be on same thread as render()
	public void init() {
		// Handle if initialization failed
		if (glfwInit() == GL_FALSE) {
			return;
		}

		// Create window
		glfwWindowHint(GLFW_RESIZABLE, GL_TRUE);

		// Will return id for window, perhaps memory adress
		window = glfwCreateWindow(width, height, "Tutorial", NULL, NULL);
		if (window == NULL) {
			System.err.println("Could not create GLFW window.");
			return;
		}
		// Set window position
		ByteBuffer videoMode = glfwGetVideoMode(glfwGetPrimaryMonitor());
		glfwSetWindowPos(window, (GLFWvidmode.width(videoMode) - width) / 2,
				(GLFWvidmode.height(videoMode) - height) / 2);

		// Initiate key callback with our Input class
		input = new Input();
		glfwSetKeyCallback(window, input);

		glfwMakeContextCurrent(window);
		glfwShowWindow(window);
		// Make sure Opengl is running under the current window context
		GLContext.createFromCurrent();

		// OpenGl stuff
		glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
		glEnable(GL_DEPTH_TEST);
		glActiveTexture(GL_TEXTURE1);

		// Print OpenGL info
		System.out.println("OpenGl: " + glGetString(GL_VERSION));

		// Load shaders
		Shader.loadAll();

		// Projection matrix
		Mat4 matrixProjection = Mat4.orthographic(-10, 10, -10 * 9 / 16, 10 * 9 / 16, -1, 1);
		Shader.BG.setUniformMat4("projection", matrixProjection);
		Shader.BG.setUniformInt("tex", 1);

		Shader.BIRD.setUniformMat4("projection", matrixProjection);
		Shader.BIRD.setUniformInt("tex", 1);

		// Create level
		level = new Level();
	}

	public void run() {
		init();

		// Timer
		long timer = System.currentTimeMillis();
		long timeLast = System.nanoTime();
		// Not time delta for checking time between frames
		double timeDelta = 0;
		double timeUpdate = 1000000000.0 / 60.0;

		int countUpdate = 0;
		int countRender = 0;

		while (bRunning) {
			long timeNow = System.nanoTime();
			timeDelta += (timeNow - timeLast) / timeUpdate;
			timeLast = timeNow;

			// Every time delta reaches 1 do update
			if (timeDelta >= 1.0) {
				update();
				countUpdate++;
				timeDelta--;
			}

			render();
			countRender++;

			// Reset counters when one second has passed
			if (System.currentTimeMillis() - timer > 1000) {
				timer += 1000;
				System.out.println(countUpdate + " ups, " + countRender + " fps");
				countUpdate = 0;
				countRender = 0;
			}

			// Window close event
			if (glfwWindowShouldClose(window) == GL_TRUE)
				bRunning = false;
		}

		glfwDestroyWindow(window);
		glfwTerminate();
	}

	// Update
	private void update() {
		glfwPollEvents();
		level.update();
	}

	// Render
	private void render() {
		// Clear previous frame
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		// Draw new things
		level.render();

		int error = glGetError();
		if (error != GL_NO_ERROR)
			System.out.println(error);

		glfwSwapBuffers(window);
	}

	public static void main(String[] args) {
		new Main().start();
	}
}

spasi

Key input works fine for me with the above code. Could you describe the sequence of key presses/releases you're doing and the expected vs actual outcome?

Lugen

Quote from: spasi on July 06, 2015, 09:37:23
Key input works fine for me with the above code. Could you describe the sequence of key presses/releases you're doing and the expected vs actual outcome?
Interesting, might be worth to mention that I have a Swedish keyboard but that shouldn't make any difference right?
http://frontype.com/keyboarding/540px-Computer-keyboard-Sweden.svg.png

Right now I'm just printing the key id to console whenever the key callback is being called, to check which keys generate a response. The pattern seems to to be that only the buttons marked with red (see reference above) calls the callback and some other buttons (W , A , S, D, Enter, Backspace, + and perhaps more) works as well if pressed shortly after.

Am I being clear? To me it seems like there's some weird timing going on and/or polls being lost for some reason.

I also have controls set up for a player object to move around. The controls do actually work but only right after pressing those red buttons as mentioned.
Update method from the player class. Is executed after glfwPollEvents().
public void update() {
		if (Input.keys[GLFW.GLFW_KEY_W])
			position.y += 0.1f;
		if (Input.keys[GLFW.GLFW_KEY_S])
			position.y -= 0.1f;
		if (Input.keys[GLFW.GLFW_KEY_A])
			position.x -= 0.1f;
		if (Input.keys[GLFW.GLFW_KEY_D])
			position.x += 0.1f;
	}

spasi

Quote from: Lugen on July 06, 2015, 11:35:28The pattern seems to to be that only the buttons marked with red (see reference above) calls the callback and some other buttons (W , A , S, D, Enter, Backspace, + and perhaps more) works as well if pressed shortly after.

That sounds very weird. Could you try on another PC or a different keyboard?

Quote from: Lugen on July 06, 2015, 11:35:28To me it seems like there's some weird timing going on and/or polls being lost for some reason.

Does it help if you enable vsync? Call glfwSwapInterval(1) after glfwMakeContextCurrent(window).

Lugen

Quote from: spasi on July 06, 2015, 15:29:51
Quote from: Lugen on July 06, 2015, 11:35:28The pattern seems to to be that only the buttons marked with red (see reference above) calls the callback and some other buttons (W , A , S, D, Enter, Backspace, + and perhaps more) works as well if pressed shortly after.

That sounds very weird. Could you try on another PC or a different keyboard?

Not at the moment sorry. It's weird indeed.

Quote from: spasi on July 06, 2015, 15:29:51
Quote from: Lugen on July 06, 2015, 11:35:28To me it seems like there's some weird timing going on and/or polls being lost for some reason.

Does it help if you enable vsync? Call glfwSwapInterval(1) after glfwMakeContextCurrent(window).

No difference. Seems like it was already enabled anyway.

Studying the console output the two "red keys" seem to pretty much enable any other key in the same way as mentioned. Also I noticed that pressing any other key while holding one of the "red keys" down stops their keycodes to get printed, so something is happening there. It's like those two buttons act like some kind of floodgate, blocking the rest of the keyboard.

Btw, doing input with the standard java libraries work fine. I haven't had any input problems with any linux applications before.

spasi

Sounds like GLFW cannot handle your keyboard layout properly. Have you tried switching layouts from the linux keyboard settings? What Linux distribution are you using?

Lugen

Quote from: spasi on July 06, 2015, 18:22:13
Sounds like GLFW cannot handle your keyboard layout properly. Have you tried switching layouts from the linux keyboard settings? What Linux distribution are you using?
Tried some of the English keyboard layouts (US, Canada, UK) but didn't seem to get any response at all.
I'm on Ubuntu 12.04, thinking of upgrading to 14.04 someday, just haven't bothered with it for the time being, just mentioning in case that's relevant. I also replaced the default desktop environment "Unity" with "Gnome Classic".

spasi

Could you try the Events demo? Try pressing a few keys, what output are you getting?

Lugen

Quote from: spasi on July 06, 2015, 20:18:52
Could you try the Events demo? Try pressing a few keys, what output are you getting?

Seems like it's working as it should. I can move the mouse cursor around and the console responds to any key presses with messages like this.
53.146: Window [0x7F5B80259F60] char mods h
53.146: Window [0x7F5B80259F60] char h


That's a positive sign. Will have a closer look at the code and see if I understand it. Anything particular with this setup I should take interest in?

spasi

The demo simply prints the events received to stdout. This is the output I see when pressing the 'H' key:

Quote28.007: Window [0xE9E850] key [GLFW_KEY_H - 35] was pressed
28.008: Window [0xE9E850] char mods h
28.008: Window [0xE9E850] char h
28.075: Window [0xE9E850] key [GLFW_KEY_H - 35] was released

spasi

Looks like there is indeed an issue related to input method in the latest GLFW builds.

Lugen

Quote from: spasi on July 07, 2015, 09:24:59
The demo simply prints the events received to stdout. This is the output I see when pressing the 'H' key:

Quote28.007: Window [0xE9E850] key [GLFW_KEY_H - 35] was pressed
28.008: Window [0xE9E850] char mods h
28.008: Window [0xE9E850] char h
28.075: Window [0xE9E850] key [GLFW_KEY_H - 35] was released

With that in mind I tried the demo again and noticed the same button enabling pattern. Only the "red keys" gives me the GLFW printouts.
The key next to "Backspace".
2.141: Window [0x7FA3CC268440] key [GLFW_KEY_EQUAL - 21] was pressed
2.220: Window [0x7FA3CC268440] key [GLFW_KEY_EQUAL - 21] was released

The key next to "Enter".
6.255: Window [0x7FD27826DFE0] key [GLFW_KEY_RIGHT_BRACKET - 35] was pressed
6.415: Window [0x7FD27826DFE0] key [GLFW_KEY_RIGHT_BRACKET - 35] was released

Same pattern as before. Some keys get GLFW messages after pressing this key.
9.922: Window [0x7FD6082705C0] key [GLFW_KEY_EQUAL - 21] was pressed
10.034: Window [0x7FD6082705C0] key [GLFW_KEY_EQUAL - 21] was released
10.162: Window [0x7FD6082705C0] key [GLFW_KEY_H - 43] was pressed
10.274: Window [0x7FD6082705C0] key [GLFW_KEY_H - 43] was released

And others after pressing this one.
51.018: Window [0x7FFAA4280760] key [GLFW_KEY_RIGHT_BRACKET - 35] was pressed
51.122: Window [0x7FFAA4280760] key [GLFW_KEY_RIGHT_BRACKET - 35] was released
51.218: Window [0x7FFAA4280760] key [GLFW_KEY_S - 39] was pressed
51.290: Window [0x7FFAA4280760] key [GLFW_KEY_S - 39] was released

I also get this when the program starts, but I assume the error is just a test?
---- [ Error callback test ] ----
[LWJGL] GLFW_NOT_INITIALIZED error
   Description : The GLFW library is not initialized
   Stacktrace  :
      test.Events.main(Events.java:42)
---- [ Error callback done ] ----
GLFW initialized
Window opened.
0.210: Window [0x7FFAA4280760] moved to 1, 28
0.229: Window [0x7FFAA4280760] restored
0.230: Window [0x7FFAA4280760] refreshed
0.230: Window [0x7FFAA4280760] cursor entered
0.231: Window [0x7FFAA4280760] gained focus

Quote from: spasi on July 07, 2015, 09:38:39
Looks like there is indeed an issue related to input method in the latest GLFW builds.
That makes sense if the reason why the keys aren't working is because they are being interpreted as characters. Pressing those two special keys seem to temporarily "correct" them.

Lugen

In case this info is useful. I recently switched to Ubuntu 14.04 and key input seems to work fine now. Still using the same version, 3.0.0a.