Advertisement
infiniteblock

Untitled

Sep 18th, 2021
674
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 37.68 KB | None | 0 0
  1. w = {}
  2.  
  3. -- APIs
  4.  
  5. -- properties
  6. local data = { }
  7. local data_name = nil
  8. local data_handlers = { }
  9.  
  10. local device_handlers = {}
  11.  
  12. local event_handlers = {}
  13. local event_timers = {}
  14.  
  15. local monitors = {}
  16. local monitor_textScale = 0.5
  17.  
  18. local page_handlers = {}
  19. local page_endText = ""
  20. local page_callbackDisplay
  21. local page_callbackKey
  22.  
  23. local run_refreshPeriod_s = 3.0
  24. local status_period_s = 1.0
  25.  
  26. local styles = {
  27.   normal   = { front = colors.black    , back = colors.lightGray },
  28.   good     = { front = colors.lime     , back = colors.lightGray },
  29.   bad      = { front = colors.red      , back = colors.lightGray },
  30.   disabled = { front = colors.gray     , back = colors.lightGray },
  31.   help     = { front = colors.white    , back = colors.blue      },
  32.   header   = { front = colors.orange   , back = colors.black     },
  33.   control  = { front = colors.white    , back = colors.blue      },
  34.   selected = { front = colors.black    , back = colors.lightBlue },
  35.   warning  = { front = colors.white    , back = colors.red       },
  36.   success  = { front = colors.white    , back = colors.lime      },
  37. }
  38.  
  39. ----------- Terminal & monitor support
  40.  
  41. local function setMonitorColorFrontBack(colorFront, colorBackground)
  42.   term.setTextColor(colorFront)
  43.   term.setBackgroundColor(colorBackground)
  44.   if monitors ~= nil then
  45.     for key, monitor in pairs(monitors) do
  46.       monitor.setTextColor(colorFront)
  47.       monitor.setBackgroundColor(colorBackground)
  48.     end
  49.   end
  50. end
  51.  
  52. local function write(text)
  53.   term.write(text)
  54.   if monitors ~= nil then
  55.     for key, monitor in pairs(monitors) do
  56.       if key ~= data.radar_monitorIndex then
  57.         monitor.write(text)
  58.       end
  59.     end
  60.   end
  61. end
  62.  
  63. local function getCursorPos()
  64.   local x, y = term.getCursorPos()
  65.   return x, y
  66. end
  67.  
  68. local function setCursorPos(x, y)
  69.   term.setCursorPos(x, y)
  70.   if monitors ~= nil then
  71.     for key, monitor in pairs(monitors) do
  72.       if key ~= data.radar_monitorIndex then
  73.         monitor.setCursorPos(x, y)
  74.       end
  75.     end
  76.   end
  77. end
  78.  
  79. local function getResolution()
  80.   local sizeX, sizeY = term.getSize()
  81.   return sizeX, sizeY
  82. end
  83.  
  84. local function setColorNormal()
  85.   w.setMonitorColorFrontBack(styles.normal.front, styles.normal.back)
  86. end
  87.  
  88. local function setColorGood()
  89.   w.setMonitorColorFrontBack(styles.good.front, styles.good.back)
  90. end
  91.  
  92. local function setColorBad()
  93.   w.setMonitorColorFrontBack(styles.bad.front, styles.bad.back)
  94. end
  95.  
  96. local function setColorDisabled()
  97.   w.setMonitorColorFrontBack(styles.disabled.front, styles.disabled.back)
  98. end
  99.  
  100. local function setColorHelp()
  101.   w.setMonitorColorFrontBack(styles.help.front, styles.help.back)
  102. end
  103.  
  104. local function setColorHeader()
  105.   w.setMonitorColorFrontBack(styles.header.front, styles.header.back)
  106. end
  107.  
  108. local function setColorControl()
  109.   w.setMonitorColorFrontBack(styles.control.front, styles.control.back)
  110. end
  111.  
  112. local function setColorSelected()
  113.   w.setMonitorColorFrontBack(styles.selected.front, styles.selected.back)
  114. end
  115.  
  116. local function setColorWarning()
  117.   w.setMonitorColorFrontBack(styles.warning.front, styles.warning.back)
  118. end
  119.  
  120. local function setColorSuccess()
  121.   w.setMonitorColorFrontBack(styles.success.front, styles.success.back)
  122. end
  123.  
  124. local function clear(colorFront, colorBack)
  125.   if colorFront == nil or colorBack == nil then
  126.     w.setColorNormal()
  127.   else
  128.     w.setMonitorColorFrontBack(colorFront, colorBack)
  129.   end
  130.   term.clear()
  131.   if monitors ~= nil then
  132.     for key, monitor in pairs(monitors) do
  133.       if key ~= data.radar_monitorIndex then
  134.         monitor.clear()
  135.       end
  136.     end
  137.   end
  138.   w.setCursorPos(1, 1)
  139. end
  140.  
  141. local function clearLine()
  142.   term.clearLine()
  143.   if monitors ~= nil then
  144.     for key, monitor in pairs(monitors) do
  145.       if key ~= data.radar_monitorIndex then
  146.         monitor.clearLine()
  147.       end
  148.     end
  149.   end
  150.   local x, y = w.getCursorPos()
  151.   w.setCursorPos(1, y)
  152. end
  153.  
  154. local function writeLn(text)
  155.   w.write(text)
  156.   local x, y = w.getCursorPos()
  157.   local xSize, ySize = w.getResolution()
  158.   if y > ySize - 1 then
  159.     y = 1
  160.   end
  161.   w.setCursorPos(1, y + 1)
  162. end
  163.  
  164. local function writeMultiLine(text)
  165.   local textToParse = text or ""
  166.   for line in string.gmatch(textToParse, "[^\n]+") do
  167.     if line ~= "" then
  168.       w.writeLn(line)
  169.     end
  170.   end
  171. end
  172.  
  173. local function writeCentered(y, text)
  174.   local unused
  175.   if text == nil then
  176.     text = y
  177.     unused, y = w.getCursorPos()
  178.   end
  179.  
  180.   w.setCursorPos((51 - text:len()) / 2, y)
  181.   term.write(text)
  182.   if monitors ~= nil then
  183.     for key, monitor in pairs(monitors) do
  184.       if key ~= data.radar_monitorIndex then
  185.         local xSize, ySize = monitor.getSize()
  186.         if xSize ~= nil then
  187.           monitor.setCursorPos((xSize - text:len()) / 2, y)
  188.           monitor.write(text)
  189.         end
  190.       end
  191.     end
  192.   end
  193.   w.setCursorPos(1, y + 1)
  194. end
  195.  
  196. local function writeFullLine(text)
  197.   w.write(text)
  198.   local xSize, ySize = w.getResolution()
  199.   local xCursor, yCursor = w.getCursorPos()
  200.   for i = xCursor, xSize do
  201.     w.write(" ")
  202.   end
  203.   w.setCursorPos(1, yCursor + 1)
  204. end
  205.  
  206. ----------- Page support
  207.  
  208. local function page_begin(text)
  209.   w.clear()
  210.   w.setCursorPos(1, 1)
  211.   w.setColorHeader()
  212.   w.clearLine()
  213.   w.writeCentered(1, text)
  214.   w.status_refresh()
  215.   w.setCursorPos(1, 2)
  216.   w.setColorNormal()
  217. end
  218.  
  219. local function page_colors()
  220.   w.clear(colors.white, colors.black)
  221.   for key, value in pairs(colors) do
  222.     local text = string.format("%12s", key)
  223.     w.setMonitorColorFrontBack(colors.white, colors.black)
  224.     w.write(text .. " ")
  225.     w.setMonitorColorFrontBack(value, colors.black)
  226.     w.write(" " .. text .. " ")
  227.     w.setMonitorColorFrontBack(colors.black, value)
  228.     w.write(" " .. text .. " ")
  229.     w.setMonitorColorFrontBack(colors.white, value)
  230.     w.write(" " .. text .. " ")
  231.     w.setMonitorColorFrontBack(value, colors.white)
  232.     w.write(" " .. text .. " ")
  233.     w.writeLn("")
  234.   end
  235.   w.writeLn("")
  236.   local index = 0
  237.   for key, value in pairs(styles) do
  238.     local text = string.format("%12s", key)
  239.     if index % 2 == 0 then
  240.       w.setMonitorColorFrontBack(colors.white, colors.black)
  241.       w.write(text .. " ")
  242.       w.setMonitorColorFrontBack(value.front, value.back)
  243.       w.write(" " .. text .. " ")
  244.     else
  245.       w.setMonitorColorFrontBack(value.front, value.back)
  246.       w.write(" " .. text .. " ")
  247.       w.setMonitorColorFrontBack(colors.white, colors.black)
  248.       w.write(text .. " ")
  249.       w.writeLn("")
  250.     end
  251.     index = index + 1
  252.   end
  253.   w.setMonitorColorFrontBack(colors.white, colors.black)
  254. end
  255.  
  256. local function page_end()
  257.   w.setCursorPos(1, 18)
  258.   w.setColorControl()
  259.   w.writeFullLine(page_endText)
  260. end
  261.  
  262. local function page_getCallbackDisplay()
  263.   return page_callbackDisplay
  264. end
  265.  
  266. local function page_register(index, callbackDisplay, callbackKey)
  267.   page_handlers[index] = { display = callbackDisplay, key = callbackKey }
  268. end
  269.  
  270. local function page_setEndText(text)
  271.   page_endText = text
  272. end
  273.  
  274. ----------- Status line support
  275.  
  276. local status_clockTarget = -1 -- < 0 when stopped, < clock when elapsed, > clock when ticking
  277. local status_isWarning = false
  278. local status_line = 0
  279. local status_text = ""
  280. local function status_clear()
  281.   if status_clockTarget > 0 then
  282.     status_clockTarget = -1
  283.     w.event_timer_stop("status")
  284.     local xSize, ySize = w.getResolution()
  285.     w.setCursorPos(1, ySize)
  286.     w.setColorNormal()
  287.     w.clearLine()
  288.   end
  289. end
  290. local function status_isActive()
  291.   return status_clockTarget > 0 and w.event_clock() < status_clockTarget
  292. end
  293. local function status_show(isWarning, text)
  294.   if isWarning or not w.status_isActive() then
  295.     status_line = 1
  296.     status_isWarning = isWarning
  297.     status_text = {}
  298.     local textToParse = (text and text ~= "") and text or "???"
  299.     for line in string.gmatch(textToParse, "[^\n]+") do
  300.       if line ~= "" then
  301.         table.insert(status_text, line)
  302.       end
  303.     end
  304.     if isWarning then
  305.       status_clockTarget = w.event_clock() + 1.0 * #status_text
  306.     else
  307.       status_clockTarget = w.event_clock() + 0.5 * #status_text
  308.     end
  309.     w.event_timer_start("status", status_period_s, "timer_status")
  310.   end
  311.   -- always refresh as a visual clue
  312.   w.status_refresh()
  313. end
  314. local function status_refresh()
  315.   if status_clockTarget > 0 then
  316.     if w.event_clock() > status_clockTarget and status_line == 1 then
  317.       w.status_clear()
  318.     else
  319.       local xSize, ySize = w.getResolution()
  320.       w.setCursorPos(1, ySize)
  321.       w.setColorNormal()
  322.       w.clearLine()
  323.  
  324.       if status_isWarning then
  325.         w.setColorWarning()
  326.       else
  327.         w.setColorSuccess()
  328.       end
  329.       local text = status_text[status_line]
  330.       w.writeCentered(" " .. text .. " ")
  331.       w.setColorNormal()
  332.     end
  333.   end
  334. end
  335. local function status_showWarning(text)
  336.   w.status_show(true, text)
  337. end
  338. local function status_showSuccess(text)
  339.   w.status_show(false, text)
  340. end
  341. local function status_tick()
  342.   if status_clockTarget > -1 then
  343.     local clockCurrent = w.event_clock()
  344.     if clockCurrent > status_clockTarget then
  345.       w.status_clear()
  346.     else
  347.       status_line = (status_line % #status_text) + 1
  348.       w.status_refresh()
  349.     end
  350.   end
  351. end
  352.  
  353. ----------- Formatting
  354.  
  355. local function format_float(value, nbchar)
  356.   local str = "?"
  357.   if value ~= nil then
  358.     if type(value) == "number" then
  359.       str = string.format("%g", value)
  360.     else
  361.       str = type(value)
  362.     end
  363.   end
  364.   if nbchar ~= nil then
  365.     str = string.sub("               " .. str, -nbchar)
  366.   end
  367.   return str
  368. end
  369.  
  370. local function format_integer(value, nbchar)
  371.   local str = "?"
  372.   if value ~= nil then
  373.     if type(value) == "number" then
  374.       str = string.format("%d", math.floor(value))
  375.     else
  376.       str = type(value)
  377.     end
  378.   end
  379.   if nbchar ~= nil then
  380.     str = string.sub("               " .. str, -nbchar)
  381.   end
  382.   return str
  383. end
  384.  
  385. local function format_boolean(value, strTrue, strFalse)
  386.   if value ~= nil then
  387.     if type(value) == "boolean" then
  388.       if value then
  389.         return strTrue
  390.       else
  391.         return strFalse
  392.       end
  393.     else
  394.       return type(value)
  395.     end
  396.   end
  397.   return "?"
  398. end
  399.  
  400. local function format_string(value, nbchar)
  401.   local str = "?"
  402.   if value ~= nil then
  403.     str = "" .. value
  404.   end
  405.   if nbchar ~= nil then
  406.     if #str > math.abs(nbchar) then
  407.       str = string.sub(str, 1, math.abs(nbchar) - 1) .. "~"
  408.     else
  409.       str = string.sub(str .. "                                                  ", 1, nbchar)
  410.     end
  411.   end
  412.   return str
  413. end
  414.  
  415. local function format_address(value)
  416.   local str = "?"
  417.   if value ~= nil then
  418.     str = "" .. value
  419.   end
  420.   str = string.sub(str, 10, 100)
  421.   return str
  422. end
  423.  
  424. local function format_charNumber(value)
  425.   if  value ~= nil
  426.   and type(value) == "number"
  427.   and value <= 255
  428.   and value >= 0 then
  429.     return string.char(value)
  430.   else
  431.     return string.char(0)
  432.   end
  433. end
  434.  
  435. ----------- Input controls
  436.  
  437. local function input_readInteger(currentValue)
  438.   local inputAbort = false
  439.   local input = w.format_integer(currentValue)
  440.   if input == "0" then
  441.     input = ""
  442.   end
  443.   local ignoreNextChar = false
  444.   local x, y = w.getCursorPos()
  445.  
  446.   term.setCursorBlink(true)
  447.   repeat
  448.     w.setCursorPos(x, y)
  449.     w.setColorNormal()
  450.     w.write(input .. "            ")
  451.     input = string.sub(input, -9)
  452.     w.setCursorPos(x + #input, y)
  453.    
  454.     local params = { os.pullEventRaw() }
  455.     local eventName = params[1]
  456.     local firstParam = params[2]
  457.     if firstParam == nil then firstParam = "none" end
  458.     if eventName == "key" then
  459.       local keycode = params[2]
  460.      
  461.       if keycode >= 2 and keycode <= 10 then -- 1 to 9
  462.         input = input .. w.format_string(keycode - 1)
  463.         ignoreNextChar = true
  464.       elseif keycode == 11 or keycode == 82 then -- 0 & keypad 0
  465.         input = input .. "0"
  466.         ignoreNextChar = true
  467.       elseif keycode >= 79 and keycode <= 81 then -- keypad 1 to 3
  468.         input = input .. w.format_string(keycode - 78)
  469.         ignoreNextChar = true
  470.       elseif keycode >= 75 and keycode <= 77 then -- keypad 4 to 6
  471.         input = input .. w.format_string(keycode - 71)
  472.         ignoreNextChar = true
  473.       elseif keycode >= 71 and keycode <= 73 then -- keypad 7 to 9
  474.         input = input .. w.format_string(keycode - 64)
  475.         ignoreNextChar = true
  476.       elseif keycode == 14 then -- Backspace
  477.         input = string.sub(input, 1, string.len(input) - 1)
  478.         ignoreNextChar = true
  479.       elseif keycode == 211 then -- Delete
  480.         input = ""
  481.         ignoreNextChar = true
  482.       elseif keycode == 28 then -- Enter
  483.         inputAbort = true
  484.         ignoreNextChar = true
  485.       elseif keycode == 74 or keycode == 12 or keycode == 49 then -- - on numeric keypad or - on US top or n letter
  486.         if string.sub(input, 1, 1) == "-" then
  487.           input = string.sub(input, 2)
  488.         else
  489.           input = "-" .. input
  490.         end
  491.         ignoreNextChar = true
  492.       elseif keycode == 78 then -- +
  493.         if string.sub(input, 1, 1) == "-" then
  494.           input = string.sub(input, 2)
  495.         end
  496.         ignoreNextChar = true
  497.       else
  498.         ignoreNextChar = false
  499.         -- w.status_showWarning("Key " .. keycode .. " is not supported here")
  500.       end
  501.      
  502.     elseif eventName == "char" then
  503.       local character = params[2]
  504.       if ignoreNextChar then
  505.         ignoreNextChar = false
  506.         -- w.status_showWarning("Ignored char #" .. string.byte(character) .. " '" .. character .. "'")
  507.       elseif character >= '0' and character <= '9' then -- 0 to 9
  508.         input = input .. character
  509.       elseif character == '-' or character == 'n' or character == 'N' then -- - or N
  510.         if string.sub(input, 1, 1) == "-" then
  511.           input = string.sub(input, 2)
  512.         else
  513.           input = "-" .. input
  514.         end
  515.       elseif character == '+' or character == 'p' or character == 'P' then -- + or P
  516.         if string.sub(input, 1, 1) == "-" then
  517.           input = string.sub(input, 2)
  518.         end
  519.       else
  520.         w.status_showWarning("Key '" .. character .. "' is not supported here (" .. string.byte(character) .. ")")
  521.       end
  522.      
  523.     elseif eventName == "terminate" then
  524.       inputAbort = true
  525.      
  526.     else
  527.       local isSupported, needRedraw = w.event_handler(eventName, firstParam)
  528.       if not isSupported then
  529.         w.status_showWarning("Event '" .. eventName .. "', " .. firstParam .. " is unsupported")
  530.       end
  531.     end
  532.   until inputAbort
  533.   term.setCursorBlink(false)
  534.   w.setCursorPos(1, y + 1)
  535.   if input == "" or input == "-" then
  536.     return currentValue
  537.   else
  538.     return tonumber(input)
  539.   end
  540. end
  541.  
  542. local function input_readText(currentValue)
  543.   local inputAbort = false
  544.   local input = w.format_string(currentValue)
  545.   local ignoreNextChar = false
  546.   local x, y = w.getCursorPos()
  547.  
  548.   term.setCursorBlink(true)
  549.   repeat
  550.     -- update display clearing extra characters
  551.     w.setCursorPos(x, y)
  552.     w.setColorNormal()
  553.     w.write(w.format_string(input, 37))
  554.     -- truncate input and set caret position
  555.     input = string.sub(input, -36)
  556.     w.setCursorPos(x + #input, y)
  557.    
  558.     local params = { os.pullEventRaw() }
  559.     local eventName = params[1]
  560.     local firstParam = params[2]
  561.     if firstParam == nil then firstParam = "none" end
  562.     if eventName == "key" then
  563.       local keycode = params[2]
  564.      
  565.       if keycode == 14 then -- Backspace
  566.         input = string.sub(input, 1, string.len(input) - 1)
  567.         ignoreNextChar = true
  568.       elseif keycode == 211 then -- Delete
  569.         input = ""
  570.         ignoreNextChar = true
  571.       elseif keycode == 28 then -- Enter
  572.         inputAbort = true
  573.         ignoreNextChar = true
  574.       else
  575.         ignoreNextChar = false
  576.         -- w.status_showWarning("Key " .. keycode .. " is not supported here")
  577.       end
  578.      
  579.     elseif eventName == "char" then
  580.       local character = params[2]
  581.       if ignoreNextChar then
  582.         ignoreNextChar = false
  583.         -- w.status_showWarning("Ignored char #" .. string.byte(character) .. " '" .. character .. "'")
  584.       elseif character >= ' ' and character <= '~' then -- any ASCII table minus controls and DEL
  585.         input = input .. character
  586.       else
  587.         w.status_showWarning("Key '" .. character .. "' is not supported here (" .. string.byte(character) .. ")")
  588.       end
  589.      
  590.     elseif eventName == "terminate" then
  591.       inputAbort = true
  592.      
  593.     else
  594.       local isSupported, needRedraw = w.event_handler(eventName, firstParam)
  595.       if not isSupported then
  596.         w.status_showWarning("Event '" .. eventName .. "', " .. firstParam .. " is unsupported")
  597.       end
  598.     end
  599.   until inputAbort
  600.   term.setCursorBlink(false)
  601.   w.setCursorPos(1, y + 1)
  602.   if input == "" then
  603.     return currentValue
  604.   else
  605.     return input
  606.   end
  607. end
  608.  
  609. local function input_readConfirmation(message)
  610.   if message == nil then
  611.     message = "Are you sure? (Y/n)"
  612.   end
  613.   w.status_showWarning(message)
  614.   repeat
  615.     local params = { os.pullEventRaw() }
  616.     local eventName = params[1]
  617.     local firstParam = params[2]
  618.     if firstParam == nil then firstParam = "none" end
  619.     if eventName == "key" then
  620.       local keycode = params[2]
  621.      
  622.       if keycode == 28 then -- Return or Enter
  623.         w.status_clear()
  624.         return true
  625.       end
  626.      
  627.     elseif eventName == "char" then
  628.       local character = params[2]
  629.       w.status_clear()
  630.       if character == 'y' or character == 'Y' then -- Y
  631.         return true
  632.       else
  633.         return false
  634.       end
  635.      
  636.     elseif eventName == "terminate" then
  637.       return false
  638.      
  639.     else
  640.       local isSupported, needRedraw = w.event_handler(eventName, firstParam)
  641.       if not isSupported then
  642.         w.status_showWarning("Event '" .. eventName .. "', " .. firstParam .. " is unsupported")
  643.       end
  644.     end
  645.     if not w.status_isActive() then
  646.       w.status_showWarning(message)
  647.     end
  648.   until false
  649. end
  650.  
  651. local function input_readEnum(currentValue, list, toValue, toDescription, noValue)
  652.   local inputAbort = false
  653.   local inputKey = nil
  654.   local input = nil
  655.   local inputDescription = nil
  656.   local ignoreNextChar = false
  657.   local x, y = w.getCursorPos()
  658.  
  659.   w.setCursorPos(1, 17)
  660.   for key, entry in pairs(list) do
  661.     if toValue(entry) == currentValue then
  662.       inputKey = key
  663.     end
  664.   end
  665.  
  666.   term.setCursorBlink(true)
  667.   repeat
  668.     w.setCursorPos(x, y)
  669.     w.setColorNormal()
  670.     if #list == 0 then
  671.       inputKey = nil
  672.     end
  673.     if inputKey == nil then
  674.       if currentValue ~= nil then
  675.         input = noValue
  676.         inputDescription = "Press enter to return previous entry"
  677.       else
  678.         input = noValue
  679.         inputDescription = "Press enter to close listing"
  680.       end
  681.     else
  682.       if inputKey < 1 then
  683.         inputKey = #list
  684.       elseif inputKey > #list then
  685.         inputKey = 1
  686.       end
  687.      
  688.       input = toValue(list[inputKey])
  689.       inputDescription = toDescription(list[inputKey])
  690.     end
  691.     w.setColorNormal()
  692.     w.write(input .. "                                                  ")
  693.     w.setCursorPos(1, y + 1)
  694.     w.setColorDisabled()
  695.     w.write(inputDescription .. "                                                  ")
  696.    
  697.     local params = { os.pullEventRaw() }
  698.     local eventName = params[1]
  699.     local firstParam = params[2]
  700.     if firstParam == nil then firstParam = "none" end
  701.     if eventName == "key" then
  702.       local keycode = params[2]
  703.      
  704.       if keycode == 14 or keycode == 211 then -- Backspace or Delete
  705.         inputKey = nil
  706.         ignoreNextChar = true
  707.       elseif keycode == 200 or keycode == 203 or keycode == 78 then -- Up or Left or +
  708.         if inputKey == nil then
  709.           inputKey = 1
  710.         else
  711.           inputKey = inputKey - 1
  712.         end
  713.         ignoreNextChar = true
  714.       elseif keycode == 208 or keycode == 205 or keycode == 74 then -- Down or Right or -
  715.         if inputKey == nil then
  716.           inputKey = 1
  717.         else
  718.           inputKey = inputKey + 1
  719.         end
  720.         ignoreNextChar = true
  721.       elseif keycode == 28 then -- Enter
  722.         inputAbort = true
  723.         ignoreNextChar = true
  724.       else
  725.         ignoreNextChar = false
  726.         -- w.status_showWarning("Key " .. keycode .. " is not supported here")
  727.       end
  728.      
  729.     elseif eventName == "char" then
  730.       local character = params[2]
  731.       if ignoreNextChar then
  732.         ignoreNextChar = false
  733.         -- w.status_showWarning("Ignored char #" .. string.byte(character) .. " '" .. character .. "'")
  734.       elseif character == '+' then -- +
  735.         if inputKey == nil then
  736.           inputKey = 1
  737.         else
  738.           inputKey = inputKey - 1
  739.         end
  740.       elseif character == '-' then -- -
  741.         if inputKey == nil then
  742.           inputKey = 1
  743.         else
  744.           inputKey = inputKey + 1
  745.         end
  746.       else
  747.         w.status_showWarning("Key '" .. character .. "' is not supported here (" .. string.byte(character) .. ")")
  748.       end
  749.      
  750.     elseif eventName == "terminate" then
  751.       inputAbort = true
  752.      
  753.     elseif not w.event_handler(eventName, firstParam) then
  754.       w.status_showWarning("Event '" .. eventName .. "', " .. firstParam .. " is unsupported")
  755.     end
  756.   until inputAbort
  757.   term.setCursorBlink(false)
  758.   w.setCursorPos(1, y + 1)
  759.   w.clearLine()
  760.   if inputKey == nil then
  761.     return nil
  762.   else
  763.     return toValue(list[inputKey])
  764.   end
  765. end
  766.  
  767. ----------- Event handlers
  768.  
  769. local function reboot()
  770.   os.reboot()
  771. end
  772.  
  773. local function sleep(delay)
  774.   os.sleep(delay)
  775. end
  776.  
  777. -- return a global clock measured in second
  778. local function event_clock()
  779.   return os.clock()
  780. end
  781.  
  782. local function event_timer_start(name_, period_s_, eventId_)
  783.   local name = name_ or "-nameless-"
  784.   local eventId = eventId_ or "timer_" .. name
  785.   -- check for an already active timer
  786.   local countActives = 0
  787.   for id, entry in pairs(event_timers) do
  788.     if entry.name == name and entry.active then -- already one started
  789.       countActives = countActives + 1
  790.     end
  791.   end
  792.   if countActives > 0 then
  793.     if name ~= "status" then -- don't report status timer overlaps to prevent a stack overflow
  794.       w.status_showWarning("Timer already started for " .. name)
  795.     end
  796.     return
  797.   end
  798.   -- start a new timer
  799.   local period_s = period_s_ or 1.0
  800.   local id = os.startTimer(period_s)
  801.   event_timers[id] = {
  802.     active = true,
  803.     eventId = eventId,
  804.     name = name,
  805.     period_s = period_s
  806.   }
  807. end
  808.  
  809. local function event_timer_stop(name_)
  810.   local name = name_ or "-nameless-"
  811.   for id, entry in pairs(event_timers) do
  812.     if entry.name == name then
  813.       if entry.active then -- kill any active one
  814.         entry.active = false
  815.         os.cancelTimer(id)
  816.       else -- purge all legacy ones
  817.         event_timers[id] = nil
  818.       end
  819.     end
  820.   end
  821. end
  822.  
  823. local function event_timer_stopAll()
  824.   for id, entry in pairs(event_timers) do
  825.     if entry.active then
  826.       event_timers[id] = nil
  827.       os.cancelTimer(id)
  828.     end
  829.   end
  830. end
  831.  
  832. local function event_timer_tick(id)
  833.   local entry = event_timers[id]
  834.   local isUnknown = entry == nil
  835.   if isUnknown then -- unknown id, report a warning
  836.     w.status_showWarning("Timer #" .. id .. " is unknown")
  837.     return
  838.   end
  839.   if not entry.active then -- dying timer, just ignore it
  840.     return
  841.   end
  842.   -- resolve the timer
  843.   os.queueEvent(entry.eventId, nil, nil, nil)
  844.   -- CC timers are one shot, so we start a new one, if still needed
  845.   if entry.active then
  846.     entry.active = false
  847.     w.event_timer_start(entry.name, entry.period_s)
  848.   end
  849. end
  850.  
  851. local function event_register(eventName, callback)
  852.   event_handlers[eventName] = callback
  853. end
  854.  
  855. -- returns isSupported, needRedraw
  856. local function event_handler(eventName, param)
  857.   local needRedraw = false
  858.   if eventName == "redstone" then
  859.     w.event_redstone(param)
  860.     transporter_energize()
  861.     return true
  862.   elseif eventName == "timer" then
  863.     w.event_timer_tick(param)
  864.   elseif eventName == "key_up" then
  865.   elseif eventName == "mouse_click" then
  866.     w.status_showSuccess("Use the keyboard, Luke!")
  867.   elseif eventName == "mouse_up" then
  868.   elseif eventName == "mouse_drag" then
  869.   elseif eventName == "mouse_scroll" then
  870.   elseif eventName == "monitor_touch" then
  871.   elseif eventName == "monitor_resize" then
  872.   elseif eventName == "disk" then
  873.   elseif eventName == "disk_eject" then
  874.   elseif eventName == "peripheral" then
  875.   elseif eventName == "peripheral_detach" then
  876.   -- not supported: task_complete, rednet_message, modem_message
  877.   elseif event_handlers[eventName] ~= nil then
  878.     needRedraw = event_handlers[eventName](eventName, param)
  879.   else
  880.     return false, needRedraw
  881.   end
  882.   return true, needRedraw
  883. end
  884.  
  885. ----------- Redstone support
  886.  
  887. local tblRedstoneState = {-- Remember redstone state on each side
  888.   ["top"] = rs.getInput("top"),
  889.   ["front"] = rs.getInput("front"),
  890.   ["left"] = rs.getInput("left"),
  891.   ["right"] = rs.getInput("right"),
  892.   ["back"] = rs.getInput("back"),
  893.   ["bottom"] = rs.getInput("bottom"),
  894. }
  895.  
  896. local function event_redstone()
  897.   -- Event only returns nil so we need to check sides manually
  898.   local message = ""
  899.   for side, state in pairs(tblRedstoneState) do
  900.     if rs.getInput(side) ~= state then
  901.       -- print(side .. " is now " .. tostring(rs.getInput(side)))
  902.       message = message .. side .. " "
  903.       tblRedstoneState[side] = rs.getInput(side)
  904.     end
  905.   end
  906.   if message ~= "" then
  907.     message = "Redstone changed on " .. message
  908.     w.status_showWarning(message)
  909.   end
  910. end
  911.  
  912. ----------- Configuration
  913.  
  914. local function data_get()
  915.   return data
  916. end
  917.  
  918. local function data_inspect(key, value)
  919.   local stringValue = type(value) .. ","
  920.   if type(value) == "boolean" then
  921.     if value then
  922.       stringValue = "true,"
  923.     else
  924.       stringValue = "false,"
  925.     end
  926.   elseif type(value) == "number" then
  927.     stringValue = value .. ","
  928.   elseif type(value) == "string" then
  929.     stringValue = "'" .. value .. "',"
  930.   elseif type(value) == "table" then
  931.     stringValue = "{"
  932.   end
  933.   print(" " .. key .. " = " .. stringValue)
  934.   if type(value) == "table" then
  935.     for subkey, subvalue in pairs(value) do
  936.       w.data_inspect(subkey, subvalue)
  937.     end
  938.     print("}")
  939.   end
  940. end
  941.  
  942. local function data_read()
  943.   w.data_shouldUpdateName()
  944.  
  945.   data = { }
  946.   if fs.exists("shipdata.txt") then
  947.     local size = fs.getSize("shipdata.txt")
  948.     if size > 0 then
  949.       local file = fs.open("shipdata.txt", "r")
  950.       if file ~= nil then
  951.         local rawData = file.readAll()
  952.         if rawData ~= nil then
  953.           data = textutils.unserialize(rawData)
  954.         end
  955.         file.close()
  956.         if data == nil then
  957.           data = {}
  958.         end
  959.       end
  960.     end
  961.   end
  962.  
  963.   for name, handlers in pairs(data_handlers) do
  964.     handlers.read(data)
  965.   end
  966. end
  967.  
  968. local function data_save()
  969.   for name, handlers in pairs(data_handlers) do
  970.     handlers.save(data)
  971.   end
  972.  
  973.   local file = fs.open("shipdata.txt", "w")
  974.   if file ~= nil then
  975.     file.writeLine(textutils.serialize(data))
  976.     file.close()
  977.   else
  978.     w.status_showWarning("No file system")
  979.     w.sleep(3.0)
  980.   end
  981. end
  982.  
  983. local function data_getName()
  984.   if data_name ~= nil then
  985.     return data_name
  986.   else
  987.     return "-noname-"
  988.   end
  989. end
  990.  
  991. local function data_setName()
  992.   -- check if any named component is connected
  993.   local componentName = "computer"
  994.   for name, handlers in pairs(data_handlers) do
  995.     if handlers.name ~= nil then
  996.       componentName = name
  997.     end
  998.   end
  999.  
  1000.   -- ask for a new name
  1001.   w.page_begin("<==== Set " .. componentName .. " name ====>")
  1002.   w.setCursorPos(1, 4)
  1003.   w.setColorHelp()
  1004.   w.writeFullLine(" Press enter to validate.")
  1005.   w.setCursorPos(1, 3)
  1006.   w.setColorNormal()
  1007.   w.write("Enter " .. componentName .. " name: ")
  1008.   data_name = w.input_readText(data_name)
  1009.  
  1010.   -- update computer name
  1011.   os.setComputerLabel(data_name)
  1012.  
  1013.   -- update connected components
  1014.   for name, handlers in pairs(data_handlers) do
  1015.     if handlers.name ~= nil then
  1016.       handlers.name(data_name)
  1017.     end
  1018.   end
  1019.  
  1020.   -- w.reboot() -- not needed
  1021. end
  1022.  
  1023. local function data_shouldUpdateName()
  1024.   local shouldUpdateName = false
  1025.  
  1026.   -- check computer name
  1027.   data_name = os.getComputerLabel()
  1028.   if data_name == nil then
  1029.     shouldUpdateName = true
  1030.     data_name = "" .. os.getComputerID()
  1031.   end
  1032.  
  1033.   -- check connected components names
  1034.   for name, handlers in pairs(data_handlers) do
  1035.     if handlers.name ~= nil then
  1036.       local componentName = handlers.name()
  1037.       if componentName == "" then
  1038.         shouldUpdateName = true
  1039.       elseif shouldUpdateName then
  1040.         data_name = componentName
  1041.       elseif data_name ~= componentName then
  1042.         shouldUpdateName = true
  1043.         data_name = componentName
  1044.       end
  1045.     end
  1046.   end
  1047.  
  1048.   return shouldUpdateName
  1049. end
  1050.  
  1051. local function data_splitString(source, sep_)
  1052.   local sep = sep_ or ":"
  1053.   local fields = {}
  1054.   local pattern = string.format("([^%s]+)", sep)
  1055.   source:gsub(pattern, function(c) fields[#fields + 1] = c end)
  1056.   return fields
  1057. end
  1058.  
  1059. local function data_register(name, callbackRead, callbackSave, callbackName)
  1060.   -- read/save callbacks are always defined
  1061.   if callbackRead == nil then
  1062.     callbackRead = function() end
  1063.   end
  1064.   if callbackSave == nil then
  1065.     callbackSave = function() end
  1066.   end
  1067.  
  1068.   -- name callback is nil when not defined
  1069.  
  1070.   data_handlers[name] = { read = callbackRead, save = callbackSave, name = callbackName }
  1071. end
  1072.  
  1073. ----------- Devices
  1074.  
  1075. local function device_get(address)
  1076.   return peripheral.wrap(address)
  1077. end
  1078.  
  1079. local function device_getMonitors()
  1080.   return monitors
  1081. end
  1082.  
  1083. local function device_register(deviceType, callbackRegister, callbackUnregister)
  1084.   device_handlers[deviceType] = { register = callbackRegister, unregister = callbackUnregister }
  1085. end
  1086.  
  1087. ----------- Main loop
  1088.  
  1089.  
  1090. local function boot()
  1091.   if not term.isColor() then
  1092.     print("Advanced computer required")
  1093.     error()
  1094.   end
  1095.   print("loading...")
  1096.  
  1097.   math.randomseed(os.time())
  1098.  
  1099.   -- read configuration
  1100.   w.data_read()
  1101.   w.clear()
  1102.   print("data_read...")
  1103.  
  1104.   -- initial scanning
  1105.   monitors = {}
  1106.   w.page_begin(data_name .. " - Connecting...")
  1107.   w.writeLn("")
  1108.  
  1109.   local sides = peripheral.getNames()
  1110.   for key, address in pairs(sides) do
  1111.     w.sleep(0)
  1112.     w.write("Checking " .. address .. " ")
  1113.     local deviceType = peripheral.getType(address)
  1114.     w.write(deviceType .. " ")
  1115.     if deviceType == "monitor" then
  1116.       w.write("wrapping!")
  1117.       local lmonitor = w.device_get(address)
  1118.       table.insert(monitors, lmonitor)
  1119.       lmonitor.setTextScale(monitor_textScale)
  1120.     else
  1121.       local handlers = device_handlers[deviceType]
  1122.       if handlers ~= nil then
  1123.         w.write("wrapping!")
  1124.         handlers.register(deviceType, address, w.device_get(address))
  1125.       end
  1126.     end
  1127.    
  1128.     w.writeLn("")
  1129.   end
  1130.  
  1131.   -- synchronize computer and connected components names
  1132.   local shouldUpdateName = w.data_shouldUpdateName()
  1133.   if shouldUpdateName then
  1134.     w.data_setName()
  1135.   end
  1136.  
  1137.   -- peripheral boot up
  1138.   if page_handlers['0'] == nil then
  1139.     w.status_showWarning("Missing handler for connection page '0'!")
  1140.     error()
  1141.   end
  1142.   page_handlers['0'].display(true)
  1143. end
  1144.  
  1145. local function run()
  1146.   local abort = false
  1147.   local refresh = true
  1148.   local ignoreNextChar = false
  1149.  
  1150.   local function selectPage(index)
  1151.     if page_handlers[index] ~= nil then
  1152.       page_callbackDisplay = page_handlers[index].display
  1153.       page_callbackKey = page_handlers[index].key
  1154.       refresh = true
  1155.       return true
  1156.     end
  1157.     return false
  1158.   end
  1159.  
  1160.   -- start refresh timer
  1161.   w.event_register("timer_refresh", function() return page_callbackDisplay ~= page_handlers['0'].display end )
  1162.   w.event_register("timer_status" , function() w.status_tick() return false end )
  1163.   w.event_timer_start("refresh", run_refreshPeriod_s, "timer_refresh")
  1164.  
  1165.   -- register common events
  1166.   w.event_register("enabled" , function(eventName, param) w.status_showWarning("Enabled '" .. w.format_string(param) .. "'") end )
  1167.   w.event_register("disabled", function(eventName, param) w.status_showWarning("Disabled '" .. w.format_string(param) .. "'") end )
  1168.  
  1169.   -- main loop
  1170.   selectPage('0')
  1171.   repeat
  1172.     if refresh then
  1173.       w.clear()
  1174.       page_callbackDisplay(false)
  1175.       w.page_end()
  1176.       refresh = false
  1177.     end
  1178.     local params = { os.pullEventRaw() }
  1179.     local eventName = params[1]
  1180.     local firstParam = params[2]
  1181.     if firstParam == nil then firstParam = "none" end
  1182.     -- w.writeLn("...")
  1183.     -- w.writeLn("Event '" .. eventName .. "', " .. firstParam .. " received")
  1184.     -- w.sleep(0.2)
  1185.    
  1186.     if eventName == "key" then
  1187.       local keycode = params[2]
  1188.      
  1189.       ignoreNextChar = false
  1190.       if keycode == 11 or keycode == 82 then -- 0
  1191.         if selectPage('0') then
  1192.           ignoreNextChar = true
  1193.         end
  1194.       elseif keycode == 2 or keycode == 79 then -- 1
  1195.         if selectPage('1') then
  1196.           ignoreNextChar = true
  1197.         end
  1198.       elseif keycode == 3 or keycode == 80 then -- 2
  1199.         if selectPage('2') then
  1200.           ignoreNextChar = true
  1201.         end
  1202.       elseif keycode == 4 or keycode == 81 then -- 3
  1203.         if selectPage('3') then
  1204.           ignoreNextChar = true
  1205.         end
  1206.       elseif keycode == 5 or keycode == 82 then -- 4
  1207.         if selectPage('4') then
  1208.           ignoreNextChar = true
  1209.         end
  1210.       elseif keycode == 6 or keycode == 83 then -- 5
  1211.         if selectPage('5') then
  1212.           ignoreNextChar = true
  1213.         end
  1214.       elseif page_callbackKey ~= nil and page_callbackKey("", keycode) then
  1215.         refresh = true
  1216.       else
  1217.         ignoreNextChar = false
  1218.         -- w.status_showWarning("Key " .. keycode .. " is not supported here")
  1219.       end
  1220.      
  1221.     elseif eventName == "char" then
  1222.       local character = params[2]
  1223.       if ignoreNextChar then
  1224.         ignoreNextChar = false
  1225.         -- w.status_showWarning("Ignored char #" .. string.byte(character) .. " '" .. character .. "'")
  1226. --      elseif character == 'x' or character == 'X' then -- x for eXit
  1227. --        -- os.pullEventRaw() -- remove key_up event
  1228. --        abort = true
  1229.       elseif character == '0' then
  1230.         selectPage('0')
  1231.       elseif character == '1' then
  1232.         selectPage('1')
  1233.       elseif character == '2' then
  1234.         selectPage('2')
  1235.       elseif character == '3' then
  1236.         selectPage('3')
  1237.       elseif character == '4' then
  1238.         selectPage('4')
  1239.       elseif character == '5' then
  1240.         selectPage('5')
  1241.       elseif page_callbackKey ~= nil and page_callbackKey(character, -1) then
  1242.         refresh = true
  1243.       elseif string.byte(character) ~= 0 then -- not a control char
  1244.         w.status_showWarning("Key '" .. character .. "' is not supported here (" .. string.byte(character) .. ")")
  1245.       end
  1246.      
  1247.     elseif eventName == "terminate" then
  1248.       abort = true
  1249.      
  1250.     else
  1251.       local isSupported, needRedraw = w.event_handler(eventName, firstParam)
  1252.       if not isSupported then
  1253.         w.status_showWarning("Event '" .. eventName .. "', " .. firstParam .. " is unsupported")
  1254.       end
  1255.       refresh = needRedraw
  1256.     end
  1257.   until abort
  1258.  
  1259.   -- stop refresh timer
  1260.   w.event_timer_stop("refresh")
  1261.   w.event_timer_stopAll()
  1262. end
  1263.  
  1264. local function close()
  1265.   w.clear(colors.white, colors.black)
  1266.   for key, handlers in pairs(device_handlers) do
  1267.     w.writeLn("Closing " .. key)
  1268.     if handlers.unregister ~= nil then
  1269.       handlers.unregister(key)
  1270.     end
  1271.   end
  1272.  
  1273.   w.clear(colors.white, colors.black)
  1274.   w.setCursorPos(1, 1)
  1275.   w.writeLn("Program closed")
  1276.   w.writeLn("Type startup to return to home page")
  1277. end
  1278.  
  1279. -- we simulate LUA library behavior, so code is closer between CC and OC
  1280. w = {
  1281.   setMonitorColorFrontBack = setMonitorColorFrontBack,
  1282.   write = write,
  1283.   getCursorPos = getCursorPos,
  1284.   setCursorPos = setCursorPos,
  1285.   getResolution = getResolution,
  1286.   setColorNormal = setColorNormal,
  1287.   setColorGood = setColorGood,
  1288.   setColorBad = setColorBad,
  1289.   setColorDisabled = setColorDisabled,
  1290.   setColorHelp = setColorHelp,
  1291.   setColorHeader = setColorHeader,
  1292.   setColorControl = setColorControl,
  1293.   setColorSelected = setColorSelected,
  1294.   setColorWarning = setColorWarning,
  1295.   setColorSuccess = setColorSuccess,
  1296.   clear = clear,
  1297.   clearLine = clearLine,
  1298.   writeLn = writeLn,
  1299.   writeMultiLine = writeMultiLine,
  1300.   writeCentered = writeCentered,
  1301.   writeFullLine = writeFullLine,
  1302.   page_begin = page_begin,
  1303.   page_colors = page_colors,
  1304.   page_end = page_end,
  1305.   page_getCallbackDisplay = page_getCallbackDisplay,
  1306.   page_register = page_register,
  1307.   page_setEndText = page_setEndText,
  1308.   status_clear = status_clear,
  1309.   status_isActive = status_isActive,
  1310.   status_show = status_show,
  1311.   status_refresh = status_refresh,
  1312.   status_showWarning = status_showWarning,
  1313.   status_showSuccess = status_showSuccess,
  1314.   status_tick = status_tick,
  1315.   format_float = format_float,
  1316.   format_integer = format_integer,
  1317.   format_boolean = format_boolean,
  1318.   format_string = format_string,
  1319.   format_address = format_address,
  1320.   format_charNumber = format_charNumber,
  1321.   input_readInteger = input_readInteger,
  1322.   input_readText = input_readText,
  1323.   input_readConfirmation = input_readConfirmation,
  1324.   input_readEnum = input_readEnum,
  1325.   reboot = reboot,
  1326.   sleep = sleep,
  1327.   event_clock = event_clock,
  1328.   event_timer_start = event_timer_start,
  1329.   event_timer_stop = event_timer_stop,
  1330.   event_timer_stopAll = event_timer_stopAll,
  1331.   event_timer_tick = event_timer_tick,
  1332.   event_register = event_register,
  1333.   event_handler = event_handler,
  1334.   event_redstone = event_redstone,
  1335.   data_get = data_get,
  1336.   data_inspect = data_inspect,
  1337.   data_read = data_read,
  1338.   data_save = data_save,
  1339.   data_getName = data_getName,
  1340.   data_setName = data_setName,
  1341.   data_shouldUpdateName = data_shouldUpdateName,
  1342.   data_splitString = data_splitString,
  1343.   data_register = data_register,
  1344.   device_get = device_get,
  1345.   device_getMonitors = device_getMonitors,
  1346.   device_register = device_register,
  1347.   boot = boot,
  1348.   run = run,
  1349.   close = close,
  1350. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement