Kingdaro

Windowing Thing (from canvas)

Mar 27th, 2013
115
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.98 KB | None | 0 0
  1. -- the table that contains all of the running windows
  2. local windows = {}
  3.  
  4. -- this is triggered by pressing left alt and allows you to drag windows without triggering them
  5. -- also to open their right-click menu (if any)
  6. local altmode = false
  7.  
  8. -- the message that briefly appears at the top of the screen if there's a problem
  9. local warningMsg = ''
  10.  
  11. -- just a nice boolean where i can stop the program safely at any point
  12. local running = true
  13.  
  14. -- localizations, to keep everything nice and neat
  15. local init, update, draw, exit
  16. local newImage, newWindow, newImageWindow, newTitlebar
  17. local bgMenu, imageMenu, colorMenu
  18.  
  19.  
  20. -- convenience for drawing rectangles
  21. local function rect(x, y, width, height, color, noshadow)
  22.     local spaces = string.rep(' ', width)
  23.     local t = term
  24.    
  25.     local r = function(x, y, color)
  26.         t.setBackgroundColor(color)
  27.         for i=y, y + height - 1 do
  28.             t.setCursorPos(x, i)
  29.             t.write(spaces)
  30.         end
  31.     end
  32.    
  33.     if not noshadow then r(x + 1, y + 1, colors.black) end
  34.     r(x, y, color)
  35. end
  36.  
  37. -- convenience for checking if x and y is in the area at position
  38. -- ax, ay with width aw and height ah
  39. local function checkArea(x, y, ax, ay, aw, ah)
  40.     return
  41.         x >= ax and
  42.         x < ax + aw and
  43.         y >= ay and
  44.         y < ay + ah
  45. end
  46.  
  47. -- i thought i would need this but i guess not
  48. -- just combines tables, e.g. combine({1,2}, {3,4}) --> {1,2,3,4}
  49. local function combine(t1, t2)
  50.     for i=1, #t2 do
  51.         table.insert(t1, t2)
  52.     end
  53.     return t1
  54. end
  55.  
  56. -- convenience for making a menu option
  57. local function menuOption(text, x, y, action, color, bg)
  58.     return {
  59.         text = text;
  60.         x = x;
  61.         y = y;
  62.         action = action;
  63.         color = color or colors.lightGray;
  64.         bg = bg or colors.gray;
  65.     }
  66. end
  67.  
  68. -- convenience for making a context menu
  69. -- which are menus that have options one atop another
  70. local function contextMenu(options)
  71.     for i=1, #options do
  72.         local opt = options[i]
  73.        
  74.         opt.x = 1
  75.         opt.y = i
  76.         opt.text = ' '..opt.text..' '
  77.     end
  78.     return options
  79. end
  80.  
  81. -- convenience for a menu separator
  82. local function separator()
  83.     return menuOption('---', 1,1, function() end)
  84. end
  85.  
  86. -- to get user input in an attractive matter.
  87. local function prompt(text, placeholder)
  88.     local t = term
  89.    
  90.     draw() -- i use this sometimes to "clear" the screen
  91.     rect(1, 1, t.getSize(), 1, colors.gray, true)
  92.    
  93.     t.setCursorPos(2,1)
  94.     t.setTextColor(colors.white)
  95.     t.write(text)
  96.    
  97.     if placeholder then
  98.         for i=1, #placeholder do
  99.             os.queueEvent('char', placeholder:sub(i,i))
  100.         end
  101.     end
  102.    
  103.     return read()
  104. end
  105.  
  106. -- for opening menus at position x, y with a table of menu options
  107. -- also passes optional arguments to menu functions
  108. -- logic for this one is really weird lol
  109. local function openMenu(x, y, menu, ...)
  110.     y = y + 1  -- i dunno
  111.     --[[
  112.         menu table format
  113.        
  114.         {
  115.             text = 'OptionText';
  116.             x = 1;
  117.             y = 1;
  118.             color = someTextColor;
  119.             bg = someBackgroundColor;
  120.             action = function()
  121.                 doStuff()
  122.                 if i decide to change the menu then
  123.                     return a new menu
  124.                 end
  125.             end;
  126.         }
  127.     ]]
  128.    
  129.     local ox, oy
  130.     local width = 1
  131.     local height = 1
  132.     local t = term
  133.     local w,h = term.getSize()
  134.    
  135.     local function offsetOptions()
  136.         -- find the longest menu option and base the width off of that
  137.         for i=1, #menu do
  138.             local opt = menu[i]
  139.             local cur = opt.x + #opt.text - 1
  140.             if cur > width then
  141.                 width = cur
  142.             end
  143.         end
  144.        
  145.         -- same for height
  146.         for i=1, #menu do
  147.             local opt = menu[i]
  148.             if opt.y > height then
  149.                 height = opt.y
  150.             end
  151.         end
  152.        
  153.         -- add the x and y offset to the options
  154.         -- also limit to screen if needed
  155.         ox = x + width - 1 > w and w - width + 1 or x
  156.         oy = y + height - 1 > h and h - height + 1 or y
  157.         for i=1, #menu do
  158.             local opt = menu[i]
  159.             opt.x = opt.x + ox - 1
  160.             opt.y = opt.y + oy - 1
  161.         end
  162.     end
  163.    
  164.     --i've stored it in a function so i can call it whenever i need to
  165.     offsetOptions()
  166.    
  167.     draw()
  168.     t.setCursorBlink(false)
  169.     rect(ox, oy - 1, width, height + 1, colors.gray)
  170.     for i=1, #menu do
  171.         local opt = menu[i]
  172.        
  173.         rect(opt.x, opt.y, #opt.text, 1, opt.bg, true)
  174.         t.setCursorPos(opt.x, opt.y)
  175.         t.setTextColor(opt.color)
  176.         t.write(opt.text)
  177.     end
  178.    
  179.     local _, button, mx, my = os.pullEvent('mouse_click')
  180.    
  181.     for i=1, #menu do
  182.         local opt = menu[i]
  183.         if checkArea(mx, my, opt.x, opt.y, #opt.text, 1) then
  184.             local res = opt.action(...)
  185.             if type(res) == 'table' then
  186.                 draw()
  187.                 openMenu(x, y, res, ...)
  188.             end
  189.             break
  190.         end
  191.     end
  192. end
  193.  
  194. -- convenience for adding new windows
  195. local function addWindow(window)
  196.     table.insert(windows, 1, window)
  197. end
  198.  
  199. -- convenience for making a new warning
  200. local function warning(message)
  201.     warningMsg = message
  202. end
  203.  
  204.  
  205. -- the titlebar class
  206. -- basically, i wanted to keep all of the window positioning stuff in the titlebar
  207. -- so i can know when the title of the window was clicked more efficiently, and keep it from
  208. -- all of what's actually in the window
  209. function newTitleBar(obj)
  210.     -- using functional OO because it's neater and fits the rest of the program
  211.     obj = obj or {}
  212.    
  213.     local titlebar = {
  214.         x = 3;
  215.         y = 2;
  216.         width = 12;
  217.         text = 'image';
  218.     }
  219.    
  220.     -- convenience for returning the dimensions of the rectangle, for use with checkArea()
  221.     -- also allows adding dimensions, so I can check specific areas
  222.     function titlebar:rect(dx, dy, dw, dh)
  223.         return
  224.             self.x + (dx or 0),
  225.             self.y + (dy or 0),
  226.             self.width + (dw or 0),
  227.             1 + (dh or 0)
  228.     end
  229.    
  230.     function titlebar:draw()
  231.         local t = term
  232.         t.setTextColor(windows[1].title == self and colors.white or colors.lightGray)
  233.        
  234.         -- limit the width if needed
  235.         self.width = self.width > 5 and self.width or 5
  236.        
  237.         -- limit the text if needed
  238.         local text =
  239.             #self.text > self.width - 2 and
  240.             self.text:sub(1, self.width - 4) .. '..' or
  241.             self.text
  242.        
  243.         -- draw the title background
  244.         rect(self.x, self.y, self.width, 1, colors.gray)
  245.        
  246.         -- draw the title text
  247.         t.setCursorPos(self.x + self.width/2 - #text/2, self.y)
  248.     --  t.setCursorPos(self.x + 1, self.y)
  249.         t.write(text)
  250.     end
  251.    
  252.     return setmetatable(obj, {__index = titlebar})
  253. end
  254.  
  255. -- THIS IS WHERE THE REAL FUN BEGINS
  256. function newWindow(obj)
  257.     obj = obj or {}
  258.    
  259.     local window = {
  260.         name = 'window';
  261.        
  262.         width = 15;
  263.         height = 8;
  264.        
  265.         drag = nil;
  266.         resize = false;
  267.         resizeTimer = nil;
  268.        
  269.         lastClick = nil;
  270.         maximized = false;
  271.        
  272.         remove = false;
  273.     }
  274.    
  275.     -- pass on some of the object properties to the titlebar
  276.     window.title = newTitleBar{
  277.         x = obj.x or 2;
  278.         y = obj.y or 3;
  279.         text = obj.name or window.name;
  280.         width = obj.width or window.width;
  281.     }
  282.    
  283.     local lastClick
  284.    
  285.     -- same as titlebar.rect
  286.     function window:rect(dx, dy, dw, dh)
  287.         return
  288.             self.title.x + (dx or 0),
  289.             self.title.y + (dy or 0),
  290.             self.width + (dw or 0),
  291.             self.height + (dh or 0)
  292.     end
  293.    
  294.     -- using a setname function to set the name of the window
  295.     -- and the title text at the same time
  296.     function window:setName(name)
  297.         self.name = name
  298.         self.title.text = name
  299.     end
  300.    
  301.     -- again, managing both the window and the title for safety
  302.     function window:setSize(width, height)
  303.         self.width = width > 1 and width or 1
  304.         self.height = height > 1 and height or 1
  305.         self.title.width = self.width
  306.     end
  307.    
  308.     -- convenience maximizing
  309.     -- stores previous size and position in nsize and npos
  310.     function window:maximize()
  311.         if not self.maximized then
  312.             self.maximized = true
  313.             local w,h = term.getSize()
  314.             -- "n" in this case stands for "normal"
  315.             self.nsize = {self.width, self.height}
  316.             self.npos = {self.title.x, self.title.y}
  317.            
  318.             self.title.x, self.title.y = 1, 1
  319.             self:setSize(w, h - 1)
  320.         end
  321.     end
  322.    
  323.     -- convenience restoring
  324.     function window:restore()
  325.         if self.maximized then
  326.             self.maximized = false
  327.             self:setSize(unpack(self.nsize))
  328.             self.title.x, self.title.y = unpack(self.npos)
  329.         end
  330.     end
  331.    
  332.     -- all windows must have an onClick event
  333.     -- called when the user clicks anywhere
  334.     function window:onClick(button, x, y, index)
  335.         -- remove dragging and resizing properties on click
  336.         -- if we want to drag or resize again, they will be reenabled by the end of this
  337.         self.drag = nil
  338.         self.resize = false
  339.        
  340.         -- first, we check if the mouse is inside the entirety of the window.
  341.         if checkArea(x, y, self:rect()) then
  342.            
  343.             -- if we're in the window, awesome. are we in the titlebar?
  344.             -- or is altmode on? cool
  345.             if checkArea(x, y, self.title:rect())
  346.             or altmode then
  347.            
  348.                 -- do this if we used the left mouse button
  349.                 if button == 1 then
  350.                    
  351.                     -- be able to access the "background" menu when maximized
  352.                     -- via the top-left corner of the window
  353.                     if self.maximized and x == self.title.x and y == self.title.y then
  354.                         openMenu(x, y, bgMenu(), x, y)
  355.                     end
  356.                    
  357.                     -- turn on dragging (only if we aren't maximized)
  358.                     -- record the dragging offset
  359.                     if not self.maximized then
  360.                         self.drag = {
  361.                             x = self.title.x - x;
  362.                             y = self.title.y - y;
  363.                         }
  364.                     end
  365.                    
  366.                     -- check double clicks using a local lastClick variable
  367.                     if lastClick then
  368.                         if os.clock() - lastClick <= 0.3 then
  369.                             if self.maximized then
  370.                                 self:restore()
  371.                             else
  372.                                 self:maximize()
  373.                             end
  374.                         end
  375.                     end
  376.                     lastClick = os.clock()
  377.                 end
  378.             end
  379.            
  380.             -- returning "focusme" brings the window to the front of the windows table
  381.             -- the window at the front has focus and is drawn last
  382.             return 'focusme'
  383.        
  384.         -- this is for checking if the mouse is at the outside bottom-right corner of the window.
  385.         elseif x == self.title.x + self.width and y == self.title.y + self.height + 1 then
  386.             -- turn on resizing, and set a timer to hide the resize "handle"
  387.             self.resize = true
  388.             self.resizeTimer = os.startTimer(1)
  389.            
  390.             return 'focusme'
  391.         end
  392.     end
  393.    
  394.     -- called on a mouse drag
  395.     function window:onDrag(button, x, y)
  396.         -- make sure we have focus
  397.         if windows[1] == self then
  398.             -- drag actions should only happen if we're maximized
  399.             if not self.maximized then
  400.                 -- if we're in dragging mode, go to the dragging posision
  401.                 if self.drag then
  402.                     self.title.x = x + self.drag.x
  403.                     self.title.y = y + self.drag.y
  404.                    
  405.                 -- if we're in resize mode, resize to wherever the mouse is
  406.                 -- WITH MATHS!!!!
  407.                
  408.                 elseif self.resize then
  409.                     local width, height = x - self.title.x, y - self.title.y - 1 -- -1 to account for the titlebar
  410.                     self:setSize(width, height)
  411.                 --  self.resizeTimer = os.startTimer(1)
  412.                 end
  413.             end
  414.         end
  415.     end
  416.    
  417.     -- called on a key event - not used by default
  418.     function window:onKey(key)
  419.     end
  420.    
  421.     -- called on a char event - again, not used
  422.     function window:onChar(char)
  423.     end
  424.    
  425.     -- called on a timer event
  426.     function window:onTimer(t)
  427.         -- only to hide the resize handle
  428.         if t == self.resizeTimer then
  429.             self.resizeTimer = nil
  430.         end
  431.     end
  432.    
  433.     -- WELL WE HAVE TO ACTUALLY FRIGGEN DRAW THE THING
  434.     function window:draw()
  435.         local t = term
  436.         local w,h = t.getSize()
  437.         local ox, oy = self.title.x, self.title.y + 1
  438.        
  439.         -- draw the titlebar
  440.         self.title:draw()
  441.        
  442.         -- draw the window backing
  443.         -- i might make a property for this so i can have transparent windows if i want
  444.         rect(self.title.x, self.title.y + 1, self.width, self.height, colors.gray)
  445.        
  446.         -- also draw a little dot to show we can access the background menu when maximized
  447.         if self.maximized then
  448.             t.setCursorPos(self.title.x, self.title.y)
  449.             t.setBackgroundColor(colors.gray)
  450.             t.setTextColor(colors.white)
  451.             t.write '.'
  452.         end
  453.        
  454.         -- draw the resize handle (if we're timed)
  455.         if self.resizeTimer then
  456.             t.setCursorPos(self.title.x + self.width, self.title.y + self.height + 1)
  457.             t.setBackgroundColor(colors.black)
  458.             t.setTextColor(colors.gray)
  459.             t.write '%'
  460.         end
  461.     end
  462.    
  463.     -- convenience for making a window to be removed from the windows table
  464.     function window:close()
  465.         self.remove = true
  466.     end
  467.    
  468.     return setmetatable(obj, {__index = window})
  469. end
  470.  
  471. -- a nice example of how to make your own program/function
  472. -- very simple, just has a little pixel moving around the window
  473. -- with the arrow keys
  474. function newTestProgram(obj)
  475.     -- make a local clone of the window so we can call its functions later if needed
  476.     local super = newWindow()
  477.     local window = setmetatable({}, {__index = super})
  478.    
  479.     -- make a local player variable to keep the "player" position
  480.     local player = {x = 3, y = 2}
  481.    
  482.     -- convenience
  483.     local function move(x, y)
  484.         player.x = player.x + x
  485.         player.y = player.y + y
  486.     end
  487.    
  488.     -- overwrite the key event to include our moving functions
  489.     function window:onKey(k)
  490.         if k == keys.right then
  491.             move(1, 0)
  492.         elseif k == keys.left then
  493.             move(-1, 0)
  494.         elseif k == keys.down then
  495.             move(0, 1)
  496.         elseif k == keys.up then
  497.             move(0, -1)
  498.         end
  499.     end
  500.    
  501.     -- to draw our character
  502.     function window:draw()
  503.         -- we need to include the super:draw() with self
  504.         -- otherwise no titlebar/window backing
  505.         super.draw(self)
  506.        
  507.         -- limit the character to the window width and height
  508.         -- width and height is already defined in the default window, so no need
  509.         -- to define it ourselves
  510.         player.x =
  511.             player.x < 1 and 1 or
  512.             player.x > self.width and self.width or
  513.             player.x
  514.            
  515.         player.y =
  516.             player.y < 1 and 1 or
  517.             player.y > self.height and self.height or
  518.             player.y
  519.        
  520.         local t = term
  521.         t.setBackgroundColor(colors.gray)
  522.         t.setTextColor(colors.white)
  523.         t.setCursorPos(self.title.x + player.x - 1, self.title.y + player.y)
  524.         t.write '@'
  525.     end
  526.    
  527.     window:setName('My Wonderful Game Thing')
  528.    
  529.     return setmetatable(obj or {}, {__index = window})
  530. end
  531.  
  532.  
  533. -- various context menus
  534. function bgMenu()
  535.     return contextMenu{
  536.         menuOption('New..', 1, 1, function(mx, my)
  537.             local name = prompt('Image Name: ')
  538.            
  539.             if #name > 0 then
  540.                 local window = newImageWindow{
  541.                     name = shell.resolve(name);
  542.                     x = mx - 6;
  543.                     y = my;
  544.                 }
  545.                 addWindow(window)
  546.             else
  547.                 -- send a warning
  548.             end
  549.         end,
  550.         colors.lime);
  551.        
  552.         menuOption('Open..', 1, 2, function(window)
  553.             local path = prompt('Image Path: ')
  554.            
  555.             if #path > 0 then
  556.                 local window = newImageWindow()
  557.                 local ok, err = window:loadImage(path)
  558.                 if not ok then
  559.                     warning(err)
  560.                 else
  561.                     addWindow(window)
  562.                 end
  563.             end
  564.         end,
  565.         colors.orange);
  566.        
  567.         separator();
  568.        
  569.         menuOption('Exit', 1, 3, function()
  570.             running = false
  571.         end,
  572.         colors.red);
  573.     }
  574. end
  575.  
  576. function imageMenu()
  577.     return contextMenu{
  578.         menuOption('Load..', 1,1, function(window)
  579.             local path = prompt('Image Path: ')
  580.            
  581.             if #path > 0 then
  582.                 local ok, err = window:loadImage(path)
  583.                 if not ok then
  584.                     warning(err)
  585.                 end
  586.             else
  587.                 -- send a warning
  588.             end
  589.         end,
  590.         colors.magenta);
  591.        
  592.         menuOption('Save', 1,1, function(window)
  593.             local ok, err = window:saveImage()
  594.             if not ok then
  595.                 warning(err)
  596.             end
  597.         end,
  598.         colors.lime);
  599.        
  600.         menuOption('Save as Script Object', 1,1, function(window)
  601.             local ok, err = window:saveImageObject()
  602.             if not ok then
  603.                 warning(err)
  604.             end
  605.         end,
  606.         colors.orange);
  607.        
  608.         menuOption('Rename', 1,1, function(window)
  609.             local name = prompt('New Name: ', window.name)
  610.             window:setName(name)
  611.         end,
  612.         colors.orange);
  613.        
  614.         separator();
  615.        
  616.         menuOption('Close', 1,1, function(window)
  617.             window:close()
  618.         end,
  619.         colors.red);
  620.     }
  621. end
  622.  
  623. function colorMenu()
  624.     local grid = {
  625.         ---[[
  626.         {'red',     'lime',         'blue',     'white'},
  627.         {'orange',  'lightBlue',    'cyan',     'lightGray'},
  628.         {'yellow',  'purple',       'green',    'gray'},
  629.         {'pink',    'magenta',      'brown',    'black'}
  630.         --]]
  631.     }
  632.     local options = {}
  633.    
  634.     for y=1, #grid do
  635.         for x=1, #grid[y] do
  636.             local color = colors[grid[y][x]]
  637.             table.insert(options, menuOption(' ', x, y, function(window)
  638.                 if window.mode == 'paint' then
  639.                     window.color = color
  640.                 elseif window.mode == 'char' then
  641.                     window.textColor = color
  642.                 end
  643.             end,
  644.             colors.white, color))
  645.         end
  646.     end
  647.    
  648.     return options
  649. end
  650.  
  651.  
  652. -- here we just make a new window on startup
  653. -- the commented out code makes a nice little display for the canvas program
  654. function init()
  655.     --[[
  656.     local function new(n)
  657.         local image = newImageWindow{
  658.             x = #windows*-2 + 11;
  659.             y = #windows*2 + 2;
  660.             name = n;
  661.         }
  662.     end
  663.    
  664.     local words = {'hello', 'and', 'welcome', 'to', 'canvas'}
  665.     for i=5, 1, -1 do
  666.         new(words[i])
  667.     end
  668.     --]]
  669.    
  670. --  addWindow(newWindow{name='woop'})
  671.     addWindow(newTestProgram())
  672. end
  673.  
  674. function update(ev, p1, p2, p3)
  675.     -- set the warning message to nothing on click or key
  676.     if ev == 'key' or ev == 'mouse_click' then
  677.         warningMsg = ''
  678.     end
  679.    
  680.     if ev == 'mouse_click' then
  681.         -- i'm using this function wrapper because of *
  682.         (function()
  683.             local button, x, y = p1, p2, p3
  684.            
  685.             -- go through the windows
  686.             for i=1, #windows do
  687.                 local window = windows[i]
  688.                
  689.                 -- check if the window wants focus
  690.                 if window:onClick(button, x, y, i) == 'focusme' then
  691.                     table.insert(windows, 1, table.remove(windows, i))
  692.                     return -- * this
  693.                     -- i don't want to use a stupid boolean
  694.                     -- to check if i've found a window to focus on.
  695.                 end
  696.             end
  697.            
  698.             if button == 2 then
  699.                 openMenu(x, y, bgMenu(), x, y)
  700.             end
  701.         end)()
  702.     elseif ev == 'mouse_drag' then
  703.         -- trigger drag events
  704.         for i=1, #windows do
  705.             windows[i]:onDrag(p1, p2, p3)
  706.         end
  707.     elseif ev == 'key' then
  708.         -- trigger key event on the focused window
  709.         windows[1]:onKey(p1)
  710.        
  711.         -- also cycle windows when i press the tab key
  712.         if p1 == keys.tab then
  713.             table.insert(windows, table.remove(windows, 1))
  714.         end
  715.     elseif ev == 'char' then
  716.         -- trigger char event on the focused window
  717.         windows[1]:onChar(p1)
  718.     elseif ev == 'timer' then
  719.         -- trigger timer events
  720.         for i=1, #windows do
  721.             windows[i]:onTimer(p1)
  722.         end
  723.     end
  724.    
  725.     -- trigger alt mode
  726.     -- it's not really a toggle, more of an activation on this current event
  727.     if ev == 'key' and (p1 == keys.leftAlt or p1 == keys.rightAlt) then
  728.         altmode = true
  729.     else
  730.         altmode = false
  731.     end
  732.    
  733.     -- remove any windows that are marked
  734.     -- go backwards to prevent problems
  735.     for i=#windows, 1, -1 do
  736.         if windows[i].remove == true then
  737.             table.remove(windows, i)
  738.         end
  739.     end
  740.    
  741.     -- make the user able to terminate the program
  742.     -- for whatever reason
  743.     if ev == 'terminate' then
  744.         running = false
  745.     end
  746.    
  747.     -- was for testing
  748.     --[[
  749.     if ev == 'key' and p1 == keys.backspace then
  750.         running = false
  751.     end
  752.     --]]
  753. end
  754.  
  755. function draw()
  756.     local t = term
  757.     local w,h = term.getSize()
  758.    
  759.     -- draw the blue background
  760.     t.setBackgroundColor(colors.blue)
  761.     t.clear()
  762.    
  763.     -- only draw the warning message if it ain't empty
  764.     if warningMsg ~= '' then
  765.         t.setCursorPos(1,1)
  766.         t.setBackgroundColor(colors.gray)
  767.         t.setTextColor(colors.white)
  768.         t.clearLine()
  769.         t.write(warningMsg)
  770.     end
  771.    
  772.     -- draw 'dem windows yo
  773.     for i=#windows, 1, -1 do
  774.         windows[i]:draw()
  775.     end
  776. end
  777.  
  778. function exit()
  779.     -- give the user a nice thank you message. :)
  780.     local t = term
  781.     t.setCursorPos(1,1)
  782.     t.setBackgroundColor(colors.black)
  783.     t.setTextColor(colors.white)
  784.     t.clear()
  785.     print 'Thanks for using canvas! -Kingdaro'
  786. end
  787.  
  788.  
  789. local function main()
  790.     -- main event loop
  791.     -- it's very nice
  792.    
  793.     init()
  794.     while running do
  795.         draw()
  796.         update(os.pullEventRaw())
  797.     end
  798.     exit()
  799. end
  800.  
  801. -- PROGRAM EXECUTION!
  802. main()
Advertisement
Add Comment
Please, Sign In to add comment