55 - KbZ key etc. map to the localized letter keys on Linux #55

Closed
jlnr opened this Issue Mar 8, 2011 · 33 comments

Comments

Projects
None yet
2 participants
Owner

jlnr commented Mar 8, 2011

KbA-KbZ have been introduced to make it easy to use keyboard presets like WASD. However, for 
that to make sense, the keys have to be at the same position on localized keyboards, i.e. by 
convention KbZ should be the key next to the KbT key.
On Linux though, KbZ will always be the key to produce the Z letter. This is useless because you 
can already find this key through charToId('z').

Original link: http://code.google.com/p/gosu/issues/detail?id=55

I just noticed that you said the "Z" key should be next to the "T" key. "Z" should be next to "X."

The problem persists regardless though.

Ok, after a long time of avoiding this issue all together, I may have figured out how to fix this. Stumbled upon some helpful information while trying to rebind some keys.

Apparently keycodes and keysymbols are two separate things. Keycodes are the "signals" sent by the keyboard, and are irrespective of layout, while keysymbols are relative to the current layout. As seen in this helpful guide on how to use the linux program showkey (http://linux.about.com/library/cmd/blcmdl1_showkey.htm).

Why does Gosu convert the keycode to a keysymbol before placing it in the keymap (GosuImpl/InputX.cpp, lines 140-151)? Keycodes are the way to go for structural keybindings, as far as I can tell.

Here's an example of some keysymbols versus keycodes.
https://gist.github.com/26400c18f69559bc7607

Owner

jlnr commented Dec 30, 2011

Aaahh. Enlightening! Thanks, I didn't even realize the code in WindowX.cpp would do that (I only maintained it, sloppily it seems) - so at least that should be easy to fix! :)

jlnr closed this Dec 30, 2011

jlnr reopened this Dec 30, 2011

Well, classes are out for the semester, and I need to learn C++ by next semester, so I think it's time I tried to tackle this problem.

That being said, is it worth it to pursue a fix for this issue? I see that the plan is to shift towards GLFW, which handles input from various devices across platforms. Would it be better to simply start working on GLFW stuff, or is that too far down the pipe?

Owner

jlnr commented May 14, 2012

Excellent question! To be honest, with the glfw 2/3 switch, I haven't yet looked at the API. The plan is to rely on glfw as much as possible, but only if they actually handle this issue correctly. :)

Apart from the mathias' glfw 2 code in in my "glfw" branch, you could also try and see if Hanmac's parallel efforts to port Gosu to glfw are any better:

https://github.com/Hanmac/gosu

Getting Gosu to use glfw even one one platform would be a nice start - everything is better than Gosu's fragile X11 support.

Slightly confused by your mention of GLFW 3. It seems as if that is still in development, no?

Both downloads and documentation currently point to 2.7.5.

Owner

jlnr commented May 14, 2012

Yes. The way I understand it, Hanmac actually started contributing to glfw3 in order to accelerate its release. I am not sure if his WIP glfw port of Gosu already uses glfw3 though.

It seems that at one point in time, it was possible to define keys based on their physical location on the US keyboard layout, as per Gosu's design. I have to do a bit more poking around before I can be sure that such is still possible in GLFW 2, let alone 3.

The default method as described in the current documentation is to use the current keyboard layout, as I believe Gosu::Window#char_to_button_id does.

Owner

jlnr commented May 15, 2012

That would be a showstopper because defining WASD controls reliably, even on French keyboards, seems pretty important to me :S

We could just go around GLFW. I assume that is to be the plan of action if GLFW does not support it. Would be better than not allowing structural keybindings at all, especially because currently only Linux is broken.

Owner

jlnr commented May 15, 2012

Right. On the other hand, I hope glfw would fix Linux gamepad support. Have you looked at it already? Is it possible to hook into the window code and keep Gosu's input for the time being?

Haven't looked at it in detail yet. However, GLFW does seem to have cross platform gamepad support. It currently works on all platforms except for OSX. Does Gosu currently have OSX gamepad support? Apparently GLFW doesn't have gamepad support on OSX until 3.

I don't quite understand how Gosu's input system works, but I see no reason why we can't use GLFW for drawing and then use Gosu's current input system.

Owner

jlnr commented May 15, 2012

Yes, Gosu has OS X gamepad support. It could (and in a branch, has) be easily extended to more than one gamepad, so it's pretty future-proof too.

glfw shouldn't even be involved in any drawing - Gosu just needs a Window and GL context from glfw. :)

Cool. I unfortunately see an interim of franken-input managers =/

That's what I meant.

Ok, I'm finally trying to implement a solution to this.

The solution is clearly to use keycodes over keysymbols, as that will give structural keybindings over semantic ones. The remaining problem thus, is to convert semantic-defined keybindings into the structural keycodes used in the backend.

For letters, this is fairly straightforward as there is already a method which processes this conversion. It will merely need to be tweaked to work with the new system.

For miscellaneous keys (Escape, or F1, Shift, Enter, etc), it is a bit more complicated.

The keyboard constants defined by X11 are semantic in nature, and thus bind to the semantic position of keys on the keyboard. To make this system work, I have decided that the backend Map should store keycodes(position data) rather than keysymbols(semantic data). Feel free to challenge this position. As a result, I need to call XKeysymToKeycode() to convert constants like kbEscape into keycodes. However, it requires a Display as it's first argument. How would I get

Thus, the problem is twofold:

  1. Should miscellaneous keys move with keyboard layout? I personally have considered swapping Super (win key) and Caps Lock, and the former is much more useful, but the latter has a prime position. This swapping is an inevitable consequence of the way I plan to implement structural bindings on Linux.
  2. More importantly, how to I convert the constants in the Gosu::ButtonName enum from keysymbols to keycodes using XKeysymToKeycode()? Do you know of another way which doesn't require passing a Display? Can I get a Display pointer somehow?

I can just hardcode the values in, using the raw integer values of the X keyboard constants as keycodes. This will certainly work, as you don't want the values layout dependent. But reability of the code with suffer.

It should be noted that the key constants are (to my knowledge) consistent across computers/keyboards. So it is a viable solution, albeit a slightly ugly one. (src: http://stackoverflow.com/questions/8333474/are-keycodes-consistent-under-linux) However, it seems like the keycodes have changed since I did the experiment seen in the gist above. While Gosu::KbQ would have been 16 before, it would now be 24. Should be noted that the new LTS of Ubuntu is out (as it was not when I made the gist), which means that hardcoding may break "less" systems, if any.

It should be noted that this is also the only way I currently know to implement previously inaccessible printable keys (brackets, period, comma, etc) on Linux. I can continue looking for a better solution, however.

Please let me know ASAP, as this is of critical importance to a game which I'm currently deep in development on.

Owner

jlnr commented Jul 24, 2012

Windows and OS X both use hardcoded key codes in the Buttons*.hpp header and also use these for the backend map.

Do I understand that right - if you hard code the values into the header file, the Display problem is solved?

How can you convert between key codes and unicode characters in buttonIdToChar and vice versa?

Harding the values will solve this problem, yes.

I believe that once inside buttonIdToChar(), a display is available, a display is accessible though pimpl->display. I think from there I can use this to convert keycode --> keysymbol --> character. Should be straightforward enough.

Going from char to ID is a bit more complicated.

Need to look into it a little bit more, but it should be fully functioning soon. Somewhere on the order of hours, I would expect.

Owner

jlnr commented Jul 24, 2012

If the key codes are limited to a small range of values (uint8 or uint16), you can always just build a table in the one direction and invert it to get the other direction.

Oh, wait. If I understand this correctly, buttonCharToId() should just be..
character --> keysymbol --> keycode.

I thought it was complicated, as it has to take into account the current keyboard layout, but I may have over thought it.

Side note: Should I implement constants for things like Period while I'm at it?

Owner

jlnr commented Jul 24, 2012

Yes - you can then just post these constants into a new ticket so I can implement them for OS X and Windows. Thanks :)

Should KbEnter and KbReturn have the same values, or different ones? Linux seems to differentiate, but I personally only see return on this machine.

Owner

jlnr commented Jul 24, 2012

Different ones. (I guess only desktop machines with numpads still have an enter key.)

Oh, I see, it's the numpad key.

I managed to find the code by using another computer, so I'll go ahead and set that.

Where is the SWIG stuff to wrap the button value enum? I thought it would do it automatically, but it seems that's not the case.

Or maybe I'm just not running the right rake task.

Owner

jlnr commented Jul 24, 2012

Line 618 in RubyGosu.swg. It uses the Mac constants right now, but you can change it to use the Linux ones.

Just realized that idToChar() is a class method, so I can't call pimpl->display. Is there any way it could be converted to an instance method? I really need access to a Display to convert from button ID to characters, in order to respect the current keyboard layout.

Owner

jlnr commented Jul 24, 2012

Does it hurt to open the Display in the instance method? Otherwise Gosu should probably have one global Display and open it on demand, and just never close it?

I'm not quite sure what the difference is, honestly.

Currently trying to access Gosu::Input::pimpl from Gosu::Input::charToID(), but it won't compile. Care to lend a hand to someone trying to learn C++?

Getting "invalid use of member ‘Gosu::Input::pimpl’ in static member function"

Owner

jlnr commented Jul 24, 2012

There is no way to access an Input instance's members from a static member function. It's the same as class methods in Ruby which cannot access instance variables because they are not called on an instance.

I think you should just create a new Display. Compare with the global screenWidth()/screenHeight() logic at the top of WindowX.cpp. Or do you need the (X11) Window too for the charToID translation?

Ok, I was actually thinking about changing it to an instance method (though I didn't explain that, thought it did). Still, it's your library. I'll build to your spec.

I only need the display, so this should work fine. It builds now, at least :P

Thanks for the help.

Owner

jlnr commented Jul 24, 2012

That would break code that calls the method without an instance. I would stop you from doing that. :D

Great to hear!

Owner

jlnr commented Apr 1, 2013

@RavensKrag - Now I see why charToId and idToChar are problematic — and my workaround is certainly not elegant :)

jlnr closed this Apr 1, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment