Omnimaga

Calculator Community => TI Calculators => Lua => Topic started by: hoffa on October 01, 2011, 06:59:39 pm

Title: [Howto] Check if a key is pressed down (more or less)
Post by: hoffa on October 01, 2011, 06:59:39 pm
(http://webgel.net/bf/1/key.png)

I was trying to find a way to know if a key is held down or not (for a Lua CHIP-8 emulator I'm writing, more as a proof of concept than anything else), but as you probably already knew if you've programmed in Lua on the TI-Nspire, there is no way of knowing anything except when a key is first pressed. That is because of the way the TI-Nspire has been made; while most of the keys only send one event signal each time the key is pressed, the tab and arrow keys continuously keep sending those signals after a short delay while holding the key down. What you first want to do is to somewhat be able to know when one of those special keys is pressed down. It won't be as accurate as you might want it to be, but using some timer tricks (to tackle that annoying delay gap before it starts bombarding with events among others) it is possible to implement something of an isDown() function. Here's some fairly simple code that displays "true" when the tab key is held down, it should be pretty self-explanatory (and as you will notice, it isn't that accurate. It might be made slightly more accurate by tuning those interval values):
Code: [Select]
Key = class()

function Key:init(eventInterval, firstEventInterval)
    self.keyDown = false
    self.eventInterval = eventInterval or 150
    self.firstEventInterval = firstEventInterval or 750
    self.time = {firstEvent = 0, lastEvent = 0}
end

function Key:keyEvent(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    if not self.keyDown then
        self.time.firstEvent = timeNow
    end
    self.time.lastEvent = timeNow
    self.keyDown = true
end

function Key:isDown(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    self.keyDown = timeNow - self.time.firstEvent < self.firstEventInterval
                or timeNow - self.time.lastEvent < self.eventInterval
    return self.keyDown
end

function on.tabKey()
    tab:keyEvent()
end

function on.timer()
    platform.window:invalidate()
end

function on.create()
    timer.start(0.01)
end

tab = Key()
function on.paint(gc)
    gc:drawString("Tab key down: " .. tostring(tab:isDown()), 0, 0, "top")
end

Now to know if a number or character key is pressed down, well, there is no way to do that. But one workaround would be to use the tab key. For example, if you wanted to make it possible for the program to know when a certain key is held down, you'd first have to press that key and then quickly press and hold the tab button. After some coding, we have this:

Code: [Select]
MasterKey = class()

function MasterKey:init(eventInterval, firstEventInterval)
    self.keyDown = false
    self.eventInterval = eventInterval or 200
    self.firstEventInterval = firstEventInterval or 750
    self.time = {firstEvent = 0, lastEvent = 0}
end

function MasterKey:keyEvent(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    if not self.keyDown then
        self.time.firstEvent = timeNow
    end
    self.time.lastEvent = timeNow
    self.keyDown = true
end

function MasterKey:updateStatus(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    self.keyDown = timeNow - self.time.firstEvent < self.firstEventInterval
                or timeNow - self.time.lastEvent < self.eventInterval
end

function MasterKey:isDown()
    return self.keyDown
end

Keys = class()

function Keys:init(keys, eventInterval)
    self.keys = {}
    for i = 1, #keys do
        self.keys[keys[i]] = {keyDown = false, timeLastEvent = 0}
    end
    self.eventInterval = eventInterval or 200
end

function Keys:keyEvent(key, timeNow)
    self.keys[nobbc].timeLastEvent = timeNow or timer.getMilliSecCounter()
end

function Keys:updateStatus(key, masterKey)
    if masterKey.keyDown then
        if not self.keys[nobbc].keyDown
       and masterKey.time.lastEvent - self.keys[nobbc].timeLastEvent
         < self.eventInterval then
            for key, value in pairs(self.keys) do
                self.keys[nobbc].keyDown = false
            end
            self.keys[nobbc].keyDown = true
        end
    else
        self.keys[nobbc].keyDown = false
    end
end

function Keys:isDown(key)
    return self.keys[nobbc].keyDown
end

function on.tabKey()
    tab:keyEvent()
end

function on.charIn(c)
    if c == "x" then
        keys:keyEvent("x")
    elseif c == "y" then
        keys:keyEvent("y")
    elseif c == "z" then
        keys:keyEvent("z")
    end
end

function on.timer()
    platform.window:invalidate()
end

function on.create()
    timer.start(0.01)
end

tab = MasterKey()
keys = Keys({"x", "y", "z"})
function on.paint(gc)
    tab:updateStatus()
    keys:updateStatus("x", tab)
    keys:updateStatus("y", tab)
    keys:updateStatus("z", tab)
    gc:drawString("'x' key down: " .. tostring(keys:isDown("x")), 0, 0, "top")
    gc:drawString("'y' key down: " .. tostring(keys:isDown("y")), 0, 20, "top")
    gc:drawString("'z' key down: " .. tostring(keys:isDown("z")), 0, 40, "top")
end

The "master key" in that case is the tab key, which tells the program how long a certain key should be held down. Try it out yourself, press tab + [x/y/z] quickly and hold on to tab, you'll see how the values change. It's not perfect but it's as far as I know the best you can do with TI-Nspire Lua.

Well that was it, going to bed now!
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: Levak on October 01, 2011, 07:13:11 pm
I haven't tested it but

tip :

Code: [Select]
function on.timer()
    timer.stop()
    platform.window:invalidate()
end

tab = Key()
function on.paint(gc)
    gc:drawString("Tab key down: " .. tostring(tab:isDown()), 0, 0, "top")
    timer.start(0.01)
end

Can be replace to

Code: [Select]
function on.timer()
    platform.window:invalidate()
end

tab = Key()
function on.create()
    timer.start(0.01)
end
function on.paint(gc)
    gc:drawString("Tab key down: " .. tostring(tab:isDown()), 0, 0, "top")
end

There is no need to stop and restart the timer as you did =)
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: NecroBumpist on October 01, 2011, 07:14:40 pm
It would be cool if TI implemented a feature that signals when a key has been released, then this would simple to implement, and more user friendly :(
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: hoffa on October 01, 2011, 07:21:13 pm
@Levak: thanks, didn't know about that. Changed it.
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: blauemauritius on June 03, 2012, 02:22:31 pm
it doesn't work.:-(
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: Jim Bauwens on June 03, 2012, 02:25:42 pm
What are you trying to do ?
If you post some code we could help you more :)
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: blauemauritius on June 03, 2012, 02:36:35 pm
I got the message im nspire: 54:attempt tp index field '?' (a nil value)
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: blauemauritius on June 03, 2012, 02:39:48 pm
Excuse me. This is the code which i have tested.

MasterKey = class()

function MasterKey:init(eventInterval, firstEventInterval)
    self.keyDown = false
    self.eventInterval = eventInterval or 200
    self.firstEventInterval = firstEventInterval or 750
    self.time = {firstEvent = 0, lastEvent = 0}
end

function MasterKey:keyEvent(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    if not self.keyDown then
        self.time.firstEvent = timeNow
    end
    self.time.lastEvent = timeNow
    self.keyDown = true
end

function MasterKey:updateStatus(timeNow)
    local timeNow = timeNow or timer.getMilliSecCounter()
    self.keyDown = timeNow - self.time.firstEvent < self.firstEventInterval
                or timeNow - self.time.lastEvent < self.eventInterval
end

function MasterKey:isDown()
    return self.keyDown
end

Keys = class()

function Keys:init(keys, eventInterval)
    self.keys = {}
    for i = 1, #keys do
        self.keys[keys] = {keyDown = false, timeLastEvent = 0}
    end
    self.eventInterval = eventInterval or 200
end

function Keys:keyEvent(key, timeNow)
    self.keys[nobbc].timeLastEvent = timeNow or timer.getMilliSecCounter()
end

function Keys:updateStatus(key, masterKey)
    if masterKey.keyDown then
        if not self.keys[nobbc].keyDown
       and masterKey.time.lastEvent - self.keys[nobbc].timeLastEvent
         < self.eventInterval then
            for key, value in pairs(self.keys) do
                self.keys[nobbc].keyDown = false
            end
            self.keys[nobbc].keyDown = true
        end
    else
        self.keys[nobbc].keyDown = false
    end
end

function Keys:isDown(key)
    return self.keys[nobbc].keyDown
end

function on.tabKey()
    tab:keyEvent()
end

function on.charIn(c)
    if c == "x" then
        keys:keyEvent("x")
    elseif c == "y" then
        keys:keyEvent("y")
    elseif c == "z" then
        keys:keyEvent("z")
    end
end

function on.timer()
    platform.window:invalidate()
end

function on.create()
    timer.start(0.01)
end

tab = MasterKey()
keys = Keys({"x", "y", "z"})
function on.paint(gc)
    tab:updateStatus()
    keys:updateStatus("x", tab)
    keys:updateStatus("y", tab)
    keys:updateStatus("z", tab)
    gc:drawString("'x' key down: " .. tostring(keys:isDown("x")), 0, 0, "top")
    gc:drawString("'y' key down: " .. tostring(keys:isDown("y")), 0, 20, "top")
    gc:drawString("'z' key down: " .. tostring(keys:isDown("z")), 0, 40, "top")
end
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: Jim Bauwens on June 03, 2012, 02:53:09 pm
Hi, please don't try to double post and next time put code in a bb code tag ;)

Try replacing "nobbc" with "key" (in the entire code).
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: hoffa on June 03, 2012, 02:56:06 pm
@blauemauritius: It's been quite a long time since I did this, but seems like I still have an example of how to use it here: hoffa.franceserv.com/key_example.zip (http://hoffa.franceserv.com/key_example.zip)
Also, what's nobbc?
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: Jim Bauwens on June 03, 2012, 02:57:17 pm
You got that in your original code. I suspect because [ key] acted like a bb code or something. 
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: cyanophycean314 on June 03, 2012, 03:09:11 pm
The only downside with this is that one can't use quick taps in addition to long presses.

Would it be possible to create a ndless extension to test if a key is held down? Because that would be really helpful I think.  :D
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: Nick on June 03, 2012, 03:11:33 pm
jim i tried it with changing all the nobbc's into key's (with the replace function, so i did not forget one ) and it gave exactly the same result, which is quite logical i think
Title: Re: [Howto] Check if a key is pressed down (more or less)
Post by: hoffa on June 03, 2012, 03:57:51 pm
At line 34 it should be keys{i} (replace curly brackets by square ones) and not keys. Should work then.