Advertisement
Tag365

Window Shell

May 18th, 2015
541
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 24.75 KB | None | 0 0
  1. local tArgs = {...}
  2. if _G.windowShell then
  3.     shell.run("fg", unpack(tArgs))
  4.     return
  5. end
  6. term.setBackgroundColor(colors.black)
  7. term.clear()
  8. term.redirect(term.native())
  9.  
  10. -- Window API --
  11. local window = {}
  12. function window.create( parent, nX, nY, nWidth, nHeight, bStartVisible, sExpectedTitle )
  13.  
  14.     if type( parent ) ~= "table" or
  15.        type( nX ) ~= "number" or
  16.        type( nY ) ~= "number" or
  17.        type( nWidth ) ~= "number" or
  18.        type( nHeight ) ~= "number" or
  19.        (bStartVisible ~= nil and type( bStartVisible ) ~= "boolean") then
  20.         error( "Expected object, number, number, number, number, [boolean]", 2 )
  21.     end
  22.  
  23.     if parent == term then
  24.         error( "term is not a recommended window parent, try term.current() instead", 2 )
  25.     end
  26.  
  27.     -- Setup
  28.     local bVisible = (bStartVisible ~= false)
  29.     local sTitle = sExpectedTitle or ""
  30.     local nCursorX = 1
  31.     local nCursorY = 1
  32.     local bCursorBlink = false
  33.     local nTextColor = colors.white
  34.     local nBackgroundColor = colors.black
  35.     local sEmpty = string.rep( " ", nWidth - 2 )
  36.     local tLines = {}
  37.     do
  38.         local tEmpty = { { sEmpty, nTextColor, nBackgroundColor } }
  39.         for y=1,nHeight do
  40.             tLines[y] = tEmpty
  41.         end
  42.     end
  43.  
  44.     -- Helper functions
  45.     local function updateCursorPos()
  46.         if nCursorX >= 1 and nCursorY >= 1 and
  47.            nCursorX <= nWidth and nCursorY <= nHeight then
  48.             parent.setCursorPos( nX + nCursorX, nY + nCursorY)
  49.         else
  50.             parent.setCursorPos( 0, 0 )
  51.         end
  52.     end
  53.    
  54.     local function updateCursorBlink()
  55.         parent.setCursorBlink( bCursorBlink )
  56.     end
  57.    
  58.     local function updateCursorColor()
  59.         parent.setTextColor( nTextColor )
  60.     end
  61.    
  62.     local function redrawLine( n )
  63.         parent.setCursorPos( nX + 1, nY + n )
  64.         local tLine = tLines[ n ]
  65.         for m=1,#tLine do
  66.             local tBit = tLine[ m ]
  67.             parent.setTextColor( tBit[2] )
  68.             parent.setBackgroundColor( tBit[3] )
  69.             parent.write( tBit[1] )
  70.         end
  71.         parent.setBackgroundColor(colors.blue)
  72.         parent.setCursorPos( nX + (nWidth - 1), nY + nCursorY )
  73.         parent.write(" ")
  74.     end
  75.  
  76.     local function lineLen( tLine )
  77.         local nLength = 0
  78.         for n=1,#tLine do
  79.             nLength = nLength + string.len( tLine[n][1] )
  80.         end
  81.         return nLength
  82.     end
  83.  
  84.     local function lineSub( tLine, nStart, nEnd )
  85.         --assert( math.floor(nStart) == nStart )
  86.         --assert( math.floor(nEnd) == nEnd )
  87.         --assert( nStart >= 1 )
  88.         --assert( nEnd >= nStart )
  89.         --assert( nEnd <= lineLen( tLine ) )
  90.         local tSubLine = {}
  91.         local nBitStart = 1
  92.         for n=1,#tLine do
  93.             local tBit = tLine[n]
  94.             local sBit = tBit[1]
  95.             local nBitEnd = math.min(nBitStart + string.len( sBit ) - 1)
  96.             if nBitEnd >= nStart and nBitStart <= nEnd then
  97.                 if nBitStart >= nStart and nBitEnd <= nEnd then
  98.                     -- Include bit wholesale
  99.                     table.insert( tSubLine, tBit )
  100.                     --assert( lineLen( tSubLine ) == (math.min(nEnd, nBitEnd) - nStart + 1) )
  101.                 elseif nBitStart < nStart and nBitEnd <= nEnd then
  102.                     -- Include end of bit
  103.                     table.insert( tSubLine, {
  104.                         string.sub( sBit, nStart - nBitStart + 1 ),
  105.                         tBit[2], tBit[3]
  106.                     } )
  107.                     --assert( lineLen( tSubLine ) == (math.min(nEnd, nBitEnd) - nStart + 1) )
  108.                 elseif nBitStart >= nStart and nBitEnd > nEnd then
  109.                     -- Include beginning of bit
  110.                     table.insert( tSubLine, {
  111.                         string.sub( sBit, 1, nEnd - nBitStart + 1 ),
  112.                         tBit[2], tBit[3]
  113.                     } )
  114.                     --assert( lineLen( tSubLine ) == (math.min(nEnd, nBitEnd) - nStart + 1) )
  115.                 else
  116.                     -- Include middle of bit
  117.                     table.insert( tSubLine, {
  118.                         string.sub( sBit, nStart - nBitStart + 1, nEnd - nBitStart + 1 ),
  119.                         tBit[2], tBit[3]
  120.                     } )
  121.                     --assert( lineLen( tSubLine ) == (math.min(nEnd, nBitEnd) - nStart + 1) )
  122.                 end
  123.             end
  124.             nBitStart = nBitEnd + 1
  125.         end
  126.         --assert( lineLen( tSubLine ) == (nEnd - nStart + 1) )
  127.         return tSubLine
  128.     end
  129.  
  130.     local function lineJoin( tLine1, tLine2 )
  131.         local tNewLine = {}
  132.         if tLine1[#tLine1][2] == tLine2[1][2] and
  133.            tLine1[#tLine1][3] == tLine2[1][3] then
  134.             -- Merge middle bits
  135.             for n=1,#tLine1-1 do
  136.                 table.insert( tNewLine, tLine1[n] )
  137.             end
  138.             table.insert( tNewLine, {
  139.                 tLine1[#tLine1][1] .. tLine2[1][1],
  140.                 tLine2[1][2], tLine2[1][3]
  141.             } )
  142.             for n=2,#tLine2 do
  143.                 table.insert( tNewLine, tLine2[n] )
  144.             end
  145.             --assert( lineLen( tNewLine ) == lineLen(tLine1) + lineLen(tLine2) )
  146.         else
  147.             -- Just concatenate
  148.             for n=1,#tLine1 do
  149.                 table.insert( tNewLine, tLine1[n] )
  150.             end
  151.             for n=1,#tLine2 do
  152.                 table.insert( tNewLine, tLine2[n] )
  153.             end
  154.             --assert( lineLen( tNewLine ) == lineLen(tLine1) + lineLen(tLine2) )
  155.         end
  156.         return tNewLine
  157.     end
  158.  
  159.     local function redraw()
  160.         -- Draw window contents.
  161.         for n=1,nHeight - 2 do
  162.             redrawLine( n )
  163.         end
  164.        
  165.         -- Draw Top and Bottom borders
  166.         local lineStr = string.rep(" ", nWidth)
  167.         parent.setBackgroundColor(colors.blue)
  168.         parent.setTextColor(colors.white)
  169.         parent.setCursorPos( nX, nY )
  170.         parent.write(lineStr)
  171.         parent.setCursorPos( nX, nY )
  172.         parent.write(sTitle or "window")
  173.         parent.setCursorPos( nX + (nWidth - 2), nY)
  174.         parent.write("_")
  175.         parent.setBackgroundColor(colors.red)
  176.         parent.setCursorPos( nX + (nWidth - 1), nY)
  177.         parent.write("X")
  178.         parent.setBackgroundColor(colors.blue)
  179.         parent.setCursorPos( nX, nY + nHeight - 1 )
  180.         parent.write(lineStr)
  181.        
  182.         -- Draw Left and Right borders
  183.         parent.setBackgroundColor(colors.blue)
  184.         for y=1, nHeight - 2 do
  185.             parent.setCursorPos( nX, nY + y )
  186.             parent.write(" ")
  187.             parent.setCursorPos( nX + (nWidth - 1), nY + y )
  188.             parent.write(" ")
  189.         end
  190.         parent.setCursorPos( nX + (nWidth - 1), nY + nHeight - 1 )
  191.         parent.setTextColor(colors.white)
  192.         parent.write("/")
  193.     end
  194.  
  195.     local window = {}
  196.  
  197.     -- Terminal implementation
  198.     function window.write( sText )
  199.         local nLen = string.len( string.sub(sText, 0, nWidth - 2) )
  200.         local nStart = nCursorX
  201.         local nEnd = nStart + nLen - 1
  202.         if nCursorY >= 1 and nCursorY <= nHeight - 2 then
  203.             -- Work out where to put new line
  204.             --assert( math.floor(nStart) == nStart )
  205.             --assert( math.floor(nEnd) == nEnd )
  206.             if nStart <= (nWidth - 2) and nEnd >= 1 then
  207.                 -- Construct new line
  208.                 local tLine = tLines[ nCursorY ]
  209.                 if nStart == 1 and nEnd == (nWidth - 2) then
  210.                     -- Exactly replace line
  211.                     tLine = {
  212.                         { sText, nTextColor, nBackgroundColor }
  213.                     }
  214.                     --assert( lineLen( tLine ) == nWidth )
  215.                 elseif nStart <= 1 and nEnd >= (nWidth - 2) then
  216.                     -- Overwrite line with subset
  217.                     tLine = {
  218.                         { string.sub( sText, 1 - nStart + 1, (nWidth - 2) - nStart + 1 ), nTextColor, nBackgroundColor }
  219.                     }
  220.                     --assert( lineLen( tLine ) == nWidth )
  221.                 elseif nStart <= 1 then
  222.                     -- Overwrite beginning of line
  223.                     tLine = lineJoin(
  224.                         { { string.sub( sText, 1 - nStart + 1 ), nTextColor, nBackgroundColor } },
  225.                         lineSub( tLine, nEnd + 1, (nWidth - 2) )
  226.                     )
  227.                     --assert( lineLen( tLine ) == nWidth )
  228.                 elseif nEnd >= nWidth - 2 then
  229.                     -- Overwrite end of line
  230.                     tLine = lineJoin(
  231.                         lineSub( tLine, 1, nStart - 1 ),
  232.                         { { string.sub( sText, 1, (nWidth - 2) - nStart + 1 ), nTextColor, nBackgroundColor } }
  233.                     )
  234.                     --assert( lineLen( tLine ) == nWidth )
  235.                 else
  236.                     -- Overwrite middle of line
  237.                     tLine = lineJoin(
  238.                         lineJoin(
  239.                             lineSub( tLine, 1, nStart - 1 ),
  240.                             { { sText, nTextColor, nBackgroundColor } }
  241.                         ),
  242.                         lineSub( tLine, nEnd + 1, (nWidth - 2) )
  243.                     )
  244.                     --assert( lineLen( tLine ) == nWidth )
  245.                 end
  246.                
  247.                 -- Destroy text outside the window region that should not be there
  248.                 for k=nWidth - 2, #tLine do
  249.                     table.remove(tLine, nWidth)
  250.                 end
  251.  
  252.                 -- Store and redraw new line
  253.                 tLines[ nCursorY ] = tLine
  254.                 if bVisible then
  255.                     redrawLine( nCursorY )
  256.                 end
  257.             end
  258.         end
  259.  
  260.         -- Move and redraw cursor
  261.         nCursorX = nEnd + 1
  262.         if bVisible then
  263.             updateCursorColor()
  264.             updateCursorPos()
  265.         end
  266.     end
  267.  
  268.     function window.clear()
  269.         local tEmpty = { { sEmpty, nTextColor, nBackgroundColor } }
  270.         for y=1,nHeight do
  271.             tLines[y] = tEmpty
  272.         end
  273.         if bVisible then
  274.             redraw()
  275.             updateCursorColor()
  276.             updateCursorPos()
  277.         end
  278.     end
  279.  
  280.     function window.clearLine()
  281.         if nCursorY >= 1 and nCursorY <= nHeight then
  282.             tLines[ nCursorY ] = { { sEmpty, nTextColor, nBackgroundColor } }
  283.             if bVisible then
  284.                 redrawLine( nCursorY )
  285.                 updateCursorColor()
  286.                 updateCursorPos()
  287.             end
  288.         end
  289.     end
  290.  
  291.     function window.getCursorPos()
  292.         return nCursorX, nCursorY
  293.     end
  294.  
  295.     function window.setCursorPos( x, y )
  296.         nCursorX = math.floor( x )
  297.         nCursorY = math.floor( y )
  298.         if bVisible then
  299.             updateCursorPos()
  300.         end
  301.     end
  302.  
  303.     function window.setCursorBlink( blink )
  304.         bCursorBlink = blink
  305.         if bVisible then
  306.             updateCursorBlink()
  307.         end
  308.     end
  309.  
  310.     function window.isColor()
  311.         return parent.isColor()
  312.     end
  313.  
  314.     function window.isColour()
  315.         return parent.isColor()
  316.     end
  317.  
  318.     local function setTextColor( color )
  319.         if not parent.isColor() then
  320.             if color ~= colors.white and color ~= colors.black then
  321.                 error( "Color not supported", 3 )
  322.             end
  323.         end
  324.         nTextColor = color
  325.         if bVisible then
  326.             updateCursorColor()
  327.         end
  328.     end
  329.  
  330.     function window.setTextColor( color )
  331.         setTextColor( color )
  332.     end
  333.  
  334.     function window.setTextColour( color )
  335.         setTextColor( color )
  336.     end
  337.    
  338.     function window.getTextColor()
  339.         return nTextColor
  340.     end
  341.  
  342.     function window.getTextColour()
  343.         return nTextColor
  344.     end
  345.  
  346.     local function setBackgroundColor( color )
  347.         if not parent.isColor() then
  348.             if color ~= colors.white and color ~= colors.black then
  349.                 error( "Colour not supported", 3 )
  350.             end
  351.         end
  352.         nBackgroundColor = color
  353.     end
  354.  
  355.     function window.setBackgroundColor( color )
  356.         setBackgroundColor( color )
  357.     end
  358.  
  359.     function window.setBackgroundColour( color )
  360.         setBackgroundColor( color )
  361.     end
  362.    
  363.     function window.getBackgroundColor()
  364.         return nBackgroundColor
  365.     end
  366.  
  367.     function window.getBackgroundColour()
  368.         return nBackgroundColor
  369.     end
  370.  
  371.     function window.getSize()
  372.         return nWidth - 2, nHeight - 2
  373.     end
  374.  
  375.     function window.scroll( n )
  376.         if n ~= 0 then
  377.             local tNewLines = {}
  378.             local tEmpty = { { sEmpty, nTextColor, nBackgroundColor } }
  379.             for newY=1,nHeight do
  380.                 local y = newY + n
  381.                 if y >= 1 and y <= nHeight then
  382.                     tNewLines[newY] = tLines[y]
  383.                 else
  384.                     tNewLines[newY] = tEmpty
  385.                 end
  386.             end
  387.             tLines = tNewLines
  388.             if bVisible then
  389.                 redraw()
  390.                 updateCursorColor()
  391.                 updateCursorPos()
  392.             end
  393.         end
  394.     end
  395.  
  396.     -- Other functions
  397.     function window.setVisible( bVis )
  398.         if bVisible ~= bVis then
  399.             bVisible = bVis
  400.             if bVisible then
  401.                 window.redraw()
  402.             end
  403.         end
  404.     end
  405.  
  406.     function window.redraw()
  407.         if bVisible then
  408.             redraw()
  409.             updateCursorBlink()
  410.             updateCursorColor()
  411.             updateCursorPos()
  412.         end
  413.     end
  414.  
  415.     function window.restoreCursor()
  416.         if bVisible then
  417.             updateCursorBlink()
  418.             updateCursorColor()
  419.             updateCursorPos()
  420.         end
  421.     end
  422.  
  423.     function window.getPosition()
  424.         return nX, nY
  425.     end
  426.  
  427.     function window.reposition( nNewX, nNewY, nNewWidth, nNewHeight )
  428.         nX = nNewX
  429.         nY = nNewY
  430.         if nNewWidth and nNewHeight then
  431.             sEmpty = string.rep( " ", nNewWidth )
  432.             local tNewLines = {}
  433.             local tEmpty = { { sEmpty, nTextColor, nBackgroundColor } }
  434.             for y=1,nNewHeight do
  435.                 if y > nHeight then
  436.                     tNewLines[y] = tEmpty
  437.                 else
  438.                     if nNewWidth == nWidth then
  439.                         tNewLines[y] = tLines[y]
  440.                     elseif nNewWidth < nWidth then
  441.                         tNewLines[y] = lineSub( tLines[y], 1, nNewWidth )
  442.                     else
  443.                         tNewLines[y] = lineJoin( tLines[y], { { string.sub( sEmpty, nWidth + 1, nNewWidth ), nTextColor, nBackgroundColor } } )
  444.                     end
  445.                 end
  446.             end
  447.             nWidth = nNewWidth
  448.             nHeight = nNewHeight
  449.             tLines = tNewLines
  450.         end
  451.         if bVisible then
  452.             window.redraw()
  453.         end
  454.     end
  455.    
  456.     -- Priviate functions not actually available.
  457.     function window.getTitle()
  458.         return sTitle
  459.     end
  460.    
  461.     function window.setTitle(sNewTitle)
  462.         sTitle = sNewTitle
  463.     end
  464.  
  465.     if bVisible then
  466.         window.redraw()
  467.     end
  468.     return window
  469. end
  470.  
  471. -- Set up arguments
  472. _G.windowShell = true
  473.  
  474. -- Setup process switching
  475. local parentTerm = term.current()
  476. local w,h = parentTerm.getSize()
  477.  
  478. local tProcesses = {}
  479. local nCurrentProcess = nil
  480. local nRunningProcess = nil
  481. local bShowMenu = false
  482. local bWindowsResized = false
  483.  
  484. local menuMainTextColor, menuMainBgColor, menuOtherTextColor, menuOtherBgColor
  485. if parentTerm.isColor() then
  486.     menuMainTextColor, menuMainBgColor = colors.black, colors.white
  487.     menuOtherTextColor, menuOtherBgColor = colors.black, colors.lightGray
  488. else
  489.     menuMainTextColor, menuMainBgColor = colors.white, colors.black
  490.     menuOtherTextColor, menuOtherBgColor = colors.black, colors.white
  491. end
  492.  
  493. local function redrawScreen()
  494.     parentTerm.setBackgroundColor(colors.black)
  495.     parentTerm.clear()
  496.     parentTerm.setCursorPos(1, h)
  497.     parentTerm.setBackgroundColor(menuOtherBgColor)
  498.     parentTerm.clearLine()
  499.     for k, tProcess in pairs(tProcesses) do
  500.         tProcess.window.redraw()
  501.     end
  502.     redrawMenu()
  503. end
  504.  
  505. local function selectProcess( n )
  506.     if nCurrentProcess ~= n then
  507.         if nCurrentProcess then
  508.             local tOldProcess = tProcesses[ nCurrentProcess ]
  509.         end
  510.         nCurrentProcess = n
  511.         if nCurrentProcess then
  512.             local tNewProcess = tProcesses[ nCurrentProcess ]
  513.             tNewProcess.bInteracted = true
  514.             tNewProcess.window.setVisible(true)
  515.         end
  516.     end
  517. end
  518.  
  519. local function setProcessTitle( n, sTitle )
  520.     tProcesses[ n ].sTitle = sTitle
  521.     tProcesses[ n ].window.setTitle(sTitle)
  522. end
  523.  
  524. local function resumeProcess( nProcess, sEvent, ... )
  525.     local tProcess = tProcesses[ nProcess ]
  526.     if tProcess then
  527.         local sFilter = tProcess.sFilter
  528.         if sFilter == nil or sFilter == sEvent or sEvent == "terminate" then
  529.             local nPreviousProcess = nRunningProcess
  530.             nRunningProcess = nProcess
  531.             term.redirect( tProcess.terminal )
  532.             local ok, result = coroutine.resume( tProcess.co, sEvent, ... )
  533.             tProcess.terminal = term.current()
  534.             if ok then
  535.                 tProcess.sFilter = result
  536.             else
  537.                 printError( result )
  538.             end
  539.             nRunningProcess = nPreviousProcess
  540.         end
  541.     end
  542. end
  543.  
  544. local function launchProcess( tProgramEnv, sProgramPath, ... )
  545.     local tProgramArgs = { ... }
  546.     local nProcess = #tProcesses + 1
  547.     local tProcess = {}
  548.     tProcess.sTitle = fs.getName( sProgramPath )
  549.     if bShowMenu then
  550.         tProcess.window = window.create( parentTerm, math.min(#tProcesses + 1, w), math.min(#tProcesses + 1, h - 12), 32, 12, false, tProcess.sTitle )
  551.     else
  552.         tProcess.window = window.create( parentTerm, 1, 1, w, h, false, tProcess.sTitle )
  553.     end
  554.     tProcess.co = coroutine.create( function()
  555.         os.run( tProgramEnv, sProgramPath, unpack( tProgramArgs ) )
  556.         if not tProcess.bInteracted then
  557.             term.setCursorBlink( false )
  558.             print( "Press any key to continue" )
  559.             os.pullEvent( "char" )
  560.         end
  561.     end )
  562.     tProcess.sFilter = nil
  563.     tProcess.terminal = tProcess.window
  564.     tProcess.bInteracted = false
  565.     tProcesses[ nProcess ] = tProcess
  566.     resumeProcess( nProcess )
  567.     return nProcess
  568. end
  569.  
  570. local function cullProcess( nProcess )
  571.     local tProcess = tProcesses[ nProcess ]
  572.     if tProcess then
  573.         if coroutine.status( tProcess.co ) == "dead" then
  574.             if nCurrentProcess == nProcess then
  575.                 selectProcess( nil )
  576.             end
  577.             table.remove( tProcesses, nProcess )
  578.             if nCurrentProcess == nil then
  579.                 if nProcess > 1 then
  580.                     selectProcess( nProcess - 1 )
  581.                 elseif #tProcesses > 0 then
  582.                     selectProcess( 1 )
  583.                 end
  584.             end
  585.             return true
  586.         end
  587.         return false
  588.     end
  589. end
  590.  
  591. local function killProcess( nProcess )
  592.     if nCurrentProcess == nProcess then
  593.         selectProcess( nil )
  594.     end
  595.     table.remove( tProcesses, nProcess )
  596.     if nCurrentProcess == nil then
  597.         if nProcess > 1 then
  598.             selectProcess( nProcess - 1 )
  599.         elseif #tProcesses > 0 then
  600.             selectProcess( 1 )
  601.         end
  602.     end
  603.     redrawScreen()
  604.     return true
  605. end
  606.  
  607. local function cullProcesses()
  608.     local culled = false
  609.     for n=#tProcesses,1,-1 do
  610.         culled = culled or cullProcess( n )
  611.     end
  612.     return culled
  613. end
  614.  
  615. -- Setup the main menu
  616.  
  617. function redrawMenu()
  618.     if bShowMenu then
  619.         -- Draw menu
  620.         parentTerm.setCursorPos( 1, h )
  621.         parentTerm.setBackgroundColor( menuOtherBgColor )
  622.         parentTerm.clearLine()
  623.         for n=1,#tProcesses do
  624.             if n == nCurrentProcess then
  625.                 parentTerm.setTextColor( menuMainTextColor )
  626.                 parentTerm.setBackgroundColor( menuMainBgColor )
  627.             else
  628.                 parentTerm.setTextColor( menuOtherTextColor )
  629.                 parentTerm.setBackgroundColor( menuOtherBgColor )
  630.             end
  631.             parentTerm.write( " " .. tProcesses[n].sTitle .. " " )
  632.         end
  633.  
  634.         -- Put the cursor back where it should be
  635.         local tProcess = tProcesses[ nCurrentProcess ]
  636.         if tProcess then
  637.             tProcess.window.restoreCursor()
  638.         end
  639.     end
  640. end
  641. local redrawMenu = redrawMenu
  642.  
  643. local function resizeWindows()
  644.     local windowY, windowHeight
  645.     if bShowMenu then
  646.         windowY = 2
  647.         windowHeight = h-2
  648.     else
  649.         windowY = 1
  650.         windowHeight = h
  651.     end
  652.     for n=1,#tProcesses do
  653.         local tProcess = tProcesses[n]
  654.         local window = tProcess.window
  655.         local x,y = tProcess.window.getCursorPos()
  656.         if y > windowHeight then
  657.             tProcess.window.scroll( y - windowHeight )
  658.             tProcess.window.setCursorPos( x, windowHeight )
  659.         end
  660.         --tProcess.window.reposition( 4, windowY, w - 4, windowHeight )
  661.     end
  662.     bWindowsResized = true
  663. end
  664.  
  665. local function setMenuVisible( bVis )
  666.     if bShowMenu ~= bVis then
  667.         bShowMenu = bVis
  668.         resizeWindows()
  669.         redrawMenu()
  670.     end
  671. end
  672.  
  673. local multishell = {}
  674.  
  675. function multishell.getFocus()
  676.     return nCurrentProcess
  677. end
  678.  
  679. function multishell.setFocus( n )
  680.     if n >= 1 and n <= #tProcesses then
  681.         selectProcess( n )
  682.         redrawMenu()
  683.         return true
  684.     end
  685.     return false
  686. end
  687.  
  688. function multishell.getTitle( n )
  689.     if n >= 1 and n <= #tProcesses then
  690.         return tProcesses[n].sTitle
  691.     end
  692.     return nil
  693. end
  694.  
  695. function multishell.setTitle( n, sTitle )
  696.     if n >= 1 and n <= #tProcesses then
  697.         setProcessTitle( n, sTitle )
  698.         redrawMenu()
  699.     end
  700. end
  701.  
  702. function multishell.getCurrent()
  703.     return nRunningProcess
  704. end
  705.  
  706. function multishell.launch( tProgramEnv, sProgramPath, ... )
  707.     local previousTerm = term.current()
  708.     setMenuVisible(true)
  709.     local nResult = launchProcess( tProgramEnv, sProgramPath, ... )
  710.     redrawMenu()
  711.     term.redirect( previousTerm )
  712.     return nResult
  713. end
  714.  
  715. function multishell.getCount()
  716.     return #tProcesses
  717. end
  718.  
  719. -- Begin
  720. parentTerm.clear()
  721. setMenuVisible( true )
  722. selectProcess( launchProcess( {
  723.     ["shell"] = shell,
  724.     ["multishell"] = multishell,
  725. }, unpack(tArgs) or "/rom/programs/shell" ) )
  726. -- Since the native terminal is bugged we must trigger a term_resize event.
  727. os.queueEvent("term_resize")
  728.  
  729. -- Run processes
  730. while #tProcesses > 0 do
  731.     -- Get the event
  732.     local tEventData = { os.pullEventRaw() }
  733.     local sEvent = tEventData[1]
  734.     if sEvent == "term_resize" then
  735.         -- Resize event
  736.         w,h = parentTerm.getSize()
  737.         resizeWindows()
  738.         redrawScreen()
  739.  
  740.     elseif sEvent == "char" or sEvent == "key" or sEvent == "paste" or sEvent == "terminate" then
  741.         -- Keyboard event
  742.         -- Passthrough to current process
  743.         resumeProcess( nCurrentProcess, unpack( tEventData ) )
  744.         if cullProcess( nCurrentProcess ) then
  745.             setMenuVisible( true )
  746.             redrawScreen()
  747.         end
  748.         if tProcesses[nCurrentProcess] then
  749.             if tProcesses[nCurrentProcess].rsX or tProcesses[nCurrentProcess].dragX then
  750.                 if tProcesses[nCurrentProcess].rsX then resumeProcess( nCurrentProcess, "term_resize") end
  751.                 tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = nil, nil
  752.                 tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY = nil, nil
  753.                 redrawScreen()
  754.             end
  755.         end
  756.  
  757.     elseif sEvent == "mouse_click" then
  758.         -- Click event
  759.         local button, x, y = tEventData[2], tEventData[3], tEventData[4]
  760.         if tProcesses[nCurrentProcess].rsX or tProcesses[nCurrentProcess].dragX then
  761.             if tProcesses[nCurrentProcess].rsX then resumeProcess( nCurrentProcess, "term_resize") end
  762.             tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = nil, nil
  763.             tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY = nil, nil
  764.         end
  765.         if bShowMenu and y == h then
  766.             -- Switch process
  767.             local tabStart = 1
  768.             for n=1,#tProcesses do
  769.                 tabEnd = tabStart + string.len( tProcesses[n].sTitle ) + 1
  770.                 if x >= tabStart and x <= tabEnd then
  771.                     selectProcess( n )
  772.                     redrawMenu()
  773.                     break
  774.                 end
  775.                 tabStart = tabEnd + 1
  776.             end
  777.         else
  778.             -- Passthrough to current process
  779.             local nX, nY = tProcesses[nCurrentProcess].window.getPosition()
  780.             local nWidth, nHeight = tProcesses[nCurrentProcess].window.getSize()
  781.             if x >= nX and x <= nX + nWidth + 2 then
  782.                 if y == nY then
  783.                     if x == nX + nWidth + 1 then
  784.                         killProcess( nCurrentProcess )
  785.                     elseif x == nX + nWidth then
  786.                         tProcesses[nCurrentProcess].window.setVisible( false )
  787.                         tProcesses[nCurrentProcess].hidden = true
  788.                         redrawScreen()
  789.                         for k, tProcess in pairs(tProcesses) do
  790.                             if not tProcesses[nCurrentProcess].hidden then
  791.                                 selectProcess( k )
  792.                             end
  793.                         end
  794.                     else
  795.                         tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = x - nX, y - nY
  796.                     end
  797.                 else
  798.                     tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = nil, nil
  799.                 end
  800.                 if x > nX + nWidth and x < nX + nWidth + 2 and y > nY + nHeight and y < nY + nHeight + 2 then
  801.                     tProcesses[nCurrentProcess].oldWidth, tProcesses[nCurrentProcess].oldHeight = nWidth, nHeight
  802.                     tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY = x - nX - 1, y - nY - 1
  803.                 elseif tProcesses[nCurrentProcess] then
  804.                     if tProcesses[nCurrentProcess].rsX then
  805.                         if tProcesses[nCurrentProcess].rsX then resumeProcess( nCurrentProcess, "term_resize") end
  806.                         tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = nil, nil
  807.                         tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY = nil, nil
  808.                         redrawScreen()
  809.                     end
  810.                 end
  811.             else
  812.                 for k, tProcess in pairs(tProcesses) do
  813.                     local nX, nY = tProcess.window.getPosition()
  814.                     local nWidth, nHeight = tProcess.window.getSize()
  815.                     if x >= nX and x <= nX + nWidth + 2 and y > nY and y < nY + nHeight then
  816.                         selectProcess( k )
  817.                         redrawScreen()
  818.                     end
  819.                 end
  820.             end
  821.             resumeProcess( nCurrentProcess, sEvent, button, x - nX or 0, y - nY or 0 )
  822.             if cullProcess( nCurrentProcess ) then
  823.                 setMenuVisible( true )
  824.                 redrawMenu()
  825.             end
  826.         end
  827.  
  828.     elseif sEvent == "mouse_drag" or sEvent == "mouse_scroll" then
  829.         -- Other mouse event
  830.         local p1, x, y = tEventData[2], tEventData[3], tEventData[4]
  831.         if not (bShowMenu and y == h) then
  832.             -- Passthrough to current process
  833.             local nX, nY = tProcesses[nCurrentProcess].window.getPosition()
  834.             if tProcesses[nCurrentProcess].dragX then
  835.                 -- Drag the window
  836.                 local dragX, dragY = tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY
  837.                 local nWidth, nHeight = tProcesses[nCurrentProcess].window.getSize()
  838.                 tProcesses[nCurrentProcess].window.reposition( x - dragX, y - dragY, nWidth + 2, nHeight + 2 )
  839.                 if #tProcesses < 8 then
  840.                     if tProcesses[nCurrentProcess].rsX then resumeProcess( nCurrentProcess, "term_resize") end
  841.                     redrawScreen()
  842.                 end
  843.                 redrawMenu()
  844.             elseif tProcesses[nCurrentProcess] then
  845.                 if tProcesses[nCurrentProcess].rsX then
  846.                     -- Resize the window
  847.                     local dragX, dragY = tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY
  848.                     local nWidth, nHeight = tProcesses[nCurrentProcess].window.getSize()
  849.                     local oldWidth, oldHeight = tProcesses[nCurrentProcess].oldWidth, tProcesses[nCurrentProcess].oldHeight
  850.                     tProcesses[nCurrentProcess].window.reposition( nX, nY, math.max(oldWidth + 1 + (x - dragX) - nX, 8), math.max(oldHeight + 1 + (y - dragY) - nY, 4))
  851.                     redrawMenu()
  852.                     if #tProcesses < 8 then
  853.                         if tProcesses[nCurrentProcess].rsX then resumeProcess( nCurrentProcess, "term_resize") end
  854.                         redrawScreen()
  855.                     end
  856.                 else
  857.                     resumeProcess( nCurrentProcess, sEvent, p1, (x - nX) or 0, (y - nY) or 0 )
  858.                 end
  859.             end
  860.             if cullProcess( nCurrentProcess ) then
  861.                 setMenuVisible( true )
  862.                 redrawMenu()
  863.             end
  864.         end
  865.  
  866.     else
  867.         if tProcesses[nCurrentProcess].rsX or tProcesses[nCurrentProcess].dragX then
  868.             resumeProcess( nCurrentProcess, "term_resize")
  869.             tProcesses[nCurrentProcess].dragX, tProcesses[nCurrentProcess].dragY = nil, nil
  870.             tProcesses[nCurrentProcess].rsX, tProcesses[nCurrentProcess].rsY = nil, nil
  871.             redrawScreen()
  872.         end
  873.         -- Other event
  874.         -- Passthrough to all processes
  875.         local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event
  876.         for n=1,nLimit do
  877.             resumeProcess( n, unpack( tEventData ) )
  878.         end
  879.         if cullProcesses() then
  880.             setMenuVisible( true )
  881.             redrawScreen()
  882.         end
  883.     end
  884.  
  885.     if bWindowsResized then
  886.         -- Pass term_resize to all processes
  887.         local nLimit = #tProcesses -- Storing this ensures any new things spawned don't get the event
  888.         for n=1,nLimit do
  889.             resumeProcess( n, "term_resize" )
  890.         end
  891.         bWindowsResized = false
  892.         if cullProcesses() then
  893.             setMenuVisible( true )
  894.             redrawScreen()
  895.         end
  896.     end
  897. end
  898.  
  899. -- Shutdown
  900. term.redirect( parentTerm )
  901. term.setBackgroundColor(colors.black)
  902. term.setTextColor(colors.white)
  903. term.clear()
  904. term.setCursorPos(1, 1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement