From 7d17c201ed1526f344f3341ea8410226933a8aa1 Mon Sep 17 00:00:00 2001 From: LDDestroier Date: Sun, 23 Feb 2020 21:36:26 -0500 Subject: [PATCH] Update windont.lua --- windont/windont.lua | 1059 ++++++++++++++++++------------------------- 1 file changed, 443 insertions(+), 616 deletions(-) diff --git a/windont/windont.lua b/windont/windont.lua index bf4bc91..3cef11b 100644 --- a/windont/windont.lua +++ b/windont/windont.lua @@ -1,658 +1,485 @@ --- Windon't --- enhanced window API by LDDestroier --- intended for general use within all me new programs --- --- Unique features: --- + Transparency within windows --- + Built-in window layering - --- stores each base terminal's framebuffers to optimize rendering -local oldScreenBuffer = {} - -local table_insert = table.insert -local table_concat = table.concat -local math_floor = math.floor -local to_blit = {} -local to_colors = {} - -local table_compare = function(tbl1, tbl2) - if type(tbl1) ~= "table" or type(tbl2) ~= "table" then - return tbl1 == tbl2 +if not fs.exists("windont.lua") then + print("'windont.lua' not found! Downloading...") + local net = http.get("https://github.com/LDDestroier/CC/raw/master/windont/windont.lua") + if net then + local file = fs.open("windont.lua", "w") + file.write(net.readAll()) + file.close() + net.close() else - for k,v in pairs(tbl1) do - if tbl1[k] ~= tbl2[k] then - return false - end - end - for k,v in pairs(tbl2) do - if tbl1[k] ~= tbl2[k] then - return false - end - end - return true + error("Could not download Windon't.", 0) end end +local windont = require "windont" -local getTime = function() - return 24 * os.day() + os.time() -end +windont.default.alwaysRender = false -for i = 1, 16 do - to_blit[2 ^ (i - 1)] = ("0123456789abcdef"):sub(i, i) - to_colors[("0123456789abcdef"):sub(i, i)] = 2 ^ (i - 1) -end -to_blit[0], to_colors["-"] = "-", 0 +local scr_x, scr_y = term.getSize() +local keysDown = {} -local nativePalette = { -- native palette colors, since some terminals are naughty and don't contain term.nativePaletteColor() - [ 1 ] = { - 0.94117647409439, - 0.94117647409439, - 0.94117647409439, - }, - [ 2 ] = { - 0.94901961088181, - 0.69803923368454, - 0.20000000298023, - }, - [ 4 ] = { - 0.89803922176361, - 0.49803921580315, - 0.84705883264542, - }, - [ 8 ] = { - 0.60000002384186, - 0.69803923368454, - 0.94901961088181, - }, - [ 16 ] = { - 0.87058824300766, - 0.87058824300766, - 0.42352941632271, - }, - [ 32 ] = { - 0.49803921580315, - 0.80000001192093, - 0.098039217293262, - }, - [ 64 ] = { - 0.94901961088181, - 0.69803923368454, - 0.80000001192093, - }, - [ 128 ] = { - 0.29803922772408, - 0.29803922772408, - 0.29803922772408, - }, - [ 256 ] = { - 0.60000002384186, - 0.60000002384186, - 0.60000002384186, - }, - [ 512 ] = { - 0.29803922772408, - 0.60000002384186, - 0.69803923368454, - }, - [ 1024 ] = { - 0.69803923368454, - 0.40000000596046, - 0.89803922176361, - }, - [ 2048 ] = { - 0.20000000298023, - 0.40000000596046, - 0.80000001192093, - }, - [ 4096 ] = { - 0.49803921580315, - 0.40000000596046, - 0.29803922772408, - }, - [ 8192 ] = { - 0.34117648005486, - 0.65098041296005, - 0.30588236451149, - }, - [ 16384 ] = { - 0.80000001192093, - 0.29803922772408, - 0.29803922772408, - }, - [ 32768 ] = { - 0.066666670143604, - 0.066666670143604, - 0.066666670143604, - } +local instances = {} +local knownNames = { + ["rom/programs/shell.lua"] = "CraftOS Shell", + ["rom/programs/edit.lua"] = "Edit", + ["rom/programs/gps.lua"] = "GPS", + ["rom/programs/shutdown.lua"] = "Shutdown", + ["rom/programs/monitor.lua"] = "Monitor Redirect", + ["rom/programs/emu.lua"] = "Emu (CCEmuX)", + ["rom/programs/exit.lua"] = "Goodbye!", + ["rom/programs/lua.lua"] = "Lua Interpreter", + + ["rom/programs/fun/adventure.lua"] = "Adventure", + ["rom/programs/fun/worm.lua"] = "Worm", + ["rom/programs/fun/dj.lua"] = "DJ", + ["rom/programs/fun/hello.lua"] = "Hello world!", + + ["rom/programs/turtle/dance.lua"] = "Dance!", + ["rom/programs/turtle/craft.lua"] = "Craft", + ["rom/programs/turtle/excavate.lua"] = "Excavate", + ["rom/programs/turtle/refuel.lua"] = "Refueling...", + ["rom/programs/turtle/go.lua"] = "Go (Turtle)", + ["rom/programs/turtle/turn.lua"] = "Turn (Turtle)", + + ["rom/programs/http/pastebin.lua"] = "Pastebin", + ["rom/programs/http/wget.lua"] = "Wget", + + ["rom/programs/pocket/falling.lua"] = "Falling", + + ["rom/programs/rednet/chat.lua"] = "Chat", + ["rom/programs/rednet/repeat.lua"] = "Rednet Repeat", + + ["rom/programs/command/exec.lua"] = "Exec (Command)", + ["rom/programs/command/commands.lua"] = "Commands", } --- check if space on screenBuffer is transparent -local checkTransparent = function(buffer, x, y, blitLayer) - if buffer[blitLayer or 1][y] then - if blitLayer then - return (buffer[blitLayer][y][x] and buffer[blitLayer][y][x] ~= "-") - else - if (not buffer[2][y][x] or buffer[2][y][x] == "-") and (not buffer[3][y][x] or buffer[3][y][x] == "-") then - return false - elseif (not buffer[3][y][x] or buffer[3][y][x] == "-") and (not buffer[1][y][x] or buffer[1][y][x] == " ") then - return false - else - return buffer[1][y][x] and buffer[2][y][x] and buffer[3][y][x] - end +-- events that can only be passed if the instance is focused +local FocusEvents = { + ["mouse_click"] = true, + ["mouse_drag"] = true, + ["mouse_up"] = true, + ["key"] = true, + ["key_up"] = true, + ["char"] = true, + ["monitor_touch"] = true, + ["paste"] = true, + ["terminate"] = true + -- mouse_scroll is intentionally excluded +} + +-- events that must have their XY values altered according to the position of the window +local CoordinateEvents = { + ["mouse_click"] = {3, 4}, + ["mouse_drag"] = {3, 4}, + ["mouse_up"] = {3, 4}, + ["mouse_scroll"] = {3, 4} +} + +local desktop = windont.newWindow(1, 1, scr_x, scr_y, { + backColor = "9" +}) +local overlay = windont.newWindow(1, 1, scr_x, scr_y, { + backColor = "-" +}) +desktop.redraw() + +local instanceBoxCheck = function(i, x, y, useMain) + assert(type(x) == "number", "x must be number") + assert(type(y) == "number", "y must be number") + return ( + x < 1 or x > (useMain and instances[i].mainWindow.meta.width or instances[i].termWindow.meta.width) or + y < 1 or y > (useMain and instances[i].mainWindow.meta.height or instances[i].termWindow.meta.height) + ) +end + +local focusInstance = function(i) + if instances[i or false] then + if i ~= 1 then + local instance = instances[i] + instances[1].focused = false + table.remove(instances, i) + table.insert(instances, 1, instance) + instances[1].focused = true end end end -local expect = function(value, default, valueType) - if value == nil or (valueType and type(value) ~= valueType) then - return default - else - return value - end +-- checks if (x, y) is within a rectangle between (rx1, ry1), (rx2, ry2) +local rectangleCheck = function(x, y, rx1, ry1, rx2, ry2) + return + (x >= rx1 and x <= rx2) and + (y >= ry1 and y <= ry2) end -local windont = { - doClearScreen = false, -- if true, will clear the screen during render - ignoreUnchangedLines = true, -- if true, the render function will check each line it renders against the last framebuffer and ignore it if they are the same - useSetVisible = false, -- if true, sets the base terminal's visibility to false before rendering - sameCharWillStencil = false, -- if true, if one window is layered atop another and both windows have a spot where the character is the same, and the top window's text color is transparent, it will use the TEXT color of the lower window instead of the BACKGROUND color - default = { - baseTerm = term.current(), -- default base terminal for all windows - textColor = "0", -- default text color (what " " corresponds to in term.blit's second argument) - backColor = "f", -- default background color (what " " corresponds to in term.blit's third argument) - blink = true, - visible = true, - alwaysRender = true, -- if true, new windows will always render if they are written to - }, - info = { - BLIT_CALLS = 0, -- amount of term.blit calls during the last render - LAST_RENDER_TIME = 0, -- last time in which render was called - LAST_RENDER_AMOUNT = 0, -- amount of windows drawn during last render - LAST_RENDER_WINDOWS = {}, -- table of the last window objects that were rendered - } -} - --- draws one or more windon't objects --- should not draw over any terminal space that isn't occupied by a window - -windont.render = function(options, ...) --- potential options: --- number: onlyX1 --- number: onlyX2 --- number: onlyY --- boolean: force (forces render / ignores optimiztaion that compares current framebuffer to old one) --- terminal: baseTerm (forces to render onto this terminal instead of the window's base terminal) - - local windows = {...} - options = options or {} - local bT, scr_x, scr_y - - -- checks if "options" is actually the first window, just in case - if type(options.meta) == "table" then - if ( - type(options.meta.buffer) == "table" and - type(options.meta.x) == "number" and - type(options.meta.y) == "number" and - type(options.meta.newBuffer) == "function" +-- checks which instance should be selected if you were to click on (x, y) +local checkInstanceByPos = function(x, y, useTerm) + for i = 1, #instances do + if rectangleCheck( + x, + y, + instances[i].mainWindow.meta.x + (useTerm and 1 or 0), + instances[i].mainWindow.meta.y + (useTerm and 1 or 0), + instances[i].mainWindow.meta.x + (useTerm and 1 or 0) + (useTerm and instances[i].termWindow or instances[i].mainWindow).meta.width - 1, + instances[i].mainWindow.meta.y + (useTerm and 1 or 0) + (useTerm and instances[i].termWindow or instances[i].mainWindow).meta.height - 1 ) then - table_insert(windows, 1, options) + return i end end + return false +end - local screenBuffer = {{}, {}, {}} - local blitList = {} -- list of blit commands per line - local c = 1 -- current blitList entry - - local cTime = getTime() - - local AMNT_OF_BLITS = 0 -- how many blit calls are there? - - local cx, cy -- each window's absolute X and Y - local char_cx, text_cx, back_cx -- each window's transformed absolute X's in table form - local char_cy, text_cy, back_cy -- each window's transformed absolute X's in table form - local buffer -- each window's buffer - local newChar, newText, newBack -- if the transformation function declares a new dot, this is it - local oriChar, oriText, oriBack - local char_out, text_out, back_out -- three tables, directly returned from the transformation functions - - local baseTerms = {} - if type(options.baseTerm) == "table" then - for i = 1, #windows do - baseTerms[options.baseTerm] = baseTerms[options.baseTerm] or {} - baseTerms[options.baseTerm][i] = true +local resumeInstance = function(i, _evt, isCoordinateEvent) + local evt = {} + for k,v in pairs(_evt) do + evt[k] = v + end + if isCoordinateEvent then + evt[3] = evt[3] - instances[i].mainWindow.meta.x + evt[4] = evt[4] - instances[i].mainWindow.meta.y + end + repeat + if (isCoordinateEvent and instanceBoxCheck(i, evt[3], evt[4])) then + break end + oldTerm = term.redirect(instances[i].termWindow) + success, result = coroutine.resume(instances[i].coroutine, table.unpack(evt)) + term.redirect(oldTerm) + if success and coroutine.status(instances[i].coroutine) ~= "dead" then + instances[i].cFilter = result + else + instances[i].alive = false + end + until true +end + +local newInstance = function(x, y, width, height, program, pName, addBorder) + local output = {} + if addBorder then + output.mainWindow = windont.newWindow(x, y, width, height + 1, { + baseTerm = desktop, + alwaysRender = false, + backColor = "7" + }) + output.termWindow = windont.newWindow(1, 2, width, height, { + baseTerm = output.mainWindow, + alwaysRender = false, + }) + output.oldTermPos = {1, 2, width, height} + output.oldMainPos = {x, y, width, height + 1} else - for i = 1, #windows do - baseTerms[windows[i].meta.baseTerm] = baseTerms[windows[i].meta.baseTerm] or {} - baseTerms[windows[i].meta.baseTerm][i] = true + output.mainWindow = windont.newWindow(x, y, width + 2, height + 2, { + baseTerm = desktop, + alwaysRender = false, + backColor = "7" + }) + output.termWindow = windont.newWindow(2, 2, width, height, { + baseTerm = output.mainWindow, + alwaysRender = false, + }) + output.oldTermPos = {2, 2, width, height} + output.oldMainPos = {x, y, width + 2, height + 2} + end + + local mw = output.mainWindow -- contains the titlebar, and is the base terminal for termWindow + local tw = output.termWindow -- contains the program's terminal output + + tw.meta.transformation = function(x, y, char, text, back, meta) + if x > mw.meta.width - 2 then + return {x, y, " "}, {x, y, "-"}, {x, y, "-"} + else + return {x, y, char}, {x, y, text}, {x, y, back} end end - for bT, bT_list in pairs(baseTerms) do - if bT == output then - bT = options.baseTerm or output.meta.baseTerm + output.refreshMainWindow = function() + for y = 1, mw.meta.height do + mw.setCursorPos(1, y) + if y == 1 or y == mw.meta.height then + mw.blit( + (" "):rep(mw.meta.width), + ("7"):rep(mw.meta.width), + ("7"):rep(mw.meta.width) + ) + else + mw.blit(" ","7","7") + mw.setCursorPos(mw.meta.width, y) + mw.blit(" ","7","7") + end end - if windont.useSetVisible and bT.setVisible then - bT.setVisible(false) + end + output.refreshMainWindow() + + -- pausing will probably be implemented later + output.paused = false + output.timeMod = 0 + output.clockMod = 0 + output.timers = {} + + output.alive = true + output.focused = true + output.manipMode = 0 + output.title = pName or (type(program) == "string" and (knownNames[fs.combine("", program)] or fs.getName(program))) or tostring(program) + output.writeTitleBar = function() + mw.setCursorPos(1, 1) + if output.focused then + mw.setTextColor(colors.white) + else + mw.setTextColor(colors.lightGray) end - scr_x, scr_y = bT.getSize() - -- try entire buffer transformations - for i = #windows, 1, -1 do - if bT_list[i] then - if windows[i].meta.metaTransformation then - -- metaTransformation functions needn't return a value - windows[i].meta.metaTransformation(windows[i].meta) + mw.setBackgroundColor(colors.gray) + mw.clearLine() + + if #output.title <= (mw.meta.width - 4) then + mw.write(output.title) -- write full title + else + mw.write(output.title:sub(1, mw.meta.width - 7) .. "...") -- draw abreviated title + end + mw.setCursorPos(mw.meta.width - 3, 1) + mw.write(" \22\94\215") -- minimize / maximize / close + end + + if type(program) == "string" then + output.main = function() + tw.clear() + return shell.run(program) + end + elseif type(program) == "function" then + output.main = program + end + + --local env = {} + --setmetatable(env, {__index = _G}) + --setfenv(output.main, env) + + output.coroutine = coroutine.create(output.main) + output.cFilter = nil + + for i = 1, #instances do + instances[i].focused = false + end + + table.insert(instances, 1, output) + + resumeInstance(1, {}, false) + + return output +end + +local moveInstance = function(i, x, y, newWidth, newHeight, relative) + desktop.clear() + newWidth = math.max(newWidth or instances[i].mainWindow.meta.width, 3) + newHeight = math.max(newHeight or instances[i].mainWindow.meta.height, 3) + if relative then + if x == 0 and y == 0 then + if (not newWidth or newWidth == instances[i].mainWindow.meta.width) and (not newHeight or newHeight == instances[i].mainWindow.meta.height) then + return + end + end + instances[i].mainWindow.reposition(instances[i].mainWindow.meta.x + x, instances[i].mainWindow.meta.y + y, newWidth, newHeight) + else + if x == instances[i].mainWindow.meta.x and y == instances[i].mainWindow.meta.y then + if (not newWidth or newWidth == instances[i].mainWindow.meta.width) and (not newHeight or newHeight == instances[i].mainWindow.meta.height) then + return + end + end + instances[i].mainWindow.reposition(x, y, newWidth, newHeight) + end + instances[i].termWindow.reposition( + 2, 2, + math.max(instances[i].oldTermPos[3], instances[i].mainWindow.meta.width - 2), + math.max(instances[i].oldTermPos[4], instances[i].mainWindow.meta.height - 2) + ) + instances[i].termWindow.redraw(nil,nil,nil,{force = true}) + instances[i].refreshMainWindow() +end + +local render = function() + local wins = {} + for i = 1, #instances do + wins[i] = instances[i].mainWindow + instances[i].termWindow.redraw() + end + windont.render({force = true}, table.unpack(wins)) + windont.render({}, overlay, desktop) +end + +local main = function() + + newInstance(3, 3, 30, 12, "rom/programs/shell.lua", nil) + newInstance(8, 5, 30, 12, "rom/programs/shell.lua", nil) + + local evt, success, result, oldTerm + local cx, cy + local isCoordinateEvent + local usedCoordinateEvent + local isManipulatingInstance = false + -- handles input system + local keyTimer = os.startTimer(0.05) + + while true do + evt = {coroutine.yield()} + if evt[1] == "key" and not evt[3] then + keysDown[evt[2]] = 0 + elseif evt[1] == "key_up" then + keysDown[evt[2]] = nil + end + if evt[1] == "mouse_click" then + focusInstance(checkInstanceByPos(evt[3], evt[4])) + end + if evt[1] == "timer" and evt[2] == keyTimer then + keyTimer = os.startTimer(0.05) + for k,v in pairs(keysDown) do + keysDown[k] = v + 0.05 + end + + render() + + -- move windows with arrow keys (for now) + if false then + if keysDown[keys.right] then + desktop.clear() + instances[1].mainWindow.reposition(instances[1].mainWindow.meta.x + 1, instances[1].mainWindow.meta.y) + end + if keysDown[keys.left] then + desktop.clear() + instances[1].mainWindow.reposition(instances[1].mainWindow.meta.x - 1, instances[1].mainWindow.meta.y) + end + if keysDown[keys.up] then + desktop.clear() + instances[1].mainWindow.reposition(instances[1].mainWindow.meta.x, instances[1].mainWindow.meta.y - 1) + end + if keysDown[keys.down] then + desktop.clear() + instances[1].mainWindow.reposition(instances[1].mainWindow.meta.x, instances[1].mainWindow.meta.y + 1) end end - end - for y = options.onlyY or 1, options.onlyY or scr_y do - screenBuffer[1][y] = {} - screenBuffer[2][y] = {} - screenBuffer[3][y] = {} - blitList = {} - c = 1 - for x = options.onlyX1 or 1, math.min(scr_x, options.onlyX2 or scr_x) do - for i = #windows, 1, -1 do - if bT_list[i] then - newChar, newText, newBack = nil - if windows[i].meta.visible then - buffer = windows[i].meta.buffer - - cx = 1 + x + -windows[i].meta.x - cy = 1 + y + -windows[i].meta.y - char_cx, text_cx, back_cx = cx, cx, cx - char_cy, text_cy, back_cy = cy, cy, cy - - oriChar = (buffer[1][cy] or {})[cx] - oriText = (buffer[2][cy] or {})[cx] - oriBack = (buffer[3][cy] or {})[cx] - - -- try transformation - if windows[i].meta.transformation then - char_out, text_out, back_out = windows[i].meta.transformation(cx, cy, oriChar, oriText, oriBack, windows[i].meta) - - if char_out then - char_cx = math_floor(char_out[1] or cx) - char_cy = math_floor(char_out[2] or cy) - if (char_out[1] % 1 ~= 0) or (char_out[2] % 1 ~= 0) then - newChar = " " - else - newChar = char_out[3] - end - end - - if text_out then - text_cx = math_floor(text_out[1] or cx) - text_cy = math_floor(text_out[2] or cy) - newText = text_out[3] - end - - if back_out then - back_cx = math_floor(back_out[1] or cx) - back_cy = math_floor(back_out[2] or cy) - newBack = back_out[3] - end + else + usedCoordinateEvent = false + isCoordinateEvent = false + for i = 1, #instances do + if (not isManipulatingInstance) and evt[1] == "mouse_click" and (keysDown[keys.leftAlt] or ( + (checkInstanceByPos(evt[3], evt[4], false) == i) and not (checkInstanceByPos(evt[3], evt[4], true) == i) + )) then + if evt[2] == 1 then + -- dragging a window + if instances[i].manipMode == 0 then + instances[i].manipMode = 1 + isManipulatingInstance = true + instances[i].dragging = {instances[i].mainWindow.meta.x - evt[3], instances[i].mainWindow.meta.y - evt[4]} + end + elseif evt[2] == 2 then + -- resizing a window + if instances[i].manipMode == 0 then + instances[i].manipMode = 2 + isManipulatingInstance = true + instances[i].oldTermPos = { + instances[i].termWindow.meta.x, + instances[i].termWindow.meta.y, + instances[i].termWindow.meta.width, + instances[i].termWindow.meta.height + } + instances[i].oldMainPos = { + instances[i].mainWindow.meta.x, + instances[i].mainWindow.meta.y, + instances[i].mainWindow.meta.width, + instances[i].mainWindow.meta.height + } + if evt[3] > (instances[i].mainWindow.meta.x + (instances[i].mainWindow.meta.width) - 1) - 2 then + instances[i].resizingRight = (instances[i].mainWindow.meta.x + instances[i].mainWindow.meta.width - 1) - evt[3] + elseif evt[3] < (instances[i].mainWindow.meta.x + 2) then + instances[i].resizingLeft = (instances[i].mainWindow.meta.x - 1) - evt[3] end - - if checkTransparent(buffer, char_cx, char_cy) or checkTransparent(buffer, text_cx, text_cy) or checkTransparent(buffer, back_cx, back_cy) then - - screenBuffer[2][y][x] = newText or checkTransparent(buffer, text_cx, text_cy, 2) and (buffer[2][text_cy][text_cx]) or ( - (buffer[1][text_cy][text_cx] == screenBuffer[1][y][x]) and (windont.sameCharWillStencil) and - screenBuffer[2][y][x] - or - screenBuffer[3][y][x] - ) - screenBuffer[1][y][x] = newChar or checkTransparent(buffer, char_cx, char_cy ) and (buffer[1][char_cy][char_cx]) or screenBuffer[1][y][x] - screenBuffer[3][y][x] = newBack or checkTransparent(buffer, back_cx, back_cy, 3) and (buffer[3][back_cy][back_cx]) or screenBuffer[3][y][x] + if evt[4] > (instances[i].mainWindow.meta.y + (instances[i].mainWindow.meta.height) - 1) - 2 then + instances[i].resizingBottom = (instances[i].mainWindow.meta.y + instances[i].mainWindow.meta.height - 1) - evt[4] + elseif evt[4] < (instances[i].mainWindow.meta.y + 2) then + instances[i].resizingTop = (instances[i].mainWindow.meta.y - 1) - evt[4] + end + end + end + else + if evt[1] == "mouse_up" then + if instances[i].manipMode == 2 then + instances[i].mainWindow.clear() + instances[i].termWindow.reposition(2, 2, instances[i].mainWindow.meta.width - 2, instances[i].mainWindow.meta.height - 2) + instances[i].termWindow.redraw(nil, nil, nil, {force = true}) + end + instances[i].manipMode = 0 + isManipulatingInstance = false + instances[i].dragging = nil + instances[i].resizingRight = nil + instances[i].resizingLeft = nil + instances[i].resizingBottom = nil + instances[i].resizingTop = nil + elseif evt[1] == "mouse_drag" then + if isManipulatingInstance then + if instances[i].manipMode == 1 then + moveInstance( + i, + instances[i].dragging[1] + evt[3], + instances[i].dragging[2] + evt[4] + ) + elseif instances[i].manipMode == 2 then + local newX, newY = instances[i].mainWindow.meta.x, instances[i].mainWindow.meta.y + local newWidth, newHeight = instances[i].mainWindow.meta.width, instances[i].mainWindow.meta.height + if instances[i].resizingRight then + newWidth = instances[i].resizingRight + (evt[3] - instances[i].oldMainPos[1] + 1) + end + if instances[i].resizingLeft then + newX = instances[i].resizingLeft + evt[3] + 1 + newWidth = instances[i].oldMainPos[3] + (instances[i].oldMainPos[1] - newX) + end + if instances[i].resizingBottom then + newHeight = instances[i].resizingBottom + (evt[4] - instances[i].oldMainPos[2] + 1) + end + if instances[i].resizingTop then + newY = instances[i].resizingTop + evt[4] + 1 + newHeight = instances[i].oldMainPos[4] + (instances[i].oldMainPos[2] - newY) + end + moveInstance( + i, + newX, + newY, + newWidth, + newHeight + ) + end + end + end + if (instances[i].cFilter == evt[1] or instances[i].cFilter == "terminate" or (not instances[i].cFilter)) then + if (instances[i].focused or not FocusEvents[evt[1]]) then + resumeInstance(i, evt, CoordinateEvents[evt[1]]) + if CoordinateEvents[evt[1]] then + usedCoordinateEvent = true end end end end + instances[i].writeTitleBar() + end + end - if windont.doClearScreen then - screenBuffer[1][y][x] = screenBuffer[1][y][x] or " " - end - screenBuffer[2][y][x] = screenBuffer[2][y][x] or windont.default.backColor -- intentionally not the default text color - screenBuffer[3][y][x] = screenBuffer[3][y][x] or windont.default.backColor - - if checkTransparent(screenBuffer, x, y) then - if checkTransparent(screenBuffer, -1 + x, y) then - blitList[c][1] = blitList[c][1] .. screenBuffer[1][y][x] - blitList[c][2] = blitList[c][2] .. screenBuffer[2][y][x] - blitList[c][3] = blitList[c][3] .. screenBuffer[3][y][x] - else - c = x - blitList[c] = { - screenBuffer[1][y][x], - screenBuffer[2][y][x], - screenBuffer[3][y][x] - } + -- check for dead instances + for i = #instances, 1, -1 do + if not instances[i].alive then + if instances[i].focused then + if instances[i - 1] then + instances[i - 1].focused = true + elseif instances[i + 1] then + instances[i + 1].focused = true end end - end - if (not oldScreenBuffer[bT]) or (not windont.ignoreUnchangedLines) or (options.force) or ( - (not table_compare(screenBuffer[1][y], oldScreenBuffer[bT][1][y])) or - (not table_compare(screenBuffer[2][y], oldScreenBuffer[bT][2][y])) or - (not table_compare(screenBuffer[3][y], oldScreenBuffer[bT][3][y])) - ) then - for k,v in pairs(blitList) do - bT.setCursorPos(k, y) - bT.blit(v[1], v[2], v[3]) - AMNT_OF_BLITS = 1 + AMNT_OF_BLITS - end + table.remove(instances, i) + desktop.clear() end end - oldScreenBuffer[bT] = screenBuffer - if windont.useSetVisible and bT.setVisible then - if not multishell then - bT.setVisible(true) - elseif multishell.getFocus() == multishell.getCurrent() then - bT.setVisible(true) + + -- make sure the focused window is on top + for i = 1, #instances do + if instances[i].focused then + instances[i], instances[1] = instances[1], instances[i] + break end end end - - windont.info.LAST_RENDER_AMOUNT = #windows - windont.info.BLIT_CALLS = AMNT_OF_BLITS - windont.info.LAST_RENDER_WINDOWS = windows - windont.info.LAST_RENDER_TIME = cTime - windont.info.LAST_RENDER_DURATION = getTime() + -cTime - end --- creates a new windon't object that can be manipulated the same as a regular window - -windont.newWindow = function( x, y, width, height, misc ) - - -- check argument types - assert(type(x) == "number", "argument #1 must be number, got " .. type(x)) - assert(type(y) == "number", "argument #2 must be number, got " .. type(y)) - assert(type(width) == "number", "argument #3 must be number, got " .. type(width)) - assert(type(height) == "number", "argument #4 must be number, got " .. type(height)) - - -- check argument validity - assert(x > 0, "x position must be above zero") - assert(y > 0, "y position must be above zero") - assert(width > 0, "width must be above zero") - assert(height > 0, "height must be above zero") - - local output = {} - misc = misc or {} - local meta = { - x = expect(x, 1), -- x position of the window - y = expect(y, 1), -- y position of the window - width = width, -- width of the buffer - height = height, -- height of the buffer - buffer = expect(misc.buffer, {}, "table"), -- stores contents of terminal in buffer[1][y][x] format - renderBuddies = expect(misc.renderBuddies, {}, "table"), -- renders any other window objects stored here after rendering here - baseTerm = expect(misc.baseTerm, windont.default.baseTerm, "table"), -- base terminal for which this window draws on - isColor = expect(misc.isColor, term.isColor(), "boolean"), -- if true, then it's an advanced computer - - transformation = expect(misc.transformation, nil, "function"), -- function that transforms the char/text/back dots of the window - metaTransformation = expect(misc.miscTransformation, nil, "function"), -- function that transforms the whole output.meta function - - cursorX = expect(misc.cursorX, 1), - cursorY = expect(misc.cursorY, 1), - - textColor = expect(misc.textColor, windont.default.textColor, "string"), -- current text color - backColor = expect(misc.backColor, windont.default.backColor, "string"), -- current background color - - blink = expect(misc.blink, windont.default.blink, "boolean"), -- cursor blink - alwaysRender = expect(misc.alwaysRender, windont.default.alwaysRender, "boolean"), -- render after every terminal operation - visible = expect(misc.visible, windont.default.visible, "boolean"), -- if false, don't render ever - - -- make a new buffer (optionally uses an existing buffer as a reference) - newBuffer = function(width, height, char, text, back, drawAtop) - local output = {{}, {}, {}} - drawAtop = drawAtop or {{}, {}, {}} - for y = 1, height do - output[1][y] = output[1][y] or {} - output[2][y] = output[2][y] or {} - output[3][y] = output[3][y] or {} - for x = 1, width do - output[1][y][x] = (drawAtop[1][y] or {})[x] or (output[1][y][x] or (char or " ")) - output[2][y][x] = (drawAtop[2][y] or {})[x] or (output[2][y][x] or (text or "0")) - output[3][y][x] = (drawAtop[3][y] or {})[x] or (output[3][y][x] or (back or "f")) - end - end - return output - end - } - - bT = meta.baseTerm - - -- initialize the buffer - meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor) - - output.meta = meta - - output.write = function(text) - assert(type(text) == "string" or type(text) == "number", "expected string, got " .. type(text)) - local initX = meta.cursorX - for i = 1, #tostring(text) do - if meta.cursorX >= 1 and meta.cursorX <= meta.width and meta.cursorY >= 1 and meta.cursorY <= meta.height then - if not meta.buffer[1] then - error("what the fuck happened") - end - meta.buffer[1][meta.cursorY][meta.cursorX] = tostring(text):sub(i,i) - meta.buffer[2][meta.cursorY][meta.cursorX] = meta.textColor - meta.buffer[3][meta.cursorY][meta.cursorX] = meta.backColor - end - meta.cursorX = meta.cursorX + 1 - end - if meta.alwaysRender then - output.redraw( - -1 + meta.x + initX, - -1 + meta.x + meta.cursorX, - -1 + meta.y + meta.cursorY - ) - end - end - - output.blit = function(char, text, back) - assert(type(char) == "string" and type(text) == "string" and type(back) == "string", "all arguments must be strings") - assert(#char == #text and #text == #back, "arguments must be same length") - local initX = meta.cursorX - for i = 1, #char do - if meta.cursorX >= 1 and meta.cursorX <= meta.width and meta.cursorY >= 1 and meta.cursorY <= meta.height then - meta.buffer[1][meta.cursorY][meta.cursorX] = char:sub(i,i) - meta.buffer[2][meta.cursorY][meta.cursorX] = text:sub(i,i) == " " and windont.default.textColor or text:sub(i,i) - meta.buffer[3][meta.cursorY][meta.cursorX] = back:sub(i,i) == " " and windont.default.backColor or back:sub(i,i) - meta.cursorX = meta.cursorX + 1 - end - end - if meta.alwaysRender then - output.redraw( - -1 + meta.x + initX, - -1 + meta.x + meta.cursorX, - -1 + meta.y + meta.cursorY - ) - end - end - - output.setCursorPos = function(x, y) - assert(type(x) == "number", "argument #1 must be number, got " .. type(x)) - assert(type(y) == "number", "argument #2 must be number, got " .. type(y)) - meta.cursorX, meta.cursorY = math.floor(x), math.floor(y) - if meta.alwaysRender then - if bT == output then - bT = output.meta.baseTerm - end - bT.setCursorPos( - -1 + meta.x + meta.cursorX, - -1 + meta.y + meta.cursorY - ) - end - end - - output.getCursorPos = function() - return meta.cursorX, meta.cursorY - end - - output.setTextColor = function(color) - if to_blit[color] then - meta.textColor = to_blit[color] - else - error("Invalid color (got " .. color .. ")") - end - end - output.setTextColour = output.setTextColor - - output.setBackgroundColor = function(color) - if to_blit[color] then - meta.backColor = to_blit[color] - else - error("Invalid color (got " .. color .. ")") - end - end - output.setBackgroundColour = output.setBackgroundColor - - output.getTextColor = function() - return to_colors[meta.textColor] - end - output.getTextColour = output.getTextColor - - output.getBackgroundColor = function() - return to_colors[meta.backColor] - end - output.getBackgroundColour = output.getBackgroundColor - - output.setVisible = function(visible) - assert(type(visible) == "boolean", "bad argument #1 (expected boolean, got " .. type(visible) .. ")") - meta.visible = visible and true or false - end - - output.clear = function() - meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor) - if meta.alwaysRender then - output.redraw() - end - end - - output.clearLine = function() - meta.buffer[1][meta.cursorY] = nil - meta.buffer[2][meta.cursorY] = nil - meta.buffer[3][meta.cursorY] = nil - meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor, meta.buffer) - if meta.alwaysRender then - bT.setCursorPos(meta.x, -1 + meta.y + meta.cursorY) - bT.blit( - (" "):rep(meta.width), - (meta.textColor):rep(meta.width), - (meta.backColor):rep(meta.width) - ) - end - end - - output.getLine = function(y) - assert(type(y) == "number", "bad argument #1 (expected number, got " .. type(y) .. ")") - assert(meta.buffer[1][y], "Line is out of range.") - return table_concat(meta.buffer[1][y]), table_concat(meta.buffer[2][y]), table_concat(meta.buffer[3][y]) - end - - output.scroll = function(amplitude) - if math.abs(amplitude) < meta.height then -- minor optimization - local blank = {{}, {}, {}} - for x = 1, meta.width do - blank[1][x] = " " - blank[2][x] = meta.textColor - blank[3][x] = meta.backColor - end - for y = 1, meta.height do - meta.buffer[1][y] = meta.buffer[1][y + amplitude] or blank[1] - meta.buffer[2][y] = meta.buffer[2][y + amplitude] or blank[2] - meta.buffer[3][y] = meta.buffer[3][y + amplitude] or blank[3] - end - else - meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor) - end - if meta.alwaysRender then - if math_floor(amplitude) ~= 0 then - output.redraw() - end - end - end - - output.getSize = function() - return meta.width, meta.height - end - - output.isColor = function() - return meta.isColor - end - output.isColour = output.isColor - - output.reposition = function(x, y, width, height) - assert(type(x) == "number", "bad argument #1 (expected number, got " .. type(x) .. ")") - assert(type(y) == "number", "bad argument #2 (expected number, got " .. type(y) .. ")") - meta.x = math_floor(x) - meta.y = math_floor(y) - if width then - assert(type(width) == "number", "bad argument #3 (expected number, got " .. type(width) .. ")") - assert(type(height) == "number", "bad argument #4 (expected number, got " .. type(height) .. ")") - meta.width = width - meta.height = height - meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor, meta.buffer) - end - if meta.alwaysRender then - output.redraw() - end - end - - output.restoreCursor = function() - bT.setCursorPos( - math.max(0, -1 + meta.x + meta.cursorX), - math.max(0, -1 + meta.y + meta.cursorY) - ) - bT.setCursorBlink(meta.blink) - end - - output.getPosition = function() - return meta.x, meta.y - end - - output.setCursorBlink = function(blink) - meta.blink = blink and true or false - end - - output.getCursorBlink = function(blink) - return meta.blink - end - - output.setPaletteColor = bT.setPaletteColor - output.setPaletteColour = bT.setPaletteColour - output.getPaletteColor = bT.getPaletteColor - output.getPaletteColour = bT.getPaletteColour - - if bT.getPaletteColor then - output.nativePaletteColor = bT.nativePaletteColor or function(col) - if nativePalette[col] then - return table.unpack(nativePalette[col]) - else - return table.unpack(nativePalette[1]) -- I don't get how this function takes in non-base2 numbers... - end - end - end - - output.redraw = function(x1, x2, y, options) - options = options or {} - options.onlyX1 = x1 - options.onlyX2 = x2 - options.onlyY = y - if #meta.renderBuddies > 0 then - windont.render(options, output, table.unpack(meta.renderBuddies)) - else - windont.render(options, output) - end - output.restoreCursor() - end - - if meta.alwaysRender then - output.redraw() - end - - return output - -end - -return windont +main()