main.lua 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. -- Entrypoint for the app. You can edit this file from within the app if
  2. -- you're careful.
  3. -- files that come with LÖVE; we can't edit those from within the app
  4. utf8 = require 'utf8'
  5. function load_file_from_source_or_save_directory(filename)
  6. local contents = love.filesystem.read(filename)
  7. local code, err = loadstring(contents, filename)
  8. if code == nil then
  9. error(err)
  10. end
  11. return code()
  12. end
  13. json = load_file_from_source_or_save_directory('json.lua')
  14. load_file_from_source_or_save_directory('app.lua')
  15. load_file_from_source_or_save_directory('test.lua')
  16. load_file_from_source_or_save_directory('keychord.lua')
  17. load_file_from_source_or_save_directory('button.lua')
  18. -- both sides require (different parts of) the logging framework
  19. load_file_from_source_or_save_directory('log.lua')
  20. -- but some files we want to only load sometimes
  21. function App.load()
  22. log_new('session')
  23. if love.filesystem.getInfo('config') then
  24. Settings = json.decode(love.filesystem.read('config'))
  25. Current_app = Settings.current_app
  26. end
  27. -- Current_app =
  28. -- | run
  29. -- | source
  30. -- | {name=warning message='...' next_app = run|source}
  31. if Current_app == nil then
  32. Current_app = 'run'
  33. end
  34. if Current_app == 'run' then
  35. load_file_from_source_or_save_directory('file.lua')
  36. load_file_from_source_or_save_directory('run.lua')
  37. load_file_from_source_or_save_directory('edit.lua')
  38. load_file_from_source_or_save_directory('text.lua')
  39. load_file_from_source_or_save_directory('search.lua')
  40. load_file_from_source_or_save_directory('select.lua')
  41. load_file_from_source_or_save_directory('undo.lua')
  42. load_file_from_source_or_save_directory('text_tests.lua')
  43. load_file_from_source_or_save_directory('run_tests.lua')
  44. elseif Current_app == 'source' then
  45. load_file_from_source_or_save_directory('source_file.lua')
  46. load_file_from_source_or_save_directory('source.lua')
  47. load_file_from_source_or_save_directory('commands.lua')
  48. load_file_from_source_or_save_directory('source_edit.lua')
  49. load_file_from_source_or_save_directory('log_browser.lua')
  50. load_file_from_source_or_save_directory('source_text.lua')
  51. load_file_from_source_or_save_directory('search.lua')
  52. load_file_from_source_or_save_directory('source_select.lua')
  53. load_file_from_source_or_save_directory('source_undo.lua')
  54. load_file_from_source_or_save_directory('colorize.lua')
  55. load_file_from_source_or_save_directory('source_text_tests.lua')
  56. load_file_from_source_or_save_directory('icons.lua')
  57. load_file_from_source_or_save_directory('drawing.lua')
  58. load_file_from_source_or_save_directory('geom.lua')
  59. load_file_from_source_or_save_directory('help.lua')
  60. load_file_from_source_or_save_directory('drawing_tests.lua')
  61. load_file_from_source_or_save_directory('source_tests.lua')
  62. elseif current_app_is_warning() then
  63. else
  64. assert(false, 'unknown app "'..Current_app..'"')
  65. end
  66. end
  67. function App.initialize_globals()
  68. Supported_versions = {'11.5', '11.4', '12.0'} -- put the recommended version first
  69. check_love_version_for_tests()
  70. if Current_app == 'run' then
  71. run.initialize_globals()
  72. elseif Current_app == 'source' then
  73. source.initialize_globals()
  74. elseif current_app_is_warning() then
  75. else
  76. assert(false, 'unknown app "'..Current_app..'"')
  77. end
  78. -- for hysteresis in a few places
  79. Current_time = 0
  80. Last_focus_time = 0 -- https://love2d.org/forums/viewtopic.php?p=249700
  81. Last_resize_time = 0
  82. -- Another weird bit for a class of corner cases. E.g.:
  83. -- * I press ctrl+e, switch Current_app. I don't want the new app to receive
  84. -- text_input and key_release events.
  85. -- If I try to avoid text_input events by switching modes on key_release, I
  86. -- hit a new problem:
  87. -- * I press ctrl+e, am running an untested version, Current_app goes to
  88. -- 'warning', and immediately rolls back out of 'warning' in the
  89. -- key_release event.
  90. -- Skip_rest_of_key_events is ugly, but feels cleaner than creating yet
  91. -- another possible value for Current_app.
  92. Skip_rest_of_key_events = nil
  93. end
  94. function check_love_version_for_tests()
  95. if array.find(Supported_versions, Version) == nil then
  96. -- warning to include in an error message if any tests failed
  97. Warning_before_tests = ("This app hasn't been tested with LÖVE version %s."):format(Version)
  98. end
  99. end
  100. function App.initialize(arg)
  101. love.keyboard.setKeyRepeat(true)
  102. love.graphics.setBackgroundColor(1,1,1)
  103. if Current_app == 'run' then
  104. run.initialize(arg)
  105. elseif Current_app == 'source' then
  106. source.initialize(arg)
  107. elseif current_app_is_warning() then
  108. else
  109. assert(false, 'unknown app "'..Current_app..'"')
  110. end
  111. check_love_version()
  112. end
  113. function check_love_version()
  114. if array.find(Supported_versions, Version) == nil then
  115. show_warning(
  116. ("This app hasn't been tested with LÖVE version %s; please switch to version %s if you run into issues. Press any key to continue."):format(Version, Supported_versions[1]))
  117. -- continue initializing everything; hopefully we won't have errors during initialization
  118. end
  119. end
  120. function App.resize(w,h)
  121. if current_app_is_warning() then return end
  122. if Current_app == 'run' then
  123. if run.resize then run.resize(w,h) end
  124. elseif Current_app == 'source' then
  125. if source.resize then source.resize(w,h) end
  126. else
  127. assert(false, 'unknown app "'..Current_app..'"')
  128. end
  129. Last_resize_time = Current_time
  130. end
  131. function App.filedropped(file)
  132. if current_app_is_warning() then return end
  133. if Current_app == 'run' then
  134. if run.file_drop then run.file_drop(file) end
  135. elseif Current_app == 'source' then
  136. if source.file_drop then source.file_drop(file) end
  137. else
  138. assert(false, 'unknown app "'..Current_app..'"')
  139. end
  140. end
  141. function App.focus(in_focus)
  142. if current_app_is_warning() then return end
  143. if in_focus then
  144. Last_focus_time = Current_time
  145. end
  146. if Current_app == 'run' then
  147. if run.focus then run.focus(in_focus) end
  148. elseif Current_app == 'source' then
  149. if source.focus then source.focus(in_focus) end
  150. else
  151. assert(false, 'unknown app "'..Current_app..'"')
  152. end
  153. end
  154. function App.draw()
  155. if Current_app == 'run' then
  156. run.draw()
  157. elseif Current_app == 'source' then
  158. source.draw()
  159. elseif current_app_is_warning() then
  160. love.graphics.setColor(0,0,1)
  161. love.graphics.rectangle('fill', 0,0, App.screen.width, App.screen.height)
  162. love.graphics.setColor(1,1,1)
  163. love.graphics.printf(Current_app.message, 40,40, 600)
  164. else
  165. assert(false, 'unknown app "'..Current_app..'"')
  166. end
  167. end
  168. function App.update(dt)
  169. Current_time = Current_time + dt
  170. if current_app_is_warning() then return end
  171. -- some hysteresis while resizing
  172. if Current_time < Last_resize_time + 0.1 then
  173. return
  174. end
  175. --
  176. if Current_app == 'run' then
  177. run.update(dt)
  178. elseif Current_app == 'source' then
  179. source.update(dt)
  180. else
  181. assert(false, 'unknown app "'..Current_app..'"')
  182. end
  183. end
  184. function App.keychord_press(chord, key)
  185. -- ignore events for some time after window in focus (mostly alt-tab)
  186. if Current_time < Last_focus_time + 0.01 then
  187. return
  188. end
  189. --
  190. Skip_rest_of_key_events = nil
  191. if current_app_is_warning() then
  192. if chord == 'C-c' then
  193. love.system.setClipboardText(warning_message())
  194. else
  195. clear_warning()
  196. Skip_rest_of_key_events = true
  197. end
  198. return
  199. end
  200. if chord == 'C-e' then
  201. -- carefully save settings
  202. if Current_app == 'run' then
  203. local source_settings = Settings.source
  204. Settings = run.settings()
  205. Settings.source = source_settings
  206. if run.quit then run.quit() end
  207. Current_app = 'source'
  208. -- preserve any Error_message when going from run to source
  209. elseif Current_app == 'source' then
  210. Settings.source = source.settings()
  211. if source.quit then source.quit() end
  212. Current_app = 'run'
  213. Error_message = nil
  214. elseif current_app_is_warning() then
  215. else
  216. assert(false, 'unknown app "'..Current_app..'"')
  217. end
  218. Settings.current_app = Current_app
  219. love.filesystem.write('config', json.encode(Settings))
  220. -- reboot
  221. load_file_from_source_or_save_directory('main.lua')
  222. App.undo_initialize()
  223. App.run_tests_and_initialize()
  224. Skip_rest_of_key_events = true
  225. return
  226. end
  227. if Current_app == 'run' then
  228. if run.keychord_press then run.keychord_press(chord, key) end
  229. elseif Current_app == 'source' then
  230. if source.keychord_press then source.keychord_press(chord, key) end
  231. else
  232. assert(false, 'unknown app "'..Current_app..'"')
  233. end
  234. end
  235. function App.textinput(t)
  236. if current_app_is_warning() then return end
  237. -- ignore events for some time after window in focus (mostly alt-tab)
  238. if Current_time < Last_focus_time + 0.01 then
  239. return
  240. end
  241. --
  242. if Skip_rest_of_key_events then return end
  243. if Current_app == 'run' then
  244. if run.text_input then run.text_input(t) end
  245. elseif Current_app == 'source' then
  246. if source.text_input then source.text_input(t) end
  247. else
  248. assert(false, 'unknown app "'..Current_app..'"')
  249. end
  250. end
  251. function App.keyreleased(key, scancode)
  252. if current_app_is_warning() then return end
  253. -- ignore events for some time after window in focus (mostly alt-tab)
  254. if Current_time < Last_focus_time + 0.01 then
  255. return
  256. end
  257. --
  258. if Skip_rest_of_key_events then return end
  259. if Current_app == 'run' then
  260. if run.key_release then run.key_release(key, scancode) end
  261. elseif Current_app == 'source' then
  262. if source.key_release then source.key_release(key, scancode) end
  263. else
  264. assert(false, 'unknown app "'..Current_app..'"')
  265. end
  266. end
  267. function App.mousepressed(x,y, mouse_button)
  268. if current_app_is_warning() then return end
  269. --? print('mouse press', x,y)
  270. if Current_app == 'run' then
  271. if run.mouse_press then run.mouse_press(x,y, mouse_button) end
  272. elseif Current_app == 'source' then
  273. if source.mouse_press then source.mouse_press(x,y, mouse_button) end
  274. else
  275. assert(false, 'unknown app "'..Current_app..'"')
  276. end
  277. end
  278. function App.mousereleased(x,y, mouse_button)
  279. if current_app_is_warning() then return end
  280. if Current_app == 'run' then
  281. if run.mouse_release then run.mouse_release(x,y, mouse_button) end
  282. elseif Current_app == 'source' then
  283. if source.mouse_release then source.mouse_release(x,y, mouse_button) end
  284. else
  285. assert(false, 'unknown app "'..Current_app..'"')
  286. end
  287. end
  288. function App.mousemoved(x,y, dx,dy, is_touch)
  289. if current_app_is_warning() then return end
  290. if Current_app == 'run' then
  291. if run.mouse_move then run.mouse_move(dx,dy) end
  292. elseif Current_app == 'source' then
  293. if source.mouse_move then source.mouse_move(dx,dy) end
  294. else
  295. assert(false, 'unknown app "'..Current_app..'"')
  296. end
  297. end
  298. function App.wheelmoved(dx,dy)
  299. if current_app_is_warning() then return end
  300. if Current_app == 'run' then
  301. if run.mouse_wheel_move then run.mouse_wheel_move(dx,dy) end
  302. elseif Current_app == 'source' then
  303. if source.mouse_wheel_move then source.mouse_wheel_move(dx,dy) end
  304. else
  305. assert(false, 'unknown app "'..Current_app..'"')
  306. end
  307. end
  308. function App.mousefocus(in_focus)
  309. if current_app_is_warning() then return end
  310. if Current_app == 'run' then
  311. if run.mouse_focus then run.mouse_focus(in_focus) end
  312. elseif Current_app == 'source' then
  313. if source.mouse_focus then source.mouse_focus(in_focus) end
  314. else
  315. assert(false, 'unknown app "'..Current_app..'"')
  316. end
  317. end
  318. function love.quit()
  319. if Disable_all_quit_handlers then return end
  320. if current_app_is_warning() then return end
  321. if Current_app == 'run' then
  322. local source_settings = Settings.source
  323. Settings = run.settings()
  324. Settings.source = source_settings
  325. else
  326. Settings.source = source.settings()
  327. end
  328. Settings.current_app = Current_app
  329. love.filesystem.write('config', json.encode(Settings))
  330. if Current_app == 'run' then
  331. if run.quit then run.quit() end
  332. elseif Current_app == 'source' then
  333. if source.quit then source.quit() end
  334. else
  335. assert(false, 'unknown app "'..Current_app..'"')
  336. end
  337. end
  338. function current_app_is_warning()
  339. return type(Current_app) == 'table' and Current_app.name == 'warning'
  340. end
  341. function show_warning(message)
  342. assert(type(Current_app) == 'string')
  343. Current_app = {
  344. name = 'warning',
  345. message = message,
  346. next_app = Current_app,
  347. }
  348. end
  349. function clear_warning()
  350. assert(type(Current_app) == 'table')
  351. Current_app = Current_app.next_app
  352. end
  353. function warning_message()
  354. assert(type(Current_app) == 'table')
  355. return Current_app.message
  356. end