glfwGetKeyOnce() request

tom9729 wrote on Monday, February 20, 2012:

Hello,

I’m switching to GLFW from GLUT (using GLFW3 from subversion).

I’d like to request adding a glfwGetKeyOnce() function that works like
glfwGetKey() except that it only returns true the first time it is checked
(even if the key is still being held down). Basically this is like turning
off key repeat.

I wrote a quick macro version that works without modifying GLFW, but it would
be nice if this could be added upstream (if anyone else finds it useful that
is :-).

char keyOnce[GLFW_KEY_LAST + 1];
#define glfwGetKeyOnce(WINDOW, KEY)				\
	(glfwGetKey(WINDOW, KEY) ?				\
	 (keyOnce[KEY] ? false : (keyOnce[KEY] = true)) :	\
	 (keyOnce[KEY] = false))

keyOnce could be added to the _GLFWwindow struct in “src/internal.h”,
glfwGetKeyOnce() would live as a function next to glfwGetKey() in
"src/input.c".

Thanks,
Tom

elmindreda wrote on Monday, February 20, 2012:

GLFW 3 is developed using Git. You can find a fairly up-to-date version in the
Git repository here on SF.net. If you found something in the Subversion
repository that claims to be GLFW 3, then it’s a several years old, abandoned
prototype. I’ll see about removing those.

It seems like what you want is the rising edge of the key state. If so, you
should be using the key callback.

tom9729 wrote on Monday, February 20, 2012:

My mistake: I am working from the GIT version.

Yes, basically I want the rising/leading edge of the key state. For example, I
don’t want the game to flicker between pause/unpaused if the player presses
the pause key long enough for my inputHandler() to get called more than once
from my game loop.

I’m not really interested in using the key callback. GLFW already maintains a
table of key states and provides glfwGetKey() as an interface.

My understanding from peeking into the code is that if I used the key
callback, then every time I call glfwPollEvents() from my gameloop it will
process window events … etc …, update GLFW’s internal keyboard state, and then
call my key callback for each key press/release. Presumably I would have my
own table of key states that would be updated by this callback, and then
somewhere else in my gameloop I would call some inputHandler() function that
queries my key state table and takes appropriate action.

So why would I want to maintain my own table of keyboard state when I could
just use the existing one and glfwGetKey()? :slight_smile:

elmindreda wrote on Monday, February 20, 2012:

I don’t see the need for the extra state table. If you want to toggle pause
this way, you can do something like this:

void GLFWCALL key_callback(int key, int action)
{
if (key == GLFW_KEY_SPACE && action == GLFW_PRESSED)
toggle_pause();
}

elmindreda wrote on Monday, February 20, 2012:

Sorry, it’s GLFW_PRESS, not GLFW_PRESSED.

tom9729 wrote on Tuesday, February 21, 2012:

Yep, that works if I disable key repeat. But what if I want key repeat for
some keys (eg. movement keys), but not others?

I think also that it is kind of awkward to handle input logic from a callback.
Here is a snippet from my old codebase built on GLUT:

void input()
{
	if (KeyPressedOnce(KEY_F5))
	{		
		ScreenDumpToFile("screenshot.tga");
	}
	
	if (KeyPressedOnce(KEY_ESCAPE))
	{
		exit(0);
	}
	if (KeyPressedOnce(KEY_LOWER_R))
	{
		drawShadowVol = !drawShadowVol;
	}
	
	if (KeyPressedOnce(KEY_LOWER_L))
	{
		numLights += (numLights == maxNumLights ? -maxNumLights : 1); 
	}
	
	angle += 0.1;
	
	float pitch = 0;
	float yaw = 0;
	float rotSpeed = 0.1;
	
	if (KeyPressed(KEY_UP))
	{
		pitch -= rotSpeed;
	}
	
	if (KeyPressed(KEY_DOWN))
	{
		pitch += rotSpeed;
	}	
	if (KeyPressed(KEY_RIGHT))
	{
		yaw += rotSpeed;
	}
	
	if (KeyPressed(KEY_LEFT))
	{
		yaw -= rotSpeed;
	}
	
	// Update object rotation.
	float adj[4] = { pitch, 0, -yaw };		
	QuaternionFromEuler(adj, adj);
	QuaternionMult(objRot, adj, objRot);	
}

I apologize if I am sounding obtuse, but I just think glfwGetKey() is a
better way of getting keyboard input than using the key callback.

elmindreda wrote on Thursday, February 23, 2012:

Sorry for the delay; life happened.

Did I understand correctly that you are using key repeat, the rate of which
varies between machines, to drive movement?

The way I see it, glfwGetKey is a way to avoid having to keep track of the
current key state when you don’t care about the individual events themselves.
However, keeping a separate full state vector for something the key callback
is specifically designed for seems redundant to me.

In my projects, I have a struct that describes logical inputs like forward,
left and jump. I then perform key mapping and any special key logic inside the
key callback, modifying an instance of this struct. It is then passed to the
player motion logic during game update, providing nice separation between keys
and actions.

I don’t think you’re sounding obtuse and I’m sorry if I am. There is an input
mode facility in GLFW 3 where this could conceivably be added. I will mull
this over a few additional times, but currently I don’t think this should be
added, as the key state cache is just a helper and cannot possibly support
every form of input logic.

tom9729 wrote on Thursday, February 23, 2012:

I call that code at a fixed interval, so movement is fairly independent of
machine speed. My game and input logic definitely is a bit tangled up at the
moment though; it’s the result of only working on small demo projects. I’m
hoping on starting a larger project soon, so I will try to do a better job of
abstracting things.

Anyways, thanks for hearing me out. Looking forward to an official release of
GLFW3!