main.lua 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. local editors = require'editors'
  2. local modifiers = {ctrl = false, alt = false, shift = false}
  3. local mounted_project
  4. local errhand
  5. -- PUC Lua 5.1 doesn't support function arguments in xpcall
  6. local xpacall = function(fn, err, ...)
  7. local args = {...}
  8. return xpcall(function() return fn(unpack(args)) end, err)
  9. end
  10. -- copy example project to save dir if it doesn't exist
  11. if not lovr.filesystem.isDirectory('projects') then
  12. lovr.filesystem.createDirectory('example')
  13. end
  14. if not lovr.filesystem.isFile('projects/example/main.lua') then
  15. print('copying example project into directory', lovr.filesystem.getSaveDirectory())
  16. local content = lovr.filesystem.read('seed/main.lua')
  17. lovr.filesystem.createDirectory('projects/example')
  18. local success = lovr.filesystem.write('projects/example/main.lua', content)
  19. if not success then print('! could not write to save directory') end
  20. end
  21. -- contained callbacks that loaded user project tried to register
  22. -- init'ed as stubs, overwritten on user project load
  23. local callbacks = {
  24. draw = function (pass) end,
  25. update = function (dt) end,
  26. keypressed = function (key, scancode, isrepeat) end,
  27. keyreleased = function (key, scancode) end,
  28. textinput = function (k) end,
  29. }
  30. local function runProject()
  31. local stored = {
  32. load = lovr.load,
  33. draw = lovr.draw,
  34. update = lovr.update,
  35. keypressed = lovr.keypressed,
  36. keyreleased = lovr.keyreleased,
  37. textinput = lovr.textinput,
  38. }
  39. package.loaded['main'] = nil
  40. -- loading user project will overwrite some of indeck's callbacks
  41. xpacall(require, errhand, 'main')
  42. -- reinstate indeck's callbacks store the user project callbacks for event forwarding
  43. for _, fname in ipairs({'draw', 'update', 'keypressed', 'keyreleased', 'textinput'}) do
  44. if stored[fname] ~= lovr[fname] then
  45. lovr[fname], callbacks[fname] = stored[fname], lovr[fname] -- the switch
  46. end
  47. end
  48. if stored.load ~= lovr.load then -- call user's load() once and forget about it
  49. xpacall(lovr.load, errhand)
  50. end
  51. end
  52. local function pauseProject()
  53. print('pausing project')
  54. callbacks = {
  55. draw = function (pass) end,
  56. update = function (dt) end,
  57. keypressed = function (key, scancode, isrepeat) end,
  58. keyreleased = function (key, scancode) end,
  59. textinput = function (k) end,
  60. }
  61. end
  62. local function switchToProject(project_dir)
  63. if mounted_project then
  64. lovr.filesystem.unmount(mounted_project)
  65. end
  66. -- reloading the user project
  67. local full_path = lovr.filesystem.getSaveDirectory() .. '/projects/' .. project_dir
  68. print('loading user project', full_path)
  69. local success = lovr.filesystem.mount(full_path, '', false)
  70. if not success then
  71. print('! unsucessful; does project dir exist?')
  72. return
  73. end
  74. mounted_project = full_path
  75. runProject()
  76. -- open project's main.lua in active editor
  77. local path_to_main = 'projects/' .. project_dir .. '/main.lua'
  78. if lovr.filesystem.isFile(path_to_main) and editors.active then
  79. editors.active:openFile(path_to_main)
  80. else
  81. print(lovr.filesystem.isFile(path_to_main), path_to_main)
  82. end
  83. end
  84. function lovr.load()
  85. if lovr.headset then lovr.headset.update() end
  86. lovr.filesystem.unmount(lovr.filesystem.getSource())
  87. lovr.filesystem.mount('lovr-api', 'help')
  88. local editor = editors.new(120, 60, switchToProject)
  89. editor:listFiles('')
  90. end
  91. function lovr.update(dt)
  92. xpacall(callbacks.update, errhand, dt)
  93. end
  94. function lovr.draw(pass)
  95. -- main pass rendering
  96. for _, editor in ipairs(editors) do
  97. editor:draw(pass)
  98. end
  99. pass:setColor(1,1,1)
  100. local _, skip = xpacall(callbacks.draw, errhand, pass)
  101. -- drawing to texture in separate passes per editor
  102. local passes = {}
  103. for _, editor in ipairs(editors) do
  104. local editor_pass = editor:drawToTexture()
  105. table.insert(passes, editor_pass)
  106. end
  107. if not skip then
  108. table.insert(passes, pass)
  109. end
  110. return lovr.graphics.submit(passes)
  111. end
  112. function lovr.keypressed(key, scancode, isrepeat)
  113. if key == 'lctrl' or key == 'rctrl' then
  114. modifiers.ctrl = true
  115. return
  116. elseif key == 'lalt' or key == 'ralt' then
  117. modifiers.alt = true
  118. return
  119. elseif key == 'lshift' or key == 'rshift' then
  120. modifiers.shift = true
  121. return
  122. end
  123. local combo = string.format('%s%s%s%s',
  124. modifiers.ctrl and 'ctrl+' or '',
  125. modifiers.alt and 'alt+' or '',
  126. modifiers.shift and 'shift+' or '',
  127. key)
  128. if combo == 'ctrl+p' then -- spawn new editor
  129. local editor = editors.new(120, 60, switchToProject)
  130. editor:listFiles()
  131. elseif combo =='ctrl+r' then -- restart project
  132. if editors.active then editors.active:saveFile() end
  133. runProject()
  134. elseif combo =='ctrl+shift+r' then
  135. lovr.event.push('restart')
  136. elseif combo =='escape' then
  137. lovr.event.push('quit')
  138. end
  139. editors.keypressed(combo)
  140. xpacall(callbacks.keypressed, errhand, key, scancode, isrepeat, combo)
  141. end
  142. function lovr.keyreleased(key, scancode)
  143. if key == 'lctrl' or key == 'rctrl' then
  144. modifiers.ctrl = false
  145. return
  146. elseif key == 'lalt' or key == 'ralt' then
  147. modifiers.alt = false
  148. return
  149. elseif key == 'lshift' or key == 'rshift' then
  150. modifiers.shift = false
  151. return
  152. end
  153. xpacall(callbacks.keyreleased, errhand, key, scancode)
  154. end
  155. function lovr.textinput(k)
  156. if k:match('[^\n]') then
  157. editors.textinput(k)
  158. end
  159. xpacall(callbacks.textinput, errhand, k)
  160. end
  161. local function wrap(str, limit)
  162. limit = limit or 60
  163. local position = 1
  164. local function check(sp, st, word, fi)
  165. if fi - position > limit then
  166. position = st
  167. return "\n" .. word
  168. end
  169. end
  170. return str:gsub("(%s+)()(%S+)()", check)
  171. end
  172. local function showStackTrace(info)
  173. local prev_active = editors.active
  174. local traceback_editor = editors.new(60, 30)
  175. editors.active = prev_active or editors.active
  176. traceback_editor:setText(wrap(info, traceback_editor.cols))
  177. traceback_editor.transform:translate(-1,0,-0.4)
  178. traceback_editor.transform:rotate(-math.rad(40), 0,1,0)
  179. end
  180. local function continueRunning()
  181. if lovr.event then
  182. lovr.event.pump()
  183. for name, a, b, c, d in lovr.event.poll() do
  184. if name == 'restart' then
  185. local cookie = lovr.restart and lovr.restart()
  186. return 'restart', cookie
  187. elseif name == 'quit' and (not lovr.quit or not lovr.quit(a)) then
  188. return a or 0
  189. end
  190. if lovr.handlers[name] then lovr.handlers[name](a, b, c, d) end
  191. end
  192. end
  193. local dt = 0
  194. if lovr.timer then dt = lovr.timer.step() end
  195. if lovr.headset then dt = lovr.headset.update() end
  196. if lovr.update then lovr.update(dt) end
  197. if lovr.graphics then
  198. if lovr.headset then
  199. local pass = lovr.headset.getPass()
  200. if pass then
  201. local skip = lovr.draw and lovr.draw(pass)
  202. if not skip then lovr.graphics.submit(pass) end
  203. end
  204. end
  205. if lovr.system.isWindowOpen() then
  206. if lovr.mirror then
  207. local pass = lovr.graphics.getWindowPass()
  208. local skip = not pass or lovr.mirror(pass)
  209. if not skip then lovr.graphics.submit(pass) end
  210. end
  211. lovr.graphics.present()
  212. end
  213. end
  214. if lovr.headset then lovr.headset.submit() end
  215. if lovr.math then lovr.math.drain() end
  216. end
  217. errhand = function(message, traceback)
  218. traceback = traceback or debug.traceback('', 3)
  219. local error_message = message .. '\n' .. traceback
  220. print('! runtime error')
  221. print(error_message)
  222. pauseProject()
  223. showStackTrace(error_message)
  224. return continueRunning
  225. end