mtos.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  1. -----------------------------------------------------
  2. -- Hard-Coded version attributes
  3. -----------------------------------------------------
  4. laptop.os_version_attr = {
  5. ['1.10'] = {
  6. releaseyear = '1976',
  7. version_string = '1.10',
  8. tty_style = 'AMBER',
  9. custom_launcher = "cs-bos_launcher",
  10. custom_theme = "Amber Shell",
  11. blacklist_commands = { EXIT = true },
  12. tty_monochrome = true,
  13. min_scrollback_size = 20,
  14. max_scrollback_size = 34,
  15. },
  16. ['3.31'] = {
  17. releaseyear = '1982',
  18. version_string = '3.31',
  19. tty_style = 'GREEN',
  20. custom_launcher = "cs-bos_launcher",
  21. custom_theme = "Green Shell",
  22. blacklist_commands = { EXIT = true },
  23. tty_monochrome = true,
  24. min_scrollback_size = 25,
  25. max_scrollback_size = 100,
  26. },
  27. ['5.02'] = {
  28. releaseyear = '1989',
  29. version_string = '5.02',
  30. tty_style = 'WHITE',
  31. custom_theme = "Circuit",
  32. blacklist_commands = { },
  33. min_scrollback_size = 25,
  34. max_scrollback_size = 300,
  35. },
  36. ['6.33'] = {
  37. releaseyear = '1995',
  38. version_string = '6.33',
  39. tty_style = 'WHITE',
  40. custom_theme = "Clouds",
  41. blacklist_commands = { },
  42. min_scrollback_size = 25,
  43. max_scrollback_size = 300,
  44. },
  45. ['10.00'] = {
  46. releaseyear = '2010',
  47. version_string = '10.00',
  48. tty_style = 'WHITE',
  49. custom_theme = "Freedom",
  50. blacklist_commands = { },
  51. min_scrollback_size = 25,
  52. max_scrollback_size = 300,
  53. },
  54. }
  55. laptop.os_version_attr.default = laptop.os_version_attr['10.00']
  56. -----------------------------------------------------
  57. -- Hard-Coded supported monochrome colors
  58. -----------------------------------------------------
  59. laptop.supported_textcolors = {
  60. GREEN = "#00FF33",
  61. AMBER = "#FFB000",
  62. WHITE = "#FFFFFF",
  63. }
  64. -----------------------------------------------------
  65. -- Operating System cache
  66. -----------------------------------------------------
  67. local mtos_cache = {
  68. list = {},
  69. is_running = false
  70. }
  71. laptop.mtos_cache = mtos_cache
  72. function mtos_cache:get(pos)
  73. local hash = minetest.hash_node_position(pos)
  74. return self.list[hash]
  75. end
  76. function mtos_cache:set(pos, mtos)
  77. local hash = minetest.hash_node_position(pos)
  78. mtos.cache_first_access = mtos.cache_first_access or os.time()
  79. mtos.cache_last_access = os.time()
  80. self.list[hash] = mtos
  81. self:check_free_step()
  82. end
  83. function mtos_cache:free(pos)
  84. local hash = minetest.hash_node_position(pos)
  85. self.list[hash] = nil
  86. end
  87. function mtos_cache:sync_and_free(mtos)
  88. mtos.bdev:sync()
  89. self:free(mtos.pos)
  90. end
  91. function mtos_cache:check_free_step()
  92. -- do not start twice
  93. if self.is_running then
  94. return
  95. end
  96. local function check_free(mtos_cache)
  97. local current_time
  98. for hash, mtos in pairs(mtos_cache.list) do
  99. current_time = current_time or os.time()
  100. if mtos.cache_last_access + 5 <= current_time or -- 5 seconds unused
  101. mtos.cache_first_access + 15 <= current_time then -- or 15 seconds active
  102. self:sync_and_free(mtos)
  103. end
  104. end
  105. mtos_cache.is_running = false
  106. mtos_cache:check_free_step()
  107. end
  108. if next(self.list) then
  109. self.is_running = true
  110. minetest.after(1, check_free, self)
  111. end
  112. end
  113. -- Sync all on shutdown
  114. minetest.register_on_shutdown(function()
  115. for hash, mtos in pairs(mtos_cache.list) do
  116. mtos_cache:sync_and_free(mtos)
  117. end
  118. end)
  119. -----------------------------------------------------
  120. -- Operating System class
  121. -----------------------------------------------------
  122. local os_class = {}
  123. os_class.__index = os_class
  124. laptop.class_lib.os = os_class
  125. -- Swap the node
  126. function os_class:swap_node(new_node_name)
  127. local node = minetest.get_node(self.pos)
  128. if new_node_name then
  129. node.name = new_node_name
  130. self.hwdef = laptop.node_config[self.node.name]
  131. end
  132. if self.hwdef.paramtype2 == "colorfacedir" then
  133. local fdir = math.floor(node.param2 % 32)
  134. node.param2 = fdir + self.theme.node_color * 32
  135. end
  136. self:set_infotext(self.hwdef.infotext)
  137. minetest.swap_node(self.pos, node)
  138. end
  139. -- Power on the system and start the launcher
  140. function os_class:power_on(new_node_name)
  141. self.bdev:free_ram_disk()
  142. mtos_cache:free(self.pos)
  143. -- update current instance with reinitialized data
  144. for k,v in pairs(laptop.os_get(self.pos)) do
  145. self[k] = v
  146. end
  147. self:swap_node(new_node_name)
  148. self:set_app() --launcher
  149. end
  150. -- Power on the system / and resume last running app
  151. function os_class:resume(new_node_name)
  152. self:swap_node(new_node_name)
  153. self:set_app('<pop>')
  154. end
  155. -- Power off the system
  156. function os_class:power_off(new_node_name)
  157. self:swap_node(new_node_name)
  158. self:set_app('os:power_off')
  159. end
  160. -- Set infotext for system
  161. function os_class:set_infotext(infotext)
  162. self.meta:set_string('infotext', infotext)
  163. end
  164. -- Get given or current theme
  165. function os_class:get_theme(theme)
  166. if not theme then
  167. if self.sysdata then
  168. theme = self.sysdata.theme
  169. end
  170. if not theme then
  171. theme = self.hwdef.custom_theme or self.os_attr.custom_theme
  172. end
  173. end
  174. return laptop.get_theme(theme)
  175. end
  176. -- Set current theme
  177. function os_class:set_theme(theme)
  178. if laptop.themes[theme] then
  179. if self.sysdata then
  180. self.sysdata.theme = theme
  181. end
  182. self.theme = self:get_theme()
  183. self:swap_node()
  184. self:save()
  185. end
  186. end
  187. function os_class:get_os_attr()
  188. local os_attr = {}
  189. local os_version = self.hwdef.os_version or 'default'
  190. for k, v in pairs(laptop.os_version_attr[os_version]) do
  191. os_attr[k] = v
  192. end
  193. os_attr.tty_style = self.hwdef.tty_style or os_attr.tty_style
  194. if self.hwdef.tty_monochrome ~= nil then
  195. os_attr.tty_monochrome = self.hwdef.tty_monochrome
  196. end
  197. if os_attr.tty_monochrome then
  198. os_attr.blacklist_commands.TEXTCOLOR = true
  199. end
  200. return os_attr
  201. end
  202. -- Add app to stack (before starting new)
  203. function os_class:appstack_add(appname)
  204. table.insert(self.sysram.stack, appname)
  205. end
  206. -- Get last app from stack
  207. function os_class:appstack_pop()
  208. local ret
  209. if #self.sysram.stack > 0 then
  210. ret = self.sysram.stack[#self.sysram.stack]
  211. table.remove(self.sysram.stack, #self.sysram.stack)
  212. end
  213. return ret
  214. end
  215. -- Free stack
  216. function os_class:appstack_free()
  217. self.sysram.stack = {}
  218. end
  219. -- Get new app instance
  220. function os_class:is_app_compatible(name)
  221. local app_def = laptop.apps[name]
  222. if not app_def then
  223. return false
  224. end
  225. if app_def.os_min_version and (tonumber(app_def.os_min_version) > tonumber(self.os_attr.version_string)) then
  226. return false
  227. end
  228. if app_def.os_max_version and (tonumber(app_def.os_max_version) < tonumber(self.os_attr.version_string)) then
  229. return false
  230. end
  231. return true
  232. end
  233. -- Get new app instance
  234. function os_class:is_theme_compatible(name)
  235. local theme_def = laptop.themes[name]
  236. if not theme_def then
  237. return false
  238. end
  239. if theme_def.os_min_version and (tonumber(theme_def.os_min_version) > tonumber(self.os_attr.version_string)) then
  240. return false
  241. end
  242. if theme_def.os_max_version and (tonumber(theme_def.os_max_version) < tonumber(self.os_attr.version_string)) then
  243. return false
  244. end
  245. return true
  246. end
  247. -- Get new app instance
  248. function os_class:get_app(name)
  249. local template = laptop.apps[name]
  250. if not template then
  251. return
  252. end
  253. local app = setmetatable({}, laptop.class_lib.app)
  254. for k,v in pairs(template) do
  255. app[k] = v
  256. end
  257. app.name = name
  258. app.os = self
  259. return app
  260. end
  261. -- suspend timer from previous app before switching to new app
  262. function os_class:save_timer()
  263. if not self.sysram.current_app then
  264. return
  265. end
  266. local appname = self.sysram.current_app
  267. if not appname then
  268. return
  269. end
  270. self.timer = minetest.get_node_timer(self.pos)
  271. if self.timer:is_started() then
  272. self.sysram.app_timer[appname] = {
  273. timeout = self.timer:get_timeout(),
  274. elapsed = self.timer:get_elapsed(),
  275. }
  276. else
  277. self.sysram.app_timer[self.sysram.current_app] = nil
  278. end
  279. end
  280. -- restore the timer of current app
  281. function os_class:resume_timer(appname)
  282. if not appname then
  283. if not self.sysram.current_app then
  284. return
  285. end
  286. appname = self.sysram.current_app
  287. end
  288. self.timer = self.timer or minetest.get_node_timer(self.pos)
  289. if self.sysram.app_timer[appname] then
  290. local data = self.sysram.app_timer[appname]
  291. self.timer:set(data.timeout, data.elapsed)
  292. else
  293. self.timer:stop()
  294. end
  295. end
  296. -- Activate the app
  297. function os_class:set_app(appname)
  298. local launcher = self.hwdef.custom_launcher or self.os_attr.custom_launcher or "launcher"
  299. local newapp = appname or launcher
  300. if newapp == launcher then
  301. self:appstack_free()
  302. elseif newapp == '<pop>' then
  303. newapp = self:appstack_pop() or launcher
  304. elseif self.sysram.current_app and
  305. self.sysram.current_app ~= launcher and
  306. self.sysram.current_app ~= newapp then
  307. self:appstack_add(self.sysram.current_app)
  308. end
  309. if self.sysram.current_app ~= newapp then
  310. self:save_timer()
  311. self:resume_timer(newapp)
  312. end
  313. self.sysram.current_app = newapp
  314. local app = self:get_app(newapp)
  315. local formspec = app:get_formspec()
  316. if formspec ~= false then
  317. self.meta:set_string('formspec', formspec)
  318. end
  319. self:save()
  320. mtos_cache:sync_and_free(self)
  321. end
  322. -- Handle input processing
  323. function os_class:pass_to_app(method, reshow, sender, ...)
  324. local appname = self.sysram.current_app or self.hwdef.custom_launcher or self.os_attr.custom_launcher or "launcher"
  325. local app = self:get_app(appname)
  326. if not app then
  327. self:set_app()
  328. return
  329. end
  330. if sender then
  331. self.sysram.current_player = sender:get_player_name()
  332. else
  333. self.sysram.current_player = nil
  334. end
  335. local ret = app:receive_data(method, reshow, sender, ...)
  336. if self.sysram.current_app == appname and reshow then
  337. local formspec = app:get_formspec()
  338. if formspec ~= false then
  339. self.meta:set_string('formspec', formspec)
  340. end
  341. end
  342. if sender then
  343. self.sysram.last_player = sender:get_player_name()
  344. else
  345. self.sysram.last_player = nil
  346. end
  347. self:save()
  348. return ret
  349. end
  350. function os_class:save()
  351. self.bdev:sync_cloud()
  352. mtos_cache:set(self.pos, self)
  353. end
  354. -- Use parameter and launch the select_file dialog
  355. -- Return values will be send as fields to the called app
  356. function os_class:select_file_dialog(param)
  357. local store = self.bdev:get_app_storage('ram', 'os:select_file')
  358. store.param = param
  359. self:set_app('os:select_file')
  360. end
  361. -- Use parameter and launch the select_file dialog
  362. -- Return values will be send as fields to the called app
  363. function os_class:print_file_dialog(param)
  364. local store = self.bdev:get_app_storage('ram', 'printer:app')
  365. store.param = param
  366. self:set_app('printer:app')
  367. end
  368. -----------------------------------------------------
  369. -- Get Operating system object
  370. -----------------------------------------------------
  371. function laptop.os_get(pos)
  372. -- get OS-object from cache
  373. local self = mtos_cache:get(pos)
  374. if self then
  375. return self
  376. end
  377. -- Create new if not in cache
  378. self = setmetatable({}, os_class)
  379. self.__index = os_class
  380. self.pos = pos
  381. self.node = minetest.get_node(pos)
  382. self.hwdef = laptop.node_config[self.node.name]
  383. if not self.hwdef then
  384. return nil -- not compatible node
  385. end
  386. self.meta = minetest.get_meta(pos)
  387. self.bdev = laptop.get_bdev_handler(self)
  388. self.sysram = self.bdev:get_app_storage('ram', 'os')
  389. self.sysram.stack = self.sysram.stack or {}
  390. self.sysram.app_timer = self.sysram.app_timer or {}
  391. self.sysdata = self.bdev:get_app_storage('system', 'os')
  392. self.os_attr = self:get_os_attr()
  393. self.theme = self:get_theme()
  394. return self
  395. end