boot.lua 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. --[[
  2. Copyright (c) 2006-2021 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(arg)
  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. love.rawGameArguments = arg
  256. local arg0 = love.arg.getLow(love.rawGameArguments)
  257. love.filesystem.init(arg0)
  258. local exepath = love.filesystem.getExecutablePath()
  259. if #exepath == 0 then
  260. -- This shouldn't happen, but just in case we'll fall back to arg0.
  261. exepath = arg0
  262. end
  263. no_game_code = false
  264. invalid_game_path = nil
  265. -- Is this one of those fancy "fused" games?
  266. local can_has_game = pcall(love.filesystem.setSource, exepath)
  267. -- It's a fused game, don't parse --game argument
  268. if can_has_game then
  269. love.arg.options.game.set = true
  270. end
  271. -- Parse options now that we know which options we're looking for.
  272. love.arg.parseOptions(love.rawGameArguments)
  273. -- parseGameArguments can only be called after parseOptions.
  274. love.parsedGameArguments = love.arg.parseGameArguments(love.rawGameArguments)
  275. local o = love.arg.options
  276. local is_fused_game = can_has_game or love.arg.options.fused.set
  277. love.filesystem.setFused(is_fused_game)
  278. love.setDeprecationOutput(not love.filesystem.isFused())
  279. local identity = ""
  280. if not can_has_game and o.game.set and o.game.arg[1] then
  281. local nouri = o.game.arg[1]
  282. if nouri:sub(1, 7) == "file://" then
  283. nouri = uridecode(nouri:sub(8))
  284. end
  285. local full_source = love.path.getFull(nouri)
  286. can_has_game = pcall(love.filesystem.setSource, full_source)
  287. if not can_has_game then
  288. invalid_game_path = full_source
  289. end
  290. -- Use the name of the source .love as the identity for now.
  291. identity = love.path.leaf(full_source)
  292. else
  293. -- Use the name of the exe as the identity for now.
  294. identity = love.path.leaf(exepath)
  295. end
  296. -- Try to use the archive containing main.lua as the identity name. It
  297. -- might not be available, in which case the fallbacks above are used.
  298. local realdir = love.filesystem.getRealDirectory("main.lua")
  299. if realdir then
  300. identity = love.path.leaf(realdir)
  301. end
  302. identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
  303. identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
  304. identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
  305. identity = #identity > 0 and identity or "lovegame"
  306. -- When conf.lua is initially loaded, the main source should be checked
  307. -- before the save directory (the identity should be appended.)
  308. pcall(love.filesystem.setIdentity, identity, true)
  309. if can_has_game and not (love.filesystem.getInfo("main.lua") or love.filesystem.getInfo("conf.lua")) then
  310. no_game_code = true
  311. end
  312. if not can_has_game then
  313. local nogame = require("love.nogame")
  314. nogame()
  315. end
  316. end
  317. function love.init()
  318. -- Create default configuration settings.
  319. -- NOTE: Adding a new module to the modules list
  320. -- will NOT make it load, see below.
  321. local c = {
  322. title = "Untitled",
  323. version = love._version,
  324. window = {
  325. width = 800,
  326. height = 600,
  327. x = nil,
  328. y = nil,
  329. minwidth = 1,
  330. minheight = 1,
  331. fullscreen = false,
  332. fullscreentype = "desktop",
  333. display = 1,
  334. vsync = 1,
  335. msaa = 0,
  336. borderless = false,
  337. resizable = false,
  338. centered = true,
  339. usedpiscale = true,
  340. },
  341. modules = {
  342. data = true,
  343. event = true,
  344. keyboard = true,
  345. mouse = true,
  346. timer = true,
  347. joystick = true,
  348. touch = true,
  349. image = true,
  350. graphics = true,
  351. audio = true,
  352. math = true,
  353. physics = true,
  354. sound = true,
  355. system = true,
  356. font = true,
  357. thread = true,
  358. window = true,
  359. video = true,
  360. },
  361. audio = {
  362. mixwithsystem = true, -- Only relevant for Android / iOS.
  363. mic = false, -- Only relevant for Android.
  364. },
  365. console = false, -- Only relevant for windows.
  366. identity = false,
  367. appendidentity = false,
  368. externalstorage = false, -- Only relevant for Android.
  369. accelerometerjoystick = true, -- Only relevant for Android / iOS.
  370. gammacorrect = false,
  371. highdpi = false,
  372. }
  373. -- Console hack, part 1.
  374. local openedconsole = false
  375. if love.arg.options.console.set and love._openConsole then
  376. love._openConsole()
  377. openedconsole = true
  378. end
  379. -- If config file exists, load it and allow it to update config table.
  380. local confok, conferr
  381. if (not love.conf) and love.filesystem and love.filesystem.getInfo("conf.lua") then
  382. confok, conferr = pcall(require, "conf")
  383. end
  384. -- Yes, conf.lua might not exist, but there are other ways of making
  385. -- love.conf appear, so we should check for it anyway.
  386. if love.conf then
  387. confok, conferr = pcall(love.conf, c)
  388. -- If love.conf errors, we'll trigger the error after loading modules so
  389. -- the error message can be displayed in the window.
  390. end
  391. -- Console hack, part 2.
  392. if c.console and love._openConsole and not openedconsole then
  393. love._openConsole()
  394. end
  395. -- Hack for disabling accelerometer-as-joystick on Android / iOS.
  396. if love._setAccelerometerAsJoystick then
  397. love._setAccelerometerAsJoystick(c.accelerometerjoystick)
  398. end
  399. if love._setGammaCorrect then
  400. love._setGammaCorrect(c.gammacorrect)
  401. end
  402. if love._setHighDPIAllowed then
  403. love._setHighDPIAllowed(c.highdpi)
  404. end
  405. if love._setAudioMixWithSystem then
  406. if c.audio and c.audio.mixwithsystem ~= nil then
  407. love._setAudioMixWithSystem(c.audio.mixwithsystem)
  408. end
  409. end
  410. if love._requestRecordingPermission then
  411. love._requestRecordingPermission(c.audio and c.audio.mic)
  412. end
  413. -- Gets desired modules.
  414. for k,v in ipairs{
  415. "data",
  416. "thread",
  417. "timer",
  418. "event",
  419. "keyboard",
  420. "joystick",
  421. "mouse",
  422. "touch",
  423. "sound",
  424. "system",
  425. "audio",
  426. "image",
  427. "video",
  428. "font",
  429. "window",
  430. "graphics",
  431. "math",
  432. "physics",
  433. } do
  434. if c.modules[v] then
  435. require("love." .. v)
  436. end
  437. end
  438. if love.event then
  439. love.createhandlers()
  440. end
  441. -- Check the version
  442. c.version = tostring(c.version)
  443. if not love.isVersionCompatible(c.version) then
  444. local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
  445. if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
  446. local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
  447. "It may not be compatible with the running version (%s)."):format(c.version, love._version)
  448. print(msg)
  449. if love.window then
  450. love.window.showMessageBox("Compatibility Warning", msg, "warning")
  451. end
  452. end
  453. end
  454. if not confok and conferr then
  455. error(conferr)
  456. end
  457. -- Setup window here.
  458. if c.window and c.modules.window then
  459. love.window.setTitle(c.window.title or c.title)
  460. assert(love.window.setMode(c.window.width, c.window.height,
  461. {
  462. fullscreen = c.window.fullscreen,
  463. fullscreentype = c.window.fullscreentype,
  464. vsync = c.window.vsync,
  465. msaa = c.window.msaa,
  466. stencil = c.window.stencil,
  467. depth = c.window.depth,
  468. resizable = c.window.resizable,
  469. minwidth = c.window.minwidth,
  470. minheight = c.window.minheight,
  471. borderless = c.window.borderless,
  472. centered = c.window.centered,
  473. display = c.window.display,
  474. highdpi = c.window.highdpi, -- deprecated
  475. usedpiscale = c.window.usedpiscale,
  476. x = c.window.x,
  477. y = c.window.y,
  478. }), "Could not set window mode")
  479. if c.window.icon then
  480. assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
  481. love.window.setIcon(love.image.newImageData(c.window.icon))
  482. end
  483. end
  484. -- Our first timestep, because window creation can take some time
  485. if love.timer then
  486. love.timer.step()
  487. end
  488. if love.filesystem then
  489. love.filesystem._setAndroidSaveExternal(c.externalstorage)
  490. love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
  491. if love.filesystem.getInfo("main.lua") then
  492. require("main")
  493. end
  494. end
  495. if no_game_code then
  496. error("No code to run\nYour game might be packaged incorrectly.\nMake sure main.lua is at the top level of the zip.")
  497. elseif invalid_game_path then
  498. error("Cannot load game at path '" .. invalid_game_path .. "'.\nMake sure a folder exists at the specified path.")
  499. end
  500. end
  501. -----------------------------------------------------------
  502. -- Default callbacks.
  503. -----------------------------------------------------------
  504. function love.run()
  505. if love.load then love.load(love.parsedGameArguments, love.rawGameArguments) end
  506. -- We don't want the first frame's dt to include time taken by love.load.
  507. if love.timer then love.timer.step() end
  508. -- Main loop time.
  509. return function()
  510. -- Process events.
  511. if love.event then
  512. love.event.pump()
  513. for name, a,b,c,d,e,f in love.event.poll() do
  514. if name == "quit" then
  515. if not love.quit or not love.quit() then
  516. return a or 0, b
  517. end
  518. end
  519. love.handlers[name](a,b,c,d,e,f)
  520. end
  521. end
  522. -- Update dt, as we'll be passing it to update
  523. local dt = love.timer and love.timer.step() or 0
  524. -- Call update and draw
  525. if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
  526. if love.graphics and love.graphics.isActive() then
  527. love.graphics.origin()
  528. love.graphics.clear(love.graphics.getBackgroundColor())
  529. if love.draw then love.draw() end
  530. love.graphics.present()
  531. end
  532. if love.timer then love.timer.sleep(0.001) end
  533. end
  534. end
  535. local debug, print, error = debug, print, error
  536. function love.threaderror(t, err)
  537. error("Thread error ("..tostring(t)..")\n\n"..err, 0)
  538. end
  539. local utf8 = require("utf8")
  540. local function error_printer(msg, layer)
  541. print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
  542. end
  543. function love.errhand(msg)
  544. msg = tostring(msg)
  545. error_printer(msg, 2)
  546. if not love.window or not love.graphics or not love.event then
  547. return
  548. end
  549. if not love.graphics.isCreated() or not love.window.isOpen() then
  550. local success, status = pcall(love.window.setMode, 800, 600)
  551. if not success or not status then
  552. return
  553. end
  554. end
  555. -- Reset state.
  556. if love.mouse then
  557. love.mouse.setVisible(true)
  558. love.mouse.setGrabbed(false)
  559. love.mouse.setRelativeMode(false)
  560. if love.mouse.isCursorSupported() then
  561. love.mouse.setCursor()
  562. end
  563. end
  564. if love.joystick then
  565. -- Stop all joystick vibrations.
  566. for i,v in ipairs(love.joystick.getJoysticks()) do
  567. v:setVibration()
  568. end
  569. end
  570. if love.audio then love.audio.stop() end
  571. love.graphics.reset()
  572. local font = love.graphics.setNewFont(14)
  573. love.graphics.setColor(1, 1, 1)
  574. local trace = debug.traceback()
  575. love.graphics.origin()
  576. local sanitizedmsg = {}
  577. for char in msg:gmatch(utf8.charpattern) do
  578. table.insert(sanitizedmsg, char)
  579. end
  580. sanitizedmsg = table.concat(sanitizedmsg)
  581. local err = {}
  582. table.insert(err, "Error\n")
  583. table.insert(err, sanitizedmsg)
  584. if #sanitizedmsg ~= #msg then
  585. table.insert(err, "Invalid UTF-8 string in error message.")
  586. end
  587. table.insert(err, "\n")
  588. for l in trace:gmatch("(.-)\n") do
  589. if not l:match("boot.lua") then
  590. l = l:gsub("stack traceback:", "Traceback\n")
  591. table.insert(err, l)
  592. end
  593. end
  594. local p = table.concat(err, "\n")
  595. p = p:gsub("\t", "")
  596. p = p:gsub("%[string \"(.-)\"%]", "%1")
  597. local function draw()
  598. local pos = 70
  599. love.graphics.clear(89/255, 157/255, 220/255)
  600. love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
  601. love.graphics.present()
  602. end
  603. local fullErrorText = p
  604. local function copyToClipboard()
  605. if not love.system then return end
  606. love.system.setClipboardText(fullErrorText)
  607. p = p .. "\nCopied to clipboard!"
  608. draw()
  609. end
  610. if love.system then
  611. p = p .. "\n\nPress Ctrl+C or tap to copy this error"
  612. end
  613. return function()
  614. love.event.pump()
  615. for e, a, b, c in love.event.poll() do
  616. if e == "quit" then
  617. return 1
  618. elseif e == "keypressed" and a == "escape" then
  619. return 1
  620. elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
  621. copyToClipboard()
  622. elseif e == "touchpressed" then
  623. local name = love.window.getTitle()
  624. if #name == 0 or name == "Untitled" then name = "Game" end
  625. local buttons = {"OK", "Cancel"}
  626. if love.system then
  627. buttons[3] = "Copy to clipboard"
  628. end
  629. local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
  630. if pressed == 1 then
  631. return 1
  632. elseif pressed == 3 then
  633. copyToClipboard()
  634. end
  635. end
  636. end
  637. draw()
  638. if love.timer then
  639. love.timer.sleep(0.1)
  640. end
  641. end
  642. end
  643. -----------------------------------------------------------
  644. -- The root of all calls.
  645. -----------------------------------------------------------
  646. return function()
  647. local func
  648. local inerror = false
  649. local function deferErrhand(...)
  650. local errhand = love.errorhandler or love.errhand
  651. local handler = (not inerror and errhand) or error_printer
  652. inerror = true
  653. func = handler(...)
  654. end
  655. local function earlyinit()
  656. -- If love.boot fails, return 1 and finish immediately
  657. local result = xpcall(love.boot, error_printer)
  658. if not result then return 1 end
  659. -- If love.init or love.run fails, don't return a value,
  660. -- as we want the error handler to take over
  661. result = xpcall(love.init, deferErrhand)
  662. if not result then return end
  663. -- NOTE: We can't assign to func directly, as we'd
  664. -- overwrite the result of deferErrhand with nil on error
  665. local main
  666. result, main = xpcall(love.run, deferErrhand)
  667. if result then
  668. func = main
  669. end
  670. end
  671. func = earlyinit
  672. while func do
  673. local _, retval, restartvalue = xpcall(func, deferErrhand)
  674. if retval then return retval, restartvalue end
  675. coroutine.yield()
  676. end
  677. return 1
  678. end