boot.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808
  1. --[[
  2. Copyright (c) 2006-2019 LOVE Development Team
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely, subject to the following restrictions:
  9. 1. The origin of this software must not be misrepresented; you must not
  10. claim that you wrote the original software. If you use this software
  11. in a product, an acknowledgment in the product documentation would be
  12. appreciated but is not required.
  13. 2. Altered source versions must be plainly marked as such, and must not be
  14. misrepresented as being the original software.
  15. 3. This notice may not be removed or altered from any source distribution.
  16. --]]
  17. -- Make sure love exists.
  18. local love = require("love")
  19. -- Used for setup:
  20. love.path = {}
  21. love.arg = {}
  22. -- Replace any \ with /.
  23. function love.path.normalslashes(p)
  24. return p:gsub("\\", "/")
  25. end
  26. -- Makes sure there is a slash at the end
  27. -- of a path.
  28. function love.path.endslash(p)
  29. if p:sub(-1) ~= "/" then
  30. return p .. "/"
  31. else
  32. return p
  33. end
  34. end
  35. -- Checks whether a path is absolute or not.
  36. function love.path.abs(p)
  37. local tmp = love.path.normalslashes(p)
  38. -- Path is absolute if it starts with a "/".
  39. if tmp:find("/") == 1 then
  40. return true
  41. end
  42. -- Path is absolute if it starts with a
  43. -- letter followed by a colon.
  44. if tmp:find("%a:") == 1 then
  45. return true
  46. end
  47. -- Relative.
  48. return false
  49. end
  50. -- Converts any path into a full path.
  51. function love.path.getFull(p)
  52. if love.path.abs(p) then
  53. return love.path.normalslashes(p)
  54. end
  55. local cwd = love.filesystem.getWorkingDirectory()
  56. cwd = love.path.normalslashes(cwd)
  57. cwd = love.path.endslash(cwd)
  58. -- Construct a full path.
  59. local full = cwd .. love.path.normalslashes(p)
  60. -- Remove trailing /., if applicable
  61. return full:match("(.-)/%.$") or full
  62. end
  63. -- Returns the leaf of a full path.
  64. function love.path.leaf(p)
  65. p = love.path.normalslashes(p)
  66. local a = 1
  67. local last = p
  68. while a do
  69. a = p:find("/", a+1)
  70. if a then
  71. last = p:sub(a+1)
  72. end
  73. end
  74. return last
  75. end
  76. -- Finds the key in the table with the lowest integral index. The lowest
  77. -- will typically the executable, for instance "lua5.1.exe".
  78. function love.arg.getLow(a)
  79. local m = math.huge
  80. for k,v in pairs(a) do
  81. if k < m then
  82. m = k
  83. end
  84. end
  85. return a[m], m
  86. end
  87. love.arg.options = {
  88. console = { a = 0 },
  89. fused = {a = 0 },
  90. game = { a = 1 }
  91. }
  92. love.arg.optionIndices = {}
  93. function love.arg.parseOption(m, i)
  94. m.set = true
  95. if m.a > 0 then
  96. m.arg = {}
  97. for j=i,i+m.a-1 do
  98. love.arg.optionIndices[j] = true
  99. table.insert(m.arg, arg[j])
  100. end
  101. end
  102. return m.a
  103. end
  104. function love.arg.parseOptions()
  105. local game
  106. local argc = #arg
  107. local i = 1
  108. while i <= argc do
  109. -- Look for options.
  110. local m = arg[i]:match("^%-%-(.*)")
  111. if m and m ~= "" and love.arg.options[m] and not love.arg.options[m].set then
  112. love.arg.optionIndices[i] = true
  113. i = i + love.arg.parseOption(love.arg.options[m], i+1)
  114. elseif m == "" then -- handle '--' as an option
  115. love.arg.optionIndices[i] = true
  116. if not game then -- handle '--' followed by game name
  117. game = i + 1
  118. end
  119. break
  120. elseif not game then
  121. game = i
  122. end
  123. i = i + 1
  124. end
  125. if not love.arg.options.game.set then
  126. love.arg.parseOption(love.arg.options.game, game or 0)
  127. end
  128. end
  129. -- Returns the arguments that are passed to your game via love.load()
  130. -- arguments that were parsed as options are skipped.
  131. function love.arg.parseGameArguments(a)
  132. local out = {}
  133. local _, lowindex = love.arg.getLow(a)
  134. local o = lowindex
  135. for i=lowindex, #a do
  136. if not love.arg.optionIndices[i] then
  137. out[o] = a[i]
  138. o = o + 1
  139. end
  140. end
  141. return out
  142. end
  143. function love.createhandlers()
  144. -- Standard callback handlers.
  145. love.handlers = setmetatable({
  146. keypressed = function (b,s,r)
  147. if love.keypressed then return love.keypressed(b,s,r) end
  148. end,
  149. keyreleased = function (b,s)
  150. if love.keyreleased then return love.keyreleased(b,s) end
  151. end,
  152. textinput = function (t)
  153. if love.textinput then return love.textinput(t) end
  154. end,
  155. textedited = function (t,s,l)
  156. if love.textedited then return love.textedited(t,s,l) end
  157. end,
  158. mousemoved = function (x,y,dx,dy,t)
  159. if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
  160. end,
  161. mousepressed = function (x,y,b,t,c)
  162. if love.mousepressed then return love.mousepressed(x,y,b,t,c) end
  163. end,
  164. mousereleased = function (x,y,b,t,c)
  165. if love.mousereleased then return love.mousereleased(x,y,b,t,c) end
  166. end,
  167. wheelmoved = function (x,y)
  168. if love.wheelmoved then return love.wheelmoved(x,y) end
  169. end,
  170. touchpressed = function (id,x,y,dx,dy,p)
  171. if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
  172. end,
  173. touchreleased = function (id,x,y,dx,dy,p)
  174. if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
  175. end,
  176. touchmoved = function (id,x,y,dx,dy,p)
  177. if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
  178. end,
  179. joystickpressed = function (j,b)
  180. if love.joystickpressed then return love.joystickpressed(j,b) end
  181. end,
  182. joystickreleased = function (j,b)
  183. if love.joystickreleased then return love.joystickreleased(j,b) end
  184. end,
  185. joystickaxis = function (j,a,v)
  186. if love.joystickaxis then return love.joystickaxis(j,a,v) end
  187. end,
  188. joystickhat = function (j,h,v)
  189. if love.joystickhat then return love.joystickhat(j,h,v) end
  190. end,
  191. gamepadpressed = function (j,b)
  192. if love.gamepadpressed then return love.gamepadpressed(j,b) end
  193. end,
  194. gamepadreleased = function (j,b)
  195. if love.gamepadreleased then return love.gamepadreleased(j,b) end
  196. end,
  197. gamepadaxis = function (j,a,v)
  198. if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
  199. end,
  200. joystickadded = function (j)
  201. if love.joystickadded then return love.joystickadded(j) end
  202. end,
  203. joystickremoved = function (j)
  204. if love.joystickremoved then return love.joystickremoved(j) end
  205. end,
  206. focus = function (f)
  207. if love.focus then return love.focus(f) end
  208. end,
  209. mousefocus = function (f)
  210. if love.mousefocus then return love.mousefocus(f) end
  211. end,
  212. visible = function (v)
  213. if love.visible then return love.visible(v) end
  214. end,
  215. quit = function ()
  216. return
  217. end,
  218. threaderror = function (t, err)
  219. if love.threaderror then return love.threaderror(t, err) end
  220. end,
  221. resize = function (w, h)
  222. if love.resize then return love.resize(w, h) end
  223. end,
  224. filedropped = function (f)
  225. if love.filedropped then return love.filedropped(f) end
  226. end,
  227. directorydropped = function (dir)
  228. if love.directorydropped then return love.directorydropped(dir) end
  229. end,
  230. lowmemory = function ()
  231. if love.lowmemory then love.lowmemory() end
  232. collectgarbage()
  233. collectgarbage()
  234. end,
  235. displayrotated = function (display, orient)
  236. if love.displayrotated then return love.displayrotated(display, orient) end
  237. end,
  238. }, {
  239. __index = function(self, name)
  240. error("Unknown event: " .. name)
  241. end,
  242. })
  243. end
  244. local function uridecode(s)
  245. return s:gsub("%%%x%x", function(str)
  246. return string.char(tonumber(str:sub(2), 16))
  247. end)
  248. end
  249. local no_game_code = false
  250. local invalid_game_path = nil
  251. -- This can't be overridden.
  252. function love.boot()
  253. -- This is absolutely needed.
  254. require("love.filesystem")
  255. local arg0 = love.arg.getLow(arg)
  256. love.filesystem.init(arg0)
  257. local exepath = love.filesystem.getExecutablePath()
  258. if #exepath == 0 then
  259. -- This shouldn't happen, but just in case we'll fall back to arg0.
  260. exepath = arg0
  261. end
  262. no_game_code = false
  263. invalid_game_path = nil
  264. -- Is this one of those fancy "fused" games?
  265. local can_has_game = pcall(love.filesystem.setSource, exepath)
  266. -- It's a fused game, don't parse --game argument
  267. if can_has_game then
  268. love.arg.options.game.set = true
  269. end
  270. -- Parse options now that we know which options we're looking for.
  271. love.arg.parseOptions()
  272. local o = love.arg.options
  273. local is_fused_game = can_has_game or love.arg.options.fused.set
  274. love.filesystem.setFused(is_fused_game)
  275. love.setDeprecationOutput(not love.filesystem.isFused())
  276. local identity = ""
  277. if not can_has_game and o.game.set and o.game.arg[1] then
  278. local nouri = o.game.arg[1]
  279. if nouri:sub(1, 7) == "file://" then
  280. nouri = uridecode(nouri:sub(8))
  281. end
  282. local full_source = love.path.getFull(nouri)
  283. can_has_game = pcall(love.filesystem.setSource, full_source)
  284. if not can_has_game then
  285. invalid_game_path = full_source
  286. end
  287. -- Use the name of the source .love as the identity for now.
  288. identity = love.path.leaf(full_source)
  289. else
  290. -- Use the name of the exe as the identity for now.
  291. identity = love.path.leaf(exepath)
  292. end
  293. -- Try to use the archive containing main.lua as the identity name. It
  294. -- might not be available, in which case the fallbacks above are used.
  295. local realdir = love.filesystem.getRealDirectory("main.lua")
  296. if realdir then
  297. identity = love.path.leaf(realdir)
  298. end
  299. identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
  300. identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
  301. identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
  302. identity = #identity > 0 and identity or "lovegame"
  303. -- When conf.lua is initially loaded, the main source should be checked
  304. -- before the save directory (the identity should be appended.)
  305. pcall(love.filesystem.setIdentity, identity, true)
  306. if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
  307. no_game_code = true
  308. end
  309. if not can_has_game then
  310. local nogame = require("love.nogame")
  311. nogame()
  312. end
  313. end
  314. function love.init()
  315. -- Create default configuration settings.
  316. -- NOTE: Adding a new module to the modules list
  317. -- will NOT make it load, see below.
  318. local c = {
  319. title = "Untitled",
  320. version = love._version,
  321. window = {
  322. width = 800,
  323. height = 600,
  324. x = nil,
  325. y = nil,
  326. minwidth = 1,
  327. minheight = 1,
  328. fullscreen = false,
  329. fullscreentype = "desktop",
  330. display = 1,
  331. vsync = 1,
  332. msaa = 0,
  333. borderless = false,
  334. resizable = false,
  335. centered = true,
  336. highdpi = false,
  337. usedpiscale = true,
  338. },
  339. modules = {
  340. data = true,
  341. event = true,
  342. keyboard = true,
  343. mouse = true,
  344. timer = true,
  345. joystick = true,
  346. touch = true,
  347. image = true,
  348. graphics = true,
  349. audio = true,
  350. math = true,
  351. physics = true,
  352. sound = true,
  353. system = true,
  354. font = true,
  355. thread = true,
  356. window = true,
  357. video = true,
  358. },
  359. audio = {
  360. mixwithsystem = true, -- Only relevant for Android / iOS.
  361. mic = false, -- Only relevant for Android.
  362. },
  363. console = false, -- Only relevant for windows.
  364. identity = false,
  365. appendidentity = false,
  366. externalstorage = false, -- Only relevant for Android.
  367. accelerometerjoystick = true, -- Only relevant for Android / iOS.
  368. gammacorrect = false,
  369. }
  370. -- Console hack, part 1.
  371. local openedconsole = false
  372. if love.arg.options.console.set and love._openConsole then
  373. love._openConsole()
  374. openedconsole = true
  375. end
  376. -- If config file exists, load it and allow it to update config table.
  377. local confok, conferr
  378. if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
  379. confok, conferr = pcall(require, "conf")
  380. end
  381. -- Yes, conf.lua might not exist, but there are other ways of making
  382. -- love.conf appear, so we should check for it anyway.
  383. if love.conf then
  384. confok, conferr = pcall(love.conf, c)
  385. -- If love.conf errors, we'll trigger the error after loading modules so
  386. -- the error message can be displayed in the window.
  387. end
  388. -- Console hack, part 2.
  389. if c.console and love._openConsole and not openedconsole then
  390. love._openConsole()
  391. end
  392. -- Hack for disabling accelerometer-as-joystick on Android / iOS.
  393. if love._setAccelerometerAsJoystick then
  394. love._setAccelerometerAsJoystick(c.accelerometerjoystick)
  395. end
  396. if love._setGammaCorrect then
  397. love._setGammaCorrect(c.gammacorrect)
  398. end
  399. if love._setAudioMixWithSystem then
  400. if c.audio and c.audio.mixwithsystem ~= nil then
  401. love._setAudioMixWithSystem(c.audio.mixwithsystem)
  402. end
  403. end
  404. if love._requestRecordingPermission then
  405. love._requestRecordingPermission(c.audio and c.audio.mic)
  406. end
  407. -- Gets desired modules.
  408. for k,v in ipairs{
  409. "data",
  410. "thread",
  411. "timer",
  412. "event",
  413. "keyboard",
  414. "joystick",
  415. "mouse",
  416. "touch",
  417. "sound",
  418. "system",
  419. "audio",
  420. "image",
  421. "video",
  422. "font",
  423. "window",
  424. "graphics",
  425. "math",
  426. "physics",
  427. } do
  428. if c.modules[v] then
  429. require("love." .. v)
  430. end
  431. end
  432. if love.event then
  433. love.createhandlers()
  434. end
  435. -- Check the version
  436. c.version = tostring(c.version)
  437. if not love.isVersionCompatible(c.version) then
  438. local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
  439. if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
  440. local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
  441. "It may not be compatible with the running version (%s)."):format(c.version, love._version)
  442. print(msg)
  443. if love.window then
  444. love.window.showMessageBox("Compatibility Warning", msg, "warning")
  445. end
  446. end
  447. end
  448. if not confok and conferr then
  449. error(conferr)
  450. end
  451. -- Setup window here.
  452. if c.window and c.modules.window then
  453. assert(love.window.setMode(c.window.width, c.window.height,
  454. {
  455. fullscreen = c.window.fullscreen,
  456. fullscreentype = c.window.fullscreentype,
  457. vsync = c.window.vsync,
  458. msaa = c.window.msaa,
  459. stencil = c.window.stencil,
  460. depth = c.window.depth,
  461. resizable = c.window.resizable,
  462. minwidth = c.window.minwidth,
  463. minheight = c.window.minheight,
  464. borderless = c.window.borderless,
  465. centered = c.window.centered,
  466. display = c.window.display,
  467. highdpi = c.window.highdpi,
  468. usedpiscale = c.window.usedpiscale,
  469. x = c.window.x,
  470. y = c.window.y,
  471. }), "Could not set window mode")
  472. love.window.setTitle(c.window.title or c.title)
  473. if c.window.icon then
  474. assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
  475. love.window.setIcon(love.image.newImageData(c.window.icon))
  476. end
  477. end
  478. -- Our first timestep, because window creation can take some time
  479. if love.timer then
  480. love.timer.step()
  481. end
  482. if love.filesystem then
  483. love.filesystem._setAndroidSaveExternal(c.externalstorage)
  484. love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
  485. if love.filesystem.getInfo("main.lua") then
  486. require("main")
  487. end
  488. end
  489. if no_game_code then
  490. error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
  491. elseif invalid_game_path then
  492. error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
  493. end
  494. end
  495. -----------------------------------------------------------
  496. -- Default callbacks.
  497. -----------------------------------------------------------
  498. function love.run()
  499. if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
  500. -- We don't want the first frame's dt to include time taken by love.load.
  501. if love.timer then love.timer.step() end
  502. local dt = 0
  503. -- Main loop time.
  504. return function()
  505. -- Process events.
  506. if love.event then
  507. love.event.pump()
  508. for name, a,b,c,d,e,f in love.event.poll() do
  509. if name == "quit" then
  510. if not love.quit or not love.quit() then
  511. return a or 0
  512. end
  513. end
  514. love.handlers[name](a,b,c,d,e,f)
  515. end
  516. end
  517. -- Update dt, as we'll be passing it to update
  518. if love.timer then dt = love.timer.step() end
  519. -- Call update and draw
  520. if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
  521. if love.graphics and love.graphics.isActive() then
  522. love.graphics.origin()
  523. love.graphics.clear(love.graphics.getBackgroundColor())
  524. if love.draw then love.draw() end
  525. love.graphics.present()
  526. end
  527. if love.timer then love.timer.sleep(0.001) end
  528. end
  529. end
  530. local debug, print, error = debug, print, error
  531. function love.threaderror(t, err)
  532. error("Thread error ("..tostring(t)..")\n\n"..err, 0)
  533. end
  534. local utf8 = require("utf8")
  535. local function error_printer(msg, layer)
  536. print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
  537. end
  538. function love.errhand(msg)
  539. msg = tostring(msg)
  540. error_printer(msg, 2)
  541. if not love.window or not love.graphics or not love.event then
  542. return
  543. end
  544. if not love.graphics.isCreated() or not love.window.isOpen() then
  545. local success, status = pcall(love.window.setMode, 800, 600)
  546. if not success or not status then
  547. return
  548. end
  549. end
  550. -- Reset state.
  551. if love.mouse then
  552. love.mouse.setVisible(true)
  553. love.mouse.setGrabbed(false)
  554. love.mouse.setRelativeMode(false)
  555. if love.mouse.isCursorSupported() then
  556. love.mouse.setCursor()
  557. end
  558. end
  559. if love.joystick then
  560. -- Stop all joystick vibrations.
  561. for i,v in ipairs(love.joystick.getJoysticks()) do
  562. v:setVibration()
  563. end
  564. end
  565. if love.audio then love.audio.stop() end
  566. love.graphics.reset()
  567. local font = love.graphics.setNewFont(14)
  568. love.graphics.setColor(1, 1, 1, 1)
  569. local trace = debug.traceback()
  570. love.graphics.origin()
  571. local sanitizedmsg = {}
  572. for char in msg:gmatch(utf8.charpattern) do
  573. table.insert(sanitizedmsg, char)
  574. end
  575. sanitizedmsg = table.concat(sanitizedmsg)
  576. local err = {}
  577. table.insert(err, "Error\n")
  578. table.insert(err, sanitizedmsg)
  579. if #sanitizedmsg ~= #msg then
  580. table.insert(err, "Invalid UTF-8 string in error message.")
  581. end
  582. table.insert(err, "\n")
  583. for l in trace:gmatch("(.-)\n") do
  584. if not l:match("boot.lua") then
  585. l = l:gsub("stack traceback:", "Traceback\n")
  586. table.insert(err, l)
  587. end
  588. end
  589. local p = table.concat(err, "\n")
  590. p = p:gsub("\t", "")
  591. p = p:gsub("%[string \"(.-)\"%]", "%1")
  592. local function draw()
  593. local pos = 70
  594. love.graphics.clear(89/255, 157/255, 220/255)
  595. love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
  596. love.graphics.present()
  597. end
  598. local fullErrorText = p
  599. local function copyToClipboard()
  600. if not love.system then return end
  601. love.system.setClipboardText(fullErrorText)
  602. p = p .. "\nCopied to clipboard!"
  603. draw()
  604. end
  605. if love.system then
  606. p = p .. "\n\nPress Ctrl+C or tap to copy this error"
  607. end
  608. return function()
  609. love.event.pump()
  610. for e, a, b, c in love.event.poll() do
  611. if e == "quit" then
  612. return 1
  613. elseif e == "keypressed" and a == "escape" then
  614. return 1
  615. elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
  616. copyToClipboard()
  617. elseif e == "touchpressed" then
  618. local name = love.window.getTitle()
  619. if #name == 0 or name == "Untitled" then name = "Game" end
  620. local buttons = {"OK", "Cancel"}
  621. if love.system then
  622. buttons[3] = "Copy to clipboard"
  623. end
  624. local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
  625. if pressed == 1 then
  626. return 1
  627. elseif pressed == 3 then
  628. copyToClipboard()
  629. end
  630. end
  631. end
  632. draw()
  633. if love.timer then
  634. love.timer.sleep(0.1)
  635. end
  636. end
  637. end
  638. -----------------------------------------------------------
  639. -- The root of all calls.
  640. -----------------------------------------------------------
  641. return function()
  642. local func
  643. local inerror = false
  644. local function deferErrhand(...)
  645. local errhand = love.errorhandler or love.errhand
  646. local handler = (not inerror and errhand) or error_printer
  647. inerror = true
  648. func = handler(...)
  649. end
  650. local function earlyinit()
  651. -- If love.boot fails, return 1 and finish immediately
  652. local result = xpcall(love.boot, error_printer)
  653. if not result then return 1 end
  654. -- If love.init or love.run fails, don't return a value,
  655. -- as we want the error handler to take over
  656. result = xpcall(love.init, deferErrhand)
  657. if not result then return end
  658. -- NOTE: We can't assign to func directly, as we'd
  659. -- overwrite the result of deferErrhand with nil on error
  660. local main
  661. result, main = xpcall(love.run, deferErrhand)
  662. if result then
  663. func = main
  664. end
  665. end
  666. func = earlyinit
  667. while func do
  668. local _, retval = xpcall(func, deferErrhand)
  669. if retval then return retval end
  670. coroutine.yield()
  671. end
  672. return 1
  673. end