lovr.errhand

Edit

The lovr.errhand callback is run whenever an error occurs. It receives a parameter containing the error message. It should return a handler function that will run in a loop to render the error screen.

This handler function is of the same type as the one returned by lovr.run and has the same requirements (such as pumping events). If an error occurs while this handler is running, the program will terminate immediately -- lovr.errhand will not be given a second chance. Errors which occur in the error handler or in the handler it returns may not be cleanly reported, so be careful.

A default error handler is supplied that renders the error message as text to the headset and to the window.

function lovr.errhand(message)
  -- your code here
end

Arguments

NameTypeDescription
messagestring The error message.

Returns

NameTypeDescription
handlerfunction The error handler function. It should return nil to continue running, "restart" to restart the app, or a number representing an exit status.

Example

The default error handler.

function lovr.errhand(message)
  local function formatTraceback(s)
    return s:gsub('\n[^\n]+$', ''):gsub('\t', ''):gsub('stack traceback:', '\nStack:\n')
  end

  message = 'Error:\n\n' .. tostring(message) .. formatTraceback(debug.traceback('', 4))

  print(message)

  if not lovr.graphics or not lovr.graphics.isInitialized() then
    return function() return 1 end
  end

  if lovr.audio then lovr.audio.stop() end

  if not lovr.headset or lovr.headset.getPassthrough() == 'opaque' then
    lovr.graphics.setBackgroundColor(.11, .10, .14)
  else
    lovr.graphics.setBackgroundColor(0, 0, 0, 0)
  end

  local font = lovr.graphics.getDefaultFont()

  return function()
    lovr.system.pollEvents()

    for name, a in lovr.event.poll() do
      if name == 'quit' then return a or 1
      elseif name == 'restart' then return 'restart', lovr.restart and lovr.restart()
      elseif name == 'keypressed' and a == 'f5' then lovr.event.restart()
      elseif name == 'keypressed' and a == 'escape' then lovr.event.quit() end
    end

    if lovr.headset and lovr.headset.getDriver() ~= 'simulator' then
      lovr.headset.update()
      local pass = lovr.headset.getPass()
      if pass then
        font:setPixelDensity()

        local scale = .35
        local font = lovr.graphics.getDefaultFont()
        local wrap = .7 * font:getPixelDensity()
        local lines = font:getLines(message, wrap)
        local width = math.min(font:getWidth(message), wrap) * scale
        local height = .8 + #lines * font:getHeight() * scale
        local x = -width / 2
        local y = math.min(height / 2, 10)
        local z = -10

        pass:setColor(.95, .95, .95)
        pass:text(message, x, y, z, scale, 0, 0, 0, 0, wrap, 'left', 'top')

        lovr.graphics.submit(pass)
        lovr.headset.submit()
      end
    end

    if lovr.system.isWindowOpen() then
      local pass = lovr.graphics.getWindowPass()
      if pass then
        local w, h = lovr.system.getWindowDimensions()
        pass:setProjection(1, lovr.math.mat4():orthographic(0, w, 0, h, -1, 1))
        font:setPixelDensity(1)

        local scale = .6
        local wrap = w * .8 / scale
        local width = math.min(font:getWidth(message), wrap) * scale
        local x = w / 2 - width / 2

        pass:setColor(.95, .95, .95)
        pass:text(message, x, h / 2, 0, scale, 0, 0, 0, 0, wrap, 'left', 'middle')

        lovr.graphics.submit(pass)
        lovr.graphics.present()
      end
    end

    lovr.math.drain()
  end
end

See also