官术网_书友最值得收藏!

Using timers

LuaSDL offers support for timer objects. The problematic part is the use of timers. The LibSDL library uses callback functions to call event functions. These callbacks run in another thread and the na?ve approach, where you put the Lua function in the position of callback function, would lead to Lua state corruption. There is a better way to accomplish this by using the internal LuaSDL callback function that invokes a special user event.

Timers aren't very precise and they are mostly used in GUI updates. If you need more precision, you'll need to use High Precision Event Timer (HPET), which is out of the scope of this book.

Getting ready

Each timer object uses a user-defined event that contains unique timer function identifiers represented by integer values. LuaSDL offers a slightly modified version of the SDL.SDL_AddTimer function, where it accepts two parameters instead of three. The first parameter is an interval value in milliseconds. The second is the user-defined event object.

How to do it…

The first thing you'll need to do is to define your user-defined event. You can use the SDL.SDL_Event_local function to create one:

local timerEvent = SDL.SDL_Event_local()

The next thing you'll need to do is to set the event code specific for a timer object. You can then create a timer object with the SDL.SDL_AddTimer function. This function returns an internal timer ID, which you can use with the SDL.SDL_RemoveTimer function to stop the timer.

So far, you've created a user-defined event generator that will generate events at a specified interval. To make this useful, you can define your user event handler with the following code:

[SDL.SDL_USEREVENT] = function(_event)
  local event = _event.user
  if event.code > 0 then
    timerCallback(event.code)
  end
end

You can use the event code to specify what callback function will be called.

How it works…

After you create the timer object with SDL.SDL_AddTimer, LuaSDL calls the real SDL_AddTimer function with its own callback function that will periodically push the event you specified in the second function argument into the event queue. Fortunately, event handling in LuaSDL is one of the few things that is thread safe. Timer callback functions don't run strictly in specified intervals. This is because there are platform-specific limitations and, what's more, callback functions only push events into the event queue. Your application can process timer events much later.

There's more…

To make timer handling more efficient, you can define a function that will take two arguments. The first one is the interval and the second is a function or a closure.

This approach uses central table timers with all the timer objects and the user event handler will always try to query the timer function from the timers table, as shown in the following code:

local timers = {}
local timerID = 0

-- Each call to createTimer function will create a new timer object.
-- Interval argument expects a time in millisecond units
-- Supplied function in the second argument will be called repeatedly
local function createTimer(interval, fn)
  assert(type(interval)=='number' and type(fn)=='function')
  local timerObj = {}
  -- Unique timerID number generator with simple incremental counting
  timerID = timerID+1
  -- timerEvent will be used to contain timerID user value
  local timerEvent = SDL.SDL_Event_local()
  -- Store a reference for timerEvent object
  -- so it won't be prematurely garbage collected
  timerObj.event = timerEvent
  timerObj.call = fn
  timerEvent.type = SDL.SDL_USEREVENT
     timerEvent.user.code = timerID
  -- Create LuaSDL timer object
  local timer = SDL.SDL_AddTimer(interval, timerEvent)
  timerObj.timer = timer
  -- Destroys current timer object
  timerObj.remove = function()
    SDL.SDL_RemoveTimer(timer)
    timers[timerID] = nil
  end
  timers[timerID] = timerObj
end

There remains one small modification in the user event handler, which is as follows:

[SDL.SDL_USEREVENT] = function(raw_event)
  local event = raw_event.user
  if event.code > 0 then
    local timer = timers[event.code]
    if timer and type(timer.call)=="function" then
      timer.call()
    end
  end
end

The last thing you need to do is to clean up all timers before exiting your application. This is caused by the fact that timer objects from LuaSDL aren't automatically garbage collected. You can achieve this with the following simple iteration:

for _, timer in pairs(timers) do
  timer.remove()
end

The remove function will stop the timer and destroy the timer object. Without this step, you'd be relying on the automatic cleanup functions of the operating system, which is never a good idea. In the worst case, it could cause a memory leak.

主站蜘蛛池模板: 沙湾县| 高邮市| 梁山县| 昆明市| 许昌县| 丹东市| 那曲县| 尉氏县| 城步| 花垣县| 宣城市| 华坪县| 永春县| 黑河市| 托里县| 嵊州市| 龙川县| 开阳县| 东城区| 金寨县| 六枝特区| 五河县| 汕头市| 华宁县| 涿鹿县| 营口市| 通辽市| 宁德市| 江永县| 皋兰县| 佛教| 沧州市| 蕲春县| 长沙市| 陆丰市| 本溪市| 墨竹工卡县| 应用必备| 盖州市| 仲巴县| 昌都县|