This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Kamikaze's profile picture

Getting key state

Started by Kamikaze, 15 March 2014 - 08:16 PM
Kamikaze #1
Posted 15 March 2014 - 09:16 PM
Hello everyone. Im new to this forum (as a member, I've been reading it for a looooooooong time). My project needs an input method and I've chosen using the keyboard, but I want to get if a key is pressed or not. The problem is that in other input APIs you can choose between event driven input or "direct" inputs, but in CC i have to stay with events. When an event is called the routine waits for it, and stops everything (in that coroutine) but i need to wrap it around and get the "direct" keys, like looking for in a table where I have all the keys and a boolean telling if it's being pressed or not. In my mind, everything was perfect and worked as i wanted to, but in code, that's not the truth. Here's my code:

[...]
function updateInput()

	while RUNNING do

		for i = 0, 0xF, 1 do
			inputKeys[i] = false
		end

		local e, rawKey = os.pullEvent("key")
		if rawKey == 82  then inputKeys[0x0] = true else inputKeys[0x0] = false end
		if rawKey == 79  then inputKeys[0x1] = true else inputKeys[0x1] = false end
		if rawKey == 80  then inputKeys[0x2] = true else inputKeys[0x2] = false end
		if rawKey == 81  then inputKeys[0x3] = true else inputKeys[0x3] = false end
		if rawKey == 75  then inputKeys[0x4] = true else inputKeys[0x4] = false end
		if rawKey == 76  then inputKeys[0x5] = true else inputKeys[0x5] = false end
		if rawKey == 77  then inputKeys[0x6] = true else inputKeys[0x6] = false end
		if rawKey == 71  then inputKeys[0x7] = true else inputKeys[0x7] = false end
		if rawKey == 72  then inputKeys[0x8] = true else inputKeys[0x8] = false end
		if rawKey == 73  then inputKeys[0x9] = true else inputKeys[0x9] = false end
		if rawKey == 210 then inputKeys[0xA] = true else inputKeys[0xA] = false end
		if rawKey == 199 then inputKeys[0xB] = true else inputKeys[0xB] = false end
		if rawKey == 201 then inputKeys[0xC] = true else inputKeys[0xC] = false end
		if rawKey == 211 then inputKeys[0xD] = true else inputKeys[0xD] = false end
		if rawKey == 207 then inputKeys[0xE] = true else inputKeys[0xE] = false end
		if rawKey == 209 then inputKeys[0xF] = true else inputKeys[0xF] = false end
	end
end
[...]
inputKeys is a table, where I store the state of each key.

And I call the function with the Parallel API:

parallel.waitForAny(main, updateInput)
main() is only the main loop, nothing special there.

The problem is, when I display my table in a computer, all the values are 'false'. Is there a CC "vanilla" way to solve my problem or do I need a custom Java API?
CometWolf #2
Posted 15 March 2014 - 11:00 PM
Quick note, this

			    if rawKey == 82  then inputKeys[0x0] = true else inputKeys[0x0] = false end
could be shortened to this

inputKeys[0x0] = (rawKey == 82)
Since the equal operator just returns true or false anyways.

The probelm is that you set all the values to false, then wait for a new key input. Meaning they will always be false when the program yields. Remove the for loop and it should be good to go.

I have to ask though, why on earth do you need this? Is it to simplify rewriting code from another language?
Kamikaze #3
Posted 15 March 2014 - 11:45 PM
Quick note, this

				if rawKey == 82  then inputKeys[0x0] = true else inputKeys[0x0] = false end
could be shortened to this

inputKeys[0x0] = (rawKey == 82)
Since the equal operator just returns true or false anyways.

The probelm is that you set all the values to false, then wait for a new key input. Meaning they will always be false when the program yields. Remove the for loop and it should be good to go.

I have to ask though, why on earth do you need this? Is it to simplify rewriting code from another language?

Thanks, it worked :D/>
I need this for kinda rewriting another language
oeed #4
Posted 16 March 2014 - 12:06 AM
I have to ask though, why on earth do you need this? Is it to simplify rewriting code from another language?

Unless I've misunderstood, it makes key combinations far easier and for frame based things such as games it make be more useful.
CometWolf #5
Posted 16 March 2014 - 12:19 AM
I must be too used to Lua, since i really can't see the benefit. Other than being able to perform other operations if no key is pressed, however a quick timer would probably be far easier to implement.
oeed #6
Posted 16 March 2014 - 12:30 AM
I must be too used to Lua, since i really can't see the benefit. Other than being able to perform other operations if no key is pressed, however a quick timer would probably be far easier to implement.

That's literally what I've just implemented in to Ink in the past 10 minutes for the menu keyboard shortcuts.

All you need to do is something like this (it's a bit messy atm):


local isControlPushed = false
controlPushedTimer = nil
closeWindowTimer = nil
function Timer(event, timer)
if timer == closeWindowTimer then
  if Current.Window then
   Current.Window:Close()
  end
  Draw()
elseif timer == controlPushedTimer then
  isControlPushed = false
end
end
local ignoreNextChar = false
function HandleKey(...)
local args = {...}
local event = args[1]
local keychar = args[2]
					   --Mac left command character
if event == 'key' and keychar == keys.leftCtrl or keychar == keys.rightCtrl or keychar == 219 then
  isControlPushed = true
  controlPushedTimer = os.startTimer(0.5)
elseif isControlPushed then
  if event == 'key' then
   if CheckKeyboardShortcut(keychar) then
    isControlPushed = false
    ignoreNextChar = true
   end
  end
elseif ignoreNextChar then
  ignoreNextChar = false
elseif Current.TextInput then
  if event == 'char' then
   Current.TextInput:Char(keychar)
  elseif event == 'key' then
   Current.TextInput:Key(keychar)
  end
end
end
Kamikaze #7
Posted 16 March 2014 - 01:16 PM
It stills doesn't work as expected. It detects when a key is pressed but when i release it, it stays as pressed (the value for that key in the table stays "true"). At the beggining of the code it resets itself for detecting the key for this frame, and if none is pressed, it stays "false", but removed that part because it didn't detect the keys as supposed. This is how my code looks like now:

function updateInput()

    while RUNNING do

        local e, rawKey = os.pullEvent("key")

        if rawKey == 29 then RUNNING = false end

        inputKeys[0x0] = (rawKey == 82)
        inputKeys[0x1] = (rawKey == 79)
        inputKeys[0x2] = (rawKey == 80)
        inputKeys[0x3] = (rawKey == 81)
        inputKeys[0x4] = (rawKey == 75)
        inputKeys[0x5] = (rawKey == 76)
        inputKeys[0x6] = (rawKey == 77)
        inputKeys[0x7] = (rawKey == 71)
        inputKeys[0x8] = (rawKey == 72)
        inputKeys[0x9] = (rawKey == 73)
        inputKeys[0xA] = (rawKey == 210)
        inputKeys[0xB] = (rawKey == 199)
        inputKeys[0xC] = (rawKey == 201)
        inputKeys[0xD] = (rawKey == 211)
        inputKeys[0xE] = (rawKey == 207)
        inputKeys[0xF] = (rawKey == 209)

    end
end

Also, the timer solution seems perfect for it, but I'm not sure how to implement it. @oeed code seems very specific for his OS(which looks very good). Also thanks for the replies :D/>
PD: i need this piece of code working for this:
CometWolf #8
Posted 16 March 2014 - 01:55 PM
Ah yes, the key press will remain true until another key is pressed, you are indeed correct. hmm… this is going to require some experimentation, but i think maybe if you re-add the loop, then throw in a sleep(0) at the end of the inputKeyes = (blah blah) part, it should yield after a button press, then reset the keyes again whenever main yields. Alternativly, the timer approach is pretty much the same thing i suppose, albeit with a different setup. We'd have to ditch the parallel alltogether, and instead setup a timer that resets itself whenever it fires, and ends the pullEvent allowing the program to do other stuff.

while true do --input loop
  local timerId = os.startTimer(0.05) --time to wait for user input
  local tEvent = {os.pullEvent()}
  if tEvent[1] == "timer" then
    if tEvent[2] == "timerId" then
      break --ends the input loop
    end
  elseif tEvent[1] == "key" then
    local key = tEvent[2]
    --key code here
  end
end