Question Trying to implement keypress detection in Lua. [SOLVED]

dgatsoulis

ele2png user
Donator
Joined
Dec 2, 2009
Messages
2,021
Reaction score
623
Points
128
Location
Sparta
I am trying to implement keypress detection in lua.
I am creating a function in a dll with GetAsyncKeyState and I am accessing the function with package.loadlib.

keystate.dll
C++:
#include <Windows.h>

extern "C" __declspec(dllexport) bool IsKeyPressed(int virtualKeyCode)
{
    return (GetAsyncKeyState(virtualKeyCode) & 0x8000) != 0;
}
lua script:
Code:
func = package.loadlib("keystate.dll", "IsKeyPressed")
if func then
    msg1 = ("DLL loaded successfully")
   
else
   msg1 =("Error loading DLL")
end


goals = 0
while goals < 1 do
 keyPressed = func(0x41) -- 0x41 is the virtual key code for the 'A' key
 if keyPressed then
        msg =("A key is pressed")
    else
        msg =("A key is not pressed")
 end
 note:set_text(msg1.." "..msg)
 proc.skip()
 if goals > 0 then end
end

whether the A key is pressed or not, the response I am getting is: "DLL loaded successfully A key is not pressed".

Can anyone tell me why this isn't working?
Any help is much appreciated.
 
Last edited:
I think the problem is that the function you pass in the second parameter of package.loadlib is meant to be a initialisation routine for the library you define, rather than a custom function to be called in the script. It has a required form of "int init(lua_State*).

This should work:

keystate.dll:
C++:
#include <windows.h>

extern "C" {
#include "Lua\lua.h"
#include "Lua\lualib.h"
#include "Lua\lauxlib.h"
}

int my_key_pressed(lua_State * L)
{
    int virtualKeyCode = lua_tointeger(L, 1);
    lua_pushboolean(L, GetAsyncKeyState(virtualKeyCode) & 0x8000 ? 1 : 0);
    return 1;
}

extern "C" __declspec(dllexport) int init(lua_State * L)
{
    static const struct luaL_Reg testlib[] = {
        {"mykeypressed", my_key_pressed},
        {NULL, NULL}
    };
    luaL_register(L, "testlib", testlib);
    return 1;
}
Script:
Code:
keylib = package.loadlib("keystate.dll", "init");
if keylib then
    msg1 = "DLL loaded successfully"
else
    msg1 = "Error loading DLL"
end

keylib()

note = oapi.create_annotation()
note:set_pos (0.25,0.15,0.9,0.95)
note:set_colour ({r=1,g=0.6,b=0.2})

goals = 0
while goals < 1 do
 keyPressed = testlib.mykeypressed(0x41) -- 0x41 is the virtual key code for the 'A' key
 if keyPressed then
        msg =("A key is pressed")
    else
        msg =("A key is not pressed")
 end
 note:set_text(msg1.." "..msg)
 proc.skip()
 if goals > 0 then end
end
It registers the key checking function as a library function. The function itself pulls the requested virtual key code from the lua stack (where it was left from the input argument), and pushes the result back to the stack (where it is available as a return value).
Note that the script, after loading the library, has to call the initialisation routine ("keylib()"), so that the registration is actually performed.

Something that confused the hell out of me when I was trying to find a solution was the fact that the name of the library you register in the initialisation routine (here: "testlib") must not be the same as the name of the library you assign to the return value of the package.loadlib call (here: "keylib"). Otherwise you overwrite the library you just created in the global namespace, and all its methods mysteriously disappear.
 
Actually, it turns out that you can abuse the initialisation routine to give you the answer directly, which simplifies the code a bit.

keystate.dll:

C++:
#include <windows.h>

extern "C" {
#include "Lua\lua.h"
}

extern "C" __declspec(dllexport) int my_key_pressed(lua_State * L)
{
    int virtualKeyCode = lua_tointeger(L, 1);
    lua_pushboolean(L, GetAsyncKeyState(virtualKeyCode) & 0x8000 ? 1 : 0);
    return 1;
}
Script:
Code:
keypress = package.loadlib("keystate.dll", "my_key_pressed");
if keypress then
    msg1 = "DLL loaded successfully"
else
    msg1 = "Error loading DLL"
end


note = oapi.create_annotation()
note:set_pos (0.25,0.15,0.9,0.95)
note:set_colour ({r=1,g=0.6,b=0.2})

goals = 0
while goals < 1 do
 keyPressed = keypress(0x41) -- 0x41 is the virtual key code for the 'A' key
 if keyPressed then
        msg =("A key is pressed")
    else
        msg =("A key is not pressed")
 end
 note:set_text(msg1.." "..msg)
 proc.skip()
 if goals > 0 then end
end
 
I have attached on this post a compiled "keypress.dll". You can unzip the file in your Orbiter root directory and you can access it from a Lua script by
Code:
keypress = package.loadlib("keystate.dll", "my_key_pressed");
You can unload it by
Code:
keypress = nil

This will cause the Lua garbage collector to eventually collect the memory used by the DLL and unload it from memory.

Here is an example of a function that detects when the "A" key has been pressed and toggles a variable (keyA_var) from 0 to 1.
Code:
keyA_var = 0
timeSinceKeyPress_A = 0

function A_key()
    local keyPressed_A = keypress(65)
    if keyPressed_A then
        timeSinceKeyPress_A = timeSinceKeyPress_A + oapi.get_sysstep()
        if timeSinceKeyPress_A >= 0.1 then
            timeSinceKeyPress_A = 0
            if keyA_var == 0 then
                keyA_var = 1
            else
                keyA_var = 0
            end
        end
    else
        timeSinceKeyPress_A = 0
    end
end

When the A key is pressed (for 0.1 seconds) the keyA_var will go from 0 to 1. Press it again and it will go back to 0. You can also do combined keys:
This one toggles the Ctrl_E_var (0 - 1) when you press Ctrl + E for 0.1 seconds.

Code:
Ctrl_E_var = 0
timeSinceKeyPress_Ctrl_E = 0

function Ctrl_E_key()
    local keyPressed_Ctrl = keypress(17)
    local keyPressed_E = keypress(69)
    if keyPressed_Ctrl and keyPressed_E then
        timeSinceKeyPress_Ctrl_E = timeSinceKeyPress_Ctrl_E + oapi.get_sysstep()
        if timeSinceKeyPress_Ctrl_E >= 0.1 then
            timeSinceKeyPress_Ctrl_E = 0
            if Ctrl_E_var == 0 then
                Ctrl_E_var = 1
            else
                Ctrl_E_var = 0
            end
        end
    else
        timeSinceKeyPress_Ctrl_E = 0
    end
end

Below is a list of some of the most common virtual key codes for standard Windows keyboard keys:
Code:
"Backspace": 8
"Tab": 9
"Enter": 13
"Shift": 16
"Ctrl": 17
"Alt": 18
"Caps Lock": 20
"Escape": 27
"Spacebar": 32
"Page Up": 33
"Page Down": 34
"End": 35
"Home": 36
"Left Arrow": 37
"Up Arrow": 38
"Right Arrow": 39
"Down Arrow": 40
"Insert": 45
"Delete": 46
"0": 48
"1": 49
"2": 50
"3": 51
"4": 52
"5": 53
"6": 54
"7": 55
"8": 56
"9": 57
"A": 65
"B": 66
"C": 67
"D": 68
"E": 69
"F": 70
"G": 71
"H": 72
"I": 73
"J": 74
"K": 75
"L": 76
"M": 77
"N": 78
"O": 79
"P": 80
"Q": 81
"R": 82
"S": 83
"T": 84
"U": 85
"V": 86
"W": 87
"X": 88
"Y": 89
"Z": 90
"F1": 112
"F2": 113
"F3": 114
"F4": 115
"F5": 116
"F6": 117
"F7": 118
"F8": 119
"F9": 120
"F10": 121
"F11": 122
"F12": 123

This is not an exhaustive list, but it includes many of the standard keyboard keys that you might need to detect. You can find the full list of virtual key codes in the Windows API documentation.

Once again a big thanks to Martin for implementing this..
 

Attachments

@dgatsoulis @martins There is an interest in porting this method into OpenOrbiter:


I'm assuming from this thread that this code was written by dgatsoulis with assistance from Martin. If so, would you be willing to permit this code to be compiled against OpenOrbiter? It would make the small and loud group of Lua proponents very happy if you did. :)
 
@dgatsoulis @martins There is an interest in porting this method into OpenOrbiter:


I'm assuming from this thread that this code was written by dgatsoulis with assistance from Martin. If so, would you be willing to permit this code to be compiled against OpenOrbiter? It would make the small and loud group of Lua proponents very happy if you did. :)
It was a shot in the dark from me after many unsuccessful attempts to get keypress detection in Lua. It was actually Martin that provided the correct working code. For my small part in this, I am absolutely fine for it to be used in OpenOrbiter.
 
It was a shot in the dark from me after many unsuccessful attempts to get keypress detection in Lua. It was actually Martin that provided the correct working code. For my small part in this, I am absolutely fine for it to be used in OpenOrbiter.
Thanks. It's unclear where it sits with regard to the OpenOrbiter license so it was best to ask. It isn't a part of the code base.
 
I thought I would not be that guy but I could not help myself.

I think the problem is that the function you pass in the second parameter of package.loadlib is meant to be a initialisation routine for the library you define, rather than a custom function to be called in the script. It has a required form of "int init(lua_State*).

This should work:

keystate.dll:
C++:
#include <windows.h>

extern "C" {
#include "Lua\lua.h"
#include "Lua\lualib.h"
#include "Lua\lauxlib.h"
}

int my_key_pressed(lua_State * L)
{
    int virtualKeyCode = lua_tointeger(L, 1);
    lua_pushboolean(L, GetAsyncKeyState(virtualKeyCode) & 0x8000 ? 1 : 0);
    return 1;
}

extern "C" __declspec(dllexport) int init(lua_State * L)
{
    static const struct luaL_Reg testlib[] = {
        {"mykeypressed", my_key_pressed},
        {NULL, NULL}
    };
    luaL_register(L, "testlib", testlib);
    return 1;
}
Script:
Code:
keylib = package.loadlib("keystate.dll", "init");
if keylib then
    msg1 = "DLL loaded successfully"
else
    msg1 = "Error loading DLL"
end

keylib()

note = oapi.create_annotation()
note:set_pos (0.25,0.15,0.9,0.95)
note:set_colour ({r=1,g=0.6,b=0.2})

goals = 0
while goals < 1 do
 keyPressed = testlib.mykeypressed(0x41) -- 0x41 is the virtual key code for the 'A' key
 if keyPressed then
        msg =("A key is pressed")
    else
        msg =("A key is not pressed")
 end
 note:set_text(msg1.." "..msg)
 proc.skip()
 if goals > 0 then end
end
It registers the key checking function as a library function. The function itself pulls the requested virtual key code from the lua stack (where it was left from the input argument), and pushes the result back to the stack (where it is available as a return value).
Note that the script, after loading the library, has to call the initialisation routine ("keylib()"), so that the registration is actually performed.

Something that confused the hell out of me when I was trying to find a solution was the fact that the name of the library you register in the initialisation routine (here: "testlib") must not be the same as the name of the library you assign to the return value of the package.loadlib call (here: "keylib"). Otherwise you overwrite the library you just created in the global namespace, and all its methods mysteriously disappear.

I didn't think I would be that guy but I guess I decided to be. Simplified this and it should still work.

keylib = package.loadlib("keystate.dll", "init")
msg1 = keylib and "DLL loaded successfully" or "Error loading DLL"

if keylib then keylib() end

note = oapi.create_annotation()
note:set_pos(0.25, 0.15, 0.9, 0.95)
note:set_colour({r=1, g=0.6, b=0.2})

while true do
keyPressed = testlib.mykeypressed(0x41) -- 'A' key
msg = keyPressed and "A key is pressed" or "A key is not pressed"
note:set_text(msg1 .. " " .. msg)
proc.skip()
end

I combined the DLL check and message assignment using a ternary operator
The goals variable was replaced with while true as unless it's used outside of the script, it does not activate in any capacity other to perform as a loop that can never be disabled.
The original code will always attempt to call keylib() regardless of whether the DLL loaded successfully which would cause an error if keylib is nil. My version adds a safety check. If that is intentional functionality, then
keylib = package.loadlib("keystate.dll", "init")
msg1 = keylib and "DLL loaded successfully" or "Error loading DLL"
keylib() -- Always call, just like the original
 
I have been out of the loop of this thread for a while. Why can't oapi.keydown(kstate, OAPI_KEY.KEY) be used in clbk_consumebufferedkey(key, down, kstate) for this in Lua? I don't see why a compiled DLL is needed at all anymore.
My Lua add-ons all have keypress events.
 
Back
Top