Ancurio / mkxp

Free Software implementation of the Ruby Game Scripting System (RGSS)
GNU General Public License v2.0
511 stars 129 forks source link

Providing full keyboard input (Win32API emulation) #73

Closed Ancurio closed 9 years ago

Ancurio commented 9 years ago

The following user32.dll functions are commonly used in scripts to query keyboard keys:

GetKeyState: Queries the current state of a key, and also whether keys such as caps lock are toggled on

GetAsyncKeyState Queries the current state, but also reports whether the key was pressed inbetween calls (so key presses are never missed due to timing)

GetKeyboardState: Like GetKeyState, but copies the state of all virtual key codes into a 256 byte array

The first two functions take Virtual Key Codes as parameters, which from what I understand, act similarly to SDL scancodes (ie. raw identifiers of physical keys, unaffected by keyboard layouts). The first few codes represent the mouse buttons, which are already available via mkxp's Input module extensions.

The way to enable implementing these that I currently have in mind is this: A single new function (let's call it raw_key_states, either under the System or Input module) returns a 512 byte string into which all SDL scancode states are memcpied into. We already keep such a state array anyway, so the new code literally amounts to a few lines.

For GetKeyState, we just translate the windows key code to the corresponding SDL one and return the byte in the array. I'm not sure if it's worth to do extra work for the special GetAsyncKeyState behavior of not missing events; in practice, the RGSS Input functions act like GetKeyState too. As for the state of toggled keys (capslock, numlock etc.), I don't think anyone makes use of this so it's not worth considering either. One thing I'm not sure of yet is how often to retrieve the raw SDL keystate array. In my current code I hook into Graphics.update and refresh one shared array there once per frame. I assume that a game that doesn't call Graphics.update very often or not at all cannot do anything useful anyway, so that shouldn't lead to complications where script code expects completely fresh key states.

For GetKeyboardState one would just iterate through all available Windows Key Codes and write the result of GetKeyState into the provided string buffer.

Here's the tentative patch for mkxp to implement raw_key_states, and here's my current win32api wrapper script that makes use of it.

If you have any comments / ideas / suggestions, please post them!

Ancurio commented 9 years ago

Ait, just committed it, seems to work fine: 4a8b0f30c8dfc59deb3a4b30ae6a845121e3f701

(Small change: raw_key_states is in MKXP module instead of System)

openmac commented 9 years ago

I built mkxp from git and got your wrapper script from here: http://pastebin.com/zXW1hdrx

Then tried to start Sin Unsullied (http://legacyblade.com/downloads/SinUnsullied/SinUnsullied_B002.zip), which uses the Blizzard ABS script, and after fixing the usual syntax issues, I got this error:

[user32:GetKeyboardLayout] [0] 
win32_wrap.rb:228:in `call': undefined method `WIN32' for Scancodes:Module (NoMethodError)
    from win32_wrap.rb:321:in `call'
    from 107:Blizz-ABS Part 2:7532:in `update'
    from 101:Tons of Addons Part 2:695:in `block in main'
    from 101:Tons of Addons Part 2:693:in `loop'
    from 101:Tons of Addons Part 2:693:in `main'
    from 108:Blizz-ABS Part 3:6715:in `main'
    from 131:Main:15:in `<main>'

Is it supposed to work out of the box or it still needs some changes to the used scripts?

Thanks a lot for adding this Win32API emulation, by the way :)

Ancurio commented 9 years ago

@openmac I have fixed all syntax errors I saw, please try again.

openmac commented 9 years ago

Thanks, I download the updated win32_wrap.rb from pastebin and here is the new error:

[user32:GetKeyboardLayout] [0] 
win32_wrap.rb:230:in `block in call': undefined method `contains' for #<Hash:0x0000010211d6d8> (NoMethodError)
    from win32_wrap.rb:228:in `each'
    from win32_wrap.rb:228:in `call'
    from win32_wrap.rb:321:in `call'
    from 107:Blizz-ABS Part 2:7532:in `update'
    from 101:Tons of Addons Part 2:695:in `block in main'
    from 101:Tons of Addons Part 2:693:in `loop'
    from 101:Tons of Addons Part 2:693:in `main'
    from 108:Blizz-ABS Part 3:6715:in `main'
    from 131:Main:15:in `<main>'
cremno commented 9 years ago

@openmac: Try replacing contains in line 230 with key?.

openmac commented 9 years ago

Thanks, here is the new error:

[user32:GetKeyboardLayout] [0] 
win32_wrap.rb:236:in `block in call': undefined method `setbyte' for #<Array:0x0000010217e870> (NoMethodError)
    from win32_wrap.rb:228:in `each'
    from win32_wrap.rb:228:in `call'
    from win32_wrap.rb:321:in `call'
    from 107:Blizz-ABS Part 2:7532:in `update'
    from 101:Tons of Addons Part 2:695:in `block in main'
    from 101:Tons of Addons Part 2:693:in `loop'
    from 101:Tons of Addons Part 2:693:in `main'
    from 108:Blizz-ABS Part 3:6715:in `main'
    from 131:Main:15:in `<main>'
Ancurio commented 9 years ago

Welp, blind fixing bug really wasn't a good idea. I have put up a new revision that works (tested).

openmac commented 9 years ago

Thanks, it now starts without any error but in the game's initial screen that asks whether to choose Full Screen or not, no matter what key I press, it does not seem to be registered, so it is stuck there.

Ancurio commented 9 years ago

New version up.

openmac commented 9 years ago

It works now :) There are other problems that I think they do not have anything to do with the keyboard and are issues with the game scripts Ruby 1.8.x syntax. Thanks again.

Edit: I fixed the game scripts syntax issues and it seems to work perfectly, you are awesome!

JeremyRand commented 1 year ago

here's my current win32api wrapper script that makes use of it.

Any reason this wrapper isn't part of the Git repo? (And independently of that, what's the license for that wrapper? Same as mkxp itself?)

Ancurio commented 1 year ago

@JeremyRand the reason is that I didn't consider it good quality code, just barely enough to make certain projects work. As for license, feel free to use it under CC0.