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

Simultaneous string and timer capturing

Started by nitrogenfingers, 14 April 2012 - 11:50 AM
nitrogenfingers #1
Posted 14 April 2012 - 01:50 PM
I've been playing around with the idea of catching input and running a timer at the same time.

My first idea was something like this:


function updateTimer()
  os.startTimer(1)
  os.pullEvent("timer")
  --Code goes here
end
function parseInput()
  local input = io.read()
  --Code goes here
end
while true do parallel.waitForAny(updateTimer, parseInput) end

Problem is my updateTimer method required moving the terminal cursor- that may not be why but io.read just didn't work.

Next idea was to just capture events, and convert the keycodes I receive into a string that made up my query:


while true do
  local id,key = os.pullEvent()
  if id=="timer" then updateTimer()
  else
	if key==28 then parseInput(input)
	else input = input..string.char(key)
  end
end

The issue here was the string.char() method has a wildly differing definition of keys to the os.pullEvent method.

Neither method worked but I think both should. They need to be adapted better to the development environment.

Could anyone suggest how either or both can be adapted to work better with the existing API's and Lua framework?

Thanks for reading,
Nitrogen Fingers
nitrogenfingers #2
Posted 14 April 2012 - 01:59 PM
A spot of hunting has resolved the second question- catching a "char" event rather than a key event seems to have done the trick.


while true do
  local id,key = os.pullEvent()
  if id=="timer" then updateTimer()
  else
	if id=="key" and key==28 then parseInput(input)
	elseif id=="char" then input = input..string.char(key) end
  end
end

Could still use an opinion on the first though.
Cloudy #3
Posted 14 April 2012 - 03:19 PM
At beginning of your function for updateTimer get the cursor pos using getCursorPos then restore it at the end of the function - that way by the time read goes to do its stuff again, the cursor is where it expects it.
nitrogenfingers #4
Posted 14 April 2012 - 05:34 PM
That works ok for the second approach, didn't work for the first however.
Cloudy #5
Posted 14 April 2012 - 05:52 PM
That works ok for the second approach, didn't work for the first however.

Maybe I explained it wrong. You should get the terminal position after any yield and then set the position back before the same yield. For instance, if you use os.pullEvent (which I'm pretty sure you do) you'd do something like this:


-- at the beginning of your code
local storedX, storedY = os.getCursorPos()

function updateTimer()
  os.startTimer(1)
  setCursorPos(storedX, storedY)
  os.pullEvent("timer")
  storedX, storedY = os.getCursorPos()
  --Code goes here
end

The parseInput would stay how it is.

As a matter of interest, are you using a while loop inside the updateTimer function? If you aren't, it will only pull the first timer event, as the function will end and the parallel API will not restart the function.
nitrogenfingers #6
Posted 14 April 2012 - 06:02 PM
Right I'm with you now, sorry it's getting quite late, not thinking straight.

Yes using a while loop, but external to the updateTimer function (encapsulates parallel), but I was using a waitForAny… oh I can't explain it right now, here's the code:


function main()
    os.startTimer(0.2)
while true do
			   --some code stuff
 
  local noMoves = #movesMade
 
  while noMoves == #movesMade do
   local id,key = os.pullEvent()
   if id == "timer" then updateTimer()
   elseif id == "key" then
    if key == 14 then
	 if #lastQuery > 1 then lastQuery = string.sub(lastQuery, 1, #lastQuery-1)
	 else lastQuery = "" end
    elseif key == 28 then
	 parseInput(lastQuery)
	 lastQuery = ""
	 break
    end
    updateInputDisplay()
   elseif id == "char" then
    lastQuery = lastQuery..key
    updateInputDisplay()
   end
  end
end
end

So rather than character-by-character reading I'm doing at the moment I'd call io.read in my parse and add the updateTimer and parse as a parallel call.

Thanks for your help!
Cloudy #7
Posted 14 April 2012 - 06:20 PM
I've stripped the concept down to its smallest size and set up some code - you should be able to flesh it out to do what you want to do. This code is completely untested, but not withstanding any syntax errors should work.

unction parseInput()
  local input
  while true do
    input = io.read()
    if input == "blah" then
      -- do stuff
    end
  end
end

function updateTimer()
  local event, param
  local timer = os.startTimer(0.2)
  local storedX, storedY = term.getCursorPos() -- get the pos when this function is initially called
  while true do
    term.setCursorPos(storedX, storedY) -- set the stored cursor pos to how it was before this function was run, or before control was returned to this function
    event, param = os.pullEvent()
    storedX, storedY = term.getCursorPos() -- get the cursor pos and store it - this will be where the read function left it
    if event == "timer" and param == timer then
      timer = os.startTimer(0.2)
      -- do timer update stuff
    end
  end
end

function main()
  while true do
    parallel.waitForAll(parseInput, updateTimer)
  end
end

main()