Advertisement
Wojbie

Mirror 5.0

Jan 13th, 2021 (edited)
2,796
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 13.07 KB | None | 0 0
  1. --------------------------------------------------------------------------------------
  2. -- Wojbies Program 5.0 - Mirror - Program to mirror terminal contents onto monitor. --
  3. --------------------------------------------------------------------------------------
  4. --   Copyright (c) 2013-2021 Wojbie (wojbie@wojbie.net)
  5. --   Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met:
  6. --   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  7. --   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  8. --   3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
  9. --   4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
  10. --   5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software.
  11. --   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY.
  12.  
  13. -- If you are looking for older version it can be found under: http://pastebin.com/DW3LCC3L
  14.  
  15. local expect, field = require "cc.expect".expect, require "cc.expect".field
  16. local completion = require "cc.shell.completion"
  17.  
  18. local function peripheralType(_, text, _, add_space, type_)
  19.     expect(2, text, "string")
  20.     expect(4, add_space, "boolean", "nil")
  21.     expect(5, type_, "string")
  22.     local names = {}
  23.     for _, name in ipairs(peripheral.getNames()) do
  24.         if peripheral.getType(name) == type_ then
  25.             table.insert(names, name)
  26.         end
  27.     end
  28.     return completion.choice(nil, text, nil, names, add_space)
  29. end
  30.  
  31. shell.setCompletionFunction(shell.getRunningProgram(), completion.build(
  32.     { peripheralType, true, "monitor" },
  33.     completion.program
  34. ))
  35.  
  36. local function printUsage()
  37.     print( "Usage: mirror <name> <program> <arguments>" )
  38.     return
  39. end
  40.  
  41. local tArgs = table.pack(...)
  42. if #tArgs < 2 then
  43.     printUsage()
  44.     return
  45. end
  46.  
  47. local sName = tArgs[1]
  48. if peripheral.getType(sName) ~= "monitor" then
  49.     print("No monitor named " .. sName)
  50.     return
  51. end
  52.  
  53. local sProgram = tArgs[2]
  54. local sPath = shell.resolveProgram(sProgram)
  55. if sPath == nil then
  56.     print("No such program: " .. sProgram)
  57.     return
  58. end
  59.  
  60. local fMain = function()
  61.     shell.run(sProgram, table.unpack(tArgs, 3, tArgs.n))
  62. end
  63.  
  64. --# Special table that will transfer all functions call to each and every sub table.
  65. local function createMultitable(...)
  66.     local output = {}
  67.     local tab = {...}
  68.     expect(1, tab[1], "table")
  69.     if #tab == 1 and tab[1] and type(tab[1]) == "table" then tab = tab[1] end
  70.     for num,t in pairs(tab) do
  71.         expect(num, t, "table")
  72.     end
  73.  
  74.     local function makeWrap(key)
  75.         return function(...)
  76.             local ret = {}
  77.             local tArgs = table.pack(...)
  78.             for _, k in ipairs(tab) do
  79.                 if k[key] then
  80.                     if #ret == 0 then ret = table.pack(k[key](table.unpack(tArgs))) --ret contains returns from first table that returned anything.
  81.                     else k[key](table.unpack(tArgs)) end
  82.                 end
  83.             end
  84.             return table.unpack(ret)
  85.         end
  86.     end
  87.  
  88.     local function repopulate()
  89.         for key in pairs(tab[1]) do --create static table of multitable functions using first one as template
  90.             rawset(output, key, makeWrap(key))
  91.         end
  92.     end
  93.     local function clean()
  94.         for key in pairs(output) do --remove all parts of static table that stopped existing.
  95.             if not tab[1][key] then rawset(output, key, nil) end
  96.         end
  97.     end
  98.  
  99.     local manymeta = { --Anytime index is requested fist table is used as refference.
  100.         __index = function(_, key)
  101.             if tab and tab[1] and tab[1][key] then --If it has value it tested then
  102.                 if type(tab[1][key]) == "function" then --If its function then a function that calls all tables in row is made
  103.                     local wrap = makeWrap(key)
  104.                     rawset(output, key, wrap) --If for some reason called function don't exists add it to static table
  105.                     return wrap
  106.                 else
  107.                     return tab[1][key]  --If its not a function then its just given out.
  108.                 end
  109.             else
  110.                 return nil --Of it not exist in first table give nothing
  111.             end
  112.         end,
  113.         __newindex = function() end, --If someone wants to add anything to the table do nothing.
  114.         __call = function(_, nTab) --If someone calls table like function give him direct acces to table list.
  115.             if nTab then tab = nTab clean() repopulate() end --Allows swapping source table. WARNING If tab is changed using different method repopulate will fail. NO SANITY CHECKS!!
  116.             return tab
  117.         end,
  118.         __len = function() --Not sure if it works but this is giving the leanght of first table or 0 if there is no first table.
  119.             return tab[1] and #tab[1] or 0
  120.         end,
  121.         __metatable = false, --No touching the metatable.
  122.     }
  123.     repopulate()
  124.  
  125.     return setmetatable(output, manymeta) --create actual manymeta table and return it
  126. end
  127.  
  128. --### FramedWindow - Creates on provided term object a framed window of specified size. If term object has setTermScale its used to get best size possible.
  129. local tBor = {c = "+", h = "-", v = "|"}--{c="#",h="=",v="H"}
  130. local function framedWindow(parent, nX, nY, sName)
  131.    
  132.     local nParentX, nParentY
  133.     local offsetX, offsetY
  134.     local tLines
  135.     local win = window.create( parent, 1, 1, nX, nY, false )
  136.     local winReposition = win.reposition
  137.     win.reposition = nil
  138.    
  139.     local function build()
  140.         if parent.setTextScale then
  141.             local nCX, nCY
  142.             for i = 5, 0.5, -0.5 do
  143.                 parent.setTextScale(i)
  144.                 nCX, nCY = parent.getSize()
  145.                 if nCX >= nX and nCY >= nY then break end
  146.             end
  147.         end
  148.         nParentX, nParentY = parent.getSize()
  149.        
  150.         offsetX = math.max(math.floor((nParentX - nX) / 2), 0)
  151.         offsetY = math.max(math.floor((nParentY - nY) / 2), 0)
  152.         local leftoverX = math.max(nParentX - nX - offsetX, 0)
  153.         --local leftoverY = math.max(nParentY-nY-offsetY,0)
  154.        
  155.         tLines = {}
  156.         tLines.empty = string.rep(" ", nParentX)
  157.         tLines.bottom = string.rep(" ", math.max(0, offsetX - 1)) .. (offsetX > 0 and tBor.c or "") .. string.rep(tBor.h, math.min(nParentX, nX)) .. (leftoverX > 0 and tBor.c or "") .. string.rep(" ", math.max(0, leftoverX - 1))
  158.         tLines.middle = string.rep(" ", math.max(0, offsetX - 1)) .. (offsetX > 0 and tBor.v or "") .. string.rep(" ", math.min(nParentX, nX)) .. (leftoverX > 0 and tBor.v or "") .. string.rep(" ", math.max(0, leftoverX - 1))
  159.         if sName then
  160.             local nameOffset = math.max(math.floor((math.min(nParentX, nX) - #sName) / 2), 0)
  161.             local nameLeftover = math.max(math.min(nParentX, nX) - #sName - nameOffset, 0)
  162.             tLines.top = string.rep(" ", math.max(0, offsetX - 1)) .. (offsetX > 0 and tBor.c or "") .. string.rep(tBor.h, nameOffset) .. string.sub(sName, 1, math.min(nParentX, nX)) .. string.rep(tBor.h, nameLeftover) .. (leftoverX > 0 and tBor.c or "") .. string.rep(" ", math.max(0, leftoverX - 1))
  163.         else
  164.             tLines.top = tLines.bottom
  165.         end
  166.         tLines.front = string.rep("7", nParentX) --0
  167.         tLines.back = string.rep("8", nParentX) --f
  168.        
  169.         for y = 1, nParentY, 1 do
  170.             if y < offsetY then tLines[y] = tLines.empty --empty
  171.             elseif y == offsetY then tLines[y] = tLines.top --top
  172.             elseif y <= offsetY + nY then tLines[y] = tLines.middle --middle
  173.             elseif y == offsetY + nY + 1 then tLines[y] = tLines.bottom --bottom
  174.             else  tLines[y] = tLines.empty --empty
  175.             end
  176.         end
  177.        
  178.         winReposition(offsetX + 1, offsetY + 1, nX, nY)
  179.        
  180.     end
  181.    
  182.     local function frame()
  183.         for y = 1, nParentY, 1 do
  184.             parent.setCursorPos(1, y)
  185.             parent.blit(tLines[y], tLines.front, tLines.back)
  186.         end
  187.         win.redraw()
  188.     end
  189.    
  190.     win.resize = function(nNewX, nNewY)
  191.         expect(1, nNewX, "number")
  192.         expect(2, nNewY, "number")
  193.         nX, nY = nNewX, nNewY
  194.         build()
  195.         frame()
  196.     end
  197.    
  198.     win.rename = function(sNewName)
  199.         expect(1, sNewName, "string", "nil")
  200.         sName = sNewName
  201.         build()
  202.         frame()
  203.     end
  204.    
  205.     win.loadState = function(tWindow)
  206.         local _, inY = tWindow.getSize()
  207.         local cX, xY = tWindow.getCursorPos()
  208.         for i = 0, 15 do
  209.             win.setPaletteColour(2 ^ i, tWindow.getPaletteColour(2 ^ i))
  210.         end
  211.         if tWindow.getLine then
  212.             for y = 1, math.min(nParentY, inY) do
  213.                 win.setCursorPos(1, y)
  214.                 win.blit(tWindow.getLine(y))
  215.             end
  216.         end
  217.         win.setCursorPos(cX, xY)
  218.         win.setCursorBlink(tWindow.getCursorBlink())
  219.         win.setTextColor(tWindow.getTextColor())
  220.         win.setBackgroundColor(tWindow.getBackgroundColor())
  221.         win.redraw()
  222.     end
  223.    
  224.     win.getName = function()
  225.         return sName
  226.     end
  227.    
  228.     win.localizeCoords = function(nCoordX, nCoordY) --localizes the parent coordinates for inside of the window.
  229.         local x, y = nCoordX - offsetX, nCoordY - offsetY
  230.         if x > 0 and y > 0 and x <= nX and y <= nY then
  231.             return x, y
  232.         end
  233.     end
  234.    
  235.     build()
  236.     frame()
  237.     win.setVisible(true)
  238.     return win
  239. end
  240.  
  241. local function mirrorToMonitors(fMain,tSides,sName) --tSides is table with monitor sides to write on. If empty or not defined it will take all possible sides.
  242.     local parent = term.current()
  243.     local x,y = parent.getSize()
  244.     local tMirrorsSides = {}
  245.     local tMirrors = {}
  246.    
  247.     if not tSides or #tSides == 0 then
  248.         peripheral.find("monitor", function(name,wrap) tMirrorsSides[name] = framedWindow(wrap,x,y,sName) table.insert(tMirrors,tMirrorsSides[name]) end)
  249.     else
  250.         for i,k in pairs(tSides) do
  251.             if peripheral.isPresent(k) and peripheral.getType(k) == "monitor" then
  252.                 tMirrorsSides[k] = framedWindow(peripheral.wrap(k),x,y,sName)
  253.                 table.insert(tMirrors,tMirrorsSides[k])
  254.             end
  255.         end
  256.     end
  257.    
  258.     local mirr = createMultitable(tMirrors)
  259.     local mix = createMultitable(parent,mirr)
  260.     mirr.loadState(parent)
  261.     term.redirect(mix)
  262.    
  263.  
  264.     local co = coroutine.create(fMain)
  265.  
  266.     local function resume( ... )
  267.         local ok, param = coroutine.resume( co, ... )
  268.         if not ok then
  269.             printError( param )
  270.         end
  271.         return param
  272.     end
  273.  
  274.     local ok, param = pcall( function()
  275.         local sFilter = resume()
  276.         local TResizeLoop = {}
  277.         while coroutine.status( co ) ~= "dead" do
  278.             local tEvent = table.pack(os.pullEventRaw())
  279.             if tEvent[1] == "term_resize" then
  280.                 mirr.resize(parent.getSize())
  281.             elseif tEvent[1] == "monitor_resize" and tMirrorsSides[tEvent[2]] then
  282.                 if TResizeLoop[tEvent[2]] then
  283.                     TResizeLoop[tEvent[2]] = false
  284.                 else
  285.                     tMirrorsSides[tEvent[2]].resize(parent.getSize())
  286.                     TResizeLoop[tEvent[2]] = true
  287.                 end
  288.             end
  289.             if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then
  290.                 sFilter = resume(table.unpack(tEvent, 1, tEvent.n))
  291.             end
  292.             if coroutine.status( co ) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then
  293.                 if tEvent[1] == "monitor_touch" and tMirrorsSides[tEvent[2]] then
  294.                     tEvent[3],tEvent[4] = tMirrorsSides[tEvent[2]].localizeCoords(tEvent[3],tEvent[4])
  295.                     if tEvent[3] then
  296.                         sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n))
  297.                         if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then
  298.                             sFilter = resume("mouse_up", 1, table.unpack(tEvent, 3, tEvent.n))
  299.                         end
  300.                     end
  301.                 end
  302.             end
  303.         end
  304.     end )  
  305.    
  306.     term.redirect(parent)
  307.     if not ok then
  308.         printError( param )
  309.     end
  310. end
  311.  
  312. mirrorToMonitors(fMain,{sName},sProgram)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement