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

Fevent - Conveniently Readable Events + Eventbuffer

Started by TurboTuTone, 22 July 2013 - 01:23 AM
TurboTuTone #1
Posted 22 July 2013 - 03:23 AM
Formatted Events API


This api ,"Formatted Events" for lack of a better name, has the following functionallity.
  • Pull events that return convenient tables so you can use for example "event.button" instead of something like "EventData[2]"
  • Buffer events to manipulate/parse later
  • Build in "bufferGetText()" function allows you to retrieve text input fast and simple from the buffer
A small example:

while true do

    local event = fevent.pull()

    if event.name == "key" then
	    print(event.keyCode)
	    if event.keyName == "enter" then
		    break
	    end
    end

    if event.name == "rednet_message" then
	    print(event.senderID," : ", event.msg)
    end

    if event.name == "timer" then
	    print(event.timerVal)
    end

end

Events


SpoilerFirst of all it wraps event parameters for all standard CC events in conviently readible tables with string keys.

You can pull events with feven.pull():

local event = fevent.pull()
Note: you can also apply a filter as with os.pullEvent( _sFilter )

The returned table event for a "key" event in the above example would have keys like this:

event.name
event.keyCode
event.keyName
Where event.name would be the event name, even.keyCode the code of the key pressed, and event.keyName the result of keys.getName( keyCode )

If the event is unknown it returns the following event table

event.name
event.p1
...
event.p5
The event.name is always present, but since the parameters are unknown they are assigned p1 to max p5
Note: its very simple to add definitions for non-standard CC events, more on that later.

A list of all event parameter keynames:

[ "Event" = "Parameter keys in returned event table" ]
unknown = name, p1, p2, p3, p4, p5
char = name, letter
key = name, keyCode, keyName
timer = name, timerVal
alarm = name, alarmVal
redstone = name
terminate = name
disk = name, side
disk_eject = name, side
peripheral = name, side
peripheral_detach = name, side
rednet_message = name, senderID, msg, distance
modem_message = name, side, freq, replyFreq, msg, distance
http_success = name, url, text
http_failure = name, url
mouse_click = name, button, x, y
mouse_scroll = name, direction, x, y
mouse_drag = name, button, x, y
monitor_touch = name, side, x, y
monitor_resize = name, side
turtle_inventory = name, side

Adding Events


SpoilerAdding events is easy.

In the API code find the following table:

local tEvents = {
["unknown"] = { "p1", "p2", "p3", "p4", "p5" },
["char"] = { "letter" },
["key"] =  { "keyCode" },
["timer"] =  { "timerVal" },
["alarm"] =  { "alarmVal" },
["redstone"] =  false,
["terminate"] =  false,
["disk"] =  { "side" } ,
["disk_eject"] =  { "side" } ,
["peripheral"] =  { "side" } ,
["peripheral_detach"] = { "side" },
["rednet_message"] = { "senderID", "msg", "distance" },
["modem_message"] = { "side", "freq", "replyFreq", "msg", "distance" },
["http_success"] = { "url", "text" },
["http_failure"] = { "url" },
["mouse_click"] = { "button", "x", "y" },
["mouse_scroll"] = { "direction", "x", "y" },
["mouse_drag"] = { "button", "x", "y" },
["monitor_touch"] = { "side", "x", "y" },
["monitor_resize"] = { "side" },
["turtle_inventory"] = { "side" },
}

To add an event simply add a key to the table with the name of the event as string key, supply a table with stringnames for the parameters of the event as value.
Set a value to "false" if the event has no additional parameters.
Note: You don't have to add the event name as a parameters since this is done by the api.

Event Buffer


SpoilerYou can optionally use the buffer functionallity provided in the API.
This allows you to yield events and perform actions on them later.

A good example of this is the integrated API function fevent.bufferGetText()

Take for example the following code:

fevent.bufferEnable( true )

fevent.bufferFilter( "key", "char" )

while true do

-- pulling any events
local event = fevent.pull()

-- breaking when key is enter
if event.name == "key" and event.keyName == "enter" then
  break
end

end

-- check if there is any text in the buffer and output if so
local userTxt = fevent.bufferGetText()

if userTxt then
print( "Text typed: ", userTxt )
end

fevent.bufferEnable( false )
In the above code the buffer is enabled and a filter is applied to only buffer "char" and "key' events
A loop is started that breaks on enter.
If before pressing enter any char keys are being pressed, their parameters are stored to the buffer.
The function fevent.bufferGetText() loops through these char events in the buffer internally and outputs them concatenated as a string.

Ofcourse you can also manipulate the buffer yourself, here are some functions:

The following code would retrieve the last stored event from the buffer (and removes it from the buffer)

local lastEvent = fevent.bufferGetLast()

You can also get the oldest (first stored) event from the buffer (will also remove event from buffer)

local lastEvent = fevent.bufferGetFirst()

This code would retrieve the complete buffer (this will not remove the buffer)
local tEvents = fevent.bufferGetRaw()

Or you can apply a filter, for example to only retrieve "key" events
local tEvents = fevent.bufferGetRaw("key")

Returns iterable tables with the event tables as value, resulting in: tEvents[0].name etc

To clear the complete buffer use
fevent.bufferClear()

for a complete list of functions see: API Function List

API Function List


Spoiler
-- pulls event, optionally apply event filter
fevent.pull( string filter )

-- Returns bool indicating wether buffer is enabled or not
fevent.buffer()

-- Enable or disable the buffer functionallity, returns bool indicating wether buffer is enabled or not
fevent.bufferEnable( boolean )

-- Pass strings containing the names of events as parameters for the buffer to save only those events
-- returns table with filter strings
fevent.bufferFilter( string event1, string event2, ... )

-- Clears the buffer
fevent.bufferClear()

-- returns number size of buffer
fevent.bufferGetSize()

-- Returns the buffer table, optionally a filter for the events to return can be applied
fevent.bufferGetRaw( string filter )

-- Returns the last buffer entree (this is the most recent event that entered the buffer) and removes it from buffer table, or nil if buffer empty
fevent.bufferGetLast()

-- Returns the first buffer entree (this is the oldest event in the buffer) and removes it from buffer table, or nil if buffer empty
-- NOTE: this function uses table.remove on position 1 which causes all the elements to shift.
-- care with very large buffers
fevent.bufferGetFirst()

-- Loops through all the char events and saves their data to a string which is returned, or nil if not letters are found
fevent.bufferGetText()

-- Returns last pulled event, or nil if there is no last event stored
fevent.getLast()

Code Example


SpoilerThe following code exposes some functionallity of the API

PasteBin: link


-- loading the api
os.loadAPI("api/fevent")

-- printing the buffer size and wether its enabled
print( "Buffer size: ", fevent.bufferGetSize(), ", enabled: ", tostring(fevent.buffer() ) )

-- enabling the buffer functionallity
fevent.bufferEnable( true )

-- setting a filter for the buffer, 23 will not get added as its not a string
fevent.bufferFilter( "key", "char", 23 )

-- starting a loop, pulling all incoming events, the "enter" key or the end of the timer will break the loop

local nTimeOut = os.startTimer(20)

while true do

-- pulling any events
local event = fevent.pull()

-- breaking when key is enter
if event.name == "key" and event.keyName == "enter" then
  break
end

-- breaking when timer runs out
if event.name == "timer" and event.timerVal == nTimeOut then
  print("timed out")
  break
end

end

-- check if there is any text in the buffer and output if so
local userTxt = fevent.bufferGetText()

if userTxt then
print( "Text typed: ", userTxt )
end

-- getting the raw buffer for event "key", count the amount of times user pressed "a"
local keys = fevent.bufferGetRaw( "key" )

local k = 0

for i, keyEvent in ipairs( keys ) do
if keyEvent.keyName == "a" then
  k = k + 1
end
end

print("You pressed the 'A' key ", k, " times")

-- printing the buffer size and enabled status once more
print( "Buffer size: ", fevent.bufferGetSize(), ", enabled: ", tostring(fevent.buffer() ) )

-- clearing the buffer
fevent.bufferClear()

-- print buffer text again, should be nil
print( tostring( fevent.bufferGetText() ) )

Download
PasteBin: PxthFvE85 or link

pastebin get PxthFvE85 fevent
thomasbogue #2
Posted 10 August 2013 - 12:35 PM
Nice work! Code looks better than vanilla event processing.

Not sure about the buffering though. It seems to add complexity. Are the speed gains significant?
theoriginalbit #3
Posted 16 August 2013 - 06:34 PM
Very nice. I quite like the idea.

One thing though, might I suggest that you allow for events with more than 5 return values. Something like this would do the trick


local function formatEvent( _event, _tEventData, _tData )
  _tEventData.name = _tData[1]
  if _event == "unkown" then
    for i = 2, #_tData do
      _tEventData[ 'p'..i ] = _tData[i]
    end
  --# all the rest is the same below this point (except the `if` becoming `elseif`)
  elseif tEvents[ _event ] then
    for i, label in ipairs( tEvents[ _event ] ) do
      _tEventData[ label ] = _tData[ i + 1 ] or nil
    end
  end
  if _tData[1] == "key" then
    _tEventData.keyName = keys.getName( _tData[2] )
  end
  return _tEventData
end

Oh also, the "turtle_inventory" event doesn't have any return values.