Author Topic: [Howto] Check if a key is pressed down (more or less)  (Read 10684 times)

0 Members and 1 Guest are viewing this topic.

Offline hoffa

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 322
  • Rating: +131/-13
    • View Profile
[Howto] Check if a key is pressed down (more or less)
« on: October 01, 2011, 06:59:39 pm »


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!
« Last Edit: January 29, 2013, 10:58:49 am by hoffa »

Offline Levak

  • LV9 Veteran (Next: 1337)
  • *********
  • Posts: 1002
  • Rating: +208/-39
    • View Profile
    • My website
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #1 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 =)
« Last Edit: October 01, 2011, 07:14:33 pm by Levak »
I do not get mad at people, I just want them to learn the way I learnt.
My website - TI-Planet - iNspired-Lua

Offline NecroBumpist

  • LV4 Regular (Next: 200)
  • ****
  • Posts: 130
  • Rating: +14/-5
  • Master of Lua
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #2 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 :(
Developing Lua scripts for the NSpire ?
Check out the Necrotorium
Need a few routines to run faster ? Checkout the MODS Lua Assembly Toolkit.
Need to save space for your scripts ? Checkout LuaSrcDiet

Offline hoffa

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 322
  • Rating: +131/-13
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #3 on: October 01, 2011, 07:21:13 pm »
@Levak: thanks, didn't know about that. Changed it.

Offline blauemauritius

  • LV2 Member (Next: 40)
  • **
  • Posts: 25
  • Rating: +0/-0
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #4 on: June 03, 2012, 02:22:31 pm »
it doesn't work.:-(

Offline Jim Bauwens

  • Lua! Nspire! Linux!
  • Editor
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1881
  • Rating: +206/-7
  • Linux!
    • View Profile
    • nothing...
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #5 on: June 03, 2012, 02:25:42 pm »
What are you trying to do ?
If you post some code we could help you more :)

Offline blauemauritius

  • LV2 Member (Next: 40)
  • **
  • Posts: 25
  • Rating: +0/-0
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #6 on: June 03, 2012, 02:36:35 pm »
I got the message im nspire: 54:attempt tp index field '?' (a nil value)

Offline blauemauritius

  • LV2 Member (Next: 40)
  • **
  • Posts: 25
  • Rating: +0/-0
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #7 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

Offline Jim Bauwens

  • Lua! Nspire! Linux!
  • Editor
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1881
  • Rating: +206/-7
  • Linux!
    • View Profile
    • nothing...
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #8 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).
« Last Edit: June 03, 2012, 02:54:39 pm by jimbauwens »

Offline hoffa

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 322
  • Rating: +131/-13
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #9 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
Also, what's nobbc?
« Last Edit: June 03, 2012, 02:56:19 pm by hoffa »

Offline Jim Bauwens

  • Lua! Nspire! Linux!
  • Editor
  • LV10 31337 u53r (Next: 2000)
  • **********
  • Posts: 1881
  • Rating: +206/-7
  • Linux!
    • View Profile
    • nothing...
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #10 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. 

Offline cyanophycean314

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 363
  • Rating: +43/-1
  • It's You!
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #11 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

Offline Nick

  • LV9 Veteran (Next: 1337)
  • *********
  • Posts: 1166
  • Rating: +161/-3
  • You just got omnom'd
    • View Profile
    • Nick Steen
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #12 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

Offline hoffa

  • LV6 Super Member (Next: 500)
  • ******
  • Posts: 322
  • Rating: +131/-13
    • View Profile
Re: [Howto] Check if a key is pressed down (more or less)
« Reply #13 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.