[Solved] GLFW input handling not smooth

My input handler seems to have trouble handling clicks, and will hang on a given state in my [icode]Button[/icode] class too long.

Here is my input handler:

public Input(long window) {
		Arrays.fill(states, false);
		keyCallback = new GLFWKeyCallback() {
        	@Override
        	public void invoke(long window, int key, int scancode, int action, int mods) {
        		boolean down = action == GLFW.GLFW_PRESS;
        		int keycode = key;
        		if (states[key] && !down) {
        			for (Adapter adapter : adapters) {
        				adapter.key(keycode);
        			}
        		}
        		states[key] = down;
        	}
        };
        GLFW.glfwSetKeyCallback(window, keyCallback);
        mouseStateCallback = new GLFWMouseButtonCallback() {
			@Override
			public void invoke(long window, int button, int action, int mods) {
				boolean down = GLFW.glfwGetMouseButton(window, button) == GLFW.GLFW_PRESS;
				if (!down && mouseDown) {
@@					for (Adapter adapter : adapters) {
@@						adapter.click(x, y);
@@					}
				}
				mouseDown = down;
			}
        };
        GLFW.glfwSetMouseButtonCallback(window, mouseStateCallback);
        cursorCallback = new GLFWCursorPosCallback() {
			@Override
			public void invoke(long window, double mousex, double mousey) {
@@				boolean down = GLFW.glfwGetMouseButton(window, GLFW.GLFW_MOUSE_BUTTON_1) == GLFW.GLFW_PRESS;
@@				if (!down && mouseDown) {
@@					for (Adapter adapter : adapters) {
@@						adapter.click(mousex, mousey);
@@					}
				}
				mouseDown = down;
				x = mousex;
				y = mousey;
				for (Adapter adapter : adapters) {
					adapter.mouse(x, y, mouseDown);
				}
			}
        };
        GLFW.glfwSetCursorPosCallback(window, cursorCallback);
        cursorEnterCallback = new GLFWCursorEnterCallback() {
			@Override
			public void invoke(long window, int entered) {
				inScreen = entered == GL11.GL_TRUE;
			}
        };
        GLFW.glfwSetCursorEnterCallback(window, cursorEnterCallback);
	}

The lag shouldn’t be caused by the rendering itself because it renders one line of text (using a vertex buffer object for each glyph) and renders one vertex buffer object for the button. However, when my Adapter interfaces handle the input, there is a hangup where they sometimes don’t even receive the event.

Any help would be appreciated.

  1. In the GLFWMouseButtonCallback, you don’t have to use glfwGetMouseButton(). The action parameter tells you the button state.

  2. The action parameter has 3 possible values: GLFW_PRESS, GLFW_RELEASE and GLFW_REPEAT. If you’re only interested in RELEASE events, check for that explicitly. This is probably what’s causing your issue.

  3. Not sure why you’re doing that, but you should not have to check the button state and handle click events in the GLFWCursorPosCallback. Only doing it in GLFWMouseButtonCallback should work fine.

Thanks Spasi :slight_smile:

Now clicks are handled just like they should :slight_smile:

However, I think I made an error too in my button state handling code. When I pass mouse events to my input adapters, I also pass the state of the mouse (whether a button is clicked). However, my state never seems to be considered true.

public Input(long window) {
		keyCallback = new GLFWKeyCallback() {
        	@Override
        	public void invoke(long window, int key, int scancode, int action, int mods) {
        		boolean down = action == GLFW.GLFW_RELEASE;
        		int keycode = key;
        		if (down) {
        			for (Adapter adapter : adapters) {
        				adapter.key(keycode);
        			}
        		}
        		states[key] = !down;
        	}
        };
        GLFW.glfwSetKeyCallback(window, keyCallback);
        mouseStateCallback = new GLFWMouseButtonCallback() {
			@Override
			public void invoke(long window, int button, int action, int mods) {
				if (action == GLFW.GLFW_RELEASE) {
					for (Adapter adapter : adapters) {
						adapter.click(x, y);
					}
				}
@@				mouseDown = (action == GLFW.GLFW_PRESS);
			}
        };
        GLFW.glfwSetMouseButtonCallback(window, mouseStateCallback);
        cursorCallback = new GLFWCursorPosCallback() {
			@Override
			public void invoke(long window, double mousex, double mousey) {
				x = mousex;
				y = mousey;
				for (Adapter adapter : adapters) {
@@					adapter.mouse(x, y, mouseDown);
				}
			}
        };
        GLFW.glfwSetCursorPosCallback(window, cursorCallback);
        cursorEnterCallback = new GLFWCursorEnterCallback() {
			@Override
			public void invoke(long window, int entered) {
				inScreen = entered == GL11.GL_TRUE;
			}
        };
        GLFW.glfwSetCursorEnterCallback(window, cursorEnterCallback);
	}

Try changing:

mouseDown = (action == GLFW.GLFW_PRESS);

to

mouseDown = (action != GLFW.GLFW_RELEASE); // either PRESS or REPEAT

Okay, so I think the issue with the state is over the separated input handling.

The mouse state will only be the same if the cursor position was updated recently.

I think this is the case because when I hold left click and move it registers the state change, while it doesn’t when I don’t move. Any ideas for workarounds?

Okay I tested my theory and fixed the problem. Thanks for your help Spasi!

Code:

public Input(long window) {
		keyCallback = new GLFWKeyCallback() {
        	@Override
        	public void invoke(long window, int key, int scancode, int action, int mods) {
        		boolean down = action == GLFW.GLFW_RELEASE;
        		int keycode = key;
        		if (down) {
        			for (Adapter adapter : adapters) {
        				adapter.key(keycode);
        			}
        		}
        		states[key] = !down;
        	}
        };
        GLFW.glfwSetKeyCallback(window, keyCallback);
        mouseStateCallback = new GLFWMouseButtonCallback() {
			@Override
			public void invoke(long window, int button, int action, int mods) {
				if (action == GLFW.GLFW_RELEASE) {
					for (Adapter adapter : adapters) {
						adapter.click(x, y);
					}
				}
				mouseDown = (action != GLFW.GLFW_RELEASE);
				if (inScreen) {	// this if-gate prevents the same event polling when the mouse leaves the screen
					for (Adapter adapter : adapters) {
						adapter.mouse(x, y, mouseDown);
					}
				}
			}
        };
        GLFW.glfwSetMouseButtonCallback(window, mouseStateCallback);
        cursorCallback = new GLFWCursorPosCallback() {
			@Override
			public void invoke(long window, double mousex, double mousey) {
				x = mousex;
				y = mousey;
				if (inScreen) {	// this if-gate prevents the same event polling when the mouse leaves the screen
					for (Adapter adapter : adapters) {
						adapter.mouse(x, y, mouseDown);
					}
				}
			}
        };
        GLFW.glfwSetCursorPosCallback(window, cursorCallback);
        cursorEnterCallback = new GLFWCursorEnterCallback() {
			@Override
			public void invoke(long window, int entered) {
				inScreen = entered == GL11.GL_TRUE;
			}
        };
        GLFW.glfwSetCursorEnterCallback(window, cursorEnterCallback);
	}