From acf66baf57218ae9461430aeec86f0534eb7e328 Mon Sep 17 00:00:00 2001 From: Bommels05 <69975756+Bommels05@users.noreply.github.com> Date: Tue, 21 Oct 2025 00:40:14 +0200 Subject: [PATCH] Upload System Image --- LevelOS/Changelog.lua | 221 ++ LevelOS/Clock.lua | 897 +++++ LevelOS/Global_Login.lua | 2063 +++++++++++ LevelOS/Login_screen.sgui | 1734 +++++++++ LevelOS/Notification.lua | 124 + LevelOS/Notifications.lua | 725 ++++ LevelOS/Start_Menu.sgui | 327 ++ LevelOS/SystemUI.lua | 1970 ++++++++++ LevelOS/Task_Manager.lua | 451 +++ LevelOS/assets/Circle_Symbols.limg | 37 + LevelOS/assets/Compact_Icons.limg | 110 + LevelOS/assets/Desktop_Icons.limg | 1 + LevelOS/assets/QR_Code.limg | 1 + LevelOS/assets/circProgress.limg | 172 + LevelOS/assets/loading.limg | 86 +- LevelOS/assets/logo_christmas.limg | 40 + LevelOS/assets/logo_pride.limg | 40 + LevelOS/assets/wifi.limg | 71 + LevelOS/data/changelog.lconf | 324 ++ LevelOS/data/settings.lconf | 3 + LevelOS/data/version.txt | 1 + LevelOS/desktop.lua | 1608 ++++++++ LevelOS/imageviewer.lua | 85 + LevelOS/lStore.lua | 258 ++ LevelOS/login.lua | 396 ++ LevelOS/modules/lex.lua | 713 ++++ LevelOS/modules/shapescape/animation.lua | 0 LevelOS/modules/shapescape/class.lua | 147 + LevelOS/modules/shapescape/init.lua | 2228 +++++++++++ LevelOS/modules/shapescape/input.lua | 1506 ++++++++ LevelOS/modules/shapescape/physics.lua | 0 LevelOS/modules/shapescape/transition.lua | 0 LevelOS/settings.lua | 121 + LevelOS/startup/MwMRender | 176 + LevelOS/startup/lUtils.lua | 24 +- LevelOS/system.lua | 1505 ++++++++ Program_Files/Common_Files/oop.lua | 648 ++++ Program_Files/LevelOS/CMD/latch.lua | 723 ++++ Program_Files/LevelOS/Cloud/icon.limg | 1 + Program_Files/LevelOS/Cloud/main.lua | 1493 ++++++++ Program_Files/LevelOS/Cloud/taskbar.limg | 1 + Program_Files/LevelOS/Explorer/icon.limg | 1 + Program_Files/LevelOS/Explorer/main.lua | 3685 +++++++++++++++++++ Program_Files/LevelOS/Explorer/taskbar.limg | 1 + Program_Files/LevelOS/Lua/icon.limg | 1 + Program_Files/LevelOS/Lua/main.lua | 172 + Program_Files/LevelOS/Lua/taskbar.limg | 1 + Program_Files/LevelOS/Notepad/icon.limg | 1 + Program_Files/LevelOS/Notepad/main.lua | 273 ++ Program_Files/LevelOS/Notepad/taskbar.limg | 1 + Program_Files/LevelOS/Store/icon.limg | 1 + Program_Files/LevelOS/Store/main.lua | 1538 ++++++++ Program_Files/LevelOS/Store/taskbar.limg | 1 + bigfont | 12 +- blittle | 743 ++++ real_startup.lua | 847 +++++ 56 files changed, 28248 insertions(+), 61 deletions(-) create mode 100644 LevelOS/Changelog.lua create mode 100644 LevelOS/Clock.lua create mode 100644 LevelOS/Global_Login.lua create mode 100644 LevelOS/Login_screen.sgui create mode 100644 LevelOS/Notification.lua create mode 100644 LevelOS/Notifications.lua create mode 100644 LevelOS/Start_Menu.sgui create mode 100644 LevelOS/SystemUI.lua create mode 100644 LevelOS/Task_Manager.lua create mode 100644 LevelOS/assets/Circle_Symbols.limg create mode 100644 LevelOS/assets/Compact_Icons.limg create mode 100644 LevelOS/assets/Desktop_Icons.limg create mode 100644 LevelOS/assets/QR_Code.limg create mode 100644 LevelOS/assets/circProgress.limg create mode 100644 LevelOS/assets/logo_christmas.limg create mode 100644 LevelOS/assets/logo_pride.limg create mode 100644 LevelOS/assets/wifi.limg create mode 100644 LevelOS/data/changelog.lconf create mode 100644 LevelOS/data/settings.lconf create mode 100644 LevelOS/data/version.txt create mode 100644 LevelOS/desktop.lua create mode 100644 LevelOS/imageviewer.lua create mode 100644 LevelOS/lStore.lua create mode 100644 LevelOS/login.lua create mode 100644 LevelOS/modules/lex.lua create mode 100644 LevelOS/modules/shapescape/animation.lua create mode 100644 LevelOS/modules/shapescape/class.lua create mode 100644 LevelOS/modules/shapescape/init.lua create mode 100644 LevelOS/modules/shapescape/input.lua create mode 100644 LevelOS/modules/shapescape/physics.lua create mode 100644 LevelOS/modules/shapescape/transition.lua create mode 100644 LevelOS/settings.lua create mode 100644 LevelOS/startup/MwMRender create mode 100644 LevelOS/system.lua create mode 100644 Program_Files/Common_Files/oop.lua create mode 100644 Program_Files/LevelOS/CMD/latch.lua create mode 100644 Program_Files/LevelOS/Cloud/icon.limg create mode 100644 Program_Files/LevelOS/Cloud/main.lua create mode 100644 Program_Files/LevelOS/Cloud/taskbar.limg create mode 100644 Program_Files/LevelOS/Explorer/icon.limg create mode 100644 Program_Files/LevelOS/Explorer/main.lua create mode 100644 Program_Files/LevelOS/Explorer/taskbar.limg create mode 100644 Program_Files/LevelOS/Lua/icon.limg create mode 100644 Program_Files/LevelOS/Lua/main.lua create mode 100644 Program_Files/LevelOS/Lua/taskbar.limg create mode 100644 Program_Files/LevelOS/Notepad/icon.limg create mode 100644 Program_Files/LevelOS/Notepad/main.lua create mode 100644 Program_Files/LevelOS/Notepad/taskbar.limg create mode 100644 Program_Files/LevelOS/Store/icon.limg create mode 100644 Program_Files/LevelOS/Store/main.lua create mode 100644 Program_Files/LevelOS/Store/taskbar.limg create mode 100644 blittle create mode 100644 real_startup.lua diff --git a/LevelOS/Changelog.lua b/LevelOS/Changelog.lua new file mode 100644 index 0000000..28b8136 --- /dev/null +++ b/LevelOS/Changelog.lua @@ -0,0 +1,221 @@ +if LevelOS and LevelOS.self and LevelOS.self.window then + LevelOS.self.window.winMode = "widget" +end +local oldlog = lUtils.asset.load("LevelOS/data/nativelog.lconf") or {} +local newlog = lUtils.asset.load("LevelOS/data/changelog.lconf") +if newlog then + fs.delete("LevelOS/data/nativelog.lconf") + fs.move("LevelOS/data/changelog.lconf","LevelOS/data/nativelog.lconf") +elseif oldlog then + newlog = oldlog +end +local run = false +for c=1,#newlog do + if not oldlog[c] then + newlog[c].new = true + run = true + end +end +local txt = term.setTextColor +local bg = term.setBackgroundColor +local function clr() + local w,h = term.getSize() + local x,y = term.getCursorPos() + term.setCursorPos(19,y) + term.write(string.rep(" ",w-21)) + term.setCursorPos(x,y) +end +local function wr(text,color,mX,mY) --max x and y + if color then + txt(color) + end + local x,y = term.getCursorPos() + local cX = x + local cY = y + for word in text:gmatch("%S+") do + --[[if cX ~= x then + word = " "..word + end]] + if cX+#word <= mX then + term.write(word.." ") + cX = cX+#word+1 + elseif cY < mY then + cX = x + cY = cY+1 + term.setCursorPos(cX,cY) + clr() + term.write(word.." ") + cX = cX+#word+1 + else + return (cY-y)+1 + end + end + return (cY-y)+1 +end +local function rm() -- reached max + local x,y = term.getCursorPos() + local w,h = term.getSize() + if y >= h then + return true + else + return false + end +end +function render(s) + local function cp(x,y) + term.setCursorPos(x,y+s) + end + bg(colors.cyan) + term.clear() + txt(colors.cyan) + bg(colors.white) + local w,h = term.getSize() + for t=0,h-s do + cp(14,4+t) + term.write("\149") + end + -- write with window height as max Y and width-4 as max X + local cY = 1 + for k=#newlog,1,-1 do + local v = newlog[k] + cY = cY+2 + + -- the date + cp(2,cY+1) + bg(colors.cyan) + txt(colors.white) + term.write(v.date) + cp(13,cY) + + -- cyan = 9, white = 0, lime=5 + -- the dot + if k == #newlog then -- to check if its on top (for the line) + term.blit("\159\143\143","999","000") + else + term.blit("\159\133\143","999","000") + end + cp(13,cY+1) + if v.major then -- if major version make orange dot + term.blit("\149 \149","911","010") + else + term.blit("\149 \149","955","050") + end + cp(13,cY+2) + term.blit("\130\148\131","090","909") + + -- the actual box + bg(colors.white) + txt(colors.cyan) + cp(19,cY) + clr() + --term.write("\151\129") + term.write("\129") + cp(w-3,cY) + term.write("\130") + --term.blit("\148","0","9") + --[[cY = cY+1 + cp(19,cY) + clr()]] + cY = cY+1 + cp(18,cY) + clr() + term.write("\145 ") + term.write("v"..v.version) + if v.new then + txt(colors.orange) + term.write(" New!") + end + cY = cY+1 + cp(19,cY) + clr() + cY = cY+1 + cp(20,cY) + clr() + if v.description then + cY = cY-1 + cp(21,cY) + bg(colors.white) + txt(colors.black) + cY = cY+wr(v.description,nil,w-4,h) + cp(20,cY) + clr() + cY = cY+1 + cp(20,cY) + clr() + end + if v.added and #v.added > 0 then + bg(colors.lime) + txt(colors.white) + term.write(" NEW ") + cY = cY+1 + bg(colors.white) + cp(19,cY) + clr() + cY = cY+1 + txt(colors.black) + for t=1,#v.added do + cp(21,cY) + clr() + term.write("\7 ") + cY = cY+wr(v.added[t],nil,w-4,h) + end + cp(20,cY) + clr() + end + if v.fixed and #v.fixed > 0 then + if v.added and #v.added > 0 then + cY = cY+1 + cp(20,cY) + clr() + end + bg(colors.lightBlue) + txt(colors.white) + term.write(" FIXED ") + cY = cY+1 + bg(colors.white) + cp(19,cY) + clr() + cY = cY+1 + txt(colors.black) + for t=1,#v.fixed do + cp(21,cY) + clr() + term.write("\7 ") + cY = cY+wr(v.fixed[t],nil,w-4,h) + end + end + bg(colors.white) + txt(colors.cyan) + cp(19,cY) + clr() + cY = cY+1 + cp(19,cY) + clr() + term.write("\144") + cp(w-3,cY) + term.blit("\159","0","9") + if rm() then break end + end + term.setCursorPos(1,1) + bg(colors.cyan) + txt(colors.white) + term.clearLine() + term.setCursorPos(2,1) + term.write("Changelog") + term.setCursorPos(w-2,1) + term.write("×") +end +render(0) +local scroll = 0 +while true do + local e = {os.pullEvent()} + local w,h = term.getSize() + if e[1] == "term_resize" then + render(scroll) + elseif e[1] == "mouse_scroll" and scroll-e[2] <= 0 then + scroll = scroll-e[2] + render(scroll) + elseif e[1] == "mouse_up" and e[3] == w-2 and e[4] == 1 then + return + end +end \ No newline at end of file diff --git a/LevelOS/Clock.lua b/LevelOS/Clock.lua new file mode 100644 index 0000000..3ed543f --- /dev/null +++ b/LevelOS/Clock.lua @@ -0,0 +1,897 @@ +local assets = { + [ "slider.lua" ] = { + id = 1, + content = "local s = shapescape.getSlide()\ +if not s.var then s.var = {} end\ +\ +local set = \"LevelOS/data/settings.lconf\"\ +\ +if lOS.settings.rTime == nil then\ + lOS.settings.rTime = false\ +end\ +\ +local r = string.rep\ +\ +local rtime = lOS.settings.rTime\ +\ +local w,h = term.getSize()\ +\ +if rtime then\ + term.setBackgroundColor(colors.green)\ + term.setCursorPos(1,1)\ + term.write(r(\" \",w-1))\ + term.setBackgroundColor(colors.white)\ + term.write(\" \")\ +else\ + term.setCursorPos(1,1)\ + term.setBackgroundColor(colors.white)\ + term.write(\" \")\ + term.setBackgroundColor(colors.red)\ + term.write(r(\" \",w-1))\ +end\ +\ +while true do\ + local w,h = term.getSize()\ + if lOS.settings.rTime ~= rtime then\ + rtime = lOS.settings.rTime\ + local b1,b2,fg\ + b1 = \"d\"\ + b2 = \"e\"\ + fg = \"0\"\ + local a,b,c\ + if rtime then\ + a,b,c = 1,w,1\ + else\ + a,b,c = w,1,-1\ + end\ + for x=a,b,c do\ + local d,e = x-1,w-x\ + term.setCursorPos(1,1)\ + term.blit(r(\" \",d)..\" \"..r(\" \",e),r(\"f\",w),r(b1,d)..fg..r(b2,e))\ + os.sleep(0.05)\ + end\ + end\ + e = {os.pullEvent()}\ +end", + name = "slider.lua", + }, + [ "switch_time.lua" ] = { + id = 2, + content = "lOS.settings.rTime = not lOS.settings.rTime", + name = "switch_time.lua", + }, + [ "rendercalender.lua" ] = { + id = 7, + content = "local w,h = term.getSize()\ +while h < 21 do\ + os.pullEvent()\ + w,h = term.getSize()\ +end\ +local epoch = os.epoch(\"local\")/1000\ +local date = os.date(\"*t\",epoch)\ +local targetmonth = date.month\ +local targetyear = date.year\ +local d = {}\ +d.sec = 1\ +d.min = d.sec*60\ +d.hour = d.min*60\ +d.day = d.hour*24\ +local function dayadd()\ + epoch = epoch+d.day\ + date = os.date(\"*t\",epoch)\ +end\ +local function daysub()\ + epoch = epoch-d.day\ + date = os.date(\"*t\",epoch)\ +end\ +while date.year < targetyear or date.month < targetmonth do\ + dayadd()\ +end\ +term.setBackgroundColor(colors.gray)\ +term.setTextColor(colors.white)\ +term.clear()\ +term.setCursorPos(1,1)\ +term.write(os.date(\"%B %Y\"))\ +term.setCursorPos(1,3)\ +days = {\"Mo\",\"Tu\",\"We\",\"Th\",\"Fr\",\"Sa\",\"Su\"}\ +for t=1,7 do\ + term.write(\" \"..days[t]..\" \")\ +end\ +local x = term.getCursorPos()\ +term.setCursorPos(x-3,1)\ +--LevelOS.setWin(x-1,24)\ +term.write(\"\\30 \\31\")\ +while date.day > 1 do\ + daysub()\ +end\ +while date.wday ~= 2 do\ + daysub()\ +end\ +local current = os.date(\"*t\")\ +for w=1,6 do\ + --term.setCursorPos(1,5+(w-1)*3)\ + for t=1,7 do\ + local x,y = 1+(t-1)*4,5+(w-1)*3\ + local cur = false\ + if current.day == date.day and current.month == date.month and current.year == date.year then\ + term.setTextColor(colors.black)\ + term.setBackgroundColor(colors.lightGray)\ + lUtils.border(x,y-1,x+3,y+1)\ + cur = true\ + else\ + term.setBackgroundColor(colors.gray)\ + end\ + if targetmonth == date.month then\ + term.setTextColor(colors.white)\ + elseif cur then\ + term.setTextColor(colors.gray)\ + else\ + term.setTextColor(colors.lightGray)\ + end\ + term.setCursorPos(x+1,y)\ + term.write(os.date(\"%d\",epoch))\ + dayadd()\ + end\ +end\ +while true do os.pullEvent() end", + name = "rendercalender.lua", + }, + [ "offset.lua" ] = { + id = 3, + content = "if lOS.settings.timeOffset then\ + local o = lOS.settings.timeOffset\ + if o >= 0 then\ + self.txt = \"+\"..o\ + else\ + self.txt = tostring(o)\ + end\ +end", + name = "offset.lua", + }, + [ "offset_up.lua" ] = { + id = 5, + content = "lOS.settings.timeOffset = lOS.settings.timeOffset + 0.5", + name = "offset_up.lua", + }, + [ "time.lua" ] = { + id = 0, + content = "if not lOS.settings.timeOffset then\ + lOS.settings.timeOffset = 0\ +end\ +local t = os.date(\"*t\",os.epoch(\"utc\")/1000+lOS.settings.timeOffset*3600)\ +term.setBackgroundColor(colors.gray)\ +term.setTextColor(colors.white)\ +term.setCursorPos(self.x1,self.y1)\ +local function tz(n)\ + return string.rep(\"0\",2-string.len(n))..n\ +end\ +if lOS.settings.rTime then\ + bigfont.bigPrint(tz(t.hour)..\":\"..tz(t.min)..\":\"..tz(t.sec))\ +else\ + local nTime = (os.time()+lOS.settings.timeOffset)%24\ + local nHour = math.floor(nTime)\ + local nMinute = math.floor((nTime - nHour) * 60)\ + bigfont.bigPrint(tz(nHour)..\":\"..tz(nMinute))\ +end\ +term.setTextColor(colors.lightGray)\ +local days = {\"Sunday\",\"Monday\",\"Tuesday\",\"Wednesday\",\"Thursday\",\"Friday\",\"Saturday\"}\ +local months = {\"Jan\",\"Feb\",\"Mar\",\"Apr\",\"May\",\"Jun\",\"Jul\",\"Aug\",\"Sep\",\"Oct\",\"Nov\",\"Dec\"}\ +term.setCursorPos(self.x1,({term.getCursorPos()})[2])\ +print(days[t.wday]..\", \"..months[t.month]..\" \"..t.day..\", \"..t.year)", + name = "time.lua", + }, + [ "init.lua" ] = { + id = 4, + content = "local w,h = 27,13\ +local tw,th = lOS.wAll.getSize()\ +if th >= 40 then\ + w,h = 32,36\ +end\ +LevelOS.setWin(w,h,\"widget\")\ +LevelOS.self.window.win.reposition(tw-w,th-(h-1+lOS.tbSize))", + name = "init.lua", + }, + [ "offset_down.lua" ] = { + id = 6, + content = "lOS.settings.timeOffset = lOS.settings.timeOffset - 0.5", + name = "offset_down.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 61, + h = 19, + w = 51, + objs = { + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + }, + ox2 = 0, + color = 128, + border = { + color = 256, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + x2 = 26, + y2 = 4, + border = { + color = 128, + type = 1, + }, + x1 = 3, + txt = "", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 0, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + color = 128, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 2, + }, + { + x2 = 50, + y2 = 8, + y1 = 7, + x1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + color = 128, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 256, + type = 1, + }, + }, + { + x2 = 50, + y2 = 8, + y1 = 8, + x1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + color = 128, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + type = "rect", + color = 128, + y2 = 7, + y1 = 7, + x1 = 2, + x2 = 2, + border = { + color = 0, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + }, + { + x2 = 50, + y2 = 7, + y1 = 7, + x1 = 50, + ox1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + color = 128, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 128, + y2 = 8, + y1 = 8, + x1 = 11, + txt = "Use real time", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 23, + border = { + color = 0, + type = 1, + }, + }, + { + color = 1, + y2 = 12, + y1 = 10, + x1 = 3, + txt = "+0", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 3, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + x2 = 9, + border = { + color = 256, + type = 1, + }, + }, + { + x2 = 9, + y2 = 10, + y1 = 10, + x1 = 3, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = 5, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 256, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 256, + y2 = 12, + y1 = 12, + x1 = 3, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = 6, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 9, + border = { + color = 0, + type = 1, + }, + }, + { + color = 128, + y2 = 11, + y1 = 11, + x1 = 11, + txt = "Offset (hours)", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 26, + border = { + color = 0, + type = 1, + }, + }, + { + type = "window", + color = 32768, + y2 = 8, + border = { + color = 0, + type = 1, + }, + x1 = 3, + y1 = 8, + x2 = 9, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = 2, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 1, + }, + }, + }, + { + color = 128, + y2 = 15, + border = { + color = 256, + type = 1, + }, + x1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + x2 = 50, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 14, + }, + { + type = "rect", + x2 = 2, + y2 = 14, + border = { + color = 0, + type = 1, + }, + x1 = 2, + color = 128, + y1 = 14, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + }, + { + color = 128, + y2 = 14, + border = { + color = 0, + type = 1, + }, + x1 = 50, + ox1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + x2 = 50, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 14, + }, + { + color = 128, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + x2 = 50, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 15, + }, + { + x2 = 49, + y2 = 18, + border = { + color = 0, + type = 1, + }, + x1 = 3, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 7, + }, + }, + ox2 = 2, + color = 32768, + y1 = 15, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 1, + }, + }, + c = 1, + }, + { + y = 21, + x = 61, + h = 19, + w = 51, + objs = {}, + c = 2, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end + end + + local tArgs = {...} + if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} + end + + + lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/LevelOS/Global_Login.lua b/LevelOS/Global_Login.lua new file mode 100644 index 0000000..4b3ab2c --- /dev/null +++ b/LevelOS/Global_Login.lua @@ -0,0 +1,2063 @@ +local assets = { + [ "init.lua" ] = { + id = 9, + content = "local gW,gH = 44,33\ +local tW,tH = lOS.wAll.getSize()\ +if tW-4 <= gW or tH-2 <= gH then\ + LevelOS.self.window.winMode = \"fullscreen\"\ +else\ + local x,y = math.ceil(tW/2)-math.floor(gW/2),math.ceil(tH/2)-math.floor(gH/2)\ + LevelOS.self.window.win.reposition(x,y,gW,gH)\ + LevelOS.setTitle(\"Leveloper Account\")\ + LevelOS.self.window.resizable = false\ +end", + name = "init.lua", + }, + [ "rememberme.lua" ] = { + id = 10, + content = "local sl = shapescape.getSlides()\ +if sl.rememberme then\ + self.txt = \" \"\ + sl.rememberme = false\ +else\ + self.txt = \" × \"\ + sl.rememberme = true\ +end", + name = "rememberme.lua", + }, + [ "box.lua" ] = { + id = 1, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +--[[if s == sl[1] and not sl.usernameBox then\ + sl.usernameBox = self\ +elseif s == sl[3] then\ + self = sl.usernameBox\ +end]]\ +s.box = self\ +if not self.init then\ + self.state = true\ + self.init = true\ +end\ +if self.errorTxt and self.txt ~= self.errorTxt then\ + s.error = nil\ + self.errorTxt = nil\ +end\ +if s.error and #s.error > 0 then\ + self.border.color = colors.red\ + if not self.errorTxt then\ + self.errorTxt = self.txt\ + end\ +elseif self.state then\ + self.border.color = colors.blue\ +else\ + self.border.color = colors.lightGray\ +end\ +if self.txt == \"\" then\ + term.setCursorPos(self.x1+1,self.y1+1)\ + term.setBackgroundColor(self.color)\ + term.setTextColor(colors.lightGray)\ + if s == sl[1] or s == sl[3] then\ + term.write(\"Username\")\ + elseif s == sl[2] or s == sl[4] then\ + term.write(\"Password\")\ + end\ + term.setCursorPos(self.x1+1,self.y1+1)\ + term.setTextColor(self.txtcolor)\ +end\ +if s == sl[1] or s == sl[3] then\ + --sl.username = self.txt\ + if sl.username == self.txt then\ + self.opt.replaceChar = nil\ + end\ + sl.username = self.txt\ +elseif s == sl[2] or s == sl[4] then\ + --sl.password = self.txt\ + if sl.password == self.txt then\ + self.opt.replaceChar = \"\\7\"\ + end\ + sl.password = self.txt\ +end", + name = "box.lua", + }, + [ "makebig.lua" ] = { + id = 0, + content = "term.setBackgroundColor(self.color)\ +term.setTextColor(self.txtcolor)\ +term.setCursorPos(self.x1,self.y1)\ +bigfont.bigWrite(self.txt)", + name = "makebig.lua", + }, + [ "Create_one.lua" ] = { + id = 8, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +while true do\ + local e = {os.pullEvent()}\ + if (e[1] == \"mouse_click\" or e[1] == \"mouse_up\") and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then\ + if e[1] == \"mouse_click\" then\ + self.txtcolor = colors.lightBlue\ + elseif e[1] == \"mouse_up\" and self.txtcolor == colors.lightBlue then\ + self.txtcolor = colors.blue\ + if s == sl[1] then\ + shapescape.setSlide(3)\ + elseif s == sl[3] then\ + shapescape.setSlide(1)\ + end\ + end\ + end\ + if e[1] == \"mouse_up\" then\ + self.txtcolor = colors.blue\ + end\ +end", + name = "Create_one.lua", + }, + [ "userback.lua" ] = { + id = 4, + content = "local sl = shapescape.getSlides()\ +self.txt = \"\\27 \"..sl.username\ +while true do\ + local e = {os.pullEvent()}\ + self.txt = \"\\27 \"..sl.username\ + if (e[1] == \"mouse_click\" or e[1] == \"mouse_up\") and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then\ + if e[1] == \"mouse_click\" then\ + self.txtcolor = colors.gray\ + elseif e[1] == \"mouse_up\" and self.txtcolor == colors.gray then\ + self.txtcolor = colors.black\ + if shapescape.getSlide() == sl[2] then\ + shapescape.setSlide(1)\ + else\ + shapescape.setSlide(3)\ + end\ + end\ + end\ + if e[1] == \"mouse_up\" then\ + self.txtcolor = colors.black\ + end\ +end", + name = "userback.lua", + }, + [ "Next.lua" ] = { + id = 2, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +if fs.exists(\"LevelOS/data/account.txt\") and not sl.putusername then\ + sl.putusername = true\ + os.sleep(0.05)\ + s.box.txt = lUtils.fread(\"LevelOS/data/account.txt\")\ + sl.username = s.box.txt\ + shapescape.setSlide(2)\ +end\ +while true do\ + local e = {os.pullEvent()}\ + if sl.username and sl.username ~= \"\" then\ + if self.color == colors.lightGray then\ + self.color = colors.blue\ + end\ + if ((e[1] == \"mouse_click\" or e[1] == \"mouse_up\") and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2) or ((e[1] == \"key\" or e[1] == \"key_up\") and e[2] == keys.enter) then\ + if e[1] == \"mouse_click\" or e[1] == \"key\" then\ + self.color = colors.lightBlue\ + elseif e[1] == \"mouse_up\" or e[1] == \"key_up\" then\ + s.box.state = false\ + if shapescape.getSlide() == sl[1] then\ + local ok,err = lUtils.login(sl.username,\"\")\ + if err and err:find(\"password\") then\ + shapescape.setSlide(2)\ + else\ + s.error = err or \"Unknown error!\"\ + end\ + elseif shapescape.getSlide() == sl[3] then\ + local res,err = http.post(\"https://old.leveloper.cc/register.php\",\"username=\" .. textutils.urlEncode(sl.username)..\"&password=\"..textutils.urlEncode(\"a\"))\ + if not res then\ + s.error = err or \"Unknown error!\"\ + else\ + local msg = res.readAll()\ + if msg:find(\"Password\") then\ + shapescape.setSlide(4)\ + else\ + s.error = msg\ + end\ + end\ + end\ + end\ + end\ + if (e[1] == \"key_up\" and e[2] == keys.enter) or e[1] == \"mouse_up\" then\ + self.color = colors.blue\ + end\ + else\ + self.color = colors.lightGray\ + end\ +end", + name = "Next.lua", + }, + [ "showError.lua" ] = { + id = 6, + content = "local s = shapescape.getSlide()\ +if not s.error then\ + self.txtcolor = colors.white\ +else\ + self.txtcolor = colors.red\ + self.txt = s.error\ +end", + name = "showError.lua", + }, + [ "Sign_in.lua" ] = { + id = 3, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +while true do\ + local e = {os.pullEvent()}\ + if sl.password and sl.password ~= \"\" then\ + if self.color == colors.lightGray then\ + self.color = colors.blue\ + end\ + if ((e[1] == \"mouse_click\" or e[1] == \"mouse_up\") and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2) or ((e[1] == \"key\" or e[1] == \"key_up\") and e[2] == keys.enter) then\ + s.box.state = false\ + if e[1] == \"mouse_click\" or e[1] == \"key\" then\ + self.color = colors.lightBlue\ + elseif e[1] == \"mouse_up\" or e[1] == \"key_up\" then\ + if shapescape.getSlide() == sl[2] then\ + local userID,msg = lUtils.login(sl.username,sl.password,false,sl.rememberme)\ + if userID then\ + lOS.userID = userID\ + lOS.username = sl.username\ + shapescape.exit()\ + else\ + s.error = msg or \"Unknown error\"\ + end\ + else\ + local res,err = http.post(\"https://old.leveloper.cc/register.php\",\"username=\"..textutils.urlEncode(sl.username)..\"&password=\"..textutils.urlEncode(sl.password))\ + if not res then\ + s.error = err or \"Unknown error\"\ + else\ + local msg = res.readAll()\ + if msg == \"200\" then\ + local userID,msg = lUtils.login(sl.username,sl.password,false,sl.rememberme)\ + lOS.userID = userID\ + lOS.username = sl.username\ + shapescape.exit()\ + else\ + s.error = msg\ + end\ + end\ + end\ + end\ + end\ + if (e[1] == \"key_up\" and e[2] == keys.enter) or e[1] == \"mouse_up\" then\ + self.color = colors.blue\ + end\ + else\ + self.color = colors.lightGray\ + end\ +end", + name = "Sign_in.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + color = 1, + y2 = 2, + y1 = 2, + x1 = 5, + txt = "Leveloper", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 9, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 128, + input = false, + x2 = 13, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 13, + y2 = 4, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Sign in", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 0, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 1, + y1 = 4, + }, + { + lines = { + "", + }, + x2 = 48, + changed = false, + y1 = 7, + x1 = 4, + scrollX = 0, + ox2 = 3, + history = {}, + border = { + color = 16384, + type = 1, + }, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 32768, + indentChar = " ", + tabSize = 4, + minWidth = 45, + overflow = "scroll", + minHeight = 3, + }, + blit = {}, + rhistory = {}, + cursor = { + y = 1, + x = 1, + a = 1, + }, + y2 = 9, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 1, + }, + update = { + [ 2 ] = 1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + state = false, + dLines = { + "", + }, + type = "input", + scr = 0, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + color = 1, + }, + { + type = "rect", + x2 = 4, + y2 = 9, + y1 = 7, + x1 = 1, + border = { + color = 0, + type = 1, + }, + color = 1, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + }, + { + color = 1, + y2 = 9, + y1 = 7, + x1 = 48, + ox1 = 3, + type = "rect", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 1, + y2 = 7, + border = { + color = 0, + type = 1, + }, + x1 = 4, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 3, + x2 = 48, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 7, + }, + { + x2 = 15, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "No account?", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + y1 = 11, + }, + { + x2 = 26, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 17, + txt = "Create one", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 8, + }, + }, + txtcolor = 2048, + input = false, + color = 0, + y1 = 11, + }, + { + color = 256, + y2 = 16, + x1 = 38, + ox1 = 13, + txt = " Next", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + [ " update" ] = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 4, + txtcolor = 1, + border = { + color = 1, + type = 1, + }, + input = false, + x2 = 47, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 14, + }, + { + color = 0, + y2 = 15, + y1 = 13, + x1 = 5, + txt = "Loading...", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + }, + txtcolor = 16384, + input = false, + x2 = 36, + border = { + color = 0, + type = 1, + }, + }, + }, + c = 1, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + x2 = 13, + y2 = 2, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Leveloper", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 128, + input = false, + color = 1, + y1 = 2, + }, + { + x2 = 18, + y2 = 6, + y1 = 6, + x1 = 5, + txt = "Password", + type = "text", + event = { + render = { + [ 2 ] = 0, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 1, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 4, + y1 = 4, + x1 = 5, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + x2 = 42, + border = { + color = 0, + type = 1, + }, + }, + { + lines = { + "", + }, + x2 = 48, + changed = false, + cursor = { + y = 1, + x = 1, + a = 1, + }, + x1 = 4, + scrollX = 0, + ox2 = 3, + history = {}, + border = { + color = 16384, + type = 1, + }, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 32768, + indentChar = " ", + tabSize = 4, + minWidth = 45, + overflow = "scroll", + minHeight = 3, + }, + blit = {}, + rhistory = {}, + y1 = 9, + y2 = 11, + event = { + render = { + [ 2 ] = 1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + state = false, + dLines = { + "", + }, + type = "input", + scr = 0, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + color = 1, + }, + { + type = "rect", + color = 1, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 1, + x2 = 4, + y1 = 9, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + }, + { + x2 = 51, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 48, + ox1 = 3, + type = "rect", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 9, + }, + { + x2 = 48, + y2 = 9, + y1 = 9, + x1 = 4, + type = "rect", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 3, + color = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 47, + y2 = 16, + x1 = 37, + ox1 = 14, + txt = " Sign in", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 3, + }, + [ " update" ] = { + [ 2 ] = -1, + }, + }, + ox2 = 4, + txtcolor = 1, + y1 = 14, + input = false, + color = 256, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 1, + type = 1, + }, + }, + { + x2 = 35, + y2 = 14, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "No connection", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + }, + ox2 = 16, + txtcolor = 16384, + input = false, + color = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 13, + }, + { + x2 = 19, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 9, + txt = "Remember me", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + y1 = 15, + }, + { + x2 = 7, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = 10, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 256, + y1 = 15, + }, + }, + c = 2, + }, + { + y = 21, + x = 61, + h = 19, + w = 51, + objs = { + { + x2 = 13, + y2 = 2, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Leveloper", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 128, + input = false, + color = 1, + y1 = 2, + }, + { + x2 = 12, + y2 = 4, + y1 = 4, + x1 = 5, + txt = "Register", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 0, + }, + }, + txtcolor = 32768, + input = false, + color = 1, + border = { + color = 0, + type = 1, + }, + }, + { + lines = { + "", + }, + x2 = 48, + changed = false, + cursor = { + y = 1, + x = 1, + a = 1, + }, + x1 = 4, + scrollX = 0, + ox2 = 3, + history = {}, + border = { + color = 16384, + type = 1, + }, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 32768, + indentChar = " ", + tabSize = 4, + minWidth = 45, + overflow = "scroll", + minHeight = 3, + }, + blit = {}, + rhistory = {}, + y1 = 7, + y2 = 9, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 1, + }, + render = { + [ 2 ] = 1, + }, + }, + txtcolor = 32768, + state = false, + dLines = { + "", + }, + type = "input", + scr = 0, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + color = 1, + }, + { + type = "rect", + x2 = 4, + y2 = 9, + y1 = 7, + x1 = 1, + color = 1, + border = { + color = 0, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + }, + { + x2 = 51, + y2 = 9, + border = { + color = 0, + type = 1, + }, + x1 = 48, + ox1 = 3, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 7, + }, + { + x2 = 48, + y2 = 7, + y1 = 7, + x1 = 4, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 3, + color = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Loading...", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 16384, + input = false, + x2 = 36, + y1 = 13, + }, + { + x2 = 47, + y2 = 16, + x1 = 38, + ox1 = 13, + txt = " Next", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + [ " update" ] = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + mouse_click = { + [ 2 ] = -1, + }, + }, + ox2 = 4, + txtcolor = 1, + y1 = 14, + input = false, + color = 256, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 1, + type = 1, + }, + }, + { + x2 = 20, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Have an account?", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + y1 = 11, + }, + { + x2 = 28, + y2 = 11, + y1 = 11, + x1 = 22, + txt = "Sign in", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 8, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 2048, + input = false, + color = 0, + border = { + color = 0, + type = 1, + }, + }, + }, + c = 3, + }, + { + y = 21, + x = 61, + h = 19, + w = 51, + objs = { + { + x2 = 13, + y2 = 2, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "Leveloper", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 128, + input = false, + color = 1, + y1 = 2, + }, + { + color = 0, + y2 = 4, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + x2 = 42, + y1 = 4, + }, + { + x2 = 20, + y2 = 6, + border = { + color = 0, + type = 1, + }, + x1 = 5, + txt = "New password", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 0, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 1, + y1 = 6, + }, + { + lines = { + "", + }, + x2 = 48, + changed = false, + y1 = 9, + x1 = 4, + scrollX = 0, + ox2 = 3, + history = {}, + border = { + color = 16384, + type = 1, + }, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 32768, + indentChar = " ", + tabSize = 4, + minWidth = 45, + overflow = "scroll", + minHeight = 3, + }, + blit = {}, + rhistory = {}, + cursor = { + y = 1, + x = 1, + a = 1, + }, + y2 = 11, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 1, + }, + }, + txtcolor = 32768, + state = false, + dLines = { + "", + }, + type = "input", + scr = 0, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + color = 1, + }, + { + type = "rect", + x2 = 4, + y2 = 11, + y1 = 9, + x1 = 1, + color = 1, + border = { + color = 0, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + }, + { + color = 1, + y2 = 11, + y1 = 9, + x1 = 48, + ox1 = 3, + type = "rect", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 1, + y2 = 9, + border = { + color = 0, + type = 1, + }, + x1 = 4, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 3, + x2 = 48, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 9, + }, + { + color = 0, + y2 = 14, + y1 = 13, + x1 = 5, + txt = "No connection", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + Coroutine = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 16, + txtcolor = 16384, + input = false, + x2 = 35, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 256, + y2 = 16, + x1 = 36, + ox1 = 15, + txt = " Register", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + [ " update" ] = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 3, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 4, + txtcolor = 1, + border = { + color = 1, + type = 1, + }, + input = false, + x2 = 47, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 14, + }, + { + x2 = 7, + y2 = 15, + y1 = 15, + x1 = 5, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = 10, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 256, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 19, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 9, + txt = "Remember me", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + y1 = 15, + }, + }, + c = 4, + }, + { + y = 13, + x = 38, + h = 19, + w = 51, + objs = {}, + c = 5, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end +end + + local tArgs = {...} +if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} +end + + +return lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/LevelOS/Login_screen.sgui b/LevelOS/Login_screen.sgui new file mode 100644 index 0000000..c46cd3d --- /dev/null +++ b/LevelOS/Login_screen.sgui @@ -0,0 +1,1734 @@ +local assets = { + [ "shutdownmenu.lua" ] = { + id = 19, + content = "menu = lUtils.contextmenu(\ + self.x2,\ + self.y1-5,\ + 10,\ + {\ + {txt=\"Shut down\",action=function() os.shutdown() end},\ + {txt=\"Reboot\",action=function() os.reboot() end},\ + },\ + {fg=colors.white,divider=colors.cyan,txt=colors.black,selected=colors.lightBlue,bg=colors.white},\ + true\ +)\ +menu.render()\ +while menu.status ~= \"dead\" do\ + menu.update(os.pullEvent())\ + menu.render()\ +end", + name = "shutdownmenu.lua", + }, + [ "sidebar.lua" ] = { + id = 1, + content = "local s = shapescape.getSlide()\ +local function render()\ + term.setBackgroundColor(colors.lightBlue)\ + term.setTextColor(colors.white)\ + local w,h = s.win.getSize()\ + if w > 90 then\ + self.x1 = w-34\ + elseif w > 60 then\ + self.x1 = w-(math.floor(w/4))\ + else\ + --[[term.setBackgroundColor(colors.cyan)\ + term.clear()\ + return]]\ + self.x1 = w-(math.floor(w/3))\ + end\ + if self.y1 == 1 then\ + self.y2 = h\ + end\ + os.sleep(0)\ + term.clear()\ + local tw,th = term.getSize()\ + if w > 90 then\ + bigfont.writeOn(term.current(),1,\"LevelOS\",nil,3)\ + else\ + term.setCursorPos(1,2)\ + lUtils.centerText(\"LevelOS\")\ + end\ + local box = lUtils.input(tw*-2,1,(tw*-2)+(tw-4),1,{overflowX=\"wrap\",overflowY=\"stretch\",text=\"LevelOS\\nThe ultimate multitasking OS. With LevelOS, you can accomplish anything.\\n\\nUses Wojbie's bigfont API and Bomb Bloke's blittle API\\nMade by Leveloper\"})\ + box.render()\ + local height = box.y2-(box.y1-1)\ + box.x1 = 3\ + box.x2 = 3+(tw-4)\ + box.y2 = th\ + box.y1 = box.y2-(height-1)\ + box.render()\ + if w > 90 and h > 30 then\ + term.setTextColor(colors.cyan)\ + bigfont.writeOn(term.current(),2,\"L\")\ + end\ +end\ +render()\ +while true do\ + local e = {os.pullEvent()}\ + render()\ +end", + name = "sidebar.lua", + }, + [ "start.lua" ] = { + id = 0, + content = "local s = shapescape.getSlide()\ +local sl = shapescape.getSlides()\ +function sl.sleep(t)\ + local sTime = os.clock()\ + local tID = os.startTimer(t)\ + while true do\ + local e = {os.pullEvent()}\ + if (e[1] == \"timer\" and e[2] == tID) or os.clock() >= sTime+t then\ + return true\ + end\ + end\ +end\ +local function repos()\ + local w,h = s.win.getSize()\ + self.x1 = 1\ + self.y1 = 1\ + self.x2 = w\ + self.y2 = h\ + sl.sleep(0)\ +end\ +if not lOS.settings.timeOffset then lOS.settings.timeOffset = 0 end\ +local function render()\ + local w,h = s.win.getSize()\ + term.setBackgroundColor(colors.cyan)\ + term.clear()\ + term.setTextColor(colors.white)\ + local t = os.epoch(\"local\")/1000+lOS.settings.timeOffset*3600\ + if w > 110 then\ + term.setCursorPos(4,h-13)\ + bigfont.hugeWrite(os.date(\"%H:%M\",t))\ + term.setCursorPos(4,h-5)\ + bigfont.bigWrite(os.date(\"%A, %B %d\",t))\ + else\ + term.setCursorPos(3,h-5)\ + bigfont.bigWrite(os.date(\"%H:%M\",t))\ + term.setCursorPos(3,h-2)\ + term.write(os.date(\"%A, %B %d\",t))\ + end\ +end\ +repos()\ +render()\ +local dragY\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"term_resize\" then\ + repos()\ + elseif e[1] == \"key\" or e[1] == \"slide_up\" then\ + local o = s.objs\ + local w,h = term.getSize()\ + local step = math.floor(h/8)\ + s.lMenu = 2\ + sl.sleep(0)\ + while self.y2 >= 1 do\ + for t=2,#o do\ + o[t].y1 = o[t].y1-step\ + o[t].y2 = o[t].y2-step\ + end\ + sl.sleep(0.05)\ + end\ + for t=2,#o do\ + o[t] = nil\ + end\ + --s.lMenu = 2\ + elseif e[1] == \"mouse_drag\" then\ + if not dragY then\ + dragY = e[4]\ + else\ + if e[4] < dragY-4 then\ + os.queueEvent(\"slide_up\")\ + end\ + end\ + elseif e[1] == \"mouse_up\" then\ + dragY = nil\ + end\ + render()\ +end", + name = "start.lua", + }, + [ "username.lua" ] = { + id = 4, + content = "local username\ +local sl = shapescape.getSlides()\ +if not sl.user and fs.exists(\"LevelOS/data/account.txt\") then\ + sl.user = lUtils.fread(\"LevelOS/data/account.txt\")\ +elseif not sl.user then\ + --sl.loggedIn = true\ + sl.user = \"Guest\"\ + sl.guest = true\ +end\ +local function render()\ + if sl.user then\ + term.setBackgroundColor(colors.cyan)\ + term.clear()\ + bigfont.writeOn(term.current(),1,sl.user)\ + end\ +end\ +render()\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"term_resize\" then\ + render()\ + end\ +end", + name = "username.lua", + }, + [ "auth.lua" ] = { + id = 11, + content = "local function theFunc()\ + local sl = shapescape.getSlides()\ + local s = shapescape.getSlide()\ + while true do\ + if (sl.pass and sl.pass.txt ~= \"\") or fs.exists(\"LevelOS/data/account2.txt\") then\ + -- auth\ + --sl.sleep(0.5)\ + local res,err\ + if fs.exists(\"LevelOS/data/account2.txt\") then\ + token = lUtils.fread(\"LevelOS/data/account2.txt\")\ + --rememberme = true\ + res,err = http.post(\"https://os.leveloper.cc/auth.php\",\"username=\"..textutils.urlEncode(sl.user)..\"&token=\"..token)\ + else\ + res,err = http.post(\"https://os.leveloper.cc/auth.php\",\"username=\"..textutils.urlEncode(sl.user)..\"&password=\"..textutils.urlEncode(sl.pass.txt))\ + end\ + if res then\ + local str = res.readAll()\ + local oldstr = str\ + str = lUtils.getField(str,\"msg\")\ + if str:find(\"Welcome\") then\ + local userID = res.getResponseHeaders()[\"Set-Cookie\"]\ + --return userID,userbox.lines[1]\ + lOS.username = sl.user\ + lOS.userID = userID\ + local dur = 10\ + local function col(c,d)\ + local n = {term.nativePaletteColor(c)}\ + local cur = {s.win.getPaletteColor(c)}\ + local rgb = {}\ + for t=1,3 do\ + local st = (d[t]-n[t])/dur\ + rgb[t] = cur[t]+st\ + end\ + return rgb[1],rgb[2],rgb[3]\ + end\ + for t=1,dur do\ + for c=0,15,1 do\ + if t < dur then\ + s.win.setPaletteColor(2^c,col(2^c,{term.nativePaletteColor(colors.cyan)}))\ + else\ + s.win.setPaletteColor(2^c,term.nativePaletteColor(colors.cyan))\ + end\ + end\ + os.sleep(0.05)\ + end\ + lOS.isCyan = true\ + shapescape.exit()\ + else\ + --lUtils.popup(\"Error\",str,27,11,{\"OK\"})\ + --sl.errorMsg = str or \"error: \"..tostring(oldstr)\ + if fs.exists(\"LevelOS/data/account2.txt\") then\ + fs.delete(\"LevelOS/data/account2.txt\")\ + shapescape.setSlide(1)\ + else\ + shapescape.setSlide(4)\ + end\ + end\ + else\ + --lUtils.popup(\"Error\",\"No connection\",27,9,{\"OK\"})\ + sl.errorMsg = err or \"Unknown connection error\"\ + shapescape.setSlide(4)\ + end\ + --shapescape.setSlide(4)\ + else\ + sl.sleep(0.5)\ + shapescape.setSlide(2)\ + -- no auth\ + end\ + coroutine.yield()\ + end\ +end\ +local ok,err = pcall(theFunc)\ +if not ok then\ + _G.theError = err\ +end", + name = "auth.lua", + }, + [ "login_bg.lua" ] = { + id = 6, + content = "term.setBackgroundColor(colors.cyan)\ +term.clear()", + name = "login_bg.lua", + }, + [ "inputrender.lua" ] = { + id = 8, + content = "local cursor = {term.getCursorPos()}\ +if self.txt == \"\" then\ + term.setCursorPos(self.x1+1,self.y1+1)\ + term.setBackgroundColor(colors.white)\ + term.setTextColor(colors.lightGray)\ + term.write(\"Password\")\ +else\ + term.setCursorPos(self.x1+1,self.y1+1)\ + term.setBackgroundColor(colors.white)\ + term.setTextColor(self.txtcolor)\ + term.write(string.rep(\"*\",#self.txt))\ +end\ +term.setTextColor(colors.black)\ +term.setCursorPos(table.unpack(cursor))", + name = "inputrender.lua", + }, + [ "opt_title.lua" ] = { + id = 15, + content = "term.setBackgroundColor(colors.cyan)\ +term.clear()\ +term.setTextColor(colors.white)\ +bigfont.writeOn(term.current(),1,\"Options\")", + name = "opt_title.lua", + }, + [ "OK.lua" ] = { + id = 12, + content = "local sl = shapescape.getSlides()\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_up\" then\ + self.color = colors.cyan\ + elseif e[1] == \"key\" and e[2] == keys.enter then\ + self.color = colors.lightBlue\ + elseif e[1] == \"key_up\" and e[2] == keys.enter then\ + self.color = colors.cyan\ + --local x,y = sl.win.getPosition()\ + os.queueEvent(\"mouse_up\",1,self.x1+1,self.y1+1)\ + end\ +end", + name = "OK.lua", + }, + [ "mclick.lua" ] = { + id = 3, + content = "", + name = "mclick.lua", + }, + [ "inputbox.lua" ] = { + id = 7, + content = "local sl = shapescape.getSlides()\ +sl.pass = self\ +self.state = true\ +self.opt.overflowX = \"scroll\"\ +self.opt.overflowY = \"none\"\ +self.opt.overflow = \"none\"\ +self.opt.replaceChar = \"\\7\"\ +local hold = {}\ +if not lUtils.isHolding then\ + lUtils.isHolding = function(key)\ + if type(key) == \"string\" then\ + key = keys[key]\ + end\ + return not not hold[key]\ + end\ +end\ +while true do\ + local e = {os.pullEvent()}\ + if self.state then\ + self.txtcolor = colors.black\ + else\ + self.txtcolor = colors.lightGray\ + end\ + if e[1] == \"key\" then\ + hold[e[2]] = true\ + if e[2] == keys.enter then\ + self.state = false\ + end\ + -- auth\ + elseif e[1] == \"key_up\" then\ + hold[e[2]] = false\ + end\ +end", + name = "inputbox.lua", + }, + [ "SI_opt.lua" ] = { + id = 16, + content = "local w,h = term.getSize()\ +local x,y\ +if h > 60 then\ + y = self.y1+1\ +else\ + y = self.y1-1\ +end\ +x = self.x1\ +local w = self.x2-(x-1)\ +local opt = {}\ +if autologin then\ +end\ +\ +-- etc\ +lUtils.clickmenu(x,y,w,{\"Turn on auto-login\"", + name = "SI_opt.lua", + }, + [ "OK_mouseup.lua" ] = { + id = 14, + content = "local sl = shapescape.getSlides()\ +sl.pass.txt = \"\"\ +sl.pass.state = true\ +sl.pass.dLines[1] = \"\"\ +sl.pass.lines[1] = \"\"\ +sl.pass.cursor.a = 1\ +sl.pass.cursor.x = 1\ +sl.pass.ref[2] = 1\ +shapescape.setSlide(1)", + name = "OK_mouseup.lua", + }, + [ "loadingicon.lua" ] = { + id = 10, + content = "local sl = shapescape.getSlides()\ +local lIco = lUtils.asset.load(\"LevelOS/assets/loading.limg\")\ +local frame = 1\ +term.setBackgroundColor(colors.cyan)\ +term.clear()\ +os.sleep(0)\ +while true do\ + lUtils.renderImg(lIco[frame],1,1)\ + sl.sleep(0.1)\ + frame = frame+1\ + if frame > #lIco then\ + frame = 1\ + end\ +end", + name = "loadingicon.lua", + }, + nothing = { + id = 17, + content = "-- nothing", + name = "nothing", + }, + [ "OK_click.lua" ] = { + id = 13, + content = "self.color = colors.lightBlue", + name = "OK_click.lua", + }, + [ "menu.lua" ] = { + id = 2, + content = "menu = lUtils.contextmenu(\ + self.x1,\ + self.y2+1,\ + 20,\ + {\ + {\ + {txt=\"Shut down\",action=function() os.shutdown() end},\ + {txt=\"Reboot\",action=function() os.reboot() end},\ + },\ + {txt=\"View Changelog\",action=function() shell.run(\"LevelOS/Changelog.lua\") end}\ + },\ + {fg=colors.white,txt=colors.white,selected=colors.lightBlue,bg=colors.cyan},\ + true\ +)\ +menu.render()\ +while menu.status ~= \"dead\" do\ + menu.update(os.pullEvent())\ + menu.render()\ +end", + name = "menu.lua", + }, + [ "nextbutton.lua" ] = { + id = 9, + content = "local sl = shapescape.getSlides()\ +while true do\ + local e = {os.pullEvent()}\ + if (e[1] == \"key\" and e[2] == keys.enter) or (e[1] == \"mouse_click\" and e[2] == 1 and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2) then\ + --self.txtcolor = colors.cyan\ + self.color = colors.lightGray\ + elseif (e[1] == \"key_up\" and e[2] == keys.enter) or (e[1] == \"mouse_up\" and e[2] == 1) then\ + self.txtcolor = colors.white\ + self.color = colors.lightBlue\ + if sl.pass.txt ~= \"\" and (e[1] == \"key_up\" or (e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2)) then\ + -- do auth tingz\ + shapescape.setSlide(3)\ + end\ + end\ +end", + name = "nextbutton.lua", + }, + [ "log_in_window.lua" ] = { + id = 5, + content = "local s = shapescape.getSlide()\ +s.lMenu = 1\ +local sl = shapescape.getSlides()\ +while true do\ + if s.lMenu < 2 then\ + coroutine.yield()\ + else\ + if fs.exists(\"LevelOS/data/account2.txt\") then\ + s.lMenu = 3\ + else\ + s.lMenu = 2\ + end\ + lUtils.shapescape.run({sl[s.lMenu]})\ + end\ +end", + name = "log_in_window.lua", + }, + [ "showErr.lua" ] = { + id = 18, + content = "local sl = shapescape.getSlides()\ +if sl.errorMsg then\ + self.txt = sl.errorMsg\ + local w,h = term.getSize()\ + --self.x1 = math.ceil(w/2)-math.floor(#self.txt)\ + --self.x2 = self.x1+#self.txt-1\ + self.txt = string.rep(\" \",(math.ceil(w/2)-math.floor(#self.txt))-self.x1)..self.txt\ + sl.errorMsg = nil\ +else\ + --sl.errorMsg = \"None\"\ +end", + name = "showErr.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 1, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 5, + }, + }, + ox2 = 0, + color = 16384, + border = { + color = 2, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + type = "window", + color = 32768, + y2 = 18, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 0, + }, + }, + x1 = 3, + x2 = 49, + border = { + color = 0, + type = 1, + }, + y1 = 2, + }, + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 35, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 1, + }, + }, + ox2 = 0, + color = 8, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 512, + type = 1, + }, + }, + { + x2 = 9, + y2 = 4, + border = { + color = 1, + type = 1, + }, + x1 = 2, + txt = " Menu", + type = "text", + event = { + mouse_up = { + [ 2 ] = 2, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 512, + y1 = 2, + }, + }, + c = 1, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + x2 = 51, + y2 = 19, + border = { + color = 512, + type = 1, + }, + x1 = 1, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 6, + }, + }, + ox2 = 0, + color = 512, + y1 = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + color = 1, + cursor = { + y = 1, + x = 1, + a = 1, + }, + x1 = 9, + scrollX = 0, + ox1 = 17, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 32768, + indentChar = " ", + tabSize = 4, + minWidth = 31, + overflow = "scroll", + minHeight = 3, + }, + event = { + render = { + [ 2 ] = 17, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 7, + }, + update = { + [ 2 ] = -1, + }, + [ " render" ] = { + [ 2 ] = -1, + }, + }, + blit = {}, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + lines = { + "", + }, + x2 = 39, + y2 = 16, + border = { + color = 512, + type = 1, + }, + scr = 0, + oy1 = -4, + type = "input", + state = false, + ox2 = -13, + changed = false, + dLines = { + "", + }, + y1 = 14, + txtcolor = 32768, + oy2 = -6, + ref = { + 1, + 1, + }, + history = {}, + rhistory = {}, + }, + { + x2 = 43, + y2 = 16, + x1 = 39, + ox1 = -13, + oy1 = -4, + txt = " ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 9, + }, + }, + ox2 = -17, + y1 = 14, + border = { + color = 512, + type = 1, + }, + color = 8, + input = false, + txtcolor = 1, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + oy2 = -6, + }, + { + color = 512, + y2 = 12, + y1 = 10, + x1 = 2, + oy1 = 0, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + }, + ox2 = 1, + border = { + color = 1, + type = 1, + }, + x2 = 50, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap center", + }, + oy2 = -2, + }, + { + x2 = 31, + y2 = 8, + x1 = 21, + ox1 = 5, + oy1 = 8, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = -5, + border = { + color = 0, + type = 1, + }, + color = 512, + y1 = 2, + oy2 = 2, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + image = { + { + "€€Ÿƒ€€€ƒ€€", + "TT99TTT97TT", + "99777777999", + }, + { + "€‡€€€€€€‹€", + "T9TTT7TTT9T", + "97777077779", + }, + { + "—€€€–€–€€€”", + "9TTT0T7TTT7", + "77777707779", + }, + { + "•€€€ƒ‘€€€•", + "9TTT770TTT7", + "77770077779", + }, + { + "‚€€—€‚”€€", + "7TT70T00TT7", + "97707777779", + }, + { + "€‚€€€€€Ÿ€", + "T79TTTTT77T", + "99777777999", + }, + { + "€€€ƒƒ€€€", + "TTT77777TTT", + "99999999999", + }, + }, + }, + { + x2 = 51, + y2 = 19, + x1 = 48, + ox1 = 3, + oy1 = 3, + type = "rect", + event = { + mouse_up = { + [ 2 ] = 19, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + border = { + color = 0, + type = 1, + }, + color = 512, + y1 = 16, + oy2 = 0, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + image = { + { + "€Ÿ€€", + "TTTT", + "T0TT", + }, + { + "œ•ˆ”", + "0T00", + "T0TT", + }, + { + "•‚€•", + "00T0", + "TTTT", + }, + { + "ŒŒ…", + "0000", + "TTTT", + }, + }, + }, + { + color = 0, + y2 = 18, + x1 = 19, + ox1 = 7, + oy1 = -8, + txt = "Sign-in options", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = -7, + border = { + color = 0, + type = 1, + }, + y1 = 18, + x2 = 33, + input = false, + txtcolor = 1, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + oy2 = -8, + }, + }, + c = 2, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + color = 512, + y2 = 19, + border = { + color = 512, + type = 1, + }, + x1 = 1, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 6, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + y1 = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + color = 512, + y2 = 8, + x1 = 21, + ox1 = 5, + oy1 = 8, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = -5, + oy2 = 2, + x2 = 31, + border = { + color = 0, + type = 1, + }, + y1 = 2, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + image = { + { + "€€Ÿƒ€€€ƒ€€", + "TT99TTT97TT", + "99777777999", + }, + { + "€‡€€€€€€‹€", + "T9TTT7TTT9T", + "97777077779", + }, + { + "—€€€–€–€€€”", + "9TTT0T7TTT7", + "77777707779", + }, + { + "•€€€ƒ‘€€€•", + "9TTT770TTT7", + "77770077779", + }, + { + "‚€€—€‚”€€", + "7TT70T00TT7", + "97707777779", + }, + { + "€‚€€€€€Ÿ€", + "T79TTTTT77T", + "99777777999", + }, + { + "€€€ƒƒ€€€", + "TTT77777TTT", + "99999999999", + }, + }, + }, + { + x2 = 50, + y2 = 12, + border = { + color = 1, + type = 1, + }, + x1 = 2, + oy1 = 0, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + y1 = 10, + color = 512, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap center", + }, + oy2 = -2, + }, + { + x2 = 30, + y2 = 15, + x1 = 24, + ox1 = 2, + oy1 = -5, + txt = "Welcome", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + [ " Coroutine" ] = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 11, + }, + }, + ox2 = -4, + y1 = 15, + border = { + color = 0, + type = 1, + }, + color = 0, + input = false, + txtcolor = 1, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + oy2 = -5, + }, + { + color = 32768, + y2 = 16, + border = { + color = 0, + type = 1, + }, + x1 = 19, + ox1 = 7, + oy1 = -4, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 10, + }, + }, + ox2 = 4, + oy2 = -6, + x2 = 22, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + y1 = 14, + }, + }, + c = 3, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 1, + type = "window", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 6, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 512, + border = { + color = 512, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + color = 512, + y2 = 8, + x1 = 21, + ox1 = 5, + oy1 = 8, + type = "rect", + oy2 = 2, + ox2 = -5, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x2 = 31, + border = { + color = 0, + type = 1, + }, + y1 = 2, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + image = { + { + "€€Ÿƒ€€€ƒ€€", + "TT99TTT97TT", + "99777777999", + }, + { + "€‡€€€€€€‹€", + "T9TTT7TTT9T", + "97777077779", + }, + { + "—€€€–€–€€€”", + "9TTT0T7TTT7", + "77777707779", + }, + { + "•€€€ƒ‘€€€•", + "9TTT770TTT7", + "77770077779", + }, + { + "‚€€—€‚”€€", + "7TT70T00TT7", + "97707777779", + }, + { + "€‚€€€€€Ÿ€", + "T79TTTTT77T", + "99777777999", + }, + { + "€€€ƒƒ€€€", + "TTT77777TTT", + "99999999999", + }, + }, + }, + { + x2 = 50, + y2 = 12, + y1 = 10, + x1 = 2, + oy1 = 0, + type = "window", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + border = { + color = 1, + type = 1, + }, + color = 512, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap center", + }, + oy2 = -2, + }, + { + color = 0, + y2 = 14, + x1 = 8, + ox1 = 18, + oy1 = -4, + txt = "Password incorrect. Please try again.", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 18, + }, + }, + ox2 = -18, + txtcolor = 1, + border = { + color = 0, + type = 1, + }, + x2 = 44, + input = false, + y1 = 14, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + oy2 = -4, + }, + { + color = 512, + y2 = 18, + x1 = 21, + ox1 = 5, + oy1 = -6, + txt = " OK", + type = "text", + event = { + mouse_up = { + [ 2 ] = 14, + }, + mouse_click = { + [ 2 ] = 13, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 12, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = -4, + txtcolor = 1, + border = { + color = 1, + type = 1, + }, + x2 = 30, + input = false, + y1 = 16, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + oy2 = -8, + }, + }, + c = 4, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 1, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 6, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 512, + border = { + color = 512, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + type = "window", + color = 32768, + y2 = 5, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 12, + x2 = 40, + border = { + color = 0, + type = 1, + }, + y1 = 3, + }, + { + type = "rect", + color = 512, + y2 = 6, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 1, + x2 = 11, + border = { + color = 2048, + type = 1, + }, + y1 = 2, + }, + { + type = "rect", + color = 512, + y2 = 6, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + x1 = 41, + y1 = 2, + x2 = 51, + border = { + color = 2048, + type = 1, + }, + }, + }, + c = 5, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end +end + + local tArgs = {...} +if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} +end + + +return lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/LevelOS/Notification.lua b/LevelOS/Notification.lua new file mode 100644 index 0000000..aefd908 --- /dev/null +++ b/LevelOS/Notification.lua @@ -0,0 +1,124 @@ +local tArgs = {...} +if not tArgs then return end +local sID = #lOS.notifWins+1 +lOS.notifWins[sID] = LevelOS.self.window +local function count(tbl) + local c = 0 + for k,v in pairs(tbl) do + c = c+1 + end + return c +end +local nAmount = count(lOS.notifWins) +term.setBackgroundColor(colors.gray) +term.clear() +local x,y = 2,2 +local icon +if type(tArgs[3]) == "string" and fs.exists(tArgs[3]) and lOS.getIcon then + icon = lOS.getIcon(tArgs[3]) +end +if icon then + lUtils.renderImg(icon,x,y) + x = x+4 +end +if type(tArgs[1]) == "string" then + term.setCursorPos(x,y) + term.setTextColor(colors.white) + local w,h = term.getSize() + local nw = w-x + local txt = lUtils.wordwrap(tArgs[1],nw-1) + if y+#txt > h then + local dif = y+#txt-h + local wX,wY = LevelOS.self.window.win.getPosition() + wY = wY-dif + h = h+dif + LevelOS.self.window.win.reposition(wX,wY,w,h) + end + for t=1,#txt do + term.setCursorPos(x,y) + term.write(txt[t]) + y = y+1 + end +end +if type(tArgs[2]) == "string" then + term.setCursorPos(x,y) + term.setTextColor(colors.lightGray) + local w,h = term.getSize() + local nw = w-x + local txt = lUtils.wordwrap(tArgs[2],nw) + if y+#txt > h then + local dif = y+#txt-h + local wX,wY = LevelOS.self.window.win.getPosition() + wY = wY-dif + h = h+dif + LevelOS.self.window.win.reposition(wX,wY,w,h) + end + for t=1,#txt do + term.setCursorPos(x,y) + term.write(txt[t]) + y = y+1 + end +end +local w,h = term.getSize() +term.setCursorPos(w-1,2) +term.setTextColor(colors.lightGray) +term.write("×") +local timer = 3 +if type(tArgs[4]) == "string" and tonumber(tArgs[4]) then + timer = tonumber(tArgs[4]) +end +local tID = os.startTimer(timer) +local tID2 +local sel = false +while true do + local e = {os.pullEvent()} + if e[1] == "timer" and e[2] == tID then + break + elseif e[1]:find("mouse") and e[3] and e[4] then + if e[3] == w-1 and e[4] == 2 then + if e[1] == "mouse_hover" then + term.setCursorPos(w-1,2) + term.setTextColor(colors.white) + term.write("×") + sel = true + elseif e[1] == "mouse_click" then + term.setCursorPos(w-1,2) + term.setTextColor(colors.red) + term.write("×") + sel = true + elseif e[1] == "mouse_up" and sel then + break + end + end + if e[1] == "mouse_up" and sel then + term.setCursorPos(w-1,2) + term.setTextColor(colors.lightGray) + term.write("×") + sel = false + end + end + --if count(lOS.notifWins) ~= nAmount then + nAmount = count(lOS.notifWins) + local tW,tH = lOS.wAll.getSize() + local w,h = term.getSize() + local newY = tH-h-lOS.tbSize + local x,y = LevelOS.self.window.win.getPosition() + for k,v in pairs(lOS.notifWins) do + --if v ~= LevelOS.self.window then + local tx,ty = v.win.getPosition() + if ty-h-1 <= newY and ty > y then -- use actual height + newY = ty-h-1 + end + --end + end + if newY > y then + if not tID2 then + tID2 = os.startTimer(0.3) + elseif tID2 and e[1] == "timer" and e[2] == tID2 then + LevelOS.self.window.win.reposition(x,newY) + tID2 = nil + end + end + --end +end +lOS.notifWins[sID] = nil \ No newline at end of file diff --git a/LevelOS/Notifications.lua b/LevelOS/Notifications.lua new file mode 100644 index 0000000..f80d6ce --- /dev/null +++ b/LevelOS/Notifications.lua @@ -0,0 +1,725 @@ +local assets = {} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 70, + w = 32, + h = 19, + objs = { + { + type = "rect", + color = 32768, + y2 = 19, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 1, + x2 = 32, + border = { + color = 0, + type = 1, + }, + y1 = 1, + }, + { + type = "rect", + color = 128, + y2 = 6, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + x1 = 2, + y1 = 2, + border = { + color = 0, + type = 1, + }, + x2 = 31, + }, + { + x2 = 19, + y2 = 3, + border = { + color = 0, + type = 1, + }, + x1 = 8, + txt = "Notification", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 0, + y1 = 3, + }, + { + type = "rect", + color = 16384, + y2 = 5, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + x1 = 4, + y1 = 3, + border = { + color = 0, + type = 1, + }, + x2 = 6, + }, + { + x2 = 30, + y2 = 5, + border = { + color = 0, + type = 1, + }, + x1 = 8, + txt = "This is a notification to tell you things yes.", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + color = 0, + y1 = 4, + }, + { + x2 = 30, + y2 = 3, + border = { + color = 0, + type = 1, + }, + x1 = 30, + txt = "×", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + color = 0, + y1 = 3, + }, + }, + c = 1, + }, + { + y = 21, + x = 70, + c = 2, + objs = { + { + type = "rect", + color = 128, + y2 = 6, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 2, + x2 = 31, + y1 = 2, + border = { + color = 0, + type = 1, + }, + }, + { + type = "rect", + color = 16384, + y2 = 5, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 3, + x2 = 5, + y1 = 3, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 3, + y1 = 3, + x1 = 7, + txt = "Notification", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 18, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 5, + y1 = 4, + x1 = 7, + txt = "This is a notification to tell you things yes.", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + x2 = 30, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 3, + y1 = 3, + x1 = 30, + txt = "×", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + x2 = 30, + border = { + color = 0, + type = 1, + }, + }, + { + type = "rect", + color = 128, + y2 = 12, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 2, + border = { + color = 0, + type = 1, + }, + x2 = 31, + y1 = 8, + }, + { + color = 0, + y2 = 9, + y1 = 9, + x1 = 3, + txt = "Notification", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 14, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 30, + y2 = 11, + border = { + color = 0, + type = 1, + }, + x1 = 3, + txt = "This notification has no icon available.", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + color = 0, + y1 = 10, + }, + { + x2 = 30, + y2 = 9, + border = { + color = 0, + type = 1, + }, + x1 = 30, + txt = "×", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + color = 0, + y1 = 9, + }, + { + type = "rect", + color = 128, + y2 = 19, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + x1 = 2, + border = { + color = 0, + type = 1, + }, + y1 = 14, + x2 = 31, + }, + { + type = "rect", + color = 16384, + y2 = 17, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 3, + x2 = 5, + border = { + color = 0, + type = 1, + }, + y1 = 15, + }, + { + color = 0, + y2 = 15, + y1 = 15, + x1 = 7, + txt = "Notification", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 18, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 18, + y1 = 16, + x1 = 7, + txt = "This notification does not have enough space so it expands downwards.", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + x2 = 30, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 30, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 30, + txt = "×", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 256, + input = false, + color = 0, + y1 = 15, + }, + }, + w = 32, + h = 19, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end + end + + local tArgs = {...} + if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} + end + + + lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/LevelOS/Start_Menu.sgui b/LevelOS/Start_Menu.sgui new file mode 100644 index 0000000..aa15b8a --- /dev/null +++ b/LevelOS/Start_Menu.sgui @@ -0,0 +1,327 @@ +local assets = { + [ "programs.lua" ] = { + id = 0, + content = "local scrollY = 0\ +local programs = {}\ +local found = fs.list(\"Program_Files\")\ +local icons = lUtils.asset.load(\"LevelOS/assets/Compact_Icons.limg\")\ +local A = string.byte(\"A\")\ +local Z = string.byte(\"Z\")\ +for f=1,#found do\ + local l = string.byte(found[f]:sub(1,1):upper())\ + local dest\ + if l >= A and l <= Z then\ + if not programs[l] then programs[l] = {} end\ + dest = programs[l]\ + else\ + if not programs[A-1] then programs[A-1] = {} end\ + dest = programs[A-1]\ + end\ + local program = {path=fs.combine(\"Program_Files\",found[f]),name=found[f]:sub(1,1):upper()..lUtils.getFileName(found[f]:sub(2))}\ + --program.icon = whatever\ + table.insert(dest,program)\ +end\ +local function render()\ + local y = 2\ + term.setBackgroundColor(colors.gray)\ + term.setTextColor(colors.white)\ + term.clear()\ + for l=A-1,Z do\ + if programs[l] then\ + local letter\ + if l == A-1 then\ + letter = \"#\"\ + else\ + letter = string.char(l)\ + end\ + term.setCursorPos(1,y)\ + term.write(letter)\ + y = y+2\ + for t=1,#programs[l] do\ + lUtils.renderImg(icons[8],1,y)\ + term.setCursorPos(5,y)\ + term.setTextColor(colors.white)\ + term.write(programs[l][t].name)\ + y = y+1\ + term.setTextColor(colors.lightGray)\ + term.write( -- creator\ + end\ + end\ +end", + name = "programs.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 61, + w = 51, + h = 19, + objs = { + { + color = 128, + y2 = 19, + border = { + color = 0, + type = 1, + }, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x2 = 5, + y1 = 1, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + color = 128, + y2 = 18, + border = { + color = 0, + type = 1, + }, + x1 = 2, + image = { + { + "”•”", + "000", + "TTT", + }, + { + "Œ…", + "000", + "TTT", + }, + }, + oy1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + oy2 = 1, + x2 = 4, + y1 = 17, + snap = { + Top = "Snap bottom", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + }, + { + color = 128, + y2 = 15, + border = { + color = 0, + type = 1, + }, + x1 = 2, + image = { + { + "‡’", + "TT0", + "00T", + }, + { + "‚‡€", + "00T", + "TTT", + }, + }, + oy1 = 5, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + oy2 = 4, + x2 = 4, + y1 = 14, + snap = { + Top = "Snap bottom", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + }, + { + color = 128, + y2 = 12, + border = { + color = 0, + type = 1, + }, + x1 = 2, + image = { + { + "‘€„", + "TT0", + "00T", + }, + { + "ˆ€", + "00T", + "TTT", + }, + }, + oy1 = 8, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + oy2 = 7, + x2 = 4, + y1 = 11, + snap = { + Top = "Snap bottom", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + }, + { + color = 128, + y2 = 19, + y1 = 1, + x1 = 6, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + border = { + color = 0, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + }, + c = 1, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end + end + + local tArgs = {...} + if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} + end + + + lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/LevelOS/SystemUI.lua b/LevelOS/SystemUI.lua new file mode 100644 index 0000000..76df690 --- /dev/null +++ b/LevelOS/SystemUI.lua @@ -0,0 +1,1970 @@ +LevelOS.self.window.events = "all" +local w,h = lOS.wAll.getSize() +local timeObj = {x=w-13,w=8} +local function round(n) + return math.floor(n + 0.5) +end +local oclickmenu = lUtils.clickmenu +lUtils.oclickmenu = oclickmenu +local opopup = lUtils.popup +local oinputbox = lUtils.inputbox +local events = {} +local doLog = false +local subprocesses = {} +local theOverlayFunction + +local oshutdown = os.shutdown +local oreboot = os.reboot +local function shutdownscreen(reboot) + local bgcolor = colors.cyan + local txtcolor = colors.white + local txtcolor2 = colors.gray + local txtcolor3 = colors.lightGray + local button1 = colors.lightBlue -- fill + local button2 = colors.cyan -- border + local button3 = colors.white -- text + + lOS.focusWin = lOS.wins[0] + local nEvents = lOS.noEvents + lOS.noEvents = true + local img = lUtils.asset.load("LevelOS/assets/loading.limg") + local iw,ih = #img[1][1][1],#img[1] + local frame = 1 + local action = 1 + local programs = {} + local objs = {} + local sel + local function genPrograms() + programs = {} + for i,process in ipairs(lOS.processes) do + if (process.env and process.env.LevelOS and type(process.env.LevelOS.close) == "function") or process.winMode ~= "background" then + table.insert(programs,process) -- ha potato + end + end + end + local function drawObj(obj) + term.setTextColor(obj.border) + term.setBackgroundColor(obj.color) + lUtils.border(obj.x1, obj.y1, obj.x2, obj.y2, "fill") + term.setCursorPos(obj.x1+1,obj.y1+1) + term.setTextColor(button3) + term.write(obj.txt) + end + local function overlayFunc() + term.setCursorBlink(false) + term.setBackgroundColor(bgcolor) + term.clear() + local w,h = term.getSize() + lUtils.renderImg(img[frame],nil,math.ceil(h/2)-2) + term.setCursorPos(1,math.ceil(h/2)+2) + term.setTextColor(txtcolor) + if reboot then + lUtils.centerText("Restarting") + else + lUtils.centerText("Shutting down") + end + end + LevelOS.overlay = overlayFunc + local oTime = os.epoch("utc") + lOS.save() + local render + local e + local closewin + while true do + local tID = os.startTimer(0.1) + while true do + e = {os.pullEventRaw()} + LevelOS.overlay = overlayFunc + if os.epoch("utc") > oTime+250 and action == 1 then + action = 2 + genPrograms() + if #programs == 0 then + if reboot then + oreboot() + else + oshutdown() + end + else + closewin = window.create(lOS.wAll,2,2,51-2,19-2,false) + function render() + objs = {} + local list = {} + for i,p in ipairs(lOS.processes) do + if p.winMode ~= "background" or (p.env and p.env.LevelOS and type(p.env.LevelOS.close) == "function") then + table.insert(list,p) + end + end + local cTerm = term.current() + local tW,tH = lOS.wAll.getSize() + local cW,cH = 51,#list*3+10 + local big = false + local spacing = 3 + local offset = 0 + if tH >= 30 and tW >= 100 then + big = true + cW = 74 + offset = 5 + end + if tH >= 30 then + spacing = 4 + cH = #list*4+10+offset + end + closewin.reposition(math.floor(tW/2-cW/2)+1,math.floor(tH/2-cH/2)+1,cW,cH) + term.redirect(closewin) + term.setBackgroundColor(bgcolor) + term.setTextColor(txtcolor) + term.clear() + local w,h = term.getSize() + term.setCursorPos(1,1) + local word = " apps" + if #list == 1 then + word = " app" + end + local act = "shutting down" + if reboot then + act = "restarting" + end + if big then + bigfont.bigPrint("Closing "..#list..word.." and") + bigfont.bigPrint(act) + else + print("Closing "..#list..word.." and "..act) + term.setTextColor(txtcolor2) -- gray + end + print("To go back and save your work, click Cancel and finish what you need to.") + for i,p in ipairs(list) do + local path = p.path + if fs.isDir(path) and fs.exists(fs.combine(path,"main.lua")) then + path = fs.combine(path,"main.lua") + end + lOS.explorer.drawIcon(path,1,i*spacing+1+offset,true,true) + term.setTextColor(txtcolor) + term.setCursorPos(7,i*spacing+3+offset) + print(p.title or lOS.explorer.getName(p.path)) + if p.env and p.env.LevelOS and type(p.env.LevelOS.close) == "function" then + term.setCursorPos(7,i*spacing+4+offset) + term.setTextColor(txtcolor3) -- light gray + if reboot then + print("This app is preventing you from restarting.") + else + print("This app is preventing shutdown.") + end + end + end + local txt = " Shut down anyway " + if reboot then + txt = " Restart anyway " + end + local obj1 = {x1=1, y1=#list*spacing+8+offset, x2=#txt+2, y2=#list*spacing+10+offset, id="shutdown", color=button1, border=button2, txt=txt} + if sel == 1 then + obj1.color = button2 + obj1.border = button1 + end + table.insert(objs,obj1) + drawObj(obj1) + local txt2 = " Cancel " + local obj2 = {x1=#txt+3, y1=#list*spacing+8+offset, x2=#txt+2+#txt2+2, y2=#list*spacing+10+offset, id="cancel", color=button1, border=button2, txt=txt2} + if sel == 2 then + obj2.color = button2 + obj2.border = button1 + end + table.insert(objs,obj2) + drawObj(obj2) + term.redirect(cTerm) + end + local ok,err = pcall(render) + if not ok then + lOS.bsod(err) + os.sleep(10) + end + function overlayFunc() + term.setCursorBlink(false) + term.setBackgroundColor(bgcolor) + term.clear() + + closewin.setVisible(true) + closewin.setVisible(false) + end + LevelOS.overlay = overlayFunc + end + elseif os.epoch("utc") > oTime+2250 and action == 2 then + action = 3 + for i=#lOS.processes,3,-1 do + local process = lOS.processes[i] + if (process.env and process.env.LevelOS and type(process.env.LevelOS.close) == "function") or process.winMode ~= "background" then + os.queueEvent("process_close",i,tostring(lOS.processes[i])) + end + end + genPrograms() + render() + elseif action == 3 then + local ocount = #programs + genPrograms() + if #programs == 0 then + if reboot then + oreboot() + else + oshutdown() + end + end + if ocount ~= #programs then + render() + end + end + if e[1] == "key" and e[2] == keys.t and lUtils.isHolding(keys.leftCtrl) then + term.setBackgroundColor(colors.black) + LevelOS.overlay = theOverlayFunction + lOS.noEvents = nil + return + elseif e[1] == "mouse_click" or e[1] == "mouse_up" then + local wx,wy = closewin.getPosition() + for i,o in ipairs(objs) do + if lUtils.isInside(e[3]-(wx-1),e[4]-(wy-1),o) then + if e[1] == "mouse_click" then + sel = i + render() + elseif e[1] == "mouse_up" and sel == i then + if o.id == "cancel" then + LevelOS.overlay = theOverlayFunction + lOS.noEvents = nil + LevelOS.events = oEvents + return + elseif o.id == "shutdown" then + if reboot then + oreboot() + else + oshutdown() + end + end + end + end + end + if e[1] == "mouse_up" and sel then + sel = nil + render() + end + elseif e[1] == "timer" and e[2] == tID then + break + end + end + frame = frame%#img+1 + end +end + +function os.shutdown() + table.insert(subprocesses,coroutine.create(shutdownscreen)) + coroutine.resume(subprocesses[#subprocesses]) +end + +function os.reboot() + table.insert(subprocesses,coroutine.create(shutdownscreen)) + coroutine.resume(subprocesses[#subprocesses],true) +end + +local tbIcoAssets = lUtils.asset.load("LevelOS/assets/Compact_Icons.limg") + +local tbIco = { + + ["LevelOS/explorer.lua"] = { + tbIcoAssets[1], + {"\143","4","7"}, + }, + + ["LevelOS/LevelCloud.lua"] = { + tbIcoAssets[2], + }, + + ["LevelOS/Pigeon.lua"] = { + tbIcoAssets[3], + {"\29","b","7"}, + }, + + ["Program_Files/Shapescape"] = { + tbIcoAssets[4], + {"\7","e","7"}, + }, + + ["rom/programs/shell.lua"] = { + tbIcoAssets[5], + }, + + ["rom/programs/lua.lua"] = { + tbIcoAssets[6], + }, + + ["LevelOS/notepad.lua"] = { + tbIcoAssets[7], + {"\143","0","7"}, + }, + + ["*"] = { + tbIcoAssets[8], + }, + + ["LevelOS/paint.lua"] = { + tbIcoAssets[9], + {"\1","1","7"}, + }, + +} + +local pHistory = {} + +local function logEvents() + while true do + local e = {os.pullEventRaw()} + if doLog and (e[1]:find("window") or e[1]:find("process")) then + table.insert(events,e) + end + for p=#subprocesses,1,-1 do + coroutine.resume(subprocesses[p],unpack(e)) + if coroutine.status(subprocesses[p]) == "dead" then + table.remove(subprocesses,p) + end + end + end +end + + +local function runOverlay(func,grayOut,system) + local levOS + if system then + levOS = lOS.sysUI.env.LevelOS + else + levOS = lOS.wins[lOS.cWin].env.LevelOS + end + local oterm = term.current() + local w,h = lOS.wAll.getSize() + local win = window.create(oterm,1,1,w,h,false) + local cor = coroutine.create(func) + lOS.noEvents = true + if grayOut then + os.sleep(0) + end + for y=1,h do + win.setCursorPos(1,y) + win.blit(lOS.wAll.getLine(y)) + end + local oldOverlay = levOS.overlay + function levOS.overlay() + for y=1,h do + term.setCursorPos(1,y) + term.blit(win.getLine(y)) + term.setCursorPos(1,1) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.red) + end + oldOverlay() + end + term.redirect(win) + local pEvent + if system or lOS.wins[lOS.cWin].events == "all" then + pEvent = os.pullEventRaw + else + pEvent = levOS.pullEvent + end + coroutine.resume(cor) + while true do + local e = {pEvent()} + term.redirect(win) + coroutine.resume(cor,unpack(e)) + term.redirect(oterm) + if coroutine.status(cor) == "dead" then + lOS.noEvents = false + break + end + end + term.redirect(oterm) + levOS.overlay = oldOverlay +end + +function runLevelOSclose(process) + if process.env and process.env.LevelOS and type(process.env.LevelOS.close) == "function" then + local func = process.env.LevelOS.close + local oEnv = getfenv(func) + local nEnv = {} + for k,v in pairs(oEnv) do + nEnv[k] = v + end + setmetatable(nEnv,getmetatable(oEnv)) + local cor = process[1] + nEnv.abort = function() + process[1] = cor + process.env.LevelOS.close = func + os.sleep(0) + end + setfenv(func,nEnv) + process[1] = coroutine.create(func) + process.env.LevelOS.close = nil + end +end + +function lUtils.clickmenu(...) + if not lOS.cWin or not lOS.wins[lOS.cWin] or not lOS.wins[lOS.cWin].win then + return oclickmenu(...) + end + local output + local x,y = lOS.wins[lOS.cWin].win.getPosition() + if term.current() ~= lOS.wins[lOS.cWin].win then + local x1,y1 = term.current().getPosition() + x = x+x1-1 + y = y+y1-1 + end + local input = table.pack(...) + input[1],input[2] = input[1]+(x-1),input[2]+(y-1) + os.sleep(0) + runOverlay(function() output = table.pack(oclickmenu(table.unpack(input,1,input.n))) end) + return table.unpack(output,1,output.n) +end + +function lOS.contextmenu(x,y,width,options,colorScheme,dividers) -- no bad change whole thing i need to put render function in LevelOS.overlay n shit + lOS.noEvents = 2 + local oterm = term.current() + --[[local tx,ty = lOS.wins[lOS.cWin].win.getPosition() + if term.current() ~= lOS.wins[lOS.cWin].win then + local tx1,ty1 = term.current().getPosition() + tx = tx+tx1-1 + ty = ty+ty1-1 + end]] + local tx,ty = lUtils.getWindowPos(oterm) + term.redirect(lOS.wAll) + local menu = lUtils.contextmenu(x+tx-1,y+ty-1,width,options,colorScheme,dividers) + lOS.debugMenu = menu + term.redirect(oterm) + levOS = lOS.wins[lOS.cWin].env.LevelOS + local oldOverlay = levOS.overlay + function levOS.overlay() + menu.render() + oldOverlay() + end + local clicked + while menu.status ~= "dead" do + local e = {levOS.pullEvent()} + term.redirect(lOS.wAll) + clicked = menu.update(unpack(e)) + term.redirect(oterm) + end + lOS.noEvents = false + levOS.overlay = oldOverlay + os.sleep(0) + return clicked +end +local function animation(v,x,y,w,h,gx,gy,gw,gh,dur) + local dur = dur or 8 + if lOS.settings.animations == false then + dur = 0 + end + local minWin + local oterm = term.current() + if v.winMode == "windowed" then + local wx,wy = v.win.getPosition() + local ww,wh = v.win.getSize() + minWin = window.create(term.current(),wx-1,wy-1,ww+2,wh+2,false) + term.redirect(minWin) + term.setBackgroundColor(colors.gray) + term.setCursorPos(1,1) + term.clearLine() + term.setCursorPos(1,wh+2) + term.clearLine() + for t=1,wh do + term.setCursorPos(1,t+1) + term.write(" ") + term.blit(v.win.getLine(t)) + term.write(" ") + end + term.redirect(oterm) + else + minWin = v.win + end + local width,height = term.getSize() + --lOS.noEvents = true + local oldOverlay = LevelOS.overlay + local lilwin + function LevelOS.overlay() + if lilwin then + lilwin.render(lilwin.x,lilwin.y) + end + oldOverlay() + end + for t=1,dur do + local nx,ny = round(x+(gx-(x))*(t/dur)),round(y+(gy-(y))*(t/dur)) + local nw,nh = round(w+(gw-(w))*(t/dur)),round(h+(gh-(h))*(t/dur)) + lilwin = lUtils.littlewin(minWin,nw,nh) + lilwin.x,lilwin.y = nx,ny + os.sleep(0.05) + end + LevelOS.overlay = oldOverlay + --lOS.noEvents = false +end + +local imgcache = {} +local nimgcache = {} +local function UI() + + local function startMenu() + local width,height = lOS.wAll.getSize() + height = height-lOS.tbSize + while true do + local opt = {{"Task Manager"},{"Settings","Search","Execute"},"Shut Down Options >"} + if fs.exists("LevelOS/Global_Login.lua") then + if not lOS.userID then + table.insert(opt[1],1,"Log in") + else + table.insert(opt[1],1,"Log out") + end + end + a = {oclickmenu(2,height,21,opt,nil,{Settings=true})} + if a[1] == false then + break + else + if a[3] == "Task Manager" then + if not fs.exists("LevelOS/Task_Manager.lua") then + local f = hpost("https://old.leveloper.cc/sGet.php","path="..textutils.urlEncode("LevelOS/Task_Manager.lua").."&code="..textutils.urlEncode("lSlb8kZq")).readAll() + fwrite("LevelOS/Task_Manager.lua",f) + end + lOS.execute("LevelOS/Task_Manager.lua") + break + elseif a[3] == "Log in" then + lOS.execute("LevelOS/Global_Login.lua") + break + elseif a[3] == "Log out" then + lUtils.logout() + break + elseif a[3] == "Execute" then + -- code + b = {lUtils.inputbox("Execute","Enter the path of the program you want to run:",27,10,{" OK ","Cancel"})} + if b[1] ~= nil and b[1] ~= "" and b[3] == 1 then + local path = string.sub(b[1],string.find(b[1],"%S+")) + -- virusscan + if fs.exists(path) == false then + lUtils.popup("Error!","File does not exist.",21,8,{"OK"}) + else + --lOS.oWins[#lOS.oWins+1] = {window.create(oldterm,1,2,w,h-3),coroutine.create(function() if shell.run(b[1]) == false then curterm = term.current() term.redirect(oldterm) lUtils.popup(fs.getName(path),fs.getName(path).." has stopped working.",25,9,{"OK"}) term.redirect(curterm) end end),fullscreen=false,minimized=false,filepath=path,icon={string.sub(fs.getName(path),1,3),string.sub(fs.getName(path),4,6)},ran=false} + --_G.cWin = #lOS.oWins + lOS.execute(b[1]) + end + end + break + elseif a[3] == "Search" then + -- run search + break + elseif a[3] == "Settings" then + lUtils.openWin("Settings","LevelOS/settings.lua",2,2,49,17,true) + break + elseif a[3] == "Shut Down Options >" then + b = {oclickmenu(22,height,20,{{"Shut Down","Reboot"}})} + if b[1] == true then + if b[3] == "Shut Down" or b[3] == "Reboot" then + --[[lOS.focusWin = lOS.wins[0] + local te = lOS.focusWin.win + local te2 = lOS.focusWin.owin + allcolors = {} + for t=0,15,1 do + allcolors[#allcolors+1] = {te.getPaletteColor(2^t)} + end + for t=1,0,-0.1 do + for c=0,15,1 do + te.setPaletteColor(2^c,allcolors[c+1][1]*t,allcolors[c+1][2]*t,allcolors[c+1][3]*t) + te2.setPaletteColor(2^c,allcolors[c+1][1]*t,allcolors[c+1][2]*t,allcolors[c+1][3]*t) + end + os.sleep(0.05) + end + lOS.save()]] + if b[3] == "Shut Down" then + table.insert(subprocesses,coroutine.create(shutdownscreen)) + coroutine.resume(subprocesses[#subprocesses]) + break + --os.shutdown() + elseif b[3] == "Reboot" then + table.insert(subprocesses,coroutine.create(shutdownscreen)) + coroutine.resume(subprocesses[#subprocesses],true) + break + end + end + else + break + end + else + break + end + end + end + lOS.noEvents = false + end + + local tbApps = {} + local tbPos = {} + _G.lOS.sysDebug = {tbApps=tbApps,tbPos=tbPos} + local icoSel + local previewWin + local function drawTaskbar() + local w,h = term.getSize() + timeObj.x = w-13 + term.setBackgroundColor(colors.gray) + term.setCursorPos(1,h) + term.clearLine() + if icoSel ~= 0 then + term.setTextColor(colors.white) + else + term.setTextColor(colors.lightGray) + end + tbApps = {} + tbPos = {} + lOS.sysDebug.tbApps,lOS.sysDebug.tbPos = tbApps,tbPos + local appLookup = {} + for i,p in ipairs(lOS.processes) do + if p.win and p.winMode ~= "widget" and p.winMode ~= "background" then + if not appLookup[p.path] then + local newLookup = {p} + table.insert(tbApps,newLookup) + appLookup[p.path] = newLookup + else + table.insert(appLookup[p.path],p) + end + end + end + if not lOS.settings then lOS.settings = {} end + if lOS.settings.timeOffset == nil then lOS.settings.timeOffset = 0 end + if lOS.settings.rTime == nil then lOS.settings.rTime = false end + local t = os.date("*t",os.epoch("utc")/1000+lOS.settings.timeOffset*3600) + local function tz(n) + return string.rep("0",2-string.len(n))..n + end + local dateTxt = t.day.."-"..t.month.."-"..(t.year-2000) + local lTime + if lOS.settings.rTime then + lTime = tz(t.hour)..":"..tz(t.min) + else + local nTime = (os.time()+lOS.settings.timeOffset)%24 + local nHour = math.floor(nTime) + local nMinute = math.floor((nTime - nHour) * 60) + lTime = tz(nHour)..":"..tz(nMinute) + end + if lOS.tbSize > 1 then + term.setCursorPos(1,h-1) + term.clearLine() + term.setCursorPos(1,h-1) + term.write("\144") + term.setCursorPos(1,h) + term.write("\141") + for i,w in ipairs(tbApps) do + local top + for t=1,#w do + if w[t] == lOS.focusWin then + top = t + break + end + end + local bg,txt + if top then + bg = colors.lightGray + else + bg = colors.gray + end + if icoSel == i then + txt = colors.lightGray + else + txt = colors.white + end + --[[for y=h-(lOS.tbSize-1),h do + term.setBackgroundColor(bg) + term.setTextColor(colors.gray) + term.setCursorPos(i*4-1,y) + term.write("\149") + term.setBackgroundColor(colors.gray) + term.setTextColor(bg) + term.setCursorPos(i*4+3,y) + term.write("\149") + end]] + if lOS.settings.taskbarIcons == false then + term.setBackgroundColor(bg) + term.setTextColor(txt) + term.setCursorPos(i*4,h-1) + local title = lUtils.getFileName(program) + title = title:sub(1,1):upper()..title:sub(2) + local t1,t2 = title:sub(1,3),title:sub(4,6) + t1 = t1..string.rep(" ",3-#t1) + t2 = t2..string.rep(" ",3-#t2) + term.write(t1) + term.setCursorPos(i*4,h) + term.write(t2) + else + term.setBackgroundColor(bg) + local ico + local dPath = w[1].path + if fs.getName(dPath) == "main.lua" then + dPath = fs.getDir(dPath) + end + if imgcache[dPath] then + ico = {imgcache[dPath]} + elseif not nimgcache[dPath] and fs.isDir(dPath) and (fs.exists(fs.combine(dPath,"taskbar.limg")) or fs.exists(fs.combine(dPath,"taskbar.bimg"))) then + local img + if fs.exists(fs.combine(dPath,"taskbar.limg")) then + img = lUtils.asset.load(fs.combine(dPath,"taskbar.limg")) + else + img = lUtils.asset.load(fs.combine(dPath,"taskbar.bimg")) + end + img = img[1] + local iw = #img[1][1] + local ih = #img + if iw <= 3 and ih <= 2 then + if not imgcache[dPath] then + imgcache[dPath] = img + end + ico = {img} + else + nimgcache[dPath] = true + ico = tbIco["*"] + end + elseif tbIco[dPath] then + ico = tbIco[dPath] + else + ico = tbIco["*"] + end + if not ico.inactive then + ico.inactive = lUtils.instantiate(ico[1]) + for l,line in ipairs(ico.inactive) do + line[2] = line[2]:gsub("0","8") + line[3] = line[3]:gsub("0","8") + end + end + if icoSel == i then + ico = ico.inactive + else + ico = ico[1] + end + -- sync pls + lUtils.renderImg(ico,i*4,h-1) + end + tbPos[w] = { x1=i*4, y1=h-1, x2=i*4+2, y2=h } + for t=1,#w do + tbPos[w[t]] = tbPos[w] + end + end + term.setTextColor(colors.white) + if icoSel == -1 then + term.setBackgroundColor(colors.lightGray) + term.setCursorPos(timeObj.x,h) + term.write(string.rep(" ",8)) + else + term.setBackgroundColor(colors.gray) + end + term.setCursorPos((timeObj.x+(timeObj.w-1))-(#dateTxt-1),h) + term.write(dateTxt) + else + term.setCursorPos(1,h) + term.write("L") + for i,w in ipairs(tbApps) do + local top + for t=1,#w do + if w[t] == lOS.focusWin then + top = t + break + end + end + local bg,txt + if top then + bg = colors.lightGray + else + bg = colors.gray + end + if icoSel == i then + txt = colors.lightGray + else + txt = colors.white + end + --[[for y=h-(lOS.tbSize-1),h do + term.setBackgroundColor(bg) + term.setTextColor(colors.gray) + term.setCursorPos(i*4-1,y) + term.write("\149") + term.setBackgroundColor(colors.gray) + term.setTextColor(bg) + term.setCursorPos(i*4+3,y) + term.write("\149") + end]] + term.setBackgroundColor(bg) + term.setTextColor(txt) + term.setCursorPos(i*4,h) + local t1 = w[1].title:sub(1,3) + t1 = t1..string.rep(" ",3-#t1) + term.setCursorPos(i*4,h) + term.write(t1) + tbPos[w] = { x1=i*4, y1=h, x2=i*4+2, y2=h } -- x1, y1, x2, y2 + for t=1,#w do + tbPos[w[t]] = tbPos[w] + end + end + end + term.setTextColor(colors.white) + if icoSel == -1 then + term.setBackgroundColor(colors.lightGray) + term.setCursorPos(timeObj.x,h-(lOS.tbSize-1)) + term.write(string.rep(" ",8)) + else + term.setBackgroundColor(colors.gray) + end + term.setCursorPos(timeObj.x+2,h-(lOS.tbSize-1)) + term.write(lTime) + + -- notifications button + term.setCursorPos(timeObj.x+timeObj.w+1,h-(lOS.tbSize-1)) + local fg = colors.white + local bg + if icoSel == -2 then + bg = colors.lightGray + else + bg = colors.gray + end + fg,bg = lUtils.toBlit(fg),lUtils.toBlit(bg) + --[[if #lOS.notifications > 0 then + term.blit("\151\131\148",bg..bg..fg,fg..fg..bg) + term.setCursorPos(timeObj.x+timeObj.w+1,h-(lOS.tbSize-2)) + term.blit("\130\135\129",fg..fg..fg,bg..bg..bg)]] + if #lOS.notifications == 0 then + fg = lUtils.toBlit(colors.lightGray) + --[[term.blit("\151\140\148",bg..fg..fg,fg..bg..bg) + term.setCursorPos(timeObj.x+timeObj.w+1,h-(lOS.tbSize-2)) + term.blit("\130\134\129",fg..fg..fg,bg..bg..bg)]] + end + if lOS.tbSize > 1 then + term.blit("\151\131\148",bg..bg..fg,fg..fg..bg) + term.setCursorPos(timeObj.x+timeObj.w+1,h-(lOS.tbSize-2)) + term.blit("\130\135\129",fg..fg..fg,bg..bg..bg) + else + term.write("msg") + end + -- draw uh mminimize button on task bar + term.setBackgroundColor(colors.gray) + term.setTextColor(colors.lightGray) + for y=h-(lOS.tbSize-1),h do + term.setCursorPos(w,y) + term.write("\149") + end + end + local function genTabMenu() + local tabMenu = {} + local w,h = lOS.wAll.getSize() + local maxW,maxH = w-(math.floor(w/8))-math.floor(math.floor(w/8)),h-(math.floor(h/8))-math.floor(math.floor(h/8)) + local minX,minY = math.floor(w/8),math.floor(h/8) + while minY+(maxH-1) >= h-lOS.tbSize do + maxH = maxH-1 + end + local maxRows = 4 + local rowH = math.floor((maxH-1)/maxRows-2) + local minRowH = 5 + if h <= 21 then + minRowH = 4 + end + while rowH < minRowH do + maxRows = maxRows-1 + rowH = math.floor((maxH-1)/maxRows-2) + end + --#pHistory + tabMenu[1] = {w=3} + tabMenu.a = {} + for i=1,#pHistory do + local p = pHistory[i] + if p.winMode ~= "background" then + local wW,wH = p.win.getSize() + local pH = rowH + local pW = round(wW/(wH/rowH)) + local lilWin = lUtils.littlewin(p.win,pW,pH) + lilWin.proc = p + lilWin.w,lilWin.h = pW,pH + if tabMenu[#tabMenu].w+pW+1 <= maxW then + table.insert(tabMenu[#tabMenu],lilWin) + tabMenu[#tabMenu].w = tabMenu[#tabMenu].w+pW+1 + else + table.insert(tabMenu,{lilWin,w=2+pW}) + end + lilWin.row = #tabMenu + table.insert(tabMenu.a,lilWin) + end + end + local rows = math.min(#tabMenu,maxRows) + tabMenu.h = 1+rows*(rowH+2) + tabMenu.w = 0 + local cenX,cenY = math.ceil(w/2),math.ceil(h/2) + tabMenu.y = cenY-(math.floor(tabMenu.h/2)) + for i,row in ipairs(tabMenu) do + tabMenu.w = math.max(row.w,tabMenu.w) + local cX = cenX-math.floor(row.w/2) + row.x = cX + row.y = tabMenu.y+2+(i-1)*(rowH+2) + cX = cX+2 + for p,win in ipairs(row) do + win.x = cX + win.y = row.y + cX = cX+win.w+1 + end + end + tabMenu.x = cenX-(math.floor(tabMenu.w/2)) + function tabMenu.render(fromRow) + local fromRow = fromRow or 1 + term.setBackgroundColor(colors.gray) + lOS.boxClear(tabMenu.x,tabMenu.y,tabMenu.x+(tabMenu.w-1),tabMenu.y+(tabMenu.h-1)) + local rows = math.min(#tabMenu,maxRows) + local toRow = math.min(fromRow+(maxRows-1),rows) + local offset = (fromRow-1)*(rowH+2) + local count = 1 + for r=1,fromRow-1 do + count = count+#tabMenu[r] + end + local startedTimer + for r=fromRow,toRow do + local row = tabMenu[r] + for p,win in ipairs(row) do + if tabMenu.clickSel and tabMenu.clickSel == win then + if not win.small then + win.small = lUtils.littlewin(win.proc.win,win.w-2,win.h-2) + end + win.small.render(win.x+1,win.y+1-offset,true) + term.setBackgroundColor(colors.gray) + term.setTextColor(colors.white) + lUtils.border(win.x,win.y,win.x+(win.w-1),win.y+(win.h-1),nil,1) + else + win.render(win.x,win.y-offset,true) + end + term.setBackgroundColor(colors.gray) + local of = 0 + term.setCursorPos(win.x,win.y-1) + if win.proc.icon then + term.blit(win.proc.icon[1],(win.proc.icon[2] or lUtils.toBlit(term.getBackgroundColor())),(win.proc.icon[3] or lUtils.toBlit(term.getBackgroundColor()))) + term.write(" ") + of = 2 + end + term.setTextColor(colors.white) + local t = win.proc.title + if #t >= win.w-1-of then + t = t:sub(1,win.w-3-of)..".." + end + term.write(t) + term.setCursorPos(win.x+win.w-2,win.y-1) + local c = "7" + if win.closeSel then + c = "e" + end + term.blit("\149×","70",c..c) + if tabMenu.sel and tabMenu.sel == count then + lUtils.border(win.x-1,win.y-2,win.x+win.w,win.y+win.h,nil,2) + end + count = count+1 + if win.nX then + if win.x < win.nX then + win.x = win.x+1 + elseif win.x > win.nX then + win.x = win.x-1 + else + win.nX = nil + end + if not startedTimer then + startedTimer = os.startTimer(0.05) + end + end + end + end + end + return tabMenu + end + + --[[local function drawTabMenu() + term.setBackgroundColor(colors.gray) + --lOS.boxClear(math.floor(w/8),math.floor(h/8),w-(math.floor(w/8)-1),h-(math.floor(h/8)-1)) + + end]] + + lOS.tbSize = 2 -- check config if disabled, usually + local function lMenu() + function theOverlayFunction() + if previewWin then + for i,win in ipairs(previewWin) do + local bg = colors.lightGray + local tcol + if previewWin.sel and previewWin.sel == win then + tcol = colors.white + else + tcol = colors.gray + end + if tbApps[icoSel][i] == lOS.focusWin then + bg = colors.gray + if previewWin.sel and previewWin.sel == win then + tcol = colors.lightGray + else + tcol = colors.white + end + end + win.render(win.x,win.y,true) + term.setTextColor(bg) + lUtils.border(win.x,win.y-1,win.x+(win.w-1),win.y+(win.h-1),"transparent") + term.setTextColor(tcol) + term.setBackgroundColor(bg) + term.setCursorPos(win.x,win.y-1) + local txt = tbApps[icoSel][i].title:sub(1,win.w-2) + term.write(" "..txt..string.rep(" ",win.w-1-#txt)) + local w,h = lOS.wAll.getSize() + if win.y+(win.h-1) > h-(lOS.tbSize) then + win.y = win.y-1 + os.startTimer(0.05) + end + end + end + if lOS.tbSize and lOS.tbSize > 0 then + local ok, err = pcall(drawTaskbar) + if not ok then + term.setCursorPos(1,1) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.red) + term.write(err) + end + end + + -- DEBUG + --[[term.setBackgroundColor(colors.white) + term.setTextColor(colors.red) + term.setCursorPos(1,1) + print(textutils.serialize(events))]] + end + LevelOS.overlay = theOverlayFunction + while true do + local w,h = lOS.wAll.getSize() + doLog = false + local hurry = false + if #events > 0 then + e = events[1] + --[[doLog = true + os.sleep(2)]] + table.remove(events,1) + hurry = true + else + e = {os.pullEventRaw()} + end + + --[[ MANAGE NOTIFICATIONS ]] + + if #lOS.notifications > 0 then + --[[timer = timer+0.1 + if timer >= 2 then + table.remove(lOS.notifications,1) + timer = 0 + end]] + end + + --[[ END OF MANAGE NOTIFICATIONS ]] + + if #lOS.wins >= 1 then + local topwin = lOS.focusWin + if topwin ~= pHistory[1] and topwin ~= lOS.wins[0] then + for t=1,#pHistory do + if pHistory[t] == topwin then + table.remove(pHistory,t) + break + end + end + table.insert(pHistory,1,topwin) + end + end + for t=#pHistory,1,-1 do + if coroutine.status(pHistory[t][1]) == "dead" or pHistory[t].winMode == "background" then + table.remove(pHistory,t) + end + end + + if e[1] == "window_open" then + if tostring(lOS.processes[e[2]]) ~= e[3] then + for t=1,#lOS.processes do + if tostring(lOS.processes[t]) == e[3] then + e[2] = t + break + end + end + end + elseif e[1]:find("window_") then + local wins = lOS.wins + if e[1] == "window_focus" then + wins = lOS.processes + end + if tostring(wins[e[2]]) ~= e[3] then + for t=1,#wins do + if tostring(wins[t]) == e[3] then + e[2] = t + break + end + end + end + end + + doLog = true + if e[1]:find("mouse") and type(e[3]) == "number" and e[4] >= h-(lOS.tbSize-1) then + local foundwidget = false + if e[1] ~= "mouse_scroll" and e[1] ~= "mouse_move" then + for t=#lOS.wins,1,-1 do + if lOS.wins[t].winMode == "widget" then + if not lOS.sysUIlog then + lOS.sysUIlog = {} + end + table.insert(lOS.sysUIlog,"Closed win "..t.." due to event "..table.concat(e,",")..".") + os.queueEvent("window_close",t,tostring(lOS.wins[t]),"some mouse shit") + foundwidget = true + end + end + end + if foundwidget then + -- nothing + elseif e[3] <= 2 then + if e[1] == "mouse_click" then + icoSel = 0 + elseif e[1] == "mouse_up" then + local oterm = term.current() + local win = window.create(term.current(),1,1,w,h,false) + local cor = coroutine.create(startMenu) + lOS.noEvents = true + os.sleep(0) + for y=1,h do + win.setCursorPos(1,y) + win.blit(lOS.wAll.getLine(y)) + end + local oldOverlay = LevelOS.overlay + function LevelOS.overlay() + for y=1,h do + term.setCursorPos(1,y) + term.blit(win.getLine(y)) + term.setCursorPos(1,1) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.red) + end + oldOverlay() + end + term.redirect(win) + coroutine.resume(cor) + while true do + local e = {os.pullEventRaw()} + term.redirect(win) + coroutine.resume(cor,unpack(e)) + term.redirect(oterm) + if lOS.noEvents == false then + break + end + end + term.redirect(oterm) + LevelOS.overlay = oldOverlay + end + -- elseif widget area + elseif e[3] >= timeObj.x and e[4] <= h and e[4] >= h-1 then + if e[3] <= timeObj.x+(timeObj.w-1) then + if e[1] == "mouse_click" then + icoSel = -1 + elseif e[1] == "mouse_up" then + lOS.execute("LevelOS/Clock.lua") + end + elseif e[3] <= w-2 then + if e[1] == "mouse_click" then + icoSel = -2 + elseif e[1] == "mouse_up" then + --lOS.execute("LevelOS/Clock.lua") + end + else + for t=#lOS.wins,1,-1 do + if lOS.wins[t].winMode == "widget" then + if not lOS.sysUIlog then + lOS.sysUIlog = {} + end + table.insert(lOS.sysUIlog,"Closed win "..t.." due to event "..table.concat(e,",")..".") + os.queueEvent("window_close",t,tostring(lOS.wins[t]),"some timeobj thing idk") + else + os.queueEvent("window_minimize",t,tostring(lOS.wins[t]),true) + end + end + end + else + for i,v in ipairs(tbApps) do + if not tbPos[v] then + _G.lOS.sysDebug.error = "tbPos["..tostring(v).." (tbApps["..i.."])] not found." + _G.lOS.curApps = {} + _G.lOS.curPos = {} + for a,b in ipairs(tbApps) do + _G.lOS.curApps[a] = {b,tostring(b)} + end + for c,d in pairs(tbPos) do + _G.lOS.curPos[c] = d + end + lOS.sysDebug.str = tostring(v).." vs "..tostring(tbApps[i]) + lOS.sysDebug.theProblem = {v,tbApps[i],tostring(v),tostring(tbApps[i])} + _G.lOS.theError = lOS.sysDebug.error + error(lOS.sysDebug.error,0) + end + if lUtils.isInside(e[3],e[4],tbPos[v]) then + if e[1] == "mouse_click" or e[1] == "mouse_drag" or e[1] == "mouse_hover" or (previewWin and e[1] == "mouse_move" and type(e[3]) == "number") then + icoSel = i + if ((e[2] == 1 and #v > 1) or e[1] ~= "mouse_click") then + local cX + local pW = {} + _G.debugPreviewWin = pW + for p,app in ipairs(v) do + local w,h = app.win.getSize() + local nH = 7 + local nW = round(w/(h/nH)) + local x,y + y = tbPos[v].y1-nH + if not cX then + x = (tbPos[v].x1+1)-(math.floor(nW/2)) + if x < 2 then + x = 2 + elseif x+(nW-1) > ({lOS.wAll.getSize()})[1] then + x = ({lOS.wAll.getSize()})[1]-(nW-1) + end + else + x = cX + end + cX = x+nW + pW[p] = lUtils.littlewin(app.win, nW, nH) + local offset = 0 + if not previewWin then + offset = 4 + end + pW[p].x,pW[p].y = x,y+offset + pW[p].w,pW[p].h = nW,nH + end + previewWin = pW + end + elseif e[1] == "mouse_up" and icoSel == i and e[2] == 2 then + previewWin = nil + local opt = {} + local dis = {} + local h = 1 + if v[1].path then + local t + if fs.getName(v[1].path) == "main.lua" then + t = lUtils.getFileName(fs.getDir(v[1].path)) + else + t = lUtils.getFileName(v[1].path) + end + t = t:sub(1,1):upper()..t:sub(2) + table.insert(opt," \4 "..t) + h = h+2 + + -- (un)pin to/from taskbar + table.insert(opt," \25 Pin to taskbar") + dis[opt[#opt]] = true + h = h+2 + end + + if #v == 1 then + table.insert(opt," × Close") + else + table.insert(opt," × Close all") + end + h = h+2 + + local tW,tH = lOS.wAll.getSize() + local w,h = 21,7 + local x,y = tbPos[v].x1-9,tH-(lOS.tbSize)-(h-1) + if x < 2 then + x = 2 + elseif x+(w-1) > tW then + x = tW + end + local function rWin() + term.setBackgroundColor(colors.gray) + term.setTextColor(colors.lightGray) + lUtils.border(x,y,x+(w-1),y+(h-1),"fill") + for t=1,#opt do + if dis[opt[t]] then + term.setTextColor(colors.lightGray) + else + term.setTextColor(colors.white) + end + term.setCursorPos(x+1,y+(t*2)-1) + term.write(opt[t]..string.rep(" ",(w-2)-#opt[t])) + if t < #opt then + term.setCursorPos(x+1,y+(t*2)) + term.write(" ",w-2) + end + end + end + y = y+3 + local oOverlay = LevelOS.overlay + function LevelOS.overlay() + rWin() + oOverlay() + end + for t=1,3 do + os.sleep(0.05) + y = y-1 + end + os.sleep(0) + function LevelOS.overlay() + term.setBackgroundColor(colors.gray) + term.setTextColor(colors.lightGray) + lUtils.border(x,y,x+(w-1),y+(h-1)) + end + local a + runOverlay(function() a = {lUtils.oclickmenu(x,y,w,opt,nil,dis,{bg=colors.gray,fg=colors.gray,txt=colors.white})} end,false,true) + if a[1] then + if a[3] == opt[1] and v[1].path then + lOS.execute(v[1].path) + elseif (a[3] == opt[1] and not v[1].path) or (v[1].path and a[3] == opt[3]) then + for t=#lOS.wins,1,-1 do + if (lOS.wins[t] == v[1] and not v[1].path) or (v[1].path and v[1].path == lOS.wins[t].path) then + if not lOS.sysUIlog then + lOS.sysUIlog = {} + end + table.insert(lOS.sysUIlog,"Closed win "..t.." through taskbar menu.") + os.queueEvent("window_close",t,tostring(lOS.wins[t])) + end + end + for t=1,#v do + if v[t].minimized then + for p=1,#lOS.processes do + if lOS.processes[p] == v[t] then + table.remove(lOS.processes,p) + break + end + end + for p=1,#pHistory do + if pHistory[p] == v[t] then + table.remove(pHistory,p) + break + end + end + end + end + elseif v[1].path and a[3] == opt[2] then + -- nothing + end + end + LevelOS.overlay = oOverlay + elseif e[1] == "mouse_up" and icoSel == i and e[2] == 1 then + if #v == 1 then + previewWin = nil + local v = v[1] + if v == lOS.focusWin then + --[[local dur = 5 + local x,y = v.win.getPosition() + local w,h = v.win.getSize() + local gx,gy = tbPos[v].x1,tbPos[v].y1 + local gw,gh = 3,2 + for t=#lOS.wins,1,-1 do + if lOS.wins[t] == lOS.focusWin then + table.remove(lOS.wins,t) + lOS.focusWin = lOS.wins[t-1] + break + end + end + animation(v,x,y,w,h,gx,gy,gw,gh,dur) + v.minimized = true]] + local id + for k1,v1 in pairs(lOS.wins) do + if v1 == v then + id = k1 + break + end + end + os.queueEvent("window_minimize",id,tostring(v)) + --[[elseif not potato then + lOS.sysDebug.v = v]] + else + local id + for k1,v1 in pairs(lOS.processes) do + if v1 == v then + id = k1 + break + end + end + os.queueEvent("window_focus",id,tostring(v)) + end + else + -- choose from previews + lOS.noEvents = true + local pW = previewWin + while true do + local e = {os.pullEventRaw()} + if e[1] == "mouse_up" or e[1] == "mouse_drag" or e[1] == "mouse_click" then + local terminate = true + for i,win in ipairs(pW) do + local lWin = tbApps[icoSel][i] + local focusWin + for t=1,#lOS.wins do + if lOS.wins[t] == lWin then + focusWin = t + break + end + end + local fID + for t=#lOS.wins,0,-1 do + if lOS.wins[t] == lOS.focusWin then + fID = t + end + end + if e[3] >= win.x and e[4] >= win.y and e[3] <= win.x+(win.w-1) and e[4] <= win.y+(win.h-1) then + terminate = false + if e[2] == 2 and e[1] == "mouse_up" then + local dis = {} + if not (lWin.snap or lWin.minimized) then + dis["= Restore"] = true + end + if lWin.snap and lWin.snap.x and lWin.snap.y then + dis["+ Maximize"] = true + end + if lWin.minimized then + dis["- Minimize"] = true + end + lOS.cWin = 0 + local a + local oOverlay = LevelOS.overlay + LevelOS.overlay = nil + runOverlay(function() a = {lUtils.oclickmenu(e[3],e[4],19,{"= Restore",{"- Minimize","+ Maximize"},"× Close Ctrl+W"},nil,dis)} end,false,true) + LevelOS.overlay = oOverlay + if a[1] then + if a[3] == "= Restore" then + if lWin.minimized then + local dur = 5 + local x,y = tbPos[lWin].x1,tbPos[lWin].y1 + local w,h = 3,2 + local gx,gy = lWin.win.getPosition() + local gw,gh = lWin.win.getSize() + animation(lWin,x,y,w,h,gx,gy,gw,gh,dur) + lWin.minimized = nil + table.insert(lOS.wins,lWin) + lOS.focusWin = lWin + elseif lWin.snap then + --os.queueEvent("timer",9999999) + os.queueEvent("window_reposition",focusWin,tostring(lWin),lWin.snap.oPos[1],lWin.snap.oPos[2],unpack(lWin.snap.oSize)) + lWin.snap = nil + end + elseif a[3] == "- Minimize" then + --os.queueEvent("timer",9999999) + os.queueEvent("window_minimize",focusWin,tostring(lWin)) + elseif a[3] == "+ Maximize" then + os.queueEvent("timer",9999999) + local totalW,totalH = lOS.wAll.getSize() + lWin.snap = {x=true,y=true,oPos={lWin.win.getPosition()},oSize={lWin.win.getSize()}} + os.queueEvent("window_reposition",focusWin,tostring(lWin),1,2,totalW,totalH-1) + elseif a[3] == "× Close Ctrl+W" then + if lWin.env and lWin.env.LevelOS and type(lWin.env.LevelOS.close) == "function" then + if lWin.minimized then + local dur = 5 + local x,y = tbPos[lWin].x1,tbPos[lWin].y1 + local w,h = 3,2 + local gx,gy = lWin.win.getPosition() + local gw,gh = lWin.win.getSize() + animation(lWin,x,y,w,h,gx,gy,gw,gh,dur) + lWin.minimized = nil + table.insert(lOS.wins,lWin) + lOS.focusWin = lWin + end + --lWin[1] = coroutine.create(lWin.env.LevelOS.close) + --lWin.env.LevelOS.close = nil + runLevelOSclose(lWin) + else + if focusWin then + --os.queueEvent("timer",9999999) + --[[if not lOS.sysUIlog then + lOS.sysUIlog = {} + end + table.insert(lOS.sysUIlog,"Closed win "..focusWin.." through taskbar submenu.")]] + os.queueEvent("window_close",focusWin,tostring(lWin)) + else + for t=1,#lOS.processes do + if lOS.processes[t] == lWin then + table.remove(lOS.processes,t) + break + end + end + for t=1,#pHistory do + if pHistory[t] == lWin then + table.remove(pHistory,t) + break + end + end + end + end + end + end + previewWin = nil + lOS.noEvents = false + break + elseif e[2] == 1 and e[1] == "mouse_up" then + -- switch + if not (focusWin and focusWin == fID) then + local v = lWin + if v.minimized then + local dur = 5 + local x,y = tbPos[v].x1,tbPos[v].y1 + local w,h = 3,2 + local gx,gy = v.win.getPosition() + local gw,gh = v.win.getSize() + animation(v,x,y,w,h,gx,gy,gw,gh,dur) + v.minimized = nil + else + for i=1,#lOS.wins do + if lOS.wins[i] == v then + table.remove(lOS.wins,i) + break + end + end + end + table.insert(lOS.wins,v) + lOS.focusWin = v + end + previewWin = nil + lOS.noEvents = false + break + else + pW.sel = win + end + end + end + if terminate or not previewWin then + icoSel = nil + previewWin = nil + lOS.noEvents = false + break + end + end + if e[1] == "mouse_up" and previewWin then + previewWin.sel = nil + end + end + end + end + break + end + end + end + elseif e[1] == "window_open" then + local v = lOS.processes[e[2]] + if v and tostring(v) == e[3] then + local dur = 2 + local gx,gy = v.win.getPosition() + local gw,gh = v.win.getSize() + local x,y = gx+4,gy+2 + local w,h = gw-8,gh-4 + local tW,tH = lOS.wAll.getSize() + if (v.winMode == "widget" or v.winMode == "borderless") then + dur = 4 + if gy > 2 and gy+(gh-1) == tH-lOS.tbSize then + x,y = gx,tH-lOS.tbSize + w,h = gw,gh + elseif gx+(gw-1) == tW then + x,y = tW,gy + w,h = gw,gh + end + end + animation(v,x,y,w,h,gx,gy,gw,gh,dur) + table.insert(lOS.wins,v) + if e[4] ~= false then + lOS.focusWin = v + end + end + elseif e[1] == "window_focus" and lOS.processes[e[2]] and tostring(lOS.processes[e[2]]) == e[3] then + local v = lOS.processes[e[2]] + if v.minimized then + local dur = 5 + local x,y = tbPos[v].x1,tbPos[v].y1 + local w,h = 3,2 + local gx,gy = v.win.getPosition() + local gw,gh = v.win.getSize() + animation(v,x,y,w,h,gx,gy,gw,gh,dur) + v.minimized = nil + else + for i=1,#lOS.wins do + if lOS.wins[i] == v then + table.remove(lOS.wins,i) + break + end + end + end + table.insert(lOS.wins,v) + lOS.focusWin = v + elseif e[1] == "window_minimize" and lOS.wins[e[2]] and tostring(lOS.wins[e[2]]) == e[3] then + local v = lOS.wins[e[2]] + local dur = 5 + if hurry or #events > 0 or e[4] then + dur = 3 + end + local x,y = v.win.getPosition() + local w,h = v.win.getSize() + local gx,gy = tbPos[v].x1,tbPos[v].y1 + local gw,gh = 3,2 + table.remove(lOS.wins,e[2]) + if lOS.focusWin == v then + lOS.focusWin = lOS.wins[e[2]-1] + end + animation(v,x,y,w,h,gx,gy,gw,gh,dur) + v.minimized = true + elseif e[1] == "window_reposition" and lOS.wins[e[2]] and tostring(lOS.wins[e[2]]) == e[3] then + local win = lOS.wins[e[2]] + local x,y = win.win.getPosition() + local w,h = win.win.getSize() + local _,focusWin,pointer,gx,gy,gw,gh = unpack(e) + local dur = 2 + table.remove(lOS.wins,e[2]) + animation(win,x,y,w,h,gx,gy,gw,gh,dur) + table.insert(lOS.wins,win) + win.win.reposition(gx,gy,gw,gh) + os.queueEvent("term_resize") + elseif (e[1] == "window_close" and tostring(lOS.wins[e[2]]) == e[3]) or (e[1] == "process_close" and tostring(lOS.processes[e[2]]) == e[3]) --[[or (e[1] == "key_up" and lUtils.isHolding(keys.leftCtrl) and e[2] == keys.w and #lOS.wins > 0 and lOS.focusWin and lOS.focusWin ~= lOS.wins[0] and not lOS.focusWin.noShortcuts)]] then + local focusWin + if e[1] == "window_close" then + focusWin = e[2] + elseif e[1] == "process_close" then + for i,lwin in pairs(lOS.wins) do + if lOS.wins[i] == lOS.processes[e[2]] then + focusWin = i + e[1] = "window_close" + e[2] = focusWin + break + end + end + else + for t=#lOS.wins,1,-1 do + if lOS.wins[t] == lOS.focusWin then + focusWin = t + break + end + end + end + local win + if focusWin then + win = lOS.wins[focusWin] + elseif e[1] == "process_close" then + win = lOS.processes[e[2]] + end + if win.env and win.env.LevelOS and type(win.env.LevelOS.close) == "function" then + --[[win[1] = coroutine.create(win.env.LevelOS.close) + win.env.LevelOS.close = nil]] + runLevelOSclose(win) + elseif e[1] == "process_close" then + table.remove(lOS.processes,e[2]) + elseif e[1] == "window_close" then + win.closing = true + local x,y = win.win.getPosition() + local w,h = win.win.getSize() + local gx,gy = x+4,y+2 + local gw,gh = w-8,h-4 + local tW,tH = lOS.wAll.getSize() + local dur = 2 + if (win.winMode == "widget" or win.winMode == "borderless") then + dur = 4 + if y > 2 and y+(h-1) == tH-lOS.tbSize then + gx,gy = x,tH-lOS.tbSize + gw,gh = w,h + elseif x+(w-1) == tW then + gx,gy = tW,y + gw,gh = w,h + end + end + if lOS.wins[focusWin].winMode ~= "background" then + local proc = lOS.processes + for i=1,#proc do + if lOS.wins[focusWin] == proc[i] then + table.remove(proc,i) + end + end + end + table.remove(lOS.wins,focusWin) + if lOS.focusWin == win then + lOS.focusWin = lOS.wins[focusWin-1] -- good + end + animation(win,x,y,w,h,gx,gy,gw,gh,dur) + for t=1,#pHistory do + if pHistory[t] == win then + table.remove(pHistory,t) + break + end + end + end + elseif e[1] == "key" then + if e[2] == keys.f5 and #lOS.wins > 0 then + if lOS.focusWin.winMode == "fullscreen" and lOS.focusWin.fullscreen then + local proc = lOS.focusWin + proc.winMode = "windowed" + proc.win.reposition(proc.fullscreen.pos[1],proc.fullscreen.pos[2],proc.fullscreen.size[1],proc.fullscreen.size[2],lOS.wAll) + proc.win.setVisible(false) + proc.fullscreen = nil + else + local proc = lOS.focusWin + proc.winMode = "fullscreen" + end + elseif e[2] == keys.tab and lUtils.isHolding(keys.leftCtrl) and #pHistory > 0 then + if lOS.focusWin.winMode == "fullscreen" then + local win = lOS.focusWin + for t=#lOS.wins,1,-1 do + if lOS.wins[t] == lOS.focusWin then + table.remove(lOS.wins,t) + break + end + end + win.minimized = true + end + local oOverlay = LevelOS.overlay + local tabMenu = genTabMenu() + lOS.sysDebug.tabMenu = tabMenu + tabMenu.sel = math.min(#pHistory,2) + function LevelOS.overlay() + oOverlay() + tabMenu.render() + end + lOS.noEvents = true + while true do + if tabMenu.sel and not tabMenu.a[tabMenu.sel] then + if #tabMenu.a < 1 then + LevelOS.overlay = oOverlay + break + else + while tabMenu.sel > #tabMenu.a do + tabMenu.sel = tabMenu.sel-1 + end + end + end + local e = {os.pullEventRaw()} + lOS.noEvents = true + if (e[1] == "key_up" and e[2] == keys.leftCtrl) or e[1] == "tabMenu_close" then + LevelOS.overlay = oOverlay + if tabMenu.sel then + local proc = tabMenu.a[tabMenu.sel].proc + if proc.minimized then + for t=1,#lOS.processes do + if lOS.processes[t] == proc then + local dur = 5 + local x,y = tbPos[proc].x1,tbPos[proc].y1 + local w,h = 3,2 + local gx,gy = proc.win.getPosition() + local gw,gh = proc.win.getSize() + animation(proc,x,y,w,h,gx,gy,gw,gh,dur) + proc.minimized = nil + table.insert(lOS.wins,proc) + lOS.focusWin = proc + break + end + end + else + for t=1,#lOS.wins do + if lOS.wins[t] == proc then + table.remove(lOS.wins,t) + break + end + end + table.insert(lOS.wins,proc) + lOS.focusWin = proc + end + end + break + elseif e[1] == "key" and e[2] == keys.tab or e[2] == keys.right then + tabMenu.sel = tabMenu.sel+1 + if tabMenu.sel > #tabMenu.a then + tabMenu.sel = 1 + end + elseif e[1] == "key" and e[2] == keys.left then + tabMenu.sel = tabMenu.sel-1 + if tabMenu.sel < 1 then + tabMenu.sel = #tabMenu.a + end + elseif e[1]:find("mouse") and e[2] == 1 and type(e[3]) == "number" then + if e[3] >= tabMenu.x and e[4] >= tabMenu.y and e[3] <= tabMenu.x+(tabMenu.w-1) and e[4] <= tabMenu.y+(tabMenu.h-1) then + tabMenu.closing = nil + for i,win in ipairs(tabMenu.a) do + if e[3] >= win.x and e[4] >= win.y and e[3] <= win.x+(win.w-1) and e[4] <= win.y+(win.h-1) then + if e[1] == "mouse_click" or e[1] == "mouse_drag" then + tabMenu.clickSel = win + elseif e[1] == "mouse_up" then + tabMenu.sel = i + os.queueEvent("tabMenu_close") + end + elseif e[3] == win.x+(win.w-1) and e[4] == win.y-1 then + if e[1] == "mouse_click" then + win.closeSel = true + elseif e[1] == "mouse_up" and win.closeSel then + local lWin = win.proc + local focusWin + for t=1,#lOS.wins do + if lOS.wins[t] == lWin then + focusWin = t + end + end + if lWin.env and lWin.env.LevelOS and type(lWin.env.LevelOS.close) == "function" then + if lWin.minimized then + local dur = 5 + local x,y = tbPos[lWin].x1,tbPos[lWin].y1 + local w,h = 3,2 + local gx,gy = lWin.win.getPosition() + local gw,gh = lWin.win.getSize() + animation(lWin,x,y,w,h,gx,gy,gw,gh,dur) + lWin.minimized = nil + table.insert(lOS.wins,lWin) + lOS.focusWin = lWin + end + --[[lWin[1] = coroutine.create(lWin.env.LevelOS.close) + lWin.env.LevelOS.close = nil]] + runLevelOSclose(lWin) + else + if focusWin then + --os.queueEvent("timer",9999999) + --os.queueEvent("window_close",focusWin,tostring(lWin)) + local win = lWin + local x,y = win.win.getPosition() + local w,h = win.win.getSize() + local gx,gy = x+4,y+2 + local gw,gh = w-8,h-4 + local tW,tH = lOS.wAll.getSize() + local dur = 2 + if (win.winMode == "widget" or win.winMode == "borderless") and y > 2 and y+(h-1) == tH-lOS.tbSize then + dur = 4 + gx,gy = x,tH-lOS.tbSize + gw,gh = w,h + end + if lOS.wins[focusWin].winMode ~= "background" then + local proc = lOS.processes + for i=1,#proc do + if lOS.wins[focusWin] == proc[i] then + table.remove(proc,i) + end + end + end + table.remove(lOS.wins,focusWin) + animation(win,x,y,w,h,gx,gy,gw,gh,dur) + for t=1,#pHistory do + if pHistory[t] == win then + table.remove(pHistory,t) + break + end + end + else + for t=1,#lOS.processes do + if lOS.processes[t] == lWin then + table.remove(lOS.processes,t) + break + end + end + for t=1,#pHistory do + if pHistory[t] == lWin then + table.remove(pHistory,t) + break + end + end + end + end + --[[LevelOS.overlay = oOverlay + break]] + table.remove(tabMenu.a,i) + local row = tabMenu[win.row] + for t=1,#row do + if win == row[t] then + table.remove(row,t) + break + end + end + if #row > 0 then + row.w = 3 + for i,win in ipairs(row) do + row.w = row.w+win.w+1 + end + local w,h = lOS.wAll.getSize() + local cenX,cenY = math.ceil(w/2),math.ceil(h/2) + local cX = cenX-math.floor(row.w/2) + --row.x = cX + cX = cX+2 + for p,win in ipairs(row) do + win.nX = cX + cX = cX+win.w+1 + end + else + local sel = tabMenu.sel + tabMenu = genTabMenu() + tabMenu.sel = sel + end + break + end + else + win.closeSel = nil + end + end + else + if e[1] == "mouse_click" then + tabMenu.closing = true + elseif e[1] == "mouse_up" and tabMenu.closing then + LevelOS.overlay = oOverlay + break + end + end + if e[1] == "mouse_up" then + tabMenu.clickSel = nil + end + end + end + lOS.noEvents = false + end + end + if e[1] == "mouse_up" then + previewWin = nil + icoSel = nil + end + end + end + local function internet() + while true do + lOS.checkinternet() + os.sleep(30) + end + end + local function fade() + local native = {} + local col = colors.black + if lOS.isCyan then + col = colors.cyan + end + for c=0,15,1 do + native[2^c] = {--[[lOS.wins[0].win.getPaletteColor(2^c)]]term.nativePaletteColor(col)} + end + for p,process in pairs(lOS.processes) do + for c=0,15,1 do + process.win.setPaletteColor(2^c,unpack(native[2^c])) + if process.owin then + process.owin.setPaletteColor(2^c,unpack(native[2^c])) + end + end + end + local prog = 1 + local dur = 5 + local function col(c,d) + local n = native[c] + local cur = {lOS.wins[0].win.getPaletteColor(c)} + local rgb = {} + for t=1,3 do + local st = (d[t]-n[t])/dur + --rgb[t] = cur[t]+st + rgb[t] = n[t]+st*prog + end + return rgb[1],rgb[2],rgb[3] + end + for t=1,dur do + prog = t + for p,process in pairs(lOS.processes) do + for c=0,15,1 do + if t < dur then + process.win.setPaletteColor(2^c,col(2^c,{term.nativePaletteColor(2^c)})) + if process.owin then + process.owin.setPaletteColor(2^c,col(2^c,{term.nativePaletteColor(2^c)})) + end + else + process.win.setPaletteColor(2^c,term.nativePaletteColor(2^c)) + if process.owin then + process.owin.setPaletteColor(2^c,term.nativePaletteColor(2^c)) + end + end + end + end + os.sleep(0.05) + end + end + parallel.waitForAny(logEvents,lMenu,function() if lOS.isCyan then fade() end lOS.fadeComplete = true while true do pcall(internet) end end) +end +local ok,err = pcall(UI) +if not ok then + lOS.systemUIerror = err + lOS.bsod(err) +else + lOS.systemUIerror = "Unknown error: "..tostring(err) +end +lOS.noEvents = false \ No newline at end of file diff --git a/LevelOS/Task_Manager.lua b/LevelOS/Task_Manager.lua new file mode 100644 index 0000000..3db334a --- /dev/null +++ b/LevelOS/Task_Manager.lua @@ -0,0 +1,451 @@ +local scroll = 0 +local procs = lOS.processes +local sel = {} +local pSel = {} +local expanded = {} +local cEnd = false +local scrollable = false +local display = 1 +local function percentage(part,full) + if display == 1 then + local f = math.min(math.ceil((part/full)*1000)/10,100) + return f.."%" + else + return math.floor(part+0.5).." ms" + end +end +function _G.invWrite(txt) + local fg = lUtils.toBlit(term.getTextColor()):rep(#txt) + local bg = lUtils.toBlit(term.getBackgroundColor()):rep(#txt) + term.blit(txt,bg,fg) +end + +local function drawProcs(x1,y1,x2,y2,e) + if e[1] == "mouse_scroll" then + if scroll+e[2] >= 0 and (e[2] <= -1 or scrollable) then + scroll = scroll+e[2] + end + end + term.setBackgroundColor(colors.yellow) + term.setTextColor(colors.lightGray) + --lOS.boxClear(x2-9,y1,x2,y2-2) + for y=y1,y2-2 do + term.setBackgroundColor(colors.white) + term.setCursorPos(x2-10,y) + invWrite("\149") + term.setBackgroundColor(colors.yellow) + term.write(string.rep(" ",10)) + term.setBackgroundColor(colors.white) + term.write("\149") + end + scrollable = false + cY = y1-scroll + local pr = {} + local bg = {} + + y2 = y2-2 + + for p=1,#procs do + if procs[p].path then + if procs[p].win and procs[p].winMode ~= "background" then + if not pr[procs[p].path] then + if expanded[procs[p].path] == nil then + expanded[procs[p].path] = false + end + pr[#pr+1] = {expanded=expanded[procs[p].path],path=procs[p].path,pr={procs[p]},yieldTime=procs[p].yieldTime} + pr[procs[p].path] = pr[#pr] + else + for t=1,#pr do + if pr[t].path == procs[p].path then + pr[t].pr[#pr[t].pr+1] = procs[p] + if procs[p].yieldTime then + pr[t].yieldTime = pr[t].yieldTime+procs[p].yieldTime + end + end + end + end + else + bg[#bg+1] = procs[p] + end + end + end + local clickmenu = {} + -- TEMP + + if type(pSel) == "table" then + sel = pSel + elseif type(pSel) == "number" then + sel = pr[pSel] + else + sel = {} + end + --if pr[2] then + --pr[2].expanded = true + --end + if cY+1 >= y1 and cY+1 <= y2 then + term.setTextColor(colors.blue) + term.setBackgroundColor(colors.white) + term.setCursorPos(x1,cY+1) + term.write("Programs ("..#pr..")") + end + cY = cY+2 + local previousColor1 + local previousColor2 + for p=1,#pr+1 do + local fg1 + local bg1 + local fg2 + local bg2 + + local WARN = colors.yellow + if p <= #pr and pr[p].yieldTime then + local f = pr[p].yieldTime/lOS.yieldTime + if f > 0.7 then + WARN = colors.red + elseif f > 0.4 then + WARN = colors.orange + end + end + fg1 = previousColor1 or colors.white + fg2 = previousColor2 or colors.yellow + if p > #pr or sel ~= pr[p] then + bg1 = colors.white + bg2 = WARN + else + bg1 = colors.lightBlue + bg2 = colors.cyan + end + previousColor1 = bg1 + previousColor2 = bg2 + if cY >= y1 and cY <= y2 then + term.setTextColor(fg1) + term.setBackgroundColor(bg1) + term.setCursorPos(x1,cY) + term.write(string.rep("\131",x2-(x1-1)-11)) + + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + invWrite("\149") + term.setBackgroundColor(bg1) + term.setTextColor(fg2) + term.setBackgroundColor(bg2) + term.write(string.rep("\131",10)) + end + term.setTextColor(fg1) + term.setBackgroundColor(bg1) + if p > #pr then + break + end + cY = cY+1 + if cY >= y1 and cY <= y2 then + --term.setTextColor(colors.black) + clickmenu[cY] = p + term.setCursorPos(x1,cY) + term.write(string.rep(" ",x2-(x1-1)-11)) + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + invWrite("\149") + term.setBackgroundColor(bg2) + term.write(string.rep(" ",10)) + term.setCursorPos(x1+1,cY) + term.setBackgroundColor(bg1) + if not pr[p].expanded then + term.setTextColor(colors.lightGray) + term.write("\16 ") + else + term.setTextColor(colors.gray) + term.write("\31 ") + end + term.setTextColor(colors.black) + local path = pr[p].path + if fs.getName(path) == "main.lua" then + path = fs.getDir(path) + end + local t = lUtils.getFileName(path) + t = t:sub(1,1):upper()..t:sub(2) + term.write(t.." ("..#pr[p].pr..")") + local txt + if pr[p].yieldTime then + txt = percentage(pr[p].yieldTime,lOS.yieldTime) + else + txt = "???" + end + term.setCursorPos(x2-#txt,cY) + term.setBackgroundColor(bg2) + term.write(txt) + end + cY = cY+1 + if pr[p].expanded then + for t=1,#pr[p].pr do + if cY >= y1 and cY <= y2 then + clickmenu[cY] = pr[p].pr[t] + local bg3 + local bg4 + if sel == pr[p].pr[t] or sel == pr[p] then + bg3 = colors.lightBlue + bg4 = colors.cyan + else + bg3 = colors.white + bg4 = WARN + end + term.setBackgroundColor(bg3) + if not (sel == pr[p]) then + term.setCursorPos(x1+3,cY) + term.write(string.rep(" ",(x2)-(x1+2)-11)) + else + term.setCursorPos(x1,cY) + term.write(string.rep(" ",x2-(x1-1)-11)) + end + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + invWrite("\149") + term.setBackgroundColor(bg4) + term.write(string.rep(" ",10)) + term.setBackgroundColor(bg3) + term.setTextColor(colors.black) + term.setCursorPos(x1+4,cY) + term.write(pr[p].pr[t].title) + local txt + if pr[p].pr[t].yieldTime then + txt = percentage(pr[p].pr[t].yieldTime,lOS.yieldTime) + else + txt = "???" + end + term.setCursorPos(x2-#txt,cY) + term.setBackgroundColor(bg4) + term.write(txt) + end + cY = cY+1 + end + end + end + -- draw bg processes (no extended) + if cY+1 >= y1 and cY+1 <= y2 then + term.setTextColor(colors.blue) + term.setBackgroundColor(colors.white) + term.setCursorPos(x1,cY+1) + term.write("Background Processes ("..#bg..")") + end + cY = cY+2 + local previousColor1 + local previousColor2 + for p=1,#bg+1 do + local fg1 + local bg1 + local fg2 + local bg2 + + local WARN = colors.yellow + if p <= #bg and bg[p].yieldTime then + local f = bg[p].yieldTime/lOS.yieldTime + if f > 0.7 then + WARN = colors.red + elseif f > 0.4 then + WARN = colors.orange + end + end + fg1 = previousColor1 or colors.white + fg2 = previousColor2 or colors.yellow + if p > #bg or sel ~= bg[p] then + bg1 = colors.white + bg2 = WARN + else + bg1 = colors.lightBlue + bg2 = colors.cyan + end + previousColor1 = bg1 + previousColor2 = bg2 + if cY >= y1 and cY <= y2 then + term.setTextColor(fg1) + term.setBackgroundColor(bg1) + term.setCursorPos(x1,cY) + term.write(string.rep("\131",x2-(x1-1)-11)) + + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + invWrite("\149") + term.setBackgroundColor(bg1) + term.setTextColor(fg2) + term.setBackgroundColor(bg2) + term.write(string.rep("\131",10)) + end + term.setTextColor(fg1) + term.setBackgroundColor(bg1) + if p > #bg then + break + end + cY = cY+1 + if cY >= y1 and cY <= y2 then + --term.setTextColor(colors.black) + clickmenu[cY] = bg[p] + term.setCursorPos(x1,cY) + term.write(string.rep(" ",x2-(x1-1)-11)) + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + invWrite("\149") + term.setBackgroundColor(bg2) + term.write(string.rep(" ",10)) + term.setCursorPos(x1+1,cY) + term.setBackgroundColor(bg1) + if sel ~= bg[p] then + term.setBackgroundColor(colors.white) + else + term.setBackgroundColor(colors.lightBlue) + end + term.write(" ") + term.setTextColor(colors.black) + local t = "" + if bg[p].title then + t = bg[p].title + else + t = bg[p].path + end + term.write(t) + local txt + if bg[p].yieldTime then + txt = percentage(bg[p].yieldTime,lOS.yieldTime) + else + txt = "???" + end + term.setCursorPos(x2-#txt,cY) + term.setBackgroundColor(bg2) + term.write(txt) + end + cY = cY+1 + end + if cY > y2 then + scrollable = true + end + term.setCursorPos(1,y2+1) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.gray) + local w,h = term.getSize() + term.write(string.rep("\131",w)) + --term.setCursorPos(x1,y2+2) + local txt = "End Task" + term.setCursorPos(x2-(string.len(txt)),y2+2) + if sel.path then + if not cEnd then + term.setTextColor(colors.blue) + else + term.setTextColor(colors.lightBlue) + end + else + term.setTextColor(colors.lightGray) + end + term.write(txt) + + if e[1] == "mouse_click" then + if e[3] >= x1 and e[3] <= x2 and e[4] >= y1 and e[4] <= y2 then + sel = {} + pSel = {} + end + if sel.path and e[4] == y2+2 and e[3] >= x2-string.len(txt) and e[3] <= x2-1 then + term.setCursorPos(x2-(string.len(txt)),y2+2) + term.setTextColor(colors.lightBlue) + term.write(txt) + cEnd = true + end + if clickmenu[e[4]] then + pSel = clickmenu[e[4]] + if type(pSel) == "number" and e[3] == x1+1 then + if expanded[pr[pSel].path] == false then + expanded[pr[pSel].path] = true + else + expanded[pr[pSel].path] = false + end + end + end + elseif e[1] == "mouse_up" then + cEnd = false + if sel.path and e[4] == y2+2 and e[3] >= x2-string.len(txt) and e[3] <= x2-1 then + local t = 1 + while true do + if lOS.processes[t] == nil then break end + if type(sel[1]) == "thread" then + if lOS.processes[t] == sel then + for i=1,#lOS.wins do + if lOS.wins[i] == lOS.processes[t] then + table.remove(lOS.wins,i) + end + end + table.remove(lOS.processes,t) + break + else + t = t+1 + end + else + if lOS.processes[t].path == sel.path then + for i=1,#lOS.wins do + if lOS.wins[i] == lOS.processes[t] then + table.remove(lOS.wins,i) + end + end + table.remove(lOS.processes,t) + else + t = t+1 + end + end + end + sel = {} + pSel = {} + end + end +end +-- sel wont work since pr gets regenerated every time +-- it will work for subprocesses tho +local w,h = term.getSize() +term.setBackgroundColor(colors.white) +term.clear() +while true do + e = {os.pullEvent()} + term.setBackgroundColor(colors.white) + term.clear() + local w,h = term.getSize() + term.setCursorPos(2,3) + term.setTextColor(colors.blue) + term.write("Name") + term.setCursorPos(w-6,3) + term.write("Yield") + term.setTextColor(colors.black) + local txt + if lOS.yieldTime then + txt = math.ceil(lOS.yieldTime).." ms" + else + txt = "???" + end + term.setCursorPos((w-1)-#txt,2) + term.write(txt) + term.setCursorPos(1,4) + term.setTextColor(colors.lightGray) + term.setBackgroundColor(colors.white) + term.write(string.rep("\131",w)) + for y=1,3 do + term.setCursorPos(w-11,y) + invWrite("\149") + term.setCursorPos(w,y) + term.write("\149") + end + term.setCursorPos(w-11,4) + invWrite("\148") + term.setBackgroundColor(colors.yellow) + term.write(string.rep("\131",10)) + term.setBackgroundColor(colors.white) + term.setCursorPos(w,4) + term.write("\151") + drawProcs(2,5,w-1,h,e) + if e[1] == "key_up" and e[2] == keys.t and lUtils.isHolding(keys.leftShift) then + term.setBackgroundColor(colors.black) + term.clear() + term.setCursorPos(1,1) + term.setTextColor(colors.red) + print("Terminated") + return + elseif e[1] == "mouse_click" and e[2] == 2 and lUtils.isInside(e[3],e[4],{x1=w-10,y1=1,x2=w-1,y2=4}) then + if display == 1 then + lOS.contextmenu(e[3],e[4],0,{{txt="Switch to ms",action=function() display = 2 end}}) + else + lOS.contextmenu(e[3],e[4],0,{{txt="Switch to %",action=function() display = 1 end}}) + end + end +end \ No newline at end of file diff --git a/LevelOS/assets/Circle_Symbols.limg b/LevelOS/assets/Circle_Symbols.limg new file mode 100644 index 0000000..ab836e5 --- /dev/null +++ b/LevelOS/assets/Circle_Symbols.limg @@ -0,0 +1,37 @@ +{ + { + { + "‡€”‚", + "TT0Tb", + "bbbbT", + }, + { + "€‰•", + "T000b", + "bbbbT", + }, + { + "‹ˆŒŸ", + "b00bb", + "TbbTT", + }, + }, + { + { + "‡€€‚", + "TTTTb", + "bbbbT", + }, + { + "€˜•", + "T000b", + "bbbbT", + }, + { + "‹‚€Ÿ", + "b0Tbb", + "TbbTT", + }, + }, + type = "lImg", +} \ No newline at end of file diff --git a/LevelOS/assets/Compact_Icons.limg b/LevelOS/assets/Compact_Icons.limg new file mode 100644 index 0000000..d73fab6 --- /dev/null +++ b/LevelOS/assets/Compact_Icons.limg @@ -0,0 +1,110 @@ +{ + { + { + "—‹", + "TT0", + "00T", + }, + { + "Š…", + "000", + "TTT", + }, + }, + { + { + "‡‹", + "TT0", + "00T", + }, + { + "ƒ‡", + "000", + "TTT", + }, + }, + { + { + "‡ƒ‹", + "TTT", + "000", + }, + { + "›‡", + "000", + "TTT", + }, + }, + { + { + "‡‹ƒ", + "TTT", + "000", + }, + { + "‹‡€", + "00T", + "TTT", + }, + }, + { + { + "ˆ€", + "00T", + "TTT", + }, + { + "‚ˆ„", + "000", + "TTT", + }, + }, + { + { + "Ÿœ’", + "T00", + "0TT", + }, + { + "‚", + "000", + "TTT", + }, + }, + { + { + "•ƒ•", + "TT0", + "00T", + }, + { + "Š…", + "000", + "TTT", + }, + }, + { + { + "œ“ƒ", + "0TT", + "T00", + }, + { + "Ž", + "000", + "TTT", + }, + }, + { + { + "€‘‹", + "TTT", + "T00", + }, + { + "˜", + "000", + "TTT", + }, + }, +} \ No newline at end of file diff --git a/LevelOS/assets/Desktop_Icons.limg b/LevelOS/assets/Desktop_Icons.limg new file mode 100644 index 0000000..3b8cdbb --- /dev/null +++ b/LevelOS/assets/Desktop_Icons.limg @@ -0,0 +1 @@ +{{{"€•‹"," 9 ","9bb",},{"€€€"," ","999",},{"€€€"," ","999",},},{{"€•‹"," 9 ","9bb",},{"€€€"," ","999",},{"lua","000","bbb",},},{{""," ","777",},{"••€","8b ","b88",},{"ƒƒƒ","888"," ",},},{{"€•‹"," 8 ","877",},{"€€€"," ","888",},{"€€€"," ","888",},},{{"€•‹"," 8 ","877",},{"€€€"," ","888",},{"txt","000","777",},},{{"€•‹"," 8 ","877",},{"€€€"," ","888",},{"cfg","000","777",},},{{"€€•"," 4","44 ",},{"€€‚"," ","444",},{"€€€"," ","444",},},{{"€•‹"," e ","e11",},{"€€€"," ","eee",},{"gui","000","111",},},{{"€•‹"," 5 ","5dd",},{"€€€"," ","555",},{"sml","000","ddd",},},{{""," ","ccc",},{"•‹•","c43","3dc",},{"ƒƒƒ","ccc"," ",},},{{"","afa","faf",},{"ƒƒƒ","faf","afa",},{"","faf"," ",},},{{"˜ƒ›","bb "," b",},{"‰†","b b"," b ",},{"˜Œ›","bb "," b",},},{{""," ","888",},{"•€•","8 b","bb8",},{"Œ€Œ"," ","888",},},{{"Ÿ•‹","00 ","988",},{"ˆŒ„","999","000",},{"ŠŠ…","999","000",},},{{"€ƒ€"," "," b ",},{"€"," ","bbb",},{"‚","bbb"," ",},},{{"Ÿ‹€"," ","88 ",},{"Ÿ…","888"," ",},{"‚ƒ€","88 "," ",},},{{""," ","000",},{"•‡•","03d","3d0",},{"ƒƒƒ","000"," ",},},{{"€•”"," b"," b ",},{"€•Š"," b"," b ",},{"‘Ÿ€"," b ","b ",},},{{"Ÿ€"," 9","9 ",},{"•€•","9 "," 9",},{"‚€","9 9"," ",},},{{"Ÿ•","4b4","b4 ",},{"ƒ","bb9","494",},{"€€€"," ","444",},},{{"€€€"," "," ",},{"Š…","888","eee",},{"•Œ•","e78","88e",},},} \ No newline at end of file diff --git a/LevelOS/assets/QR_Code.limg b/LevelOS/assets/QR_Code.limg new file mode 100644 index 0000000..960b668 --- /dev/null +++ b/LevelOS/assets/QR_Code.limg @@ -0,0 +1 @@ +{{{"€€Ÿ€€•"," 000b b00 b 000b0","0bbb000bb000bbb0b",},{"€•ƒ”•Šƒ”‰†•ƒ”••"," b0bb00b0bbbb0bb0","00b00bb0b0000b00b",},{"€ŠŒŽŠ……ˆƒŒŽŠŒŽ••"," 00000000000000b0","0bbbbbbbbbbbbbb0b",},{"€›•‚–œˆ‚ˆ›˜ƒ€•"," 0bbbb0bbbbb0b0 0","0b0000b00000b0b0b",},{"€†–˜€Œ•Ÿ€€–ŒˆŒ€•"," b00 bb0 0b0b b0","00bbb00900b0b000b",},{"€“•‰••€™‹™–‘•"," 0b0bbb00 0bbbbb0","0b0b000990b00000b",},{"€ƒž™ŠŽŠ„€ž‰”•"," 0b00000b00 b0bb0","0b0bbbbb0bb00b00b",},{"€…ŠŽŸˆ™›ƒ‚†Œ‹Œ„•"," bbbb0b0b0bbb0bb0","00000b0b0b000b00b",},{"€—Œ“•‚Ž…œŽ”ŠŽˆ‰€•"," b0bbb00000000b 0","00b000bbbbbbbb00b",},{"€•…•Š–†‹Š–ŒŸ•"," bbbbb000bb0b0bb0","000000bbb00b0b00b",},{"€ƒƒƒ€€€ƒ‚ƒ€•"," bbbbb b bbbbb 0","0000000000000000b",},},{{"€€ŸŸ€€"," 000bb 00b b000b ","0bbb000bb000bbb00",},{"€•ƒ”•‚’•Šƒš…•ƒ”•€"," b0bb0bb0bbbb0bb ","00b00b00b0000b000",},{"€ŠŒŽ•ž•“‘œ”ŠŒŽ•€"," 000bbbbbbbb000b ","0bbb00000000bbb00",},{"€œƒ„‡›ƒ•Ÿ‡˜œ„€"," b00bbb0bb000bbb ","00bb000b00bbb0000",},{"€†›œ“Žž‘–Ž—Ž”ށ€"," bbbbb00b000000b ","000000bb0bbbbbb00",},{"€‚ž–Ž‘‚‡”Ž‹‘‡’€€"," bb0b00bb00bbb0 ","000b0bb00bb000b00",},{"€„Œœ›”–‡†š˜›‰€„€"," bbbbbb00bbbb0 b ","0000000bb0000bb00",},{"€Œ‰…ŒšŒŠŸ–‘ŸŒ‹’€€"," bbbbbb00b0bb0b ","0000000bb0b00b000",},{"€—Œ“•ƒŸ—•Œ’ŠŽˆ‘€"," b0bb0b0bb0000bb ","00b00b0b00bbbb000",},{"€•…•š—Ÿ…›’‚Œ‰Ÿ„€"," bbbbb0bb00b00bb ","000000b00bb0bb000",},{"€ƒƒƒ€€ƒƒ‚€€"," bbbbb bbbbbbb ","00000000000000000",},},} \ No newline at end of file diff --git a/LevelOS/assets/circProgress.limg b/LevelOS/assets/circProgress.limg new file mode 100644 index 0000000..f13a445 --- /dev/null +++ b/LevelOS/assets/circProgress.limg @@ -0,0 +1,172 @@ +{ + { + { + "ŸŒ›€", + "T8TT", + "8T8T", + }, + { + "•€€•", + "8TT8", + "TTTT", + }, + { + "‚Œ†€", + "888T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T8T", + }, + { + "•€€•", + "8TT8", + "TTTT", + }, + { + "‚Œ†€", + "888T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "8TT8", + "TTTT", + }, + { + "‚Œ†€", + "888T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "8TT0", + "TTTT", + }, + { + "‚Œ†€", + "888T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "8TT0", + "TTTT", + }, + { + "‚Œ†€", + "880T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "8TT0", + "TTTT", + }, + { + "‚Œ†€", + "800T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "8TT0", + "TTTT", + }, + { + "‚Œ†€", + "000T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "8T0T", + }, + { + "•€€•", + "0TT0", + "TTTT", + }, + { + "‚Œ†€", + "000T", + "TTTT", + }, + }, + { + { + "ŸŒ›€", + "T0TT", + "0T0T", + }, + { + "•€€•", + "0TT0", + "TTTT", + }, + { + "‚Œ†€", + "000T", + "TTTT", + }, + }, + { + { + "Ÿƒ‹€", + "TTTT", + "bbbT", + }, + { + "€›†•", + "Tb0b", + "b0bT", + }, + { + "‚‡€", + "bbbT", + "TTTT", + }, + }, +} \ No newline at end of file diff --git a/LevelOS/assets/loading.limg b/LevelOS/assets/loading.limg index 852cb5a..f30fb39 100644 --- a/LevelOS/assets/loading.limg +++ b/LevelOS/assets/loading.limg @@ -1,241 +1,241 @@ { { { - "----", + "Ÿ€€€", "TTTT", "0TTT", }, { - "----", + "•€€€", "0TTT", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "€„€€", "T0TT", "TTTT", }, { - "----", + "•€€€", "0TTT", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "Ÿˆ€€", "T0TT", "0TTT", }, { - "----", + "”€€€", "0TTT", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "€„„€", "T00T", "TTTT", }, { - "----", + "…€€€", "0TTT", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "ŸˆŸ€", "T0TT", "0T0T", }, { - "----", + "€€€", "0TTT", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "ŸˆŸ€", "T0TT", "0T0T", }, { - "----", + "€€€„", "TTT0", "TTTT", }, { - "----", + "€€€€", "TTTT", "TTTT", }, }, { { - "----", + "€„„€", "T00T", "TTTT", }, { - "----", + "€€€", "TTT0", "TTTT", }, { - "----", + "€€‚€", "TT0T", "TTTT", }, }, { { - "----", + "€ˆŸ€", "T0TT", "TT0T", }, { - "----", + "€€€", "TTT0", "TTTT", }, { - "----", + "€€„€", "TT0T", "TTTT", }, }, { { - "----", + "€€Ÿ€", "TTTT", "TT0T", }, { - "----", + "€€€", "TTT0", "TTTT", }, { - "----", + "‚€„€", "0T0T", "TTTT", }, }, { { - "----", + "€€€€", "TTTT", "TTTT", }, { - "----", + "€€„", "0TT0", "TTTT", }, { - "----", + "€„„€", "T00T", "TTTT", }, }, { { - "----", + "€€€€", "TTTT", "TTTT", }, { - "----", + "„€€€", "0TTT", "TTTT", }, { - "----", + "‚ˆ‚€", "000T", "TTTT", }, }, { { - "----", + "€€€€", "TTTT", "TTTT", }, { - "----", + "…€€€", "0TTT", "TTTT", }, { - "----", + "‚ˆ€€", "00TT", "TTTT", }, }, { { - "----", + "€€€€", "TTTT", "TTTT", }, { - "----", + "•€€€", "0TTT", "TTTT", }, { - "----", + "€„€€", "T0TT", "TTTT", }, }, { { - "----", + "€€€€", "TTTT", "TTTT", }, { - "----", + "•€€€", "0TTT", "TTTT", }, { - "----", + "‚€€€", "0TTT", "TTTT", }, }, type = "lImg", -} +} \ No newline at end of file diff --git a/LevelOS/assets/logo_christmas.limg b/LevelOS/assets/logo_christmas.limg new file mode 100644 index 0000000..df8f6e0 --- /dev/null +++ b/LevelOS/assets/logo_christmas.limg @@ -0,0 +1,40 @@ +{ + { + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€•€€€€€€", + "TdTTTTTT", + "dTTTTTTT", + }, + { + "€•€€€€€€", + "TdTTTTTT", + "dTTTTTTT", + }, + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€€•€€€€•", + "TTdTTTTd", + "ddeeeddT", + }, + }, + type = "lImg", +} \ No newline at end of file diff --git a/LevelOS/assets/logo_pride.limg b/LevelOS/assets/logo_pride.limg new file mode 100644 index 0000000..4d7c5a9 --- /dev/null +++ b/LevelOS/assets/logo_pride.limg @@ -0,0 +1,40 @@ +{ + { + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€•€€€€€€", + "TeTTTTTT", + "eTTTTTTT", + }, + { + "€•€€€€€€", + "T1TTTTTT", + "1TTTTTTT", + }, + { + "€•€€€€€€", + "T1TTTTTT", + "1TTTTTTT", + }, + { + "€•€€€€€€", + "T4TTTTTT", + "4TTTTTTT", + }, + { + "€•€€€€€€", + "T4TTTTTT", + "4TTTTTTT", + }, + { + "€€•€€€€•", + "TTdTTTTa", + "ddbbbaaT", + }, + }, + type = "lImg", +} \ No newline at end of file diff --git a/LevelOS/assets/wifi.limg b/LevelOS/assets/wifi.limg new file mode 100644 index 0000000..5afd192 --- /dev/null +++ b/LevelOS/assets/wifi.limg @@ -0,0 +1,71 @@ +{ + { + { + "Œ›€", + "0TT", + "T0T", + }, + { + "“”•", + "000", + "TTT", + }, + { + "€€€", + "TTT", + "TTT", + }, + }, + { + { + "Œ›€", + "8TT", + "T8T", + }, + { + "“”•", + "008", + "TTT", + }, + { + "€€€", + "TTT", + "TTT", + }, + }, + { + { + "Œ›€", + "8TT", + "T8T", + }, + { + "“”•", + "888", + "TTT", + }, + { + "€€€", + "TTT", + "TTT", + }, + }, + { + { + "Œ›€", + "eTT", + "TeT", + }, + { + "“”•", + "eee", + "TTT", + }, + { + "€€€", + "TTT", + "TTT", + }, + }, + type = "lImg", +} \ No newline at end of file diff --git a/LevelOS/data/changelog.lconf b/LevelOS/data/changelog.lconf new file mode 100644 index 0000000..882005a --- /dev/null +++ b/LevelOS/data/changelog.lconf @@ -0,0 +1,324 @@ +{ + { + date = "01-06-2021", + version = "2.2.5", + description = "It's pride month! To show support, the LevelOS logo has been replaced by a rainbow variant throughout June.", + added = { + "Added changelog", + }, + fixed = { + "lOS.execute now properly takes a window mode argument", + "Loading assets through lUtils no longer errors on invalid file path", + "lUtils.renderImg now handles transparency slightly better", + }, + }, + { + date = "01-06-2021", + version = "2.2.6", + fixed = { + "Modified changelog design and resizing", + "Fixed startup erroring after update", + }, + }, + { + date = "02-06-2021", + version = "2.2.7", + added = { + "Integrated global login into the LevelOS system", + }, + }, + { + date = "02-06-2021", + version = "2.2.8", + fixed = { + "Global login now only appears when LevelCloud is enabled, as was intended", + "Shapescape programs no longer crash on versions with mouse_move events", + }, + }, + { + date = "03-06-2021", + version = "2.2.9", + description = "The LevelOS search feature is currently disabled until input boxes are added.", + added = { + "LevelOS start menu is now an overlay and can be opened by both mouse buttons", + "Shapescape programs now clear their window when exited", + }, + fixed = { + "Fixed LevelOS infinite-looping when booted without internet", + }, + }, + { + date = "03-06-2021", + version = "2.2.10", + description = "Mouse simulator will be added to LevelOS in the future", + added = { + "LevelOS can now run on classic computers when an external mouse simulator is used", + }, + fixed = { + "Fixed the login screen getting stuck on boot until it received an event", + "Fixed the LevelCloud GUI showing an error when booted without internet", + }, + }, + { + date = "16-06-2021", + version = "2.2.11", + description = "This took too long but I finally fixed a bunch of issues in the OS. Input boxes coming soon.", + fixed = { + "Shutdown fade restored (broke in 2.2.9)", + "Fixed startup processes not being able to execute programs before their first yield", + "Fixed term_resize events being sent to every process instead of only the resizing one", + "Only the focused window has constant thread resuming now to improve performance", + "LevelOS now properly reports errors in System and SystemUI processes", + }, + }, + { + date = "19-06-2021", + version = "2.2.12", + description = "Some temporary fixes until the desktop and system are rewritten", + added = { + "New desktop icons for .lua files, files without extension, files with unknown extensions and LevelOS executables", + }, + fixed = { + "LevelOS can now resize itself", + "Desktop no longer has weird dragging limits", + "Icon creation from the explorer and using lOS.genIco now works properly", + "Pigeon icon has been restored", + }, + }, + { + date = "22-11-2021", + version = "2.3.0", + description = "It's been a while, but LevelOS is back. And it's better than ever! Sorry for the long wait! The window manager has been rewritten entirely, and all sorts of cool functionality has been added. Desktop icons have been renewed but don't refresh automatically, so if you want those you have to delete desktop.lconf in LevelOS/data and hold ctrl + r.", + added = { + "The window manager now has animations on resizing, closing, opening and minimizing of windows", + "A tab menu (like Window's alt tab) has been added. Instead of alt, ctrl is used in LevelOS", + "Shell and Lua have new icons", + "Windows can now be in fullscreen mode by pressing f5", + "There's a new login screen and register screen, remember me can be toggled by logging out through the LevelCloud interface and pressing remember me in the prompt that comes up", + "Taskbar icons are now actual icons. Custom icons coming soon", + "When multiple windows of a certain program are open, they appear in the same taskbar icon", + "Taskbar icons can now be right clicked", + "Right click menu's are now overlays meaning they can go beyond their window's border", + "Bootup now displays what it's currently doing", + "The pride LevelOS icon is back due to popular demand", + }, + fixed = { + "Desktop auto generation now works properly, may require deleting desktop.lconf in LevelOS/data and holding ctrl + r to work", + "The LevelOS start menu (opened by clicking the L in the bottom left corner) now appears on top of other windows", + }, + major = true, + }, + { + date = "26-12-2021", + version = "2.3.1", + description = "Merry Christmas! More changes are on the way for premium users.", + added = { + "Premium users now have an auto updater for dev builds", + "LevelOS now has an integrity check at startup", + "Replaced asterisks in login screen with circles", + }, + fixed = { + "Minimizing widgets no longer causes a bluescreen", + "Clicks on the taskbar now also close widgets", + "Desktop shortcuts to program folders now execute properly", + "Start screen now properly resizes", + }, + }, + { + date = "22-05-2022", + version = "2.3.2", + description = "I seem to make a habit out of incredibly long delays between updates. Sorry about that. This update contains some very necessary bugfixes and some API rewrites, in wait of a bigger update with rewritten GUIs for several components in LevelOS. Stay tuned!", + added = { + "Redesigned and improved notifications", + "The clock now has a calendar as well", + "The window manager now uses a window_focus event", + "Shapescape now displays proper error messages when a script errors", + }, + fixed = { + "Improved lUtils.renderImg transparency handling", + "Cursor no longer displays outside of window", + "The certificate error in the login screen has been fixed", + "Cursor no longer keeps blinking when a popup opens", + }, + }, + { + date = "22-05-2022", + version = "2.3.3", + description = "Wow! Two updates in one day!", + fixed = { + "Fixed another occurence of the certificate error in lUtils.login that I missed earlier", + }, + }, + { + major = true, + date = "04-08-2022", + version = "2.4.0", + description = "Version 2.4 is here! This version is all about the rewrites. LevelOS has gotten a completely new and rewritten desktop, which is now bound to the actual folder User/Desktop. There is also a completely new explorer, which together with the desktop supports a new shortcut system. Alongside with these big new changes, there have also been several fixes and other new features, and more will follow.", + added = { + "Rewrote the desktop, now has custom background support (right click any .nfp, .bimg or .limg image using explorer)", + "There is now a new shortcut system (right click any file or folder using explorer and click create shortcut)", + "Rewrote the explorer, now comes with a file tree, search function, icon view (icon in the bottom right) and more", + "Created the advanced lOS.contextmenu function, documentation will come soon", + "Made better taskbar icons", + "Changed the LevelOS file structure, LevelOS programs are now in Program_Files/LevelOS", + "Replaced openWith.lconf with formats.lconf, which has a better stucture", + "There is now a 'Yield' column in Task Manager", + "Added 'abort()' function to the LevelOS.close() callback function, to abort closing the program", + "Shutting down/rebooting now shows an interface to be able to save your work if apps have LevelOS.close() set", + "Added an early version of the Store", + "Replaced the lua repl with a custom version", + "Added 1 character icon support to the window manager", + "Added bimg support across LevelOS", + "Added a new program folder structure which is HIGHLY recommended you use for LevelOS programs as it allows setting custom icons: main.lua for file that gets executed when the program is ran, taskbar.bimg for the icon appearing on the taskbar (must be mostly white!) and icon.bimg for the icon appearing on desktop and more" + }, + fixed = { + "Shapescape windows now resize properly", + "Fixed numerous bugs with CraftOS-PC", + "Solved some incompatibility issues with 1.12.2 (may not have caught all, please report if there are more)", + "Fixed some bugs in the login screen", + } + }, + { + date = "08-08-2022", + version = "2.4.1", + description = "Thanks for the bug reports everyone! All bugs reported have been fixed, and the desktop got some necessary additions as well.", + added = { + "Right clicking an empty space on the desktop now opens a context menu for creating files and refreshing", + "Files can now be renamed directly from the desktop", + }, + fixed = { + "Changed fs.combine into a vararg function on 1.12.2 to fix compatibility issues", + "Modified lUtils.wordwrap to account for the extra empty line that appears on CraftOS-PC", + "Prepared URLs for porting to a different server", + } + }, + { + date = "11-08-2022", + version = "2.4.2", + added = { + "The text color of icons on the desktop can now be changed through the desktop context menu", + }, + fixed = { + "Trying to perform operations on a shortcut of a deleted file no longer errors" + } + }, + { + date = "01-09-2022", + version = "2.4.3", + description = "The new servers are finally here! Expect a lot of new features to follow soon that have become possible thanks to the migration.", + added = { + "Added 3x3 icons to some system folders", + "Migrated to new servers", + }, + fixed = { + "Improved LevelCloud efficiency", + "Fixed a number of issues in the login screen", + } + }, + { + date = "17-09-2022", + version = "2.4.4", + description = "Finally implemented some much needed LevelCloud features. Sync conflicts now have a proper interface and you can see the status of file synchronization in the LevelOS file explorer.", + added = { + "Explorer now shows synchronization status of cloud files", + "Sync conflicts in LevelCloud can now be resolved through the interface", + "Old context menus in LevelOS now have scrolling like the new ones", + } + }, + { + date = "18-09-2022", + version = "2.4.5", + description = "It seems I broke something big last update. Sorry guys, fixed it now.", + fixed = { + "Fixed a recursion issue in the LevelOS Desktop generation that caused a crash on startup", + }, + }, + { + date = "12-02-2023", + version = "2.4.6", + description = "This update is all about bugfixes. The entire list of bugs has been worked through except for one, so thanks for all of the reports!", + fixed = { + "Explorer no longer crashes when the opened folder is deleted", + "LevelOS no longer displays a warning about LuaJIT when not using LuaJIT", + "LevelOS no longer crashes when being ran again after being terminated", + "Pressing space during a blue screen actually restarts now", + "The Lua REPL no longer crashes when documentation is not found", + "Icons no longer go under the taskbar", + }, + added = { + "The store now displays icons of apps that use the recommended LevelOS program structure (a main.lua file with a 3x3 icon.bimg file in a folder)", + }, + }, + { + date = "25-11-2023", + version = "2.4.7", + description = "A small update to fix some things in the lUtils API. LevelOS 3 is in development and coming soon!", + fixed = { + "The lUtils text editor (used by the Lua REPL and Slime Text) now recognizes indenting as a change to be saved and undone", + "The lUtils.popup function now automatically sets width and/or height if not provided", + "The lUtils.popup function now uses a better word wrapping algorithm so text doesn't go outside of the box anymore", + "The start menu on the taskbar now opens instantly without having to receive another event", + }, + }, + { + date = "28-11-2023", + version = "2.4.8", + description = "Another small update with some bugfixes that were reported a while ago.", + fixed = { + "Resizing the entirety of LevelOS now works correctly", + "Trying to execute LevelOS within itself no longer causes a bluescreen", + }, + }, + { + date = "09-12-2023", + version = "2.4.9", + added = { + "You can now copy/paste text within the lUtils text editor using ctrl+c and ctrl+v outside of emulators as well, as long as you have some kind of text in your clipboard", + "Autocomplete in the lUtils text editor can now list nil values", + "Added lOS.getRunningProcess()", + } + }, + { + date = "09-04-2024", + version = "2.5.0", + major = true, + description = "LevelOS 2 is finally completely compatible with CC: Tweaked 1.109! Apologies it took so long!", + added = { + "HTTP requests are now private to the programs sending them and can't conflict with eachother", + "The tabs and search in the store are now functional, and creator names are displayed below the program titles", + }, + fixed = { + "LevelOS is now compatible with CC 1.109 and above", + "lOS.getRunningProcess() now returns the correct process at all times", + } + }, + { + date = "11-04-2024", + version = "2.5.1", + fixed = { + "Restored compatibility with 1.12.2 versions of CC: Tweaked", + }, + }, + { + date = "18-04-2024", + version = "2.5.2", + fixed = { + "Added compatibility with the newest CraftOS-PC Accelerated", + }, + }, + { + date = "10-08-2024", + version = "2.5.3", + fixed = { + "Fixed the bluescreen upon closing the changelog on newer versions of ComputerCraft", + "Fixed a bug with UTF-8 decoding", + }, + }, + { + date = "11-08-2024", + version = "2.5.4", + fixed = { + "Resolved an error in the updater with the newer versions of ComputerCraft", + }, + }, +} \ No newline at end of file diff --git a/LevelOS/data/settings.lconf b/LevelOS/data/settings.lconf new file mode 100644 index 0000000..6036ba6 --- /dev/null +++ b/LevelOS/data/settings.lconf @@ -0,0 +1,3 @@ +{ + package = "Full", +} \ No newline at end of file diff --git a/LevelOS/data/version.txt b/LevelOS/data/version.txt new file mode 100644 index 0000000..a1acc00 --- /dev/null +++ b/LevelOS/data/version.txt @@ -0,0 +1 @@ +1723386918734 \ No newline at end of file diff --git a/LevelOS/desktop.lua b/LevelOS/desktop.lua new file mode 100644 index 0000000..3d75768 --- /dev/null +++ b/LevelOS/desktop.lua @@ -0,0 +1,1608 @@ +local terminal = term.current() +local deskIcons = lUtils.asset.load("LevelOS/assets/Desktop_Icons.limg") +local desktop = {} +local idReference = {} +local fc = fs.combine + +local function locateEntry(tTable,tEntry) + if type(tTable) ~= "table" then + error("Invalid input #1",2) + end + for t=1,#tTable do + if tTable[t] == tEntry then + return true,t + end + end + return false,0 +end + +local fileTypes +if fs.exists("LevelOS/data/formats.lconf") then + fileTypes = lUtils.asset.load("LevelOS/data/formats.lconf") +end + +if not fileTypes then + local copy = lUtils.instantiate + fileTypes = { + _VERSION="1.3", + + [""] = { + name="File", + program="Lua", + contextMenu={ + {"Execute","Edit"}, + {"Lua","rom/programs/edit.lua"} + }, + openWith={ + "Lua", + "rom/programs/edit.lua" + }, + icon=deskIcons[1] + }, + + lua = { + name="Lua script", + program="Lua", + contextMenu={ + {"Execute","Edit"}, + {"Lua","rom/programs/edit.lua"} + }, + openWith={ + "Lua", + "rom/programs/edit.lua" + }, + icon=deskIcons[2], + emptyFilePreset="" + }, + + txt = { + name="Text file", + program="LevelOS/notepad.lua", + contextMenu={ + {"Edit"}, + {"LevelOS/notepad.lua"} + }, + openWith={ + "LevelOS/notepad.lua", + "rom/programs/edit.lua" + }, + icon=deskIcons[5], + emptyFilePreset="" + }, + + lconf = { + name="Config file", + program="LevelOS/notepad.lua", + contextMenu={ + {"Edit"}, + {"LevelOS/notepad.lua"} + }, + openWith={ + "LevelOS/notepad.lua", + "rom/programs/edit.lua" + }, + icon=deskIcons[6], + emptyFilePreset="{}" + }, + + sgui = { + name="Shapescape GUI", + program="Program_Files/Shapescape", + contextMenu={ + {"Open","Execute"}, + {"Program_Files/Shapescape","Lua"} + }, + openWith={ + "Lua", + "Program_Files/Shapescape", + "rom/programs/edit.lua" + }, + icon=deskIcons[8], + emptyFilePreset="return {assets={},slides={{contextMenu=1,objs={},w=51,h=19}}}" + }, + + sml = { + name="SML Document", + program="Program_Files/sml", + contextMenu={ + {"Open","Edit"}, + {"Program_Files/sml","rom/programs/edit.lua"} + }, + openWith={ + "Program_Files/sml", + "rom/programs/edit.lua" + }, + icon=deskIcons[9], + emptyFilePreset="" + }, + + limg = { + name="Image", + program="LevelOS/imageviewer.lua", + contextMenu={ + {"View","Edit"}, + {"LevelOS/imageviewer.lua","rom/programs/edit.lua"} + }, + openWith={ + "LevelOS/imageviewer.lua", + "rom/programs/edit.lua" + }, + icon=copy(deskIcons[10]) + }, + + bimg = { + name="Image", + program="LevelOS/imageviewer.lua", + contextMenu={ + {"View","Edit"}, + {"LevelOS/imageviewer.lua","rom/programs/edit.lua"} + }, + openWith={ + "LevelOS/imageviewer.lua", + "rom/programs/edit.lua" + }, + icon=copy(deskIcons[10]) + }, + + nfp = { + name="NFP Image", + program="LevelOS/imageviewer.lua", + contextMenu={ + {"View","Edit"}, + {"LevelOS/imageviewer.lua","rom/programs/fun/advanced/paint.lua"} + }, + openWith={ + "LevelOS/imageviewer.lua", + "rom/programs/fun/advanced/paint.lua", + "rom/programs/edit.lua" + }, + icon=copy(deskIcons[10]), + emptyFilePreset="" + }, + + nfg = { + name="Animation", + program="LevelOS/imageviewer.lua", + contextMenu={ + {"View","Edit"}, + {"LevelOS/imageviewer.lua","rom/programs/edit.lua"} + }, + openWith={ + "LevelOS/imageviewer.lua", + "rom/programs/edit.lua" + }, + icon=copy(deskIcons[10]) + }, + } + + lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf", false) + +end + +if fileTypes._VERSION == "1.0" then + for k,v in pairs(fileTypes) do + if type(v) == "table" then + v.name = v.name or v.n + v.n = nil + v.program = v.program or v.p + v.p = nil + v.icon = v.icon or v.i + v.i = nil + v.contextMenu = v.contextMenu or v.c + v.c = nil + v.openWith = v.openWith or v.o + v.o = nil + v.emptyFilePreset = v.emptyFilePreset or v.e + end + end + fileTypes._VERSION = "1.1" + fileTypes["llnk"] = {name="Shortcut",program="Lua"} + lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf") +end +if fileTypes._VERSION == "1.1" then + for k,v in pairs(fileTypes) do + if type(v) == "table" then + v._VERSION = nil + end + end + for k,v in pairs(fileTypes.nfp.openWith) do + if v == "rom/programs/fun/paint.lua" then + fileTypes.nfp.openWith[k] = "rom/programs/fun/advanced/paint.lua" + end + end + fileTypes._VERSION = "1.2" + lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf",false) +end +if fileTypes._VERSION == "1.2" then + fileTypes.bimg = { + name="Image", + program="LevelOS/imageviewer.lua", + contextMenu={ + {"View","Edit"}, + {"LevelOS/imageviewer.lua","rom/programs/edit.lua"} + }, + openWith={ + "LevelOS/imageviewer.lua", + "rom/programs/edit.lua" + }, + icon=deskIcons[10] + } + fileTypes._VERSION = "1.3" + lUtils.asset.save(lUtils.instantiate(fileTypes),"LevelOS/data/formats.lconf",false) +end +local dConfig +if not lOS.explorer then lOS.explorer = {} end +lOS.explorer.shortcache = {} +lOS.explorer.imgcache = {} +lOS.explorer.nimgcache = {} +lOS.explorer.desktopBackground = nil +lOS.fClipboard = {} + +local disks = {} +lOS.explorer.disks = disks +local function reloadDisks() + for k,v in pairs(disks) do + disks[k] = nil + end + local ndisks = {peripheral.find("drive",function(name,object) return object.hasData() end)} + for t=1,#ndisks do + ndisks[ndisks[t].getMountPath()] = ndisks[t] + end + for k,v in pairs(ndisks) do + disks[k] = v + end +end +reloadDisks() + +local function getName(path) + local isDisk = false + if disks[path] and disks[path].getDiskLabel() then + str = disks[path].getDiskLabel().." ("..string.gsub(path,"_"," ")..")" + isDisk = true + elseif path == "" then + str = "Local disk" + else + str = string.gsub(fs.getName(path),"_"," ") + if lUtils.getFileType(str) == ".llnk" then + str = str:sub(1,#str-5) + end + end + return str,isDisk +end + +lOS.explorer.getName = getName + +local symbols = { + ["User/Cloud"] = {symbol="",color=colors.blue}, + [""] = {symbol="",color=colors.lightGray}, + ["rom"] = {symbol="",color=colors.red}, + ["User"] = {symbol="",color=colors.lightBlue}, + ["User/Desktop"] = {symbol="",color=colors.lightBlue}, + ["User/Documents"] = {symbol="",color=colors.lightGray}, + ["User/Downloads"] = {symbol="",color=colors.blue}, + ["User/Images"] = {symbol="",color=colors.lightBlue}, + ["User/Scripts"] = {symbol="",color=colors.cyan}, + ["User/Games"] = {symbol="",color=colors.lightGray}, + ["User/Music"] = {symbol="",color=colors.blue}, +} + +lOS.explorer.getIcon = function(path) + local disks = {peripheral.find("drive",function(name,object) return object.hasData() end)} + for t=1,#disks do + disks[disks[t].getMountPath()] = disks[t] + end + if symbols[path] then + return symbols[path].symbol, symbols[path].color + elseif disks[path] then + return "", colors.blue + elseif fs.isDir(path) then + return "\143", colors.yellow + else + return "\143", colors.white + end +end + +local function genFile(f) + if not fs.exists(f) then + error(f.." does not exist",2) + end + local file = {path=f,name=fs.getName(f),type=lUtils.getFileType(f):sub(2),readOnly=fs.isReadOnly(f),isDir=fs.isDir(f)} + if not ((file.isDir and file.readOnly) or disks[file.path]) then + local a = fs.attributes(f) + for k,v in pairs(a) do + file[k] = v + end + end + return file +end + +local function uFP(filepath2) -- unique filepath + if fs.exists(filepath2) == true then + t = 1 + while fs.exists(string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2))).."_("..t..")"..lUtils.getFileType(filepath2)) == true do + t = t+1 + end + filepath2 = string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2))).."_("..t..")"..lUtils.getFileType(filepath2) + end + return filepath2 +end +lOS.explorer.uFP = uFP + +function lOS.genIco(path) + local name = getName(path) + if fs.isDir(path) and fs.exists(fs.combine(path,"main.lua")) then + path = fs.combine(path,"main.lua") + end + local pth = fs.combine("User/Desktop",name..".llnk") + lUtils.asset.save({path},pth) + os.queueEvent("explorer_reload") +end + +local presetIcons = { + ["User"] = deskIcons[12], + ["User/Desktop"] = deskIcons[13], + ["User/Documents"] = deskIcons[14], + ["User/Downloads"] = deskIcons[15], + ["User/Games"] = deskIcons[16], + ["User/Images"] = deskIcons[17], + ["User/Music"] = deskIcons[18], + ["User/Scripts"] = deskIcons[19], + ["User/Cloud"] = deskIcons[20], + ["rom"] = deskIcons[21], +} +function lOS.explorer.drawIcon(path,fx,fy,onlyIcon,transparent,maxLines,shortcutIcon) + --local file = {path=path,type=lUtils.getFileType(path):sub(2)} + local ok,file = pcall(genFile,path) + if not ok then + lUtils.renderImg(deskIcons[11],fx+2,fy+1,nil,transparent) + return + end + local bg = term.getBackgroundColor() + local fg = term.getTextColor() + local cTerm = term.current() + local isCut = false + if lOS.fCut then + isCut = locateEntry(lOS.fCut,file.path) + end + local shortcache = lOS.explorer.shortcache + local imgcache = lOS.explorer.imgcache + local nimgcache = lOS.explorer.nimgcache + local fc = fs.combine + local rPath = path + local rType = file.type + local isShort = false + local lines = lUtils.wordwrap(lUtils.getFileName(file.path),7) + local lineH = math.min(#lines,4) + local fw,fh = 7,5 + if #lines > lineH then + lines[4] = lines[4]:sub(1,5)..".." + end + fh = math.max(4+lineH,fh) + if not transparent then + lOS.boxClear(fx,fy,fx+7-1,fy+5-1+lineH-1) + end + if shortcache[file.path] then + isShort = true + rPath = shortcache[file.path][1] + rType = lUtils.getFileType(rPath):sub(2) + elseif file.type == "llnk" then + local info = lUtils.asset.load(file.path) + if info and type(info[1]) == "string" then + isShort = true + rPath = info[1] + if not fs.exists(rPath) then + imgcache[file.path] = {} + for i=0,15 do + imgcache[file.path][i] = deskIcons[4] + end + else + rType = lUtils.getFileType(info[1]):sub(2) + imgcache[file.path] = {} + for i=0,15 do + imgcache[file.path][i] = info.icon + end + end + shortcache[file.path] = info + end + end + local pFolder = fs.getDir(rPath) + if not isCut then + if not fs.exists(rPath) then + lUtils.renderImg(deskIcons[4],fx+2,fy+1,nil,transparent) + elseif presetIcons[rPath] then + lUtils.renderImg(presetIcons[rPath],fx+2,fy+1,nil,transparent) + elseif imgcache[file.path] and imgcache[file.path][bg] then + local win = imgcache[file.path][bg] + if win.reposition then + for t=1,3 do + term.setCursorPos(fx+2,fy+t) + term.blit(win.getLine(t)) + end + else + lUtils.renderImg(win,fx+2,fy+1,nil,transparent) + end + elseif fs.getName(rPath) == "main.lua" --[[and fs.exists(fc(pFolder,"icon.limg"))]] then + if not nimgcache[file.path] and (fs.exists(fc(pFolder,"icon.limg")) or fs.exists(fc(pFolder,"icon.bimg"))) then + local img + if fs.exists(fc(pFolder,"icon.limg")) then + img = lUtils.asset.load(fc(pFolder,"icon.limg")) + else + img = lUtils.asset.load(fc(pFolder,"icon.bimg")) + end + img = img[1] + local iw = #img[1][1] + local ih = #img + if iw <= 3 and ih <= 3 then + --[[local tWin = window.create(cTerm,fx+2,fy+1,iw,ih,true) + term.redirect(tWin) + lUtils.renderImg(img,1,1) + term.redirect(cTerm) + imgcache[file.path] = tWin]] + if not imgcache[file.path] then + imgcache[file.path] = {} + end + imgcache[file.path][bg] = img + lUtils.renderImg(img,fx+2,fy+1,nil,transparent) + else + nimgcache[file.path] = true + end + else + lUtils.renderImg(deskIcons[3],fx+2,fy+1,nil,transparent) + end + elseif fs.isDir(rPath) then + if not nimgcache[file.path] and (fs.exists(fc(rPath,"icon.limg")) or fs.exists(fc(rPath,"icon.bimg"))) and fs.exists(fc(rPath,"main.lua")) then + local img + if fs.exists(fc(rPath,"icon.limg")) then + img = lUtils.asset.load(fc(rPath,"icon.limg")) + else + img = lUtils.asset.load(fc(rPath,"icon.bimg")) + end + img = img[1] + local iw = #img[1][1] + local ih = #img + if iw <= 3 and ih <= 3 then + local tWin = window.create(cTerm,fx+2,fy+1,iw,ih,true) + term.redirect(tWin) + if transparent then + for y=1,2 do + local line = {cTerm.getLine(fy+y)} + for t=1,3 do + line[t] = line[t]:sub(fx+2,fx+4) + end + term.setCursorPos(1,y) + term.blit(unpack(line)) + end + else + term.setBackgroundColor(bg) + term.clear() + end + lUtils.renderImg(img,1,1,nil,transparent) + term.setBackgroundColor(colors.yellow) + term.setCursorPos(1,3) + term.write(" ") + term.redirect(cTerm) + if not imgcache[file.path] then + imgcache[file.path] = {} + end + imgcache[file.path][bg] = tWin + else + nimgcache[file.path] = true + end + else + lUtils.renderImg(deskIcons[7],fx+2,fy+1,nil,transparent) + end + elseif ((rType == "limg" or file.type == "bimg") and lUtils.asset.load(rPath)) or rType == "nfp" then + local iw,ih + local img + if rType == "nfp" then + img = lUtils.fread(rPath) + ih = 0 + iw = 0 + for line in img:gmatch("([^\n]*)\n?") do + iw = math.max(iw,#line) + ih = ih+1 + end + else + img = lUtils.asset.load(rPath) + img = img[1] + iw = #img[1][1] + ih = #img + end + local tWin = window.create(cTerm,1,1,iw,ih,false) + term.redirect(tWin) + term.setBackgroundColor(bg) + term.clear() + lUtils.renderImg(img,1,1) + term.redirect(cTerm) + if not imgcache[file.path] then + imgcache[file.path] = {} + end + if iw <= 3 and ih <= 3 then + imgcache[file.path][bg] = img + lUtils.renderImg(img,fx+2,fy+1,nil,transparent) + else + local lWin = lUtils.littlewin(tWin,6,math.floor(9*(ih/iw)+0.5)) + local fWin = window.create(cTerm,fx+2,fy+1,3,3,true) + --_G.debuglwin[file.path] = {lWin=lWin,tWin=tWin,img=img} + term.redirect(fWin) + local win = blittle.createWindow(fWin,1,1,3,3,true) + term.redirect(win) + if transparent then + for y=1,3 do + local line = {cTerm.getLine(fy+y)} + for t=1,3 do + line[t] = line[t]:sub(fx+2,fx+4) + end + term.setCursorPos(1,y) + term.blit(unpack(line)) + end + else + term.setBackgroundColor(bg) + term.clear() + end + lWin.render(1,1) + term.redirect(fWin) + win.redraw() + term.redirect(cTerm) + fWin.redraw() + imgcache[file.path][bg] = fWin + end + elseif fileTypes[rType] and fileTypes[rType].icon then + lUtils.renderImg(fileTypes[rType].icon,fx+2,fy+1,nil,transparent) + else + lUtils.renderImg(deskIcons[4],fx+2,fy+1,nil,transparent) + end + if shortcutIcon then + if isShort and not onlyIcon then + term.setCursorPos(fx+2,fy+3) + term.blit(unpack(shortcutIcon)) + elseif lOS.cloud and lOS.cloud.lastSync and lOS.cloud.files and lOS.cloud.files[file.path] then + term.setCursorPos(fx+2,fy+3) + local pix = {lUtils.getPixel(term.current(),fx+2,fy+3)} + term.setBackgroundColor(lUtils.toColor(pix[3])) + if lOS.cloud.conflicts[file.path] then + term.setTextColor(colors.red) + term.write("×") + elseif file.modification > lOS.cloud.lastSync then + term.setTextColor(colors.blue) + term.write("\24") + else + term.setTextColor(colors.lime) + term.write("\7") + end + end + end + end + term.setBackgroundColor(bg) + term.setTextColor(fg) + if not onlyIcon then + if maxLines then + lineH = math.min(maxLines,lineH) + end + for l=1,lineH do + local line = lines[l]:gsub("%s+$","") + term.setCursorPos(fx+math.floor(fw/2-#line/2),fy+3+l) + if transparent then + lUtils.transWrite(line,(not dConfig.textColor)) + else + term.write(line) + end + end + end + return fh +end + +dConfig = {_VERSION=1,files={},sizes={},shortcutIcon=true,background={color=colors.white}} +if fs.exists("LevelOS/data/desktop.lconf") then + dConfig = lUtils.asset.load("LevelOS/data/desktop.lconf") +end + +if not dConfig._VERSION then + -- backup n shit + if fs.exists("LevelOS/data/olddesktop.lconf") then + fs.delete("LevelOS/data/olddesktop.lconf") + end + fs.move("LevelOS/data/desktop.lconf","LevelOS/data/olddesktop.lconf") + dConfig = {_VERSION=1,files={},sizes={},shortcutIcon=true,background={color=colors.white}} +end + +lOS.explorer.desktopConfig = dConfig + +function lOS.save() + lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf") + lUtils.fwrite("LevelOS/data/settings.lconf",textutils.serialize(lOS.settings)) + return true +end + +if not fs.exists("User/Desktop") then + fs.makeDir("User/Desktop") + -- my PC + lUtils.asset.save( + { + "Program_Files/LevelOS/Explorer/main.lua", + icon = { + { + "—ƒ”", + "ff ", + " f", + }, + { + "ƒŽ", + "f f", + " f ", + }, + { + "‚ƒ", + "fff", + " ", + } + }, + }, + "User/Desktop/My_PC.llnk" + ) + -- shell + lUtils.asset.save( + { + "rom/programs/shell.lua", + icon = { + { + "‡ƒ‹", + " ", + "fff", + }, + { + "„", + "f44", + "4ff", + }, + { + "‹‡", + "fff", + " ", + } + }, + }, + "User/Desktop/Shell.llnk" + ) + local function makeShortcuts(folder) + local ls = fs.list(folder) + for i,f in ipairs(ls) do + local path = fc(folder,f) + if fs.isDir(path) then + if fs.exists(fc(path,"main.lua")) then + lUtils.asset.save({fc(path,"main.lua")},fc("User/Desktop",fs.getName(f))..".llnk") + else + makeShortcuts(path) + end + end + end + end + makeShortcuts("Program_Files") +end + + +-- dConfig should be loaded after this +local currentID = 1 +if not dConfig.files then dConfig.files = {} end +for id,path in pairs(dConfig.files) do + if id >= currentID then + currentID = id+1 + end + idReference[path] = id +end + +local desktopMap = setmetatable({}, { + __index = function(self, id) + local default = {} + self[id] = default + return default + end +}) +_G.debugdesktopmap = desktopMap + +local function clearDesktopMap() + for k,v in pairs(desktopMap) do + desktopMap[k] = nil + end +end + +local function getDesktopSize() + local w,h = term.getSize() + if lOS.tbSize then + h = h - lOS.tbSize + end + local deskW,deskH = math.floor((w+1)/8),math.floor(h/5) + return deskW,deskH +end + +local currentSize +do + local desktopWidth,desktopHeight = getDesktopSize() + currentSize = desktopWidth..","..desktopHeight +end +if not dConfig.sizes[currentSize] then + dConfig.sizes[currentSize] = {} +end + +local function reloadDesktop() + + if not fs.exists("User/Desktop") then + fs.makeDir("User/Desktop") + end + + local desktopFiles = fs.list("User/Desktop") + local changed = false + for f=1,#desktopFiles do + local name = desktopFiles[f] + + if not idReference[name] then + idReference[name] = currentID + dConfig.files[currentID] = name + currentID = currentID+1 + changed = true + end + + end + + if changed then + lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf") + end + -- i only need to store position, but if i store them based on names everything gets messed up if a file gets renamed... + -- iterate through IDs too to see if theres a file there that doesnt exist anymore wait nah just do in the thing actually wait hmm + +end + +reloadDesktop() + +local function drawBackground(background) + for i=0,15 do + term.setPaletteColor(2^i,term.nativePaletteColor(2^i)) + end + if not background then + term.setBackgroundColor(colors.white) + term.clear() + elseif background.path and fs.exists(background.path) then + local col = background.color or colors.white + local tw,th = term.getSize() + local rImg + local img + local w,h + local t = lUtils.getFileType(background.path) + if t == ".bimg" or t == ".limg" or t == ".nfg" then + img = lUtils.asset.load(background.path) + rImg = img[1] + w = #rImg[1][1] + h = #rImg + elseif t == ".nfp" then + rImg = lUtils.fread(background.path) + h = 0 + w = 0 + local emptyLine = false + for line in rImg:gmatch("([^\n]*)\n?") do + w = math.max(w,#line) + h = h+1 + if line == "" then + emptyLine = true + else + emptyLine = false -- only if emptyLine is at end itll be true, very good + end + end + if emptyLine then -- fking craftos pc man + h = h-1 + end + end + if background.resize == "stretch" then + local tWin = window.create(term.current(),1,1,w,h,false) + local oterm = term.current() + term.redirect(tWin) + term.setBackgroundColor(col) + term.clear() + lUtils.renderImg(rImg,1,1) + term.redirect(oterm) + if h/w > th/tw then -- height should overflow, width equals screen width + local iw,ih=tw,math.floor(tw*(h/w)+0.5) + local lWin = lUtils.littlewin(tWin,iw,ih) + lWin.render(1,math.floor(th/2-ih/2)+1) + else + local iw,ih=math.floor(th*(w/h)+0.5),th + local lWin = lUtils.littlewin(tWin,iw,ih) + lWin.render(math.floor(tw/2-iw/2)+1,1) + end + elseif background.resize == "center" then + lUtils.renderImg(rImg,math.floor(tw/2-w/2)+1,math.floor(th/2-h/2)+1,nil,true) + elseif background.resize == "repeat" or true then + local rw,rh = math.ceil(tw/w),math.ceil(th/h) + local frame = 1 + for ry=0,rh-1 do + for rx=0,rw-1 do + if img then + lUtils.renderImg(img[frame],1+rx*w,1+ry*h,nil,true) + frame = frame+1 + if frame > #img then + frame = 1 + end + else + lUtils.renderImg(rImg,1+rx*w,1+ry*h,nil,true) + end + end + end + end + elseif background.color then + term.setBackgroundColor(background.color) + term.clear() + end +end +local selected = {} +local drag +local oldWidth,oldHeight = getDesktopSize() +local function render(dodebug) + term.redirect(terminal) + -- if not position gen position + -- make order table with IDs, then sort that table and iterate through + local dolog + + + if lOS.explorer.desktopBackground then + local dw,dh = lOS.explorer.desktopBackground.getSize() + local tw,th = term.getSize() + if dh ~= th-lOS.tbSize then + lOS.explorer.desktopBackground = nil + else + lOS.explorer.desktopBackground.setVisible(true) + lOS.explorer.desktopBackground.setVisible(false) + end + end + if not lOS.explorer.desktopBackground then + local col = dConfig.background.color or colors.white + local tw,th = term.getSize() + th = th-lOS.tbSize + local cTerm = term.current() + local bWin = window.create(term.current(),1,1,tw,th,true) + term.redirect(bWin) + term.setBackgroundColor(col) + term.clear() + if dConfig.background and dConfig.background[1] and type(dConfig.background[1]) == "table" then + for t=1,#dConfig.background do + drawBackground(dConfig.background[t]) + end + else + drawBackground(dConfig.background) + end + term.redirect(cTerm) + lOS.explorer.desktopBackground = bWin + lOS.explorer.desktopBackground.setVisible(false) + end + + term.setTextColor(dConfig.textColor or colors.black) + local newWidth,newHeight = getDesktopSize() + if oldWidth ~= newWidth or oldHeight ~= newHeight then + currentSize = newWidth..","..newHeight + oldWidth = newWidth + oldHeight = newHeight + if not dConfig.sizes[currentSize] then + dConfig.sizes[currentSize] = {} + end + clearDesktopMap() + end + local order = {} + for path,id in pairs(idReference) do + local absolutePath = fc("User/Desktop",path) + if not fs.exists(absolutePath) then + if dConfig.sizes[currentSize][id] then + local self = dConfig.sizes[currentSize][id] + if desktopMap[self.x] then + desktopMap[self.x][self.y] = nil + end + dConfig.sizes[currentSize][id] = nil + end + idReference[path] = nil + dConfig.files[id] = nil + else + table.insert(order,id) + end + end + table.sort(order) + local icons = dConfig.sizes[currentSize] + for i=1,#order do + local id = order[i] + if not icons[id] then + icons[id] = {} + end + if not icons[id].x or not icons[id].y then + local x,y = 1,1 + local success = true + while desktopMap[x][y] do + y = y+1 + if y > newHeight then + y = 1 + x = x+1 + if x > newWidth then + success = false + break + end + end + end + if success then + icons[id].x,icons[id].y = x,y + desktopMap[x][y] = id + end + end + if icons[id].x and icons[id].y and not selected[id] then + local x,y = icons[id].x,icons[id].y + desktopMap[x][y] = id + local path = fc("User/Desktop",dConfig.files[id]) + local maxLines = 1 + term.setBackgroundColor(colors.white) + lOS.explorer.drawIcon(path,1+(x-1)*8,1+(y-1)*5,false,true,maxLines,{"\24","0","8"}) + end + --os.sleep(0.5) + end + for id,v in pairs(selected) do + if not icons[id] then + selected[id] = nil + else + local x,y = icons[id].x,icons[id].y + local path = fc("User/Desktop",dConfig.files[id]) + local maxLines = 4 + term.setBackgroundColor(colors.lightBlue) + lOS.explorer.drawIcon(path,1+(x-1)*8,1+(y-1)*5,false,false,maxLines,{"\24","0","8"}) + end + end + if drag and drag.moved then + lOS.explorer.drawIcon(fc("User/Desktop",dConfig.files[drag.id]),drag.x-drag.ox,drag.y-drag.oy,false,true,1,{"\24","0","8"}) + end +end + + +-- CONTEXT MENU FUNCTIONS + +local fTypes = fileTypes +local function fExec(obj) + local f = obj.file + --[[local num = obj.num + local p = fTypes[f.type].c[2][num]] + local p = obj.p + if p == "Lua" then + lOS.execute(f.path) + else + lOS.execute(p.." "..f.path) + end +end +lOS.explorer.fExec = fExec + +local function fOpenWin(obj) + local f = obj.file + lOS.execute("Program_Files/LevelOS/Explorer "..f.path) +end +lOS.explorer.fOpenWin = fOpenWin + +function lOS.explorer.fRename(obj,s,sl,render,LevelOS,isDesktop) + if type(obj) == "string" then + if isDesktop then + for i,v in pairs(dConfig.sizes[currentSize]) do + _G.debugcompare = {} + if obj == fs.combine("User/Desktop",dConfig.files[i]) then + local f = genFile(obj) + local x,y = v.x,v.y + f.x1,f.y1 = 1+(x-1)*8,1+(y-1)*5 + local lineH = math.min(#lUtils.wordwrap(lUtils.getFileName(f.path),7),4) + f.x2,f.y2 = f.x1+6,f.y1+3+lineH + obj = {file=f} + break + end + table.insert(debugcompare,{obj,fs.combine("User/Desktop",dConfig.files[i])}) + end + if type(obj) == "string" then + error("not found") + end + else + for k,v in ipairs(sl.files) do + if v.path == obj then + obj = {file=v} + break + end + end + end + end + LevelOS.overlay = nil + local f = obj.file + sl.renaming = f.path + if isDesktop then + term.setBackgroundColor(colors.white) + term.setTextColor(colors.black) + else + term.setBackgroundColor(colors.pink) + opink = {s.win.getPaletteColor(colors.pink)} + sl.opink = opink + s.win.setPaletteColor(colors.pink,0.15,0.15,0.15) + term.setTextColor(colors.white) + end + local fname + local isShortcut = false + if lUtils.getFileType(f.path) == ".llnk" then + fname = lUtils.getFileName(f.path,false) + isShortcut = true + else + fname = lUtils.getFileName(f.path,true) + end + fname = fname:gsub(" ","_") + local w,h = term.getSize() + if sl.rMode == 1 then + sl.renamebox = lUtils.input(sl.NAME.x+2,f.y2,sl.NAME.x+sl.NAME.width-2,f.y2,{overflowX="scroll",overflowY="none",text=fname,selectColor=colors.blue}) + sl.renamebox.x2 = math.min(math.max(sl.NAME.width,#sl.renamebox.txt+5),w) + else + sl.renamebox = lUtils.input(f.x1,f.y1+4,f.x2,f.y2,{overflowX="scroll",overflowY="none",text=fname,selectColor=colors.blue}) -- ah fuck ah FCK shit fuck + if isDesktop then + sl.renamebox.opt.selectTxtColor = colors.white + end + -- put renamebox in sl or something i guess FUCK why did i make it this way + end + local renamebox = sl.renamebox + renamebox.isShortcut = isShortcut + renamebox.scrollX = 0 + renamebox.state = true + local ext = lUtils.getFileType(f.path) + if ext == ".llnk" then + ext = "" + end + renamebox.select = {1,#fname-#ext} + renamebox.cursor.a = 1+#fname-#ext + renamebox.cursor.x = renamebox.cursor.a + renamebox.update("timer") + local ok,err = pcall(function() render(true) end) + if not ok then + lOS.bsod(err) + end + renamebox.render() + os.queueEvent("renamebox") +end + +local function fInvShort(obj,updateFiles,render) + local o = {lUtils.popup("Problem with shortcut","The item '"..obj.file.sName.."' that this shortcut refers to has been changed or moved, so this shortcut will no longer work properly.\n\nDo you want to delete this shortcut?",37,13,{"Yes","No"})} + if o[1] and o[3] == "Yes" then + fs.delete(obj.file.path) + updateFiles() + end + term.setTextColor(colors.white) + render() +end +lOS.explorer.fInvShort = fInvShort + +local function genContextMenu(f,s,sl,selected,updateFiles,render,LevelOS,isDesktop) + local shortcache = lOS.explorer.shortcache + local winObj = term.current() + + local function fInvShortInternal(obj) + term.redirect(winObj) + LevelOS.overlay = nil + lOS.noEvents = false + LevelOS.self.window.events = nil + fInvShort(obj,updateFiles,render) + term.setTextColor(colors.white) + end + + local function fDelete(obj) + for k,v in pairs(selected) do + fs.delete(k) + end + selected = {} + updateFiles() + end + + local function fOpen(obj) + local f = obj.file + selected = {} + sl.folder = f.path + updateFiles() + scrollY = 0 + end + + local function fCut(obj) + lOS.fClipboard = {} + for k,v in pairs(selected) do + table.insert(lOS.fClipboard,k) + end + lOS.fCut = lUtils.instantiate(lOS.fClipboard) + selected = {} + end + + local function fCopy(obj) + lOS.fClipboard = {} + lOS.fCut = {} + for k,v in pairs(selected) do + table.insert(lOS.fClipboard,k) + end + end + + local function fPaste(obj) + local tFolder = sl.folder + for c=1,#lOS.fClipboard do + if fs.exists(lOS.fClipboard[c]) == true then + fs.copy(lOS.fClipboard[c],uFP(fs.combine(tFolder,fs.getName(lOS.fClipboard[c])))) + end + if locateEntry(lOS.fCut,lOS.fClipboard[c]) == true then + fs.delete(lOS.fClipboard[c]) + table.remove(lOS.fCut,({locateEntry(lOS.fCut,lOS.fClipboard[c])})[2]) + end + end + lOS.fClipboard = {} + lOS.fCut = {} + updateFiles() + end + + local opink + local function fRename(obj) + lOS.explorer.fRename(obj,s,sl,render,LevelOS,isDesktop) + if isDesktop then + local renamebox = sl.renamebox + term.redirect(terminal) + render() + renamebox.render() + lOS.noEvents = false + while true do + local e = {os.pullEventRaw()} + if e[1] == "char" and e[2] == " " then + e[2] = "_" + end + if e[1] == "key" and e[2] == keys.enter then + renamebox.state = false + --sl.renaming = nil + else + renamebox.update(unpack(e)) + --render() + renamebox.render() + end + if not renamebox.state then + local pth = fs.combine("User/Desktop",renamebox.txt) + if renamebox.isShortcut then + pth = pth..".llnk" + end + if pth == sl.renaming or fs.combine(pth,"") == "User/Desktop" then + -- nothing + elseif fs.exists(pth) then + local newPth = uFP(pth) + local r = {lUtils.popup("Rename File","Do you want to rename \""..fs.getName(sl.renaming):gsub("_"," ").."\" to \""..fs.getName(newPth):gsub("_"," ").."\"?\n\nThere is already a file with the same name in this location.",41,11,{"Yes","No"})} + if r[1] and r[3] == "Yes" then + fs.move(sl.renaming,newPth) + end + else + fs.move(sl.renaming,pth) + end + renamebox = nil + sl.renamebox = nil + sl.renaming = nil + term.setCursorBlink(false) + updateFiles() + render() + break + end + end + end + end + + local function fNew(obj) + local pth = uFP(fs.combine(sl.folder,"New_"..obj.txt:gsub(" ","_").."."..obj.format)) + lUtils.fwrite(pth,obj.preset) + term.redirect(winObj) + updateFiles() + render() + fRename(pth) + -- rename + end + + local function fNewFolder(obj) + local pth = uFP(fs.combine(sl.folder,"New_Folder")) + fs.makeDir(pth) + term.redirect(winObj) + updateFiles() + render() + fRename(pth) + -- rename + end + + local function fOpenWith(obj) + if LevelOS and LevelOS.self and LevelOS.self.window then + LevelOS.self.window.events = nil + end + LevelOS.overlay = nil + lOS.noEvents = false + local p = sl.openWith(obj.file) + if p then + p = p.path + local f = obj.file + if p == "Lua" then + lOS.execute(f.path) + else + lOS.execute(p.." "..f.path) + end + end + end + + local function fPin(obj) + table.insert(sl.config.quickaccess,obj.file.path) + sl.saveConfig() + sl.reloadQuickAccess() + end + + local function fUnpin(obj) + local s,i = locateEntry(sl.config.quickaccess,obj.file.path) + if s then + table.remove(sl.config.quickaccess,i) + sl.saveConfig() + sl.reloadQuickAccess() + end + end + + local function fShortcut(obj) + local pth = uFP(fs.combine(obj.folder,obj.name:gsub(" ","_").."_-_Shortcut.llnk")) + lUtils.asset.save({obj.file.path},pth) + updateFiles() + render() + end + + local function fDesktopShortcut(obj) + lOS.genIco(obj.file.path) + --[[local pth = uFP(fs.combine(obj.folder,obj.name:gsub(" ","_")..".llnk")) + lUtils.asset.save({obj.file.path},pth) + os.queueEvent("explorer_reload")]] + end + + local function fBackground(obj) + lOS.explorer.desktopConfig.background = {path=obj.file.path,resize=obj.txt:lower()} + os.queueEvent("explorer_reload") + end + + local function fAddToBackground(obj) + local dConfig = lOS.explorer.desktopConfig + if not dConfig.background[1] then + local obg = dConfig.background + dConfig.background = {color=obg.color,obg} + end + table.insert(dConfig.background,{path=obj.file.path,resize=obj.txt:lower()}) + os.queueEvent("explorer_reload") + lOS.explorer.desktopBackground = nil + end + + local function fCopyPath(obj) + ccemux.setClipboard(obj.file.path) + end + + if isDesktop then + fOpen = fOpenWin + end + + local options + if not f then + local newfiles = {} + for k,v in pairs(fTypes) do + if type(v) == "table" and type(v.emptyFilePreset) == "string" then + table.insert(newfiles,{txt=v.name,preset=v.emptyFilePreset,action=fNew,format=k}) + end + end + table.insert(newfiles,{txt="bImg Icon",preset='{{{" "," "," "},{" "," "," "},{" "," "," "}}}',action=fNew,format="bimg"}) + local opt = { + {{txt="Refresh",action=updateFiles}}, + {{txt="Paste",action=fPaste,disabled=(#lOS.fClipboard == 0 or fs.isReadOnly(sl.folder))}}, + {{txt="New",disabled=(fs.isReadOnly(sl.folder)),action={{txt="Folder",action=fNewFolder},newfiles}}} -- import from formats.lconf + } + if isDesktop then + local txtcolors = {} + for k,v in pairs(colors) do + if type(v) == "number" then + table.insert(txtcolors,k) + end + end + table.sort(txtcolors) + local txtcoloroptions = {} + for c=1,#txtcolors do + local col = colors[txtcolors[c]] + local name = txtcolors[c]:sub(1,1):upper()..txtcolors[c]:sub(2) + local b = lUtils.toBlit(col) + if dConfig.textColor == col then + table.insert(txtcoloroptions,{txt={" \7 "..name,nil,string.rep(b,3)}}) + if col == colors.white then + txtcoloroptions[#txtcoloroptions].txt[2] = "fff" + end + else + table.insert(txtcoloroptions,{txt={" "..name,nil,string.rep(b,3)},action=function() dConfig.textColor = col updateFiles() render() end}) + end + end + if not dConfig.textColor then + table.insert(txtcoloroptions,1,{txt=" \7 Automatic"}) + else + table.insert(txtcoloroptions,1,{txt=" Automatic",action=function() dConfig.textColor = nil updateFiles() render() end}) + end + table.insert(opt[1],1,{txt="View",action={ + {txt="Text color",action={txtcoloroptions}} + }}) + end + if ccemux then + table.insert(opt,{{txt="Open External Explorer",action=function() ccemux.openDataDir() end}}) + end + options = opt + else + if type(f) == "string" then + f = genFile(f) + end + local file = f.path + local nf = f + local invalidShort + if shortcache[file] then + f.sPath = shortcache[file][1] + f.sName = fs.getName(f.sPath) + if fs.exists(f.sPath) then + nf = genFile(f.sPath) + else + invalidShort = true + end + end + options = { + { + + }, + { + {txt="Cut",action=fCut}, + {txt="Copy",action=fCopy} + }, + { + {txt="Delete",action=fDelete}, + {txt="Rename",action=fRename,file=f,sl=sl} + } + } + local createshortcut = {txt="Create shortcut",file=nf,folder=sl.folder,name=nf.name,action=fShortcut} + local createshortcut2 = {txt="Create shortcut on desktop",file=nf,folder="User/Desktop",name=nf.name,action=fDesktopShortcut} + if fs.getName(nf.path) == "main.lua" then + createshortcut.name = fs.getName(fs.getDir(nf.path)) + createshortcut2.name = fs.getName(fs.getDir(nf.path)) + end + if invalidShort then + createshortcut.action = fInvShortInternal + createshortcut2.action = fInvShortInternal + end + if not isDesktop then + table.insert(options[3],1,createshortcut2) + table.insert(options[3],1,createshortcut) + end + local disabled = {} + if f.readOnly == true then + disabled = {Edit=true,Cut=true,Delete=true,Rename=true} + elseif disks[file] ~= nil then + disabled = {Edit=true,Cut=true,Delete=true,Rename=true} + disabled["Create Shortcut"] = true + end + local fc = fs.combine + if ccemux then + table.insert(options[3],{txt="Copy path",file=f,action=fCopyPath}) + end + local fOpen = fOpen + local fOpenWin = fOpenWin + local fOpenWith = fOpenWith + local fExec = fExec + local fUnpin = fUnpin + local fPin = fPin + if invalidShort then + fOpen = fInvShortInternal + fOpenWin = fInvShortInternal + fOpenWith = fInvShortInternal + fExec = fInvShortInternal + fUnpin = fInvShortInternal + fPin = fInvShortInternal + end + if nf.isDir then + local etxt = "" + local isProgram = false + if fs.exists(fc(nf.path,"main.lua")) then + isProgram = true + etxt = "folder " + end + options[1] = {{txt="Open "..etxt,action=fOpen,file=nf}} + if not isDesktop then + table.insert(options[1],{txt="Open "..etxt.."in new window",action=fOpenWin,file=nf}) + end + if isProgram then + table.insert(options[1],{txt="Execute",action=fExec,p="Lua",file=nf}) + createshortcut.action = {{txt="To program",file=genFile(fc(nf.path,"main.lua")),folder=sl.folder,name=nf.name,action=fShortcut},{txt="To folder",file=nf,folder=sl.folder,name=nf.name,action=fShortcut}} + end + if not isDesktop then + if locateEntry(sl.config.quickaccess,nf.path) then + table.insert(options[1],{txt="Unpin from Quick access",action=fUnpin,file=nf}) + else + table.insert(options[1],{txt="Pin to Quick access",action=fPin,file=nf}) + end + end + elseif fTypes[nf.type] and fTypes[nf.type].contextMenu and not invalidShort then + local tbl = {} + for k,v in ipairs(fTypes[nf.type].contextMenu[1]) do + table.insert(tbl,{txt=v,file=nf,p=fTypes[nf.type].contextMenu[2][k],action=fExec}) + end + if nf.type == "bimg" or nf.type == "limg" or nf.type == "nfp" then + table.insert(tbl,{txt="Set as desktop background",action={{{txt="Stretch",file=nf,action=fBackground},{txt="Repeat",file=nf,action=fBackground},{txt="Center",file=nf,action=fBackground}}}}) + table.insert(tbl,{txt="Add to desktop background",action={{{txt="Repeat",file=nf,action=fAddToBackground},{txt="Center",file=nf,action=fAddToBackground}}},disabled=(not lOS.explorer.desktopConfig.background)}) + end + options[1] = tbl + else + options[1] = {{txt="Edit",action=fExec,p="rom/programs/edit.lua",file=nf}} + end + if shortcache[file] then + if invalidShort then + table.insert(options[1],2,{txt="Open file location",disabled=true}) + else + table.insert(options[1],2,{txt="Open file location",action=fOpen,file=genFile(fs.getDir(nf.path))}) + end + end + local ow + if fTypes[nf.type] and fTypes[nf.type].openWith and not nf.isDir then + local owtbl = {} + for k,v in ipairs(fTypes[nf.type].openWith) do + table.insert(owtbl,{txt=lUtils.getFileName(v),file=nf,p=v,action=fExec}) + end + ow = {txt="Open with",action={owtbl,{txt="Choose another app",action=fOpenWith,file=nf,disabled=isDesktop}}} + if invalidShort then + ow.disabled = true + end + table.insert(options[1],ow) + end + if nf.readOnly == true --[[and locateEntry(options[1],"Edit") == true]] then + for k,v in ipairs(options[1]) do + if type(v) == "table" and v.txt == "Edit" then + v.txt = "View" + end + end + --options[1][({locateEntry(options[1],"Edit")})[2]] = "View" + end + for _,cat in ipairs(options) do + for k,v in ipairs(cat) do + if type(v) == "string" and disabled[v] then + cat[k] = {txt=v,disabled=true} + elseif type(v) == "table" and disabled[v.txt] then + v.disabled = true + end + end + end + end + return options +end + +lOS.explorer.genContextMenu = genContextMenu + +local function cMenu(f, x, y) + local sel = {} + for k,v in pairs(selected) do + local path = fc("User/Desktop",dConfig.files[k]) + sel[path] = true + end + local options = genContextMenu(f,{win=term.current()},{folder="User/Desktop"},sel,reloadDesktop,render,LevelOS,true) + local output = {lOS.contextmenu(x,y,30,options,nil,true)} + return unpack(output) +end + +render() +os.sleep(0.05) +render() +local lastUpdate = os.epoch("utc") +local tID = os.startTimer(10) +while true do + local e = {os.pullEvent()} + if e[1] == "explorer_full_reload" then + lOS.explorer.shortcache = {} + lOS.explorer.imgcache = {} + lOS.explorer.nimgcache = {} + lOS.explorer.desktopBackground = nil + reloadDesktop() + render() + elseif e[1] == "term_resize" or e[1] == "explorer_reload" then + lOS.explorer.desktopBackground = nil + reloadDesktop() + render() + elseif e[1]:find("mouse") and e[3] and e[4] then + local x,y = math.ceil(e[3]/8),math.ceil(e[4]/5) + if e[1] == "mouse_click" then + lOS.focusWin = lOS.wins[0] + end + if e[1] == "mouse_drag" and drag then + drag.x,drag.y = e[3],e[4] + drag.moved = true + render() + elseif e[1] == "mouse_up" and drag then + if not desktopMap[x][y] then + desktopMap[x][y] = drag.id + local icon = dConfig.sizes[currentSize][drag.id] + desktopMap[icon.x][icon.y] = nil + icon.x,icon.y = x,y + lUtils.asset.save(dConfig,"LevelOS/data/desktop.lconf") + end + drag = nil + render() + elseif e[3]%8 ~= 0 and desktopMap[x][y] then + if e[1] == "mouse_click" then + local id = desktopMap[x][y] + if selected[id] and os.epoch("utc") < selected[id]+500 and e[2] == 1 then + local path = fc("User/Desktop",dConfig.files[id]) + local rPath = path + local cmd = path + local shortcache = lOS.explorer.shortcache + local exec = false + local invalidShort = false + local f = genFile(path) + if shortcache[path] then + cmd = shortcache[path][1] + rPath = cmd + f.sName = rPath + if not fs.exists(rPath) then + invalidShort = true + end + if shortcache[path].args and #shortcache[path].args > 0 then + cmd = cmd.." "..table.concat(shortcache[path].args) + end + if shortcache[path].program then + if shortcache[path].program ~= "Lua" then + cmd = shortcache[path].program.." "..cmd + end + exec = true + end + end + if invalidShort then + fInvShort({file=f},reloadDesktop,render) + else + local fType = lUtils.getFileType(rPath):sub(2) + if exec then + lOS.execute(cmd) + elseif fs.isDir(rPath) then + lOS.execute("Program_Files/LevelOS/Explorer "..cmd) + elseif fileTypes[fType] and fileTypes[fType].program then + local p = fileTypes[fType].program + if p == "Lua" then + lOS.execute(cmd) + else + lOS.execute(p.." "..cmd) + end + elseif fType == "" then + lOS.execute(cmd) + end + end + selected = {} + else + if not (lUtils.isHolding(keys.leftCtrl) or lUtils.isHolding(keys.leftShift)) then + selected = {} + end + if e[2] == 1 then + selected[id] = os.epoch("utc") + drag = {id=id,ox=(e[3]-1)%8,oy=(e[4]-1)%5,x=e[3],y=e[4],moved=false} + else + selected[id] = 0 + end + end + render() + elseif e[1] == "mouse_up" and e[2] == 2 then + local id = desktopMap[x][y] + local path = fc("User/Desktop",dConfig.files[id]) + --[[local rPath = path + local cmd = path + local shortcache = lOS.explorer.shortcache + local exec = false]] + local f = genFile(path) + f.x1,f.y1 = 1+(x-1)*8,1+(y-1)*5 + local lineH = math.min(#lUtils.wordwrap(lUtils.getFileName(f.path),7),4) + f.x2,f.y2 = f.x1+6,f.y1+3+lineH + cMenu(f,e[3],e[4]) + end + elseif e[1] == "mouse_click" and not (lUtils.isHolding(keys.leftCtrl) or lUtils.isHolding(keys.leftShift)) then + selected = {} + render() + if e[2] == 2 then + cMenu(nil, e[3], e[4]) + end + end + elseif (e[1] == "timer" and e[2] == tID) or os.epoch("utc")-10000 > lastUpdate then + lastUpdate = os.epoch("utc") + if not (e[1] == "timer" and e[2] == tID) then + os.cancelTimer(tID) + end + tID = os.startTimer(10) + reloadDesktop() + render() + end + if e[1] == "mouse_up" then + drag = nil + end +end \ No newline at end of file diff --git a/LevelOS/imageviewer.lua b/LevelOS/imageviewer.lua new file mode 100644 index 0000000..0d31afc --- /dev/null +++ b/LevelOS/imageviewer.lua @@ -0,0 +1,85 @@ +local format + +local function getLines(str) + local lines = {} + local w = 0 + for potato in str:gmatch("[^\n]+") do + table.insert(lines,potato) + if #potato > w then + w = #potato + end + end + return lines,w +end + +--[[local function render(spr) + term.setBackgroundColor(colors.black) + term.clear() + if format == "lImg" then + local sW,sH = #spr[1][1],#spr + local w,h = term.getSize() + for l=1,#spr do + term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),math.ceil(h/2)-math.floor(sH/2)+(l-1)) + local bl = {} + bl[1] = spr[l][1] + bl[2] = string.gsub(spr[l][2],"T",lUtils.toBlit(term.getBackgroundColor())) + bl[3] = string.gsub(spr[l][3],"T",lUtils.toBlit(term.getBackgroundColor())) + term.blit(unpack(bl)) + end + elseif format == "nfp" or format == "nfg" then + local b,e = string.find(spr,"\n") + local sW,sH + local w,h = term.getSize() + local lines,sW = getLines(spr) + sH = #lines + for l=1,sH do + term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),math.ceil(h/2)-math.floor(sH/2)+(l-1)) + term.blit(string.rep(" ",#lines[l]),lines[l],lines[l]) + end + end +end]] +local render = lUtils.renderImg + +local tArgs = {...} +local filepath +if tArgs[1] ~= nil and fs.exists(tArgs[1]) then + filepath = tArgs[1] +end +while not filepath do + filepath = lUtils.explorer("User","SelFile false") +end + +local sprite = {} +local ext = lUtils.getFileType(filepath) +if ext == ".nfp" then + format = "nfp" + sprite[1] = lUtils.fread(filepath) +elseif ext == ".nfg" then + format = "nfg" + sprite = lUtils.asset.load(filepath) +elseif ext == ".limg" or ext == ".bimg" then + format = "lImg" + sprite = lUtils.asset.load(filepath) +else + lUtils.popup("Error","This file type is not supported!",22,9,{"OK"}) + return +end + +local tID = os.startTimer(0.5) +render(sprite[1]) +local spr = 1 +while true do + local e = {os.pullEvent()} + if #sprite > 1 and e[1] == "timer" and e[2] == tID then + tID = os.startTimer(0.1) + spr = spr+1 + if spr > #sprite then + spr = 1 + end + term.clear() + render(sprite[spr]) + elseif e[1] == "term_resize" then + term.clear() + render(sprite[spr]) + end +end \ No newline at end of file diff --git a/LevelOS/lStore.lua b/LevelOS/lStore.lua new file mode 100644 index 0000000..b571ff7 --- /dev/null +++ b/LevelOS/lStore.lua @@ -0,0 +1,258 @@ +local cookie +if lOS and lOS.userID then + cookie = {Cookie=lOS.userID} +end + +local function printUsage() + local programName = arg[0] or fs.getName(shell.getRunningProgram()) + print("Usages:") + print(programName .. " put [listing]") + print(programName .. " get ") + print(programName .. " run ") +end + +local tArgs = { ... } +if #tArgs < 2 then + printUsage() + return +end + +if not http then + printError("LevelStore requires the http API") + printError("Set http.enabled to true in CC: Tweaked's config") + return +end + +local function fread(file) + local f = fs.open(file,"r") + local o = f.readAll() + f.close() + return o +end + +local function fwrite(file,content) + local f = fs.open(file,"w") + f.write(content) + f.close() + return true +end + +local hpost = http.post + +local function getField(thing,fieldname) + if string.find(thing,"<"..fieldname..">",1,true) ~= nil and string.find(thing,"",1,true) ~= nil then + begin = nil + ending = nil + trash,begin = string.find(thing,"<"..fieldname..">",1,true) + ending,ending2 = string.find(thing,"",begin+1,true) + if begin ~= nil and ending ~= nil then + return string.sub(thing,begin+1,ending-1),string.sub(thing,1,trash-1)..string.sub(thing,ending2+1,string.len(thing)) + end + end + return nil +end + +local rType + +local function download(name,pth,saveto,run) + local f = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode(pth).."&"..rType.."="..textutils.urlEncode(name),cookie).readAll() + if f and f ~= "409" and f ~= "403" and f ~= "401" then + if run then + return f + else + fwrite(saveto,f) + return true + end + else + return false + end +end + + +local function get(name) + + write("Connecting to LevelStore... ") + + rType = "code" + local response, err = http.post("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("").."&code="..textutils.urlEncode(name),cookie) + + if not response then + rType = "name" + response, err = http.post("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("").."&name="..textutils.urlEncode(name),cookie) + end + + if response then + local tree = {} + local folders = {} + local function searchFolder(folder) + --print("Searching folder root/"..folder) + local f = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode(folder).."&"..rType.."="..textutils.urlEncode(name),cookie).readAll() + --print(f) + local f2 = f + while true do + local file = nil + file,f = getField(f,"file") + if not file then + break + else + local name = getField(file,"name") + tree[#tree+1] = fs.combine(folder,name) + --print("Found "..fs.combine(folder,name)) + end + end + f = f2 + while true do + local file = nil + file,f = getField(f,"folder") + if not file then + break + else + local name = getField(file,"name") + --if not fs.exists(fs.combine(folder,name)) then + --fs.makeDir(fs.combine(folder,name)) + --end + folders[#folders+1] = fs.combine(folder,name) + searchFolder(fs.combine(folder,name)) + end + end + return true + end + searchFolder("") + print("Success.") + + return tree,folders + else + printError("Failed.") + print(err) + end +end + +local sCommand = tArgs[1] +if sCommand == "put" then + if #tArgs < 3 then + printUsage() + return + end + local listings = {"unlisted","public"} + for t=1,#listings do + listings[listings[t]] = true + end + local listing + if tArgs[4] then + if not listings[tArgs[4]] then + error(tArgs[4].." is not a valid listing. Choose one of the following: "..table.concat( listings, ", "),0) + else + listing = tArgs[4] + end + end + if not lOS.userID then + print("Not logged in") + print("Please have an active instance of LevelCloud running") + return + end + + + local sFile = tArgs[2] + local sPath = shell.resolve(fs.combine("User/Cloud",sFile)) + if not fs.exists(sPath) or not string.find(sPath,"User/Cloud") then + print("Path not found") + print("Note: provide a relative path to a file or folder starting from cloud") + return + end + + local sName = string.gsub(tArgs[3]," ","_") + + local list = "" + if listing then + list = "&listing="..textutils.urlEncode(listing) + end + write("Connecting to LevelStore... ") + local response = http.post( + "https://os.leveloper.cc/sProject.php", + "path="..textutils.urlEncode(tArgs[2]).. + "&title="..textutils.urlEncode(sName).. + "×tamp="..textutils.urlEncode(tostring(os.epoch("utc"))).. + "&direct="..textutils.urlEncode("false").. + list, + { + Cookie = lOS.userID + } + ) + + if response then + print("Success.") + + local sResponse = response.readAll() + response.close() + + print("Uploaded as " .. sResponse) + print("Run \"lStore get " .. sName .. "\" or \"lStore get " .. sResponse .. "\" to download anywhere") + + else + printError("Failed.") + end + +elseif sCommand == "get" then + + if #tArgs < 3 then + printUsage() + return + end + + -- Determine file to download + local sCode = tArgs[2] + local sFile = tArgs[3] + local sPath = shell.resolve(sFile) + if fs.exists(sPath) then + print("Path already exists") + return + end + + local tree,folders = get(sCode) + if not tree then return end + term.write("Downloading... ") + local x,y = term.getCursorPos() + term.write("0%") + if #tree > 1 or #folders > 0 then + fs.makeDir(sPath) + for f=1,#folders do + fs.makeDir(fs.combine(sPath,folders[f])) + end + for f=1,#tree do + download(sCode,tree[f],fs.combine(sPath,tree[f])) + term.setCursorPos(x,y) + term.write(math.ceil((f/#tree)*100+0.5).."%") + end + else + download(sCode,tree[1],sPath) + end + term.setCursorPos(x,y) + print("100%") + print("Downloaded as " .. sFile) +elseif sCommand == "run" then + local sCode = tArgs[2] + local res + local tree,folders = get(sCode) + if not tree then return end + if #tree > 1 or #folders > 0 then + print("This project must be downloaded to run.") + return + else + --print(textutils.serialize(tree)) + res = download(sCode,tree[1],"",true) + end + if res then + local func, err = load(res, sCode, "t", _ENV) + if not func then + printError(err) + return + end + local success, msg = pcall(func, select(3, ...)) + if not success then + printError(msg) + end + end +else + printUsage() + return +end \ No newline at end of file diff --git a/LevelOS/login.lua b/LevelOS/login.lua new file mode 100644 index 0000000..cd39f4c --- /dev/null +++ b/LevelOS/login.lua @@ -0,0 +1,396 @@ + +local bg = term.getBackgroundColor() +local fg = term.getTextColor() + +if fs.exists("bigfont") then + os.loadAPI("bigfont") +end + +local register = false + +local rememberme = false + +local w,h = term.getSize() + +local userbox = lUtils.makeEditBox("username",w,1) +local pwbox = lUtils.makeEditBox("password",w,1) +local pwconfirm = lUtils.makeEditBox("pwconfirm",w,1) + + +if fs.exists("LevelOS/data/account.txt") then + userbox.lines[1] = lUtils.fread("LevelOS/data/account.txt") +end + +if fs.exists("LevelOS/data/account2.txt") then + --pwbox.lines[1] = lUtils.fread("LevelOS/data/account2.txt") + token = lUtils.fread("LevelOS/data/account2.txt") + --rememberme = true + local res = http.post("https://www.old.leveloper.cc/auth.php","username="..userbox.lines[1].."&token="..token) + local str = res.readAll() + if str:find("Welcome") then + local userID = res.getResponseHeaders()["Set-Cookie"] + return userID,userbox.lines[1] + end +end + +local trysub = false + +local continue = true + +local boxes = {} + +local e = {} + +local webresponse + +local userID = "" + +local pause = false + +local function submit() + trysub = true + continue = true + os.sleep(0.5) + if continue == true then + saveuser = fs.open("LevelOS/data/account.txt","w") + saveuser.write(userbox.lines[1]) + saveuser.close() + if rememberme then + --lUtils.fwrite("LevelOS/data/account2.txt",pwbox.lines[1]) + else + fs.delete("LevelOS/data/account2.txt") + end + if register then + response = {http.post("https://www.old.leveloper.cc/register.php","username="..userbox.lines[1].."&password="..pwbox.lines[1])} + local webres = response[1].readAll() + if webres == "200" then + local xtra = "" + if rememberme then + xtra = "&rememberme=true" + end + response2 = {http.post("https://www.old.leveloper.cc/auth.php","username="..userbox.lines[1].."&password="..pwbox.lines[1]..xtra)} + userID = response2[1].getResponseHeaders()["Set-Cookie"] + webresponse = webres + if rememberme then + token = lUtils.getField(response2[1].readAll(),"token") + lUtils.fwrite("LevelOS/data/account2.txt",token) + end + end + else + local xtra = "" + if rememberme then + xtra = "&rememberme=true" + end + response = {http.post("https://www.old.leveloper.cc/auth.php","username="..userbox.lines[1].."&password="..pwbox.lines[1]..xtra)} + webresponse = response[1].readAll() + userID = response[1].getResponseHeaders()["Set-Cookie"] + if rememberme then + token = lUtils.getField(webresponse,"token") + lUtils.fwrite("LevelOS/data/account2.txt",token) + end + end + --print("username="..userbox.lines[1].."&password="..pwbox.lines[1]) + -- check if things are present communicate with PHP + end +end + +local regsel = false + +local function draw() + boxes = {} + local cY = 1 + local w,h = term.getSize() + term.setBackgroundColor(colors.white) + term.clear() + term.setBackgroundColor(colors.white) + if h < 19 then + -- absolutely fkin nothin + elseif h < 25 or w < 26 or not bigfont then + for t=0,2 do + term.setCursorPos(1,cY+t) + term.clearLine() + end + term.setCursorPos(2,cY+2) + term.setTextColor(colors.white) + term.setBackgroundColor(colors.lightGray) + term.write(string.rep("\143",w-2)) + term.setTextColor(colors.blue) + term.setBackgroundColor(colors.white) + cY = cY+1 + term.setCursorPos(1,cY) + if register then + lUtils.centerText("Register") + else + lUtils.centerText("Log in") + end + cY = cY+2 + else + if h < 50 or w < 90 then + for t=0,4 do + term.setCursorPos(1,cY+t) + term.clearLine() + end + term.setCursorPos(2,cY+4) + else + for t=0,10 do + term.setCursorPos(1,cY+t) + term.clearLine() + end + term.setCursorPos(2,cY+10) + end + term.setTextColor(colors.white) + term.setBackgroundColor(colors.lightGray) + term.write(string.rep("\143",w-2)) + term.setTextColor(colors.blue) + term.setBackgroundColor(colors.white) + cY = cY+1 + local size = 1 + --cY = cY+4 + if h >= 50 and w >= 90 then + size = 2 + cY = cY+10 + else + cY = cY+4 + end + if register then + bigfont.writeOn(term.current(),size,"Register",nil,2) + else + bigfont.writeOn(term.current(),size,"Log in",nil,2) + end + end + term.setBackgroundColor(colors.white) + for y=cY,h do + term.setCursorPos(1,y) + term.clearLine() + end + cY = cY+1 + term.setTextColor(colors.lightGray) + local offset = math.ceil(w/30) + local spacing = 0 + if h > 50 then + spacing = 5 + elseif h > 45 then + spacing = 4 + elseif h > 40 then + spacing = 3 + elseif h > 35 then + spacing = 2 + elseif h > 30 then + spacing = 1 + end + cY = cY+spacing + if (register and trysub and userbox.lines[1] and userbox.lines[1] ~= "" and string.len(userbox.lines[1]) >= 3 and #userbox.lines[1] <= 15 and not string.find(userbox.lines[1],"[^a-zA-Z0-9]")) then -- also check for special characters + term.setTextColor(colors.lime) + elseif register and trysub then + term.setTextColor(colors.red) + term.setCursorPos(1,cY+3) + if #userbox.lines[1] < 3 or #userbox.lines[1] > 15 then + lUtils.centerText("Username must be between 3-15 characters.") + else + lUtils.centerText("Username can not contain special characters.") + end + continue = false + else + term.setTextColor(colors.lightGray) + end + lUtils.border(1+offset,cY,w-offset,cY+2) + userbox.x = 2+offset + userbox.y = cY+1 + userbox.width = ((w-offset)-1)-(1+offset) + boxes[#boxes+1] = {x1=1+offset,y1=cY,x2=w-offset,y2=cY+2,func=function() lUtils.drawEditBox(userbox,nil,nil,nil,nil,nil,nil,true,false) end} + cY = cY+1 + term.setCursorPos(2+offset,cY) + if userbox.lines[1] and userbox.lines[1] ~= "" then + term.setTextColor(colors.gray) + term.write(string.sub(userbox.lines[1],1,userbox.width)) + else + term.write("Username") + end + cY = cY+3+spacing + if (register and trysub and pwbox.lines[1] and pwbox.lines[1] ~= "" and string.len(pwbox.lines[1]) >= 5) then + term.setTextColor(colors.lime) + elseif register and trysub then + term.setTextColor(colors.red) + continue = false + else + term.setTextColor(colors.lightGray) + end + lUtils.border(1+offset,cY,w-offset,cY+2) + pwbox.x = 2+offset + pwbox.y = cY+1 + pwbox.width = ((w-offset)-1)-(1+offset) + boxes[#boxes+1] = {x1=1+offset,y1=cY,x2=w-offset,y2=cY+2,func=function() lUtils.drawEditBox(pwbox,nil,nil,nil,nil,nil,nil,true,false,"*") end} + cY = cY+1 + term.setCursorPos(2+offset,cY) + if pwbox.lines[1] and pwbox.lines[1] ~= "" then + term.setTextColor(colors.gray) + term.write(string.rep("*",string.len(string.sub(pwbox.lines[1],1,pwbox.width)))) + else + term.write("Password") + end + if register then + cY = cY+3+spacing + if (register and trysub and pwconfirm.lines[1] and pwconfirm.lines[1] ~= "" and string.len(pwconfirm.lines[1]) > 3 and pwconfirm.lines[1] == pwbox.lines[1]) then + term.setTextColor(colors.lime) + elseif register and trysub then + term.setTextColor(colors.red) + if spacing > 0 then + term.setCursorPos(1,cY+3) + lUtils.centerText("Passwords don't match.") + end + continue = false + else + term.setTextColor(colors.lightGray) + end + lUtils.border(1+offset,cY,w-offset,cY+2) + pwconfirm.x = 2+offset + pwconfirm.y = cY+1 + pwconfirm.width = ((w-offset)-1)-(1+offset) + boxes[#boxes+1] = {x1=1+offset,y1=cY,x2=w-offset,y2=cY+2,func=function() lUtils.drawEditBox(pwconfirm,nil,nil,nil,nil,nil,nil,true,false,"*") end} + cY = cY+1 + term.setCursorPos(2+offset,cY) + if pwconfirm.lines[1] and pwconfirm.lines[1] ~= "" then + term.setTextColor(colors.gray) + term.write(string.rep("*",string.len(string.sub(pwconfirm.lines[1],1,pwconfirm.width)))) + else + term.write("Confirm Password") + end + end + cY = cY+3+spacing + term.setTextColor(colors.white) + if e[1] == "mouse_click" and e[3] >= 1+(offset+2) and e[4] >= cY and e[3] <= w-(offset+2) and e[4] <= cY+2 then + regsel = true + elseif e[1] == "mouse_up" then + regsel = false + end + if regsel or trysub then + term.setBackgroundColor(colors.lightBlue) + else + term.setBackgroundColor(colors.blue) + end + lUtils.border(1+(offset+2),cY,w-(offset+2),cY+2,"fill") + boxes[#boxes+1] = {x1=1+(offset+2),y1=cY,x2=w-(offset+2),y2=cY+2,func=submit} + cY = cY+1 + term.setCursorPos(1,cY) + if register then + lUtils.centerText("Register") + term.setBackgroundColor(colors.white) + term.setTextColor(colors.lightBlue) + term.setCursorPos(1,cY-2) + local w,h = term.getSize() + if w < string.len("Already have an account? Log in.") then + lUtils.centerText("Log in") + else + lUtils.centerText("Already have an account? Log in.") + end + boxes[#boxes+1] = {x1=math.ceil(w/2)-16,x2=math.ceil(w/2)+16,y1=cY-2,y2=cY-2,func=function() register = false end} + else + lUtils.centerText("Log in") + term.setBackgroundColor(colors.white) + term.setTextColor(colors.lightBlue) + term.setCursorPos(1,cY+2) + local w,h = term.getSize() + if w < string.len("Don't have an account yet? Register.") then + lUtils.centerText("Register") + else + lUtils.centerText("Don't have an account yet? Register.") + end + boxes[#boxes+1] = {x1=math.ceil(w/2)-18,x2=math.ceil(w/2)+18,y1=cY+2,y2=cY+2,func=function() register = true end} + end + cY = cY+4 + local bl = { + { + "Ÿ", + "00f", + "ff0", + }, + { + "• •", + "0ff", + "f00", + }, + { + "‚ƒ", + "fff", + "000", + }, + } + if rememberme then + bl[2][1] = "•x•" + end + local txt = " Auto-login" -- 4 spaces for the box + local rX = math.ceil(w/2)-math.floor(#txt/2) + term.setCursorPos(rX,cY) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.gray) + term.write(txt) + for b=1,#bl do + term.setCursorPos(rX,(cY-2)+b) + term.blit(unpack(bl[b])) + end + table.insert(boxes,{x1=rX,y1=cY,x2=rX+2,y2=cY,func=function() local f = function() rememberme = not rememberme end pause = true f() pause = false end}) +end +local function textboxes() + while true do + local e = {os.pullEvent()} + term.setCursorPos(1,1) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.black) + --term.write(table.concat(e,", ")) + if e[1] == "mouse_up" and e[2] == 1 then + for b=1,#boxes do + local box = boxes[b] + if e[3] >= box.x1 and e[4] >= box.y1 and e[3] <= box.x2 and e[4] <= box.y2 then + trysub = false + box.func() + os.startTimer(0.1) + end + end + end + end +end +local txtbox = coroutine.create(textboxes) +if rememberme then submit() end +draw() +while true do + e = {os.pullEvent()} + local w,h = term.getSize() + local pW,pH + if w < 29 then + pW = 19 + pH = 15 + else + pW = 29 + pH = 9 + end + if not webresponse then + if not pause then + draw() + end + coroutine.resume(txtbox,table.unpack(e)) + while pause do + local e = {os.pullEvent()} + coroutine.resume(txtbox,table.unpack(e)) + end + else + if (register and webresponse ~= "200") or (not register and not string.find(webresponse,"Welcome")) then + lUtils.popup("LevelOS",lUtils.getField(webresponse,"msg"),pW,pH,{"OK"}) + term.setTextColor(fg) + term.setBackgroundColor(bg) + if rememberme then + rememberme = false + fs.delete("LevelOS/data/account2.txt") + end + return false + elseif register then + lUtils.popup("LevelOS","You have successfully registered! You are now logged in.",pW,pH,{"OK"}) + term.setTextColor(fg) + term.setBackgroundColor(bg) + return userID,userbox.lines[1] + else + term.setTextColor(fg) + term.setBackgroundColor(bg) + return userID,userbox.lines[1] + end + end +end \ No newline at end of file diff --git a/LevelOS/modules/lex.lua b/LevelOS/modules/lex.lua new file mode 100644 index 0000000..6868032 --- /dev/null +++ b/LevelOS/modules/lex.lua @@ -0,0 +1,713 @@ +-- Lex, by LoganDark +-- Can be loaded using os.loadAPI, has only a single function: lex.lex('code here') +-- If loaded using dofile(), it returns the lex function (for environments outside ComputerCraft) +-- It returns a list of lists, where each list is one line. +-- Each line contains tokens (in the order they are found), where each token is formatted like this: +-- { +-- type = one of the token types below, +-- data = the source code that makes up the token, +-- posFirst = the position (inclusive) within THAT LINE that the token starts +-- posLast = the position (inclusive) within THAT LINE that the token ends +-- } + +-- Possible token types: +-- whitespace: Self-explanatory. Can match spaces, newlines, tabs, and carriage returns (although I don't know why anyone would use those... WINDOWS) +-- comment: Either multi-line or single-line comments. +-- string: A string. Usually the part of the string that is not an escape. +-- escape: Can only be found within strings (although they are separate tokens) +-- keyword: Keywords. Like "while", "end", "do", etc +-- value: Special values. Only true, false, and nil. +-- ident: Identifier. Variables, function names, etc.. +-- number: Numbers! +-- symbol: Symbols, like brackets, parenthesis, ., .., ... etc +-- operator: Operators, like =, ==, >=, <=, ~=, etc +-- unidentified: Anything that isn't one of the above tokens. Consider them ERRORS. + +local chars = { + whitespace = { + [' '] = true, + ['\n'] = true, + ['\t'] = true, + ['\r'] = true + }, + + validEscapes = { + ['a'] = true, + ['b'] = true, + ['f'] = true, + ['n'] = true, + ['r'] = true, + ['t'] = true, + ['v'] = true, + ['"'] = true, + ['\''] = true, + ['\\'] = true, + ['\n'] = true + }, + + ident = { + ['a'] = true, + ['b'] = true, + ['c'] = true, + ['d'] = true, + ['e'] = true, + ['f'] = true, + ['g'] = true, + ['h'] = true, + ['i'] = true, + ['j'] = true, + ['k'] = true, + ['l'] = true, + ['m'] = true, + ['n'] = true, + ['o'] = true, + ['p'] = true, + ['q'] = true, + ['r'] = true, + ['s'] = true, + ['t'] = true, + ['u'] = true, + ['v'] = true, + ['w'] = true, + ['x'] = true, + ['y'] = true, + ['z'] = true, + ['A'] = true, + ['B'] = true, + ['C'] = true, + ['D'] = true, + ['E'] = true, + ['F'] = true, + ['G'] = true, + ['H'] = true, + ['I'] = true, + ['J'] = true, + ['K'] = true, + ['L'] = true, + ['M'] = true, + ['N'] = true, + ['O'] = true, + ['P'] = true, + ['Q'] = true, + ['R'] = true, + ['S'] = true, + ['T'] = true, + ['U'] = true, + ['V'] = true, + ['W'] = true, + ['X'] = true, + ['Y'] = true, + ['Z'] = true, + ['_'] = true, + ['0'] = true, + ['1'] = true, + ['2'] = true, + ['3'] = true, + ['4'] = true, + ['5'] = true, + ['6'] = true, + ['7'] = true, + ['8'] = true, + ['9'] = true, + + start = { + ['a'] = true, + ['b'] = true, + ['c'] = true, + ['d'] = true, + ['e'] = true, + ['f'] = true, + ['g'] = true, + ['h'] = true, + ['i'] = true, + ['j'] = true, + ['k'] = true, + ['l'] = true, + ['m'] = true, + ['n'] = true, + ['o'] = true, + ['p'] = true, + ['q'] = true, + ['r'] = true, + ['s'] = true, + ['t'] = true, + ['u'] = true, + ['v'] = true, + ['w'] = true, + ['x'] = true, + ['y'] = true, + ['z'] = true, + ['A'] = true, + ['B'] = true, + ['C'] = true, + ['D'] = true, + ['E'] = true, + ['F'] = true, + ['G'] = true, + ['H'] = true, + ['I'] = true, + ['J'] = true, + ['K'] = true, + ['L'] = true, + ['M'] = true, + ['N'] = true, + ['O'] = true, + ['P'] = true, + ['Q'] = true, + ['R'] = true, + ['S'] = true, + ['T'] = true, + ['U'] = true, + ['V'] = true, + ['W'] = true, + ['X'] = true, + ['Y'] = true, + ['Z'] = true, + ['_'] = true + }, + }, + + digits = { + ['0'] = true, + ['1'] = true, + ['2'] = true, + ['3'] = true, + ['4'] = true, + ['5'] = true, + ['6'] = true, + ['7'] = true, + ['8'] = true, + ['9'] = true, + + hex = { + ['0'] = true, + ['1'] = true, + ['2'] = true, + ['3'] = true, + ['4'] = true, + ['5'] = true, + ['6'] = true, + ['7'] = true, + ['8'] = true, + ['9'] = true, + ['a'] = true, + ['b'] = true, + ['c'] = true, + ['d'] = true, + ['e'] = true, + ['f'] = true, + ['A'] = true, + ['B'] = true, + ['C'] = true, + ['D'] = true, + ['E'] = true, + ['F'] = true + } + }, + + symbols = { + ['+'] = true, + ['-'] = true, + ['*'] = true, + ['/'] = true, + ['^'] = true, + ['%'] = true, + [','] = true, + ['{'] = true, + ['}'] = true, + ['['] = true, + [']'] = true, + ['('] = true, + [')'] = true, + [';'] = true, + ['#'] = true, + ['.'] = true, + [':'] = true, + + equality = { + ['~'] = true, + ['='] = true, + ['>'] = true, + ['<'] = true + }, + + operators = { + ['+'] = true, + ['-'] = true, + ['*'] = true, + ['/'] = true, + ['^'] = true, + ['%'] = true, + ['#'] = true + } + } +} + +local keywords = { + structure = { + ['and'] = true, + ['break'] = true, + ['do'] = true, + ['else'] = true, + ['elseif'] = true, + ['end'] = true, + ['for'] = true, + ['function'] = true, + ['goto'] = true, + ['if'] = true, + ['in'] = true, + ['local'] = true, + ['not'] = true, + ['or'] = true, + ['repeat'] = true, + ['return'] = true, + ['then'] = true, + ['until'] = true, + ['while'] = true + }, + + values = { + ['true'] = true, + ['false'] = true, + ['nil'] = true, + ['self'] = true, + } +} + +function lex(text) + local pos = 1 + local start = 1 + local len = #text + local buffer = {} + local lines = {} + + local function look(delta) + delta = pos + (delta or 0) + + return text:sub(delta, delta) + end + + local function get() + local char = text:sub(pos, pos) + + pos = pos + 1 + + return char + end + + local function getLevel() + local num = 0 + + while look(num) == '=' do + num = num + 1 + end + + if look(num) == '[' then + pos = pos + num + + return num + else + return nil + end + end + + local function getToken() + return text:sub(start, pos - 1) + end + + local currentLineLength = 0 + local lineoffset = 0 + + local function token(type, text) + local tk = buffer[#buffer] + + if not tk or tk.type ~= type then + local tk = { + type = type, + data = text or getToken(), + posFirst = start - lineoffset, + posLast = pos - 1 - lineoffset + } + + if tk.data ~= '' then + buffer[#buffer + 1] = tk + end + else + tk.data = tk.data .. (text or getToken()) + tk.posLast = tk.posFirst + #tk.data - 1 + --tk.posLast = getCol(pos - 1) + end + + currentLineLength = currentLineLength + (text or getToken()):len() + + start = pos + return tk + end + + local function newline() + lines[#lines + 1] = buffer + buffer = {} + get() + token('newline') + buffer[1] = nil + lineoffset = lineoffset + currentLineLength + currentLineLength = 0 + end + + local function getData(level, type) + while true do + local char = get() + + if char == '' then + return + elseif char == '\n' then + pos = pos - 1 + token(type) + newline() + elseif char == ']' then + local valid = true + + for i = 1, level do + if look() == '=' then + pos = pos + 1 + else + valid = false + break + end + end + + if valid and look() == ']' then + pos = pos + 1 + + return + end + end + end + end + + while true do + while true do + local char = look() + + if char == '\n' then + token('whitespace') + newline() + elseif chars.whitespace[char] then + pos = pos + 1 + else + break + end + end + + token('whitespace') + + local char = get() + + if char == '' then + break + elseif char == '-' and look() == '-' then + pos = pos + 1 + + if look() == '[' then + pos = pos + 1 + + local level = getLevel() + + if level then + getData(level, 'comment') + else + while true do + local char2 = get() + + if char2 == '' or char2 == '\n' then + pos = pos - 1 + token('comment') + + if char2 == '\n' then + newline() + end + + break + end + end + end + else + while true do + local char2 = get() + + if char2 == '' or char2 == '\n' then + pos = pos - 1 + token('comment') + + if char2 == '\n' then + newline() + end + + break + end + end + end + + token('comment') + elseif char == '\'' or char == '"' then + local cbuf = #buffer + while true do + if not buffer[cbuf] then + break + elseif buffer[cbuf].type == "whitespace" then + cbuf = cbuf-1 + elseif buffer[cbuf].type == "ident" then + buffer[cbuf].type = "function" + break + else + break + end + end + while true do + local char2 = get() + + if char2 == '\\' then + pos = pos - 1 + token('string') + get() + + local char3 = get() + + if chars.digits[char3] then + for i = 1, 2 do + if chars.digits[look()] then + pos = pos + 1 + end + end + elseif char3 == 'x' then + if chars.digits.hex[look()] and chars.digits.hex[look(1)] then + pos = pos + 2 + else + token('unidentified') + end + elseif char3 == '\n' then + pos = pos - 1 + token('escape') + newline() + elseif not chars.validEscapes[char3] then + token('unidentified') + end + + token('escape') + elseif char2 == '\n' then + pos = pos - 1 + token('string') + newline() + + break + elseif char2 == char or char2 == '' then + break + end + end + + token('string') + elseif chars.ident.start[char] then + while chars.ident[look()] do + pos = pos + 1 + end + + local word = getToken() + if word == 'self' or word == '_ENV' or word == "_G" then + token('arg') + elseif word == 'function' then + local findBracket = false + local c = 0 + while true do + _G.debugstr = "" + local lChar = look(c) + _G.debugstr = debugstr..lChar + if lChar == " " or lChar == "\t" then + c = c+1 + elseif lChar == "(" then + findBracket = true + break + else + break + end + end + if findBracket then + local cbuf = #buffer + local findEquals = false + while true do + --_G.debugstr = debugstr..buffer[cbuf].type + if not buffer[cbuf] then + break + elseif buffer[cbuf].type == "whitespace" then + cbuf = cbuf-1 + elseif buffer[cbuf].data == "=" and not findEquals then + cbuf = cbuf-1 + findEquals = true + elseif buffer[cbuf].type == "ident" and findEquals then + buffer[cbuf].type = "nfunction" + break + else + break + end + end + end + token('function') + elseif keywords.structure[word] then + token('keyword') + elseif keywords.values[word] then + token('value') + else + local findBracket = false + local c = 0 + while true do + local lChar = look(c) + if lChar == " " or lChar == "\t" then + c = c+1 + elseif lChar == "(" then + findBracket = true + break + else + break + end + end + if findBracket then + if buffer[#buffer-1] and buffer[#buffer-1].data == "function" and buffer[#buffer].type == "whitespace" then + token('nfunction') + else + token('function') + end + else + local b = #buffer + local isArg = true + local closedArgs = false + while true do + local buf = buffer[b] + if not buf then + isArg = false + break + elseif buf.data == "(" or buf.type == "whitespace" or buf.data == "," or buf.type == "arg" then + if buf.data == "(" then + closedArgs = true + end + b = b-1 + elseif (buf.data == "function" or buf.type == "nfunction") and closedArgs then + token('arg') + break + else + isArg = false + break + end + end + if not isArg then + token('ident') + end + end + end + elseif chars.digits[char] or (char == '.' and chars.digits[look()]) then + if char == '0' and look() == 'x' then + pos = pos + 1 + + while chars.digits.hex[look()] do + pos = pos + 1 + end + else + while chars.digits[look()] do + pos = pos + 1 + end + + if look() == '.' then + pos = pos + 1 + + while chars.digits[look()] do + pos = pos + 1 + end + end + + if look():lower() == 'e' then + pos = pos + 1 + + if look() == '-' then + pos = pos + 1 + end + + while chars.digits[look()] do + pos = pos + 1 + end + end + end + + token('number') + elseif char == '[' then + local level = getLevel() + + if level then + local cbuf = #buffer + while true do + if not buffer[cbuf] then + break + elseif buffer[cbuf].type == "whitespace" then + cbuf = cbuf-1 + elseif buffer[cbuf].type == "ident" then + buffer[cbuf].type = "function" + break + else + break + end + end + getData(level, 'string') + token('string') + else + token('symbol') + end + elseif char == '.' then + if look() == '.' then + pos = pos + 1 + + if look() == '.' then + pos = pos + 1 + token('value') + else + token('operator') + end + else + token('symbol') + end + elseif chars.symbols.equality[char] then + if look() == '=' then + pos = pos + 1 + else + + end + + token('operator') + elseif chars.symbols[char] then + if chars.symbols.operators[char] then + token('operator') + else + if char == "{" then + local cbuf = #buffer + while true do + if not buffer[cbuf] then + break + elseif buffer[cbuf].type == "whitespace" then + cbuf = cbuf-1 + elseif buffer[cbuf].type == "ident" then + buffer[cbuf].type = "function" + break + else + break + end + end + end + token('symbol') + end + else + token('unidentified') + end + end + + lines[#lines + 1] = buffer + + return lines +end + +return lex \ No newline at end of file diff --git a/LevelOS/modules/shapescape/animation.lua b/LevelOS/modules/shapescape/animation.lua new file mode 100644 index 0000000..e69de29 diff --git a/LevelOS/modules/shapescape/class.lua b/LevelOS/modules/shapescape/class.lua new file mode 100644 index 0000000..4b44a2d --- /dev/null +++ b/LevelOS/modules/shapescape/class.lua @@ -0,0 +1,147 @@ +local class = {} + +local tAPI = {} -- local class API for objects + +local shapescape + +class.init = function(api) + shapescape = api + setmetatable(tAPI, {__index=api.group}) + api.generic.convertToClass = class.convertToClass +end + +tAPI.initialize = function(obj) + + local scape = obj:getScape() + local publicProperties = obj.publicProperties or {} + setmetatable(obj, {__index=tAPI}) + obj.isClass = true + local str = "" + + for k,v in pairs(publicProperties) do + if v == "" then + table.remove(publicProperties,k) + end + end + + if not shapescape.utils.locateEntry(publicProperties, "properties") then + table.insert(publicProperties,"properties") + end + + for i,p in ipairs(publicProperties) do + if p == "width" then + str = str.."instance:resize(width or instance.width)\n" + elseif p == "height" then + str = str.."instance:resize(nil, height or instance.height)\n" + elseif p == "x" then + str = str.."instance:move(x or instance.x)\n" -- "or instance.x1" is already done by API when provided with nil value + elseif p == "y" then + str = str.."instance:move(nil, y or instance.y)\n" + else + str = str.."instance."..p.." = "..p.." or instance."..p.."\n" + end + end + + table.insert(publicProperties,1,"self") + + local fStr = [[ +return function(]]..table.concat(publicProperties,",")..[[) + local instance = shapescape.utils.instantiate(self) + if instance.name then + instance.class = instance.name + instance.name = nil + end + ]]..str..[[ + for k,v in pairs(properties or {}) do + instance[k] = v + end + instance.isClass = nil + return instance +end]] + + local func,err = load(fStr,"@instantiate",nil,{shapescape=shapescape,pairs=pairs}) + if func then + obj.instantiate = func() + else + _G.debugstr = fStr + error(err,0) + end + if obj.name then + scape.variables[obj.name] = obj + end +end + +tAPI.instantiateTo = function(self, target, ...) + if not self.instantiate then + self:initialize() + end + return target:addObject(self:instantiate(...)) +end + +class.convertToClass = function(self, publicProperties, posProperties) + publicProperties = publicProperties or {} + if self.active then + error("Cannot convert active objects",2) + end + local sl = self:getSlide() + if sl then + -- remove obj from slide + self:destroy(false) + end + setmetatable(self, {__index=tAPI}) + self.isClass = true + self.publicProperties = publicProperties + local coords = {} + if posProperties == 1 or posProperties == nil then -- if posProperties is false then no position + coords = {"x1","y1","x2","y2"} + elseif posProperties == 2 then + coords = {"x","y","width","height"} + elseif posProperties == 3 then + coords = {"x","y"} + elseif posProperties == 4 then + coords = {"width","height"} + end + for i,c in ipairs(coords) do + if not shapescape.utils.locateEntry(publicProperties, c) then + table.insert(publicProperties, i, c) + end + end + table.insert(self:getScape().classes,self) + if self:getScape().active then + self:initialize() + end +end + +tAPI.convertToObject = function(self,slide) + self.publicProperties = nil + self.isClass = nil + for k,v in ipairs(self:getScape().classes) do + if v == self then + table.remove(self:getScape().classes,k) + end + end + slide:addObject(self) +end + +tAPI.destroy = function(self) + -- remove from classes + for k,v in ipairs(scape.classes) do + if v == self then + table.remove(scape.classes,k) + end + end + shapescape.generic.destroy(self) +end + +class.load = function(classObj) + setmetatable(classObj, {__index=tAPI}) + if classObj.children then + for i,child in ipairs(classObj.children) do + shapescape.loadObject(child) + child.parent = classObj + child.id = i + end + end +end + +return class \ No newline at end of file diff --git a/LevelOS/modules/shapescape/init.lua b/LevelOS/modules/shapescape/init.lua new file mode 100644 index 0000000..c01e62e --- /dev/null +++ b/LevelOS/modules/shapescape/init.lua @@ -0,0 +1,2228 @@ +-- shapescape + +-- somehow you need to detect whether the inputted argument is the path from require or a path from shell to execute a .scape file +-- i guess the best way to do that is to just check if the file extension is .scape +-- lol yea + +-- also if the file extension IS .scape, that means the API path is actually shell.getRunningProgram() + +local tArgs = {...} +local shapescape = {utils={},slide={},scape={},generic={}} + +local scapeRef = {} +local slideRef = {} + +local corFilterCache = {} + +local doExecute + +if type(tArgs[1]) == "string" and string.find(tArgs[1],"%.%w+$") and string.sub(tArgs[1],string.find(tArgs[1],"%.%w+$")) == ".scape" then + -- execute file + -- somehow + doExecute = tArgs[1] + shapescape.path = shell.getRunningProgram() +else + shapescape.path = tArgs[2] or tArgs[1] +end + +if shapescape.path then + shapescape.path = "/"..fs.getDir(shapescape.path) +end + +do -- imports + local function doImport(t,k) + local n + for k,v in pairs(shapescape) do + if v == t then + n = k + break + end + end + if not n then + error("Module not found",2) + end + shapescape[n] = require("/"..fs.combine(shapescape.path,n)) + if shapescape[n].init then + shapescape[n].init(shapescape) + end + for k,v in pairs(shapescape[n]) do + t[k] = v + end + return shapescape[n][k] + end + + local function getFileType(filename) + if string.find(filename,"%.%w+$") == nil then + return "" + else + return string.sub(filename,string.find(filename,"%.%w+$")) + end + end + + + + local function getFileName(filename,ext) + local f = filename + if string.find(filename," ") then + f = string.sub(filename,1,({string.find(filename," ")})[1]-1) + end + f = fs.getName(f) + if not ext or getFileType(f) == ".llnk" then + f = string.sub(f,1,string.len(f)-string.len(getFileType(f))) + end + return string.gsub(f,"_"," ") + end + + local ls = fs.list(shapescape.path) + for i,f in ipairs(ls) do + if f ~= "init.lua" then + local n = getFileName(f) + shapescape[n] = {import=function() doImport(shapescape[n]) end} + setmetatable(shapescape[n],{__index=doImport}) + end + end +end + + + +-- [[ UTILITIES API ]] +local utils = shapescape.utils +utils.wordwrap = function(txt,tWidth) + local lines = {} + for line in txt:gmatch("([^\n]*)\n?") do + table.insert(lines,"") + for word in line:gmatch("%S*%s?") do + local width + if type(tWidth) == "table" then + if not tWidth[#lines] then + lines[#lines] = nil + return lines + else + width = tWidth[#lines] + end + else + width = tWidth + end + if #lines[#lines]+#word > width and #(lines[#lines]:gsub(" ","")) > 0 then + lines[#lines+1] = "" + end + if #lines[#lines]+#word > width then + local tWord = word + while #lines[#lines]+#tWord > width do + lines[#lines] = tWord:sub(1,width) + table.insert(lines,"") + tWord = tWord:sub(width+1) + end + lines[#lines] = tWord + else + lines[#lines] = lines[#lines]..word + end + end + end + if lines[#lines] == "" then + lines[#lines] = nil + end + if txt:sub(#txt) == "\n" then + table.insert(lines,"") + end + while type(tWidth) == "table" and #lines > #tWidth do + lines[#lines] = nil + end + return lines +end + +local animationTbl = {} +utils.renderImage = function(spr,x,y,format,transparency) + local format + local img = spr + local cterm = term.current() + if not format then + if type(spr) == "string" then + format = "nfp" + elseif type(spr) == "table" then + format = "bImg" + if type(spr[1][1]) == "table" then + if #spr == 1 or not spr.animated then + spr = spr[1] + else + if not animationTbl[spr] then + animationTbl[spr] = {cFrame=0,oTime=os.epoch("utc")/1000} + else + local cFrame = animationTbl[spr].cFrame+0.0001 + local dur = spr[math.ceil(cFrame)].duration or spr.secondsPerFrame or 0.05 + local oTime = animationTbl[spr].oTime + local cTime = os.epoch("utc")/1000 + local delta = (cTime-oTime) + animationTbl[spr].cFrame = (cFrame + delta/dur) % #spr + animationTbl[spr].oTime = cTime + end + spr = spr[math.ceil(animationTbl[spr].cFrame+0.0001)] + end + end + end + end + if format == "bImg" or format == "lImg" then + if not spr[1] or not spr[1][1] or type(spr[1][1]) ~= "string" then + error("Unrecognized format",2) + end + local sW,sH = #spr[1][1],#spr + local w,h = term.getSize() + for l=1,#spr do + --[[if not y then + term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),(math.ceil(h/2)-math.floor(sH/2)+(l-1))) + else + term.setCursorPos(x,y+(l-1)) + end]] + local line + if y then + line = y+(l-1) + end + local cX,cY = x or math.ceil(w/2)-math.floor(sW/2), line or (math.ceil(h/2)-math.floor(sH/2)+(l-1)) + term.setCursorPos(cX,cY) + local bl = {} + bl[1] = spr[l][1] + if transparency then + bl[2] = spr[l][2] + bl[3] = spr[l][3] + -- thing + local line + if cY >= 1 and cY <= h then + line = {cterm.getLine(cY)} + for t=1,3 do + line[t] = line[t]:sub(cX,cX+(#spr[l][1]-1)) + end + else + line = {string.rep("f",#spr[l][1])} + line[2] = line[1] + line[3] = line[2] + end + if #line[2] < #spr[l][1] then + line[1] = line[1]..string.rep(" ",#spr[l][1]-#line[1]) + line[2] = line[2]..string.rep("f",#spr[l][1]-#line[2]) + line[3] = line[3]..string.rep("f",#spr[l][1]-#line[3]) + end + local start,final = 0,0 + while true do + start,final = bl[2]:gsub(" ","T"):find("T") + if start then + bl[2] = bl[2]:sub(1,start-1)..line[3]:sub(start,final)..bl[2]:sub(final+1,#bl[2]) + else + break + end + end + while true do + start,final = bl[3]:gsub(" ","T"):find("T") + if start then + if bl[1]:sub(start,final) == " " or bl[1]:sub(start,final) == "\128" then + bl[1] = bl[1]:sub(1,start-1)..line[1]:sub(start,final)..bl[1]:sub(final+1,#bl[1]) + bl[2] = bl[2]:sub(1,start-1)..line[2]:sub(start,final)..bl[2]:sub(final+1,#bl[2]) + end + bl[3] = bl[3]:sub(1,start-1)..line[3]:sub(start,final)..bl[3]:sub(final+1,#bl[3]) + else + break + end + end + else + bl[2] = string.gsub(spr[l][2]:gsub(" ","T"),"T",utils.toBlit(term.getBackgroundColor())) + bl[3] = string.gsub(spr[l][3]:gsub(" ","T"),"T",utils.toBlit(term.getBackgroundColor())) + end + term.blit(unpack(bl)) + end + elseif format == "nfp" or format == "nfg" then + local b,e = string.find(spr,"\n") + local sW,sH + local w,h = term.getSize() + local lines,sW = getLines(spr) + sH = #lines + for l=1,sH do + local line + if y then + line = y+(l-1) + end + term.setCursorPos(x or math.ceil(w/2)-math.floor(sW/2), line or math.ceil(h/2)-math.floor(sH/2)+(l-1)) + for lX=1,sW do + local ch = lines[l]:sub(lX,lX) + if ch ~= " " and ch ~= "" then + term.blit(" ","f",ch) + else + local cx,cy = term.getCursorPos() + term.setCursorPos(cx+1,cy) + end + end + end + end +end + +utils.instantiate = function(orig) + local function deepCopy(o, seen) -- so that "seen" doesn't appear in autocomplete + seen = seen or {} + if seen[o] then + return seen[o] + end + local copy + if type(o) == 'table' then + copy = {} + seen[o] = copy + for k,v in pairs(o) do + copy[deepCopy(k, seen)] = deepCopy(v, seen) + end + setmetatable(copy, deepCopy(getmetatable(o), seen)) + else + copy = o + end + return copy + end + return deepCopy(orig) +end + +local to_colors, to_blit = {}, {} +for i = 1, 16 do + to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i) + to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1) +end + +utils.toColor = function(theblit) + return to_colors[theblit] or nil +end + +utils.toBlit = function(thecolor) + return to_blit[thecolor] or nil +end + +utils.fread = function(filepath) + local fread = fs.open(filepath,"r") + local thing = fread.readAll() + fread.close() + return thing +end + +utils.locateEntry = function(tbl,value) + for k,v in pairs(tbl) do + if v == value then + return k + end + end +end + +utils.genStatelessIterator = function(...) + local tbls = {...} + local function iter(tbl) + local temp = {} + for i=#tbls,1 do + for k,v in pairs(tbls[i]) do + temp[k] = v + end + end + return next,temp + end + return iter +end + +-- [[ LOCAL API ]] +shapescape.debug = {scapeRef=scapeRef,slideRef=slideRef} +if not lOS.pDebug then + lOS.pDebug = {} +end +if not lOS.pDebug.scapes then + lOS.pDebug.scapes = {} +end +shapescape.debug.scapes = lOS.pDebug.scapes +local dLocal = shapescape.debug + +local function runListener(listeners,scape) + for k=#listeners,1,-1 do + local func = listeners[k] + local ok,err = pcall(func,unpack(scape.args)) + if not ok then + if not scape.blacklist then + scape.blacklist = {} + end + if scape.blacklist[func] then + table.remove(listeners,k) + else + scape.blacklist[func] = true + scape:warn(err) + end + end + end +end +dLocal.runListener = runListener + +local pointerCache = {} +dLocal.pointerCache = {} +local function runPointer(pointerValue,scape,self) + local pointer = self.pointers[pointerValue] + local ret + if type(pointer) == "string" or pointer.type == "formula" then + local formula + if type(pointer) == "string" then + formula = pointer + else + formula = pointer.content + end + local env = { + self = self, + -- load shapescape api or something too + math = math, + } + env.width,env.height = scape.slides[scape.currentSlide]:getSize() + setmetatable(env,{__index=scape.variables}) + local id = self.name or self.id + local func,err = load("return "..formula,"@"..id.."."..pointerValue, nil, env) + if not func then + func,err = load(formula,"@"..id.."."..pointerValue, nil, env) + end + if not func then + scape:warn(err) + else + local ok,output = pcall(func) + if ok then + if pointerValue == "text" and type(output) == "string" then + output = {content=output, color=self.textColor or colors.white} + elseif (pointerValue == "fill" or pointerValue == "border") and type(output) == "number" then + output = {color=output} + end + ret = output + else + scape:warn(output) + ret = 0 + end + end + elseif pointer.type == "variable" then + -- parse dots + ret = scape.variables[pointer.content] + end + if not pointerCache[self] then + pointerCache[self] = {} + end + if pointerCache[self][pointerValue] ~= ret then + pointerCache[self][pointerValue] = ret + self:markAsChanged() + end + return ret +end +dLocal.runPointer = runPointer + +local function deleted(t,k) + if k == "deleted" or k == "destroyed" then + return t + else + error("This object no longer exists.",2) + end +end +dLocal.deleted = deleted + +local function captureCursor() + local cursor = {} + cursor.x,cursor.y = term.getCursorPos() + cursor.color = term.getTextColor() + cursor.blink = term.getCursorBlink() + return cursor +end + +local function restoreCursor(cursor) + term.setCursorPos(cursor.x,cursor.y) + term.setTextColor(cursor.color) + term.setCursorBlink(cursor.blink) +end + +local function resumeCoroutines(self,...) + local ev = {...} + local scape = self:getScape() + for i=#self.coroutines,1,-1 do + local cor = self.coroutines[i] + if coroutine.status(cor) == "dead" then + table.remove(self.coroutines,i) + elseif not (corFilterCache[cor] and ev[1] ~= corFilterCache[cor]) then + local oterm = term.current() + if self.window then + term.redirect(self.window) + end + local ok, filter = coroutine.resume(cor,...) + if not ok then + scape:warn(filter) + table.remove(self.coroutines,i) + else + corFilterCache[cor] = filter + end + term.redirect(oterm) + end + end +end +dLocal.resumeCoroutines = resumeCoroutines + +local function fixChildren(o) + for i,obj in ipairs(o.children) do + scapeRef[obj] = scapeRef[o] + slideRef[obj] = slideRef[o] + if obj.children then + fixChildren(obj) + end + end +end + +local changeCache = {} +local renderCache = {} +dLocal.renderCache = renderCache +dLocal.changeCache = changeCache + +-- [[ GENERIC API ]] +local generic = shapescape.generic + +generic.getAbsolutePosition = function(self,includePadding) + local x1,y1,x2,y2 = self.x1,self.y1,self.x2,self.y2 + local o = self + while o.parent do + --local p = o.parent.padding or 0 + --local pL,pR,pT,pB = (o.parent.paddingLeft or p), (o.parent.paddingRight or p), (o.parent.paddingTop or p), (o.parent.paddingBottom or p) + local off = 0 + if o.parent.border then + off = 1 + end + x1 = x1+o.parent.x1-1+off + x2 = x2+o.parent.x1-1+off + y1 = y1+o.parent.y1-1+off + y2 = y2+o.parent.y1-1+off + o = o.parent + end + if includePadding then + local p = self.padding or 0 + local pL,pR,pT,pB = (self.paddingLeft or p), (self.paddingRight or p), (self.paddingTop or p), (self.paddingBottom or p) + x1, y1, x2, y2 = x1-pL, y1-pT, x2+pR, y2+pB + end + return x1,y1,x2,y2 +end + +generic.render = function(self, baseX, baseY, full) + + -- for variable parameters use custom objects using metamethods; indexing self with that field would return the value of the variable it refers to + -- the metamethods would be applied on the parent of the parameter and be defined during object initialization + + local oCursor = captureCursor() + + baseX = baseX or 1 + baseY = baseY or 1 + + local off = 0 + if self.border then + off = 1 + end + + local p = self.padding or 0 + local pL,pR,pT,pB = (self.paddingLeft or p), (self.paddingRight or p), (self.paddingTop or p), (self.paddingBottom or p) + + local x1, y1, x2, y2 = self.x1+baseX-1-pL, self.y1+baseY-1-pT, self.x2+baseX-1+pR, self.y2+baseY-1+pB + + if self.type == "circle" then + -- different rendering + + elseif self.border or self.fill or self.text then -- literally all other rendering + + + local mode = "fill" + if not self.fill then + mode = "transparent" + end + -- no lUtils allowed + -- center, top, bottom, left, right, corTL, corTR, corBL, corBR + local p + local t = " " + local fg = " " + local bg = " " + if self.border and self.border.color then + fg = utils.toBlit(self.border.color) + elseif self.fill and self.fill.color and self.fill.color ~= 0 then + fg = utils.toBlit(self.fill.color) + end + if self.fill and self.fill.color and self.fill.color ~= 0 and not (self.window and self.active) then + bg = utils.toBlit(self.fill.color) + end + --print("fg="..fg.."\nbg="..bg) + --os.sleep(0.5) + local pc = {" ",bg,bg} + if self.border then + local weight = self.border.weight or 1 + local dist = self.border.padding or 2 + local ctbl = { + [1] = { -- weight + [0] = { -- distance + top = {"\143",t,fg}, + bottom = {"\131",fg,t}, + left = {"\149",t,fg}, + right = {"\149",fg,t}, + corTL = {"\159",t,fg}, + corTR = {"\144",fg,t}, + corBL = {"\130",fg,t}, + corBR = {"\129",fg,t}, + }, + [1] = { + top = {"\140",fg,bg}, + bottom = {"\140",fg,bg}, + left = {"\149",fg,bg}, + right = {"\149",bg,fg}, + corTL = {"\156",fg,bg}, + corTR = {"\147",bg,fg}, + corBL = {"\141",fg,bg}, + corBR = {"\142",fg,bg}, + }, + [2] = { + top = {"\131",fg,bg}, + bottom = {"\143",bg,fg}, + left = {"\149",fg,bg}, + right = {"\149",bg,fg}, + corTL = {"\151",fg,bg}, + corTR = {"\148",bg,fg}, + corBL = {"\138",bg,fg}, + corBR = {"\133",bg,fg}, + }, --maybe make more efficient in future using the strings "bg" and "fg" so grid doesn't have to regenerate on color change + } + } + p = utils.instantiate(ctbl[weight][dist]) + p.center = pc + + else + p = {center=pc, top=pc, bottom=pc, left=pc, right=pc, corTL=pc, corTR=pc, corBL=pc, corBR=pc} + end + local txt + local lines = {} + local txtlines = {} + if self.text and self.text.color and self.text.content then + txt = self.text.content or self.textContent + -- if self.fill.image or self.fill.pattern then replace center lines with that etc etc + + -- add handling for blit lines + txtlines = utils.wordwrap(txt, (x2-pR-off)-(x1+pL+off)+1) + end + + + --_G.debuglines = {} + local winW,winH + if self.window then + winW,winH = self.window.getSize() + end + for y=y1,y2 do + local winLine + local absY = y-y1+1 - off-pT + if self.window then + if absY < 1 then + winLine = {self.window.getLine(1)} + elseif absY <= winH then + winLine = {self.window.getLine(absY)} + else + winLine = {self.window.getLine(winH)} + end + end + local txtline = txtlines[y-y1+1-pT-off] + term.setCursorPos(x1,y) + local line = {{},{},{}} + --table.insert(debuglines,line) + local function addPix(pix,amount) + amount = amount or 1 + for i=1,amount do + table.insert(line[1],pix[1]) + table.insert(line[2],pix[2]) + table.insert(line[3],pix[3]) + end + end + if not self.border then + addPix(p.center,x2-x1+1) + elseif y == y1 then + addPix(p.corTL) + addPix(p.top,x2-x1-1) + addPix(p.corTR) + elseif y == y2 then + addPix(p.corBL) + addPix(p.bottom,x2-x1-1) + addPix(p.corBR) + else + addPix(p.left) + addPix(p.center,x2-x1-1) + addPix(p.right) + end + local wLine + if self.window then + wLine = { + string.rep(" ",off+pL)..winLine[1]..string.rep(" ",off+pR), + string.rep(winLine[2]:sub(1,1),off+pL)..winLine[2]..string.rep(winLine[2]:sub(winW,winW),off+pR), + string.rep(winLine[3]:sub(1,1),off+pL)..winLine[3]..string.rep(winLine[3]:sub(winW,winW),off+pR), + } + elseif full then + local tw,th = term.getSize() + if y >= 1 and y <= th then + wLine = {term.current().getLine(y)} + else + wLine = {"","",""} + end + end + --_G.mydebugwline = wLine + if wLine then + for c=1,#line[1] do + local p = c+x1-1 + if self.window then + p = c + end + local c1,c2,c3 = wLine[1]:sub(p,p),wLine[2]:sub(p,p),wLine[3]:sub(p,p) + if c1 == "" then c1 = " " end + if c2 == "" then c2 = "f" end + if c3 == "" then c3 = "f" end + if line[2][c] == " " and line[3][c] == " " then + line[1][c] = c1 + line[2][c] = c2 + line[3][c] = c3 + elseif line[2][c] == " " then + line[2][c] = c3 + elseif line[3][c] == " " then + line[3][c] = c3 + end + end + end + for l=1,3 do + line[l] = table.concat(line[l]) + end + if txtline then + line[1] = line[1]:sub(1,off+pL)..txtline..line[1]:sub(off+pL+#txtline+1) + --[[for s=off+pL+1,off+pL+#txtline do + local c = s-(off+pL) + line[1][s] = txtline:sub(c,c)]] + + line[2] = line[2]:sub(1,off+pL)..string.rep(utils.toBlit(self.text.color or self.textColor),#txtline)..line[2]:sub(off+pL+#txtline+1) + end + if full then + term.blit(unpack(line)) + else + table.insert(lines,line) + end + end + if not full then + renderCache[self] = lines + end + --[[if self.fill and self.fill.image then -- temporary, make it stretch n shit and put it in the line fill + if self.fill.color then + term.setBackgroundColor(self.fill.color) + end + utils.renderImage(self.fill.image,x1+pL+off,y1+pT+off,nil,(not self.fill.color)) + end]] + + + end + if self.window and self.window.getCursorBlink() then + local x,y = self:getAbsolutePosition() + local col = self.window.getTextColor() + local cX,cY = self.window.getCursorPos() + term.setCursorPos(x+cX+off-1,y+cY+off-1) + term.setCursorBlink(true) + term.setTextColor(col) + else + restoreCursor(oCursor) + end + if self.children then + -- add base coords AND self coords (u become new parent but m aybe u already have parent) + for k,v in ipairs(self.children) do + if generic.isVisible(v) then + generic.render(v,x1+pL+off, y1+pT+off, full) --x1 and y1 are already base plus self stoopid + end + end + end +end + +generic.update = function(self, ...) + if not self.active then + if not scapeRef[slideRef[self]] then + error("No scape attached",2) + end + self:initialize() + end + if self.window then + local sw,sh = self:getSize() + local ww,wh = self.window.getSize() + if sw ~= ww or sh ~= wh then + self:resize(sw,sh) + end + end + local fEv = "self.mouse" + if not self:getScape() then + fEv = "mouse" + end + local e = {...} + local runCor = true + local runChildren = true + if e[1]:find(fEv) == 1 then + --e[3] = e[3]-self.x1+1 + --e[4] = e[4]-self.y1+1 + -- completely wrong, get absolute position + if e[3] and e[4] then + local x1,y1,x2,y2 = self:getAbsolutePosition() + local off = 0 + if self.border then + off = 1 + end + e[3] = e[3]-(x1+off)+1 + e[4] = e[4]-(y1+off)+1 -- test + end + local cE = table.pack(table.unpack(e)) + cE[1] = cE[1]:gsub("self.","") + if self.coroutines then + resumeCoroutines(self,unpack(cE)) + end + runCor = false + runChildren = false + elseif e[1]:find("mouse") == 1 then + runCor = false + end + local oterm = term.current() + if self.window then + term.redirect(self.window) + end + --[=[if self.eventListeners[e[1]] then + runListener(self.eventListeners[e[1]],self:getScape()) + end]=] + local sTbl = {} + for i=1,#e do + sTbl[i] = tostring(e[i]) + local l = table.concat(sTbl,".",1,i) + if self.eventListeners[l] then + runListener(self.eventListeners[l],self:getScape()) + end + end + if self.coroutines and runCor then + resumeCoroutines(self,unpack(e)) + end + if not self.destroyed then + if self.children and runChildren then + for i,child in ipairs(self.children) do + child:update(...) + end + end + end + term.redirect(oterm) +end + +local privRef = {} + +generic.initialize = function(self) -- provide the scape table to it for variables + local slide = slideRef[self] + local scape = scapeRef[slide] + if not scape then + error("No scape attached",2) + end + -- NOTE: CLASSES MUST NOT BE INITIALIZED, ONLY THEIR INSTANCES + -- except they should, just using custom class initialize + -- oh yeah i didnt make classes yet hm + -- figure out how to handle variables + -- maybe a table in an object with names of properties with variable values and then this table isn't looked at until initialize is ran and the properties are replaced with custom variable shit stuff + -- load the scripts into functions + -- etc + -- do everything that would make it not serializable + + -- add scape log to log errors and shit and warnings + -- generate env + if not self.parent or not self.parent.env then + self.env = { + shell = scape.env.shell, + multishell = scape.env.multishell, + package = scape.env.package, + require = scape.env.require, + LevelOS = scape.env.LevelOS, + shapescape = scape.env.shapescape, --no. add as __index. + _SCAPE = scape, + _SLIDE = slide, + } + else + self.env = {} + for k,v in pairs(self.parent.env) do + self.env[k] = v + end + end + -- make local sahpesacpe api + self.env._ENV = self.env + self.env.self = self + setmetatable(self.env,{__index=scape.variables,__newindex=scape.variables}) -- scape.variables index is _G + if self.type == "window" then + local p = self.padding or 0 + local pL,pR,pT,pB = (self.paddingLeft or p), (self.paddingRight or p), (self.paddingTop or p), (self.paddingBottom or p) + local off = 0 + if self.border then + off = 1 + end + local w,h = (self.x2-off)-(self.x1+off)+1, (self.y2-off)-(self.y1+off)+1 + self.window = window.create(term.current(),self.x1+off,self.y1+off,w,h,false) + if self.fill and self.fill.color then + self.window.setBackgroundColor(self.fill.color) + self.window.clear() + self.window.setCursorPos(1,1) + end + if self.textColor then + self.window.setTextColor(self.textColor) + end + end + + for i,e in pairs(self.eventListeners) do + if type(e) ~= "table" then + e = {e} + end + local newE = {} + for k,v in ipairs(e) do + local asset + if type(v) == "number" then + asset = scape:getAssetByID(v) + elseif type(v) == "string" then + asset = scape:getAssetByName(v) + else + asset = v + end + id, name = asset.id, asset.name + if asset then + local func,err = load(asset.content,"@"..(asset.name or asset.id),nil,self.env) + if not func then + scape:warn(err) + else + table.insert(newE,func) + end + -- put erorr if not loaded correctly idk + end + end + self.eventListeners[i] = newE + end + -- generate coroutines, run once with scape.args + + for k,v in pairs(self.pointers) do + self[k] = nil + end + local oldindex = getmetatable(self).__index + local private = {} + privRef[self] = private + for k,v in pairs(self) do + private[k] = v + self[k] = nil + end + local mt = { + __index=function(tbl,k) + if private[k] then + return private[k] + elseif self.pointers[k] then + return runPointer(k,scape,self) + elseif k == "width" then + return self.x2-self.x1+1 + elseif k == "height" then + return self.y2-self.y1+1 + elseif k == "x" then + return self.x1 + elseif k == "y" then + return self.y1 + else + return oldindex[k] + end + end, + __newindex=function(tbl,k,v) + if tbl[k] ~= v then + if k == "width" then + self:resize(v,self.height) + elseif k == "height" then + self:resize(self.width,v) + elseif k == "x" then + self:move(v,self.y1) + elseif k == "y" then + self:move(self.x1,v) + elseif k == "x1" then + self:reposition(v) + elseif k == "y1" then + self:reposition(self.x1,v) + elseif k == "x2" then + self:reposition(self.x1,self.y1,v,self.y2) + elseif k == "y2" then + self:reposition(self.x1,self.y1,self.x2,v) + else + private[k] = v + self:markAsChanged() + end + end + end + } + if self.type == "group" then + mt.__pairs = utils.genStatelessIterator(private, group, generic) + else + mt.__pairs = utils.genStatelessIterator(private, generic) + end + setmetatable(self, mt) + self.active = true + + if self.children then + for k,v in ipairs(self.children) do + if v.name then + if not self[v.name] then + self[v.name] = v + end + if not self.env[v.name] then + self.env[v.name] = v + end + end + v:initialize() + end + end + + if self.coroutines then + local args = scape.args or {} + for k=#self.coroutines,1,-1 do + local v = self.coroutines[k] + local asset = scape.assets[v] + if asset then + local func,err = load(asset.content,"@"..(asset.name or asset.id),nil,self.env) + if not func then + scape:warn(err) + table.remove(self.coroutines,k) + else + self.coroutines[k] = coroutine.create(func) + end + end + end + resumeCoroutines(self,unpack(args)) + end + + if self.eventListeners["initialize"] then + runListener(self.eventListeners["initialize"],scape) + end + + return true +end + +generic.reposition = function(self,x1,y1,x2,y2) + -- take care of aligning n shit idk + x1,y1,x2,y2 = x1 or self.x1, y1 or self.y1, x2 or self.x2, y2 or self.y2 + if self.active then + if not self.pointers.x1 then + privRef[self].x1 = x1 + end + if not self.pointers.y1 then + privRef[self].y1 = y1 + end + if not self.pointers.x2 then + privRef[self].x2 = x2 + end + if not self.pointers.y2 then + privRef[self].y2 = y2 + end + --self:markAsChanged() + renderCache[self:getSlide()] = nil + else + self.x1,self.y1,self.x2,self.y2 = x1,y1,x2,y2 + end + + if self.window then-- position dont matter shit, just size (window is invisible) thats wrong motherfucker it absolutely does + local p = self.padding or 0 + local pL,pR,pT,pB = (self.paddingLeft or p), (self.paddingRight or p), (self.paddingTop or p), (self.paddingBottom or p) + local off = 0 + if self.border then + off = 1 + end + local w,h = (self.x2-off)-(self.x1+off)+1, (self.y2-off)-(self.y1+off)+1 + local x,y = self:getAbsolutePosition() + self.window.reposition(x+off,y+off,w,h) + end +end + +generic.move = function(self,x,y) + x = x or self.x1 + y = y or self.y1 + local offX,offY = x-self.x1,y-self.y1 + self:reposition(x,y,self.x2+offX,self.y2+offY) +end + +generic.resize = function(self,width,height) + self:reposition(self.x1,self.y1,((width and (self.x1+width-1)) or self.x2),((height and (self.y1+height-1)) or self.y2)) +end + +generic.duplicate = function(self,properties) + if self.active then + error("Cannot duplicate active objects",2) + end + return self:getSlide():addObject(utils.instantiate(self)) +end + +generic.destroy = function(self,full) + if full == nil then + full = true + end + if self.parent then + for k,v in pairs(self.parent.children) do + if v == self then + table.remove(self.parent.children,k) + break + end + end + for k,v in pairs(self.parent) do + if v == self then + self.parent[k] = nil + end + end + local sl = self:getSlide() + elseif not self.isClass then + local sl = self:getSlide() + for k,v in pairs(sl.objects) do + if v == self then + table.remove(sl.objects,k) + end + end + for id,obj in ipairs(sl.objects) do + obj.id = id -- so ID is not static hm + -- then again it doesn't really need to be because string IDs *are* static + end + end + if full then + for k,v in pairs(self) do + self[k] = nil + end + setmetatable(self,{__index=deleted,__newindex=deleted}) + end + if slideRef[self] then + renderCache[slideRef[self]] = nil + slideRef[self] = nil + end +end + +generic.markAsChanged = function(self) + local slide = self:getSlide() + if not slide then + return false + else + if not changeCache[slide] then + changeCache[slide] = {} + end + if changeCache[self] then + for k,v in pairs(changeCache[self]) do + if k ~= "old" and self[k] ~= v then + changeCache[slide][self] = 2 + break + end + end + end + local x1,y1,x2,y2 = self:getAbsolutePosition(true) + changeCache[self] = {x1=x1,y1=y1,x2=x2,y2=y2,old=changeCache[self]} + changeCache[slide][self] = changeCache[slide][self] or 1 + if self.children then + for k,v in pairs(self.children) do + v:markAsChanged() + end + end + end +end + +generic.getSize = function(self) + return self.x2-self.x1+1,self.y2-self.y1+1 +end + +generic.getPosition = function(self) + return self.x1,self.y1 +end + +generic.getBackgroundColor = function(self) + if not self.fill then + return 0 + else + return self.fill.color or 0 + end +end + +generic.getBorderColor = function(self) + if not self.border then + return 0 + else + return self.border.color or 0 + end +end + +generic.getTextColor = function(self) + if not self.text then + return 0 + else + return self.text.color or 0 + end +end + +generic.getText = function(self) + if not self.text then + return nil + else + return self.text.content + end +end + +generic.setBackgroundColor = function(self,color) + if color == nil or color == 0 then + self.fill = nil + else + if not self.fill then self.fill = {} end + self.fill.color = color + end + self:markAsChanged() +end + +generic.setTextColor = function(self,color) + if not self.text then self.text = {content=""} end + self.text.color = color + self:markAsChanged() +end + +generic.setText = function(self,content,color) + if not self.text then self.text = {} end + self.text.color = color or self.text.color or self.txtcolor or self.textColor or colors.black + self.text.content = content + self:markAsChanged() +end + +generic.setBorderColor = function(self,color) + if color == nil or color == 0 then + self.border = nil + else + if not self.border then self.border = {weight=1,padding=2} end + self.border.color = color + end + self:markAsChanged() +end + +generic.setPointer = function(self, key, value) + if not self.pointers then self.pointers = {} end + if self.active then + privRef[self][key] = nil + end + self.pointers[key] = value +end + +generic.removePointer = function(self, key) + if self.active then + self[key] = self[key] + end + self.pointers[key] = nil +end + +generic.addListener = function(self, sEvent, sAsset) + local scape = scapeRef[self] + if not self.eventListeners then + self.eventListeners = {} + end + if sEvent == "coroutine" then + if not self.coroutines then + self.coroutines = {} + end + elseif not self.eventListeners[sEvent] then + self.eventListeners[sEvent] = {} + end + if self.active and (type(sAsset) == "string" or type(sAsset) == "number") and scape then + local asset + if type(sAsset) == "string" then + asset = scape:getAssetByName(sAsset) + else + asset = scape:getAssetByID(sAsset) + end + if asset then + local func,err = load(asset.content,"@"..(asset.name or asset.id),nil,self.env) + if not func then + scape:warn(err) + elseif sEvent == "coroutine" then + local args = scape.args or {} + local k = #self.coroutines+1 + self.coroutines[k] = coroutine.create(func) + local oterm = term.current() + if self.window then + term.redirect(self.window) + end + local ok,filter = coroutine.resume(self.coroutines[k],unpack(args)) + if not ok then + scape:warn(filter) + table.remove(self.coroutines,k) + else + corFilterCache[self.coroutines[k]] = filter + end + term.redirect(oterm) + else + table.insert(self.eventListeners[sEvent],func) + end + end + elseif self.active and sEvent == "coroutine" then + local func = sAsset + local args = scape.args or {} + local k = #self.coroutines+1 + self.coroutines[k] = coroutine.create(func) + local oterm = term.current() + if self.window then + term.redirect(self.window) + end + local ok,filter = coroutine.resume(self.coroutines[k],unpack(args)) + if not ok then + scape:warn(filter) + table.remove(self.coroutines,k) + else + corFilterCache[self.coroutines[k]] = filter + end + term.redirect(oterm) + elseif sEvent == "coroutine" then + table.insert(self.coroutines,sAsset) + else + table.insert(self.eventListeners[sEvent],sAsset) + end +end + +generic.getSlide = function(self) + return slideRef[self] +end + +generic.getScape = function(self) + return scapeRef[self] +end + +generic.isInside = function(self,x,y) + local x1,y1,x2,y2 = self:getAbsolutePosition(true) + + if x >= x1 and y >= y1 and x <= x2 and y <= y2 then + return true + end +end + +generic.arrange = function(self,newPositionID) + local slide = self:getSlide() + if not slide then + error("No slide attached to object",2) + end + local parent = slide.objects + if self.parent then + parent = self.parent.children + end + if newPositionID < 0 then + newPositionID = #parent+newPositionID+2 + end + newPositionID = math.min(#parent+1,newPositionID) + table.remove(parent,self.id) + table.insert(parent,newPositionID,self) + for id,iObj in ipairs(parent) do + iObj.id = id + end +end + +generic.moveToFront = function(self) + self:arrange(-1) +end + +generic.moveToBack = function(self) + self:arrange(1) +end + +generic.moveForward = function(self) + self:arrange(self.id+1) +end + +generic.moveBackward = function(self) + self:arrange(math.max(1,self.id-1)) +end + +-- [[ GROUP API ]] +shapescape.group = {} +local group = shapescape.group +setmetatable(group, {__index=generic}) + +group.resetSize = function(self) + local off = 0 + if self.border then + off = 1 + end + local baseX,baseY = self.x1+off,self.y1+off + self.x1 = self.children[1].x1+baseX-1-off + self.y1 = self.children[1].y1+baseY-1-off + self.x2 = self.children[1].x2+baseX-1+off + self.y2 = self.children[1].y2+baseY-1+off + for i=2,#self.children do + local o = self.children[i] + self.x1 = math.min(self.x1,o.x1+baseX-1-off) + self.y1 = math.min(self.y1,o.y1+baseY-1-off) + self.x2 = math.max(self.x2,o.x2+baseX-1+off) + self.y2 = math.max(self.y2,o.y2+baseY-1+off) + end + local nBaseX,nBaseY = self.x1+off,self.y1+off + local offX,offY = nBaseX-baseX,nBaseY-baseY + for i=1,#self.children do + self.children[i]:move(self.children[i].x1-offX,self.children[i].y1-offY) + end +end + +group.addObject = function(self,obj) + local off = 0 + if self.border then + off = 1 + end + local xOff,yOff,_,_ = self:getAbsolutePosition() + xOff = xOff+off-1 + yOff = yOff+off-1 + obj.x1,obj.y1,obj.x2,obj.y2 = obj.x1-xOff,obj.y1-yOff,obj.x2-xOff,obj.y2-yOff + obj.parent = self + shapescape.loadObject(obj) + scapeRef[obj] = scapeRef[self] + slideRef[obj] = slideRef[self] + if obj.children then + fixChildren(obj) + end + table.insert(self.children,obj) + obj.id = #self.children + self:resetSize() + if self.active then + obj:initialize() + end + self:resetSize() + local sl = self:getSlide() + if sl then + renderCache[sl] = nil + end +end + +local invisibleObjects = {} +generic.setVisible = function(self,visibility) + if not visibility then + invisibleObjects[self] = true + else + invisibleObjects[self] = false + end +end + +generic.isVisible = function(self) + return not invisibleObjects[self] +end + +-- [[ SLIDE API ]] +local slide = shapescape.slide + +slide.initialize = function(self) + self:setVisible(true) + local scape = scapeRef[self] + self.window = window.create(term.current(), 1, 1, self.width, self.height, self:isVisible()) + self:render(true) + for i=1,#self.objects do + local obj = self.objects[i] + obj:initialize(scape) + if obj.name and not scape.variables[obj.name] then + scape.variables[obj.name] = obj + end + end + self.active = true +end + +slide.getScape = function(self) + return scapeRef[self] +end + +slide.getSize = function(self) + return self.width,self.height +end + +slide.resize = function(self, width, height) + self.width, self.height = width, height + if self.window then + local x,y = self.window.getPosition() + self.window.reposition(x,y,width,height) + end +end + +slide.loadObject = function(self,obj) + shapescape.loadObject(obj) + scapeRef[obj] = scapeRef[self] + slideRef[obj] = self + if obj.children then + fixChildren(obj) + end + renderCache[self] = nil + return obj +end + +slide.addObject = function(self,obj) + obj.id = #self.objects+1 + table.insert(self.objects,obj) + return self:loadObject(obj) +end + +slide.createObject = function(self,oType,x1,y1,x2,y2,properties) + return self:addObject(shapescape.createObject(oType,x1,y1,x2,y2,properties)) +end + +slide.createRectangle = function(self,x1,y1,x2,y2,fill,border,properties) + return self:addObject(shapescape.createRectangle(x1,y1,x2,y2,fill,border,properties)) +end + +slide.createWindow = function(self,x1,y1,x2,y2,fill,textColor,border,properties) + return self:addObject(shapescape.createWindow(x1,y1,x2,y2,fill,textColor,border,properties)) +end + +slide.createGroup = function(self,objects,properties) + return self:addObject(shapescape.createGroup(objects,properties)) +end + +slide.isVisible = function(self) + return self.visible +end + +slide.setVisible = function(self,visibility) + self.visible = not not visibility + if self.window then + self.window.setVisible(self.visible) + end +end + +slide.getBackgroundColor = function(self) + if not self.background then + return 0 + else + return self.background.color or 0 + end +end + +slide.setBackgroundColor = function(self,color) + if not self.background then self.background = {} end + self.background.color = color +end + +slide.genCache = function(self,objs,doDebug) + if not objs then + renderCache[self] = {} + objs = self.objects + end + local c = renderCache[self] + for i=1,#objs do + local o = objs[i] + if o:isVisible() then + local x1,y1,x2,y2 = o:getAbsolutePosition(true) + for x=x1,x2 do + if not c[x] then + c[x] = {} + end + local cx = c[x] + for y=y1,y2 do + if not cx[y] then + cx[y] = {o} + else + table.insert(cx[y],1,o) + end + end + end + if doDebug then + o:render(1,1,true) + os.sleep(1) + term.clear() + os.sleep(1) + end + if o.children then + self:genCache(o.children, doDebug) + end + end + end +end + +slide.render = function(self,full) + local oterm = term.current() + if self.window then + term.redirect(self.window) + end + local function get(pix,x,y) + local fgn = 1 + local o = pix[fgn] + local ox,oy = o:getAbsolutePosition(true) + local fgp = renderCache[o] and renderCache[o][y-oy+1] and renderCache[o][y-oy+1][2]:sub(x-ox+1,x-ox+1) or " " + while fgp == " " do + fgn = fgn+1 + if fgn > #pix then + fgp = utils.toBlit(term.getBackgroundColor()) + break + end + local o = pix[fgn] + ox,oy = o:getAbsolutePosition(true) + fgp = renderCache[o] and renderCache[o][y-oy+1] and renderCache[o][y-oy+1][2]:sub(x-ox+1,x-ox+1) or " " + end + local bgn = 1 + local o = pix[bgn] + local ox,oy = o:getAbsolutePosition(true) + local bgp = renderCache[o] and renderCache[o][y-oy+1] and renderCache[o][y-oy+1][3]:sub(x-ox+1,x-ox+1) or " " + while bgp == " " do + bgn = bgn+1 + if bgn > #pix then + bgp = utils.toBlit(term.getTextColor()) + break + end + local o = pix[bgn] + ox,oy = o:getAbsolutePosition(true) + bgp = renderCache[o] and renderCache[o][y-oy+1] and renderCache[o][y-oy+1][3]:sub(x-ox+1,x-ox+1) or " " + end + local txtn = math.min(fgn,bgn) + if txtn > #pix then + txtp = " " + else + local o = pix[txtn] + local ox,oy = o:getAbsolutePosition(true) + txtp = renderCache[pix[txtn]] and renderCache[pix[txtn]][y-oy+1][1]:sub(x-ox+1,x-ox+1) or " " + end + if #txtp ~= #fgp or #txtp ~= #bgp or #fgp ~= #bgp then + _G.x,_G.y,_G.txtp,_G.fgp,_G.bgp,_G.pix = x,y,txtp,fgp,bgp,pix + error("wtf") + end + --table.insert(debuglogshit,{txtp=txtp,fgp=fgp,bgp=bgp,pix=pix,x=x,y=y}) + return txtp,fgp,bgp + end + if self.background then + if self.background.color then + term.setBackgroundColor(self.background.color) + elseif self.background.image then + -- idk + end + end + local bg = utils.toBlit(term.getBackgroundColor()) + if full or not renderCache[self] then + term.clear() + for i,o in ipairs(self.objects) do + if not renderCache[o] then + o:render() + end + end + if not renderCache[self] then + self:genCache() + end + + local w,h = self:getSize() + local c = renderCache[self] + + _G.debugLines = {} + for y=1,h do + term.setCursorPos(1,y) + local line = {{},{},{}} + for x=1,w do + if c[x] and c[x][y] then + local cxy = c[x][y] + l1,l2,l3 = get(cxy,x,y) + _G.debugPix = {l1,l2,l3} + table.insert(line[1],l1) + table.insert(line[2],l2) + table.insert(line[3],l3) + --table.insert(debuglogshit,{l1,l2,l3}) + else + table.insert(line[1]," ") + table.insert(line[2],bg) + table.insert(line[3],bg) + --table.insert(debuglogshit,{" ",bg,bg}) + end + end + -- THIS LINE INTERRUPTS YOU MOTHERFUCKER WORK PLEASE + _G.moredebugshit = {line[1],line[2],line[3]} + table.insert(debugLines,{table.concat(line[1]),table.concat(line[2]),table.concat(line[3])}) + term.blit(table.concat(line[1]),table.concat(line[2]),table.concat(line[3])) + end + elseif changeCache[self] then + --[[for k,v in pairs(changeCache[self]) do + if v == 2 then + renderCache[self] = nil + self:genCache() + self:render(true) + break + end + end]] + if self.debugMode and not self.i then + self.i = 0 + end + local c = renderCache[self] + _G.debugRenderList = {} + if not changeCache.previous then + changeCache.previous = {} + end + for o,t in pairs(changeCache[self]) do + if not o.destroyed then + o:render() + table.insert(debugRenderList,o) + local x1,y1,x2,y2 = o:getAbsolutePosition(true) + local cur = {x1=x1,y1=y1,x2=x2,y2=y2} + local changePos = false + if changeCache.previous[o] then + local p = changeCache.previous[o] + for k,v in pairs(p) do + if v ~= cur[k] then + -- render previous position + changePos = true + for y=p.y1,p.y2 do + local line = {{},{},{}} + for x=p.x1,p.x2 do + if c[x] and c[x][y] then + local cxy = c[x][y] + table.remove(cxy,utils.locateEntry(cxy,o)) + if #cxy == 0 then + c[x][y] = nil + end + end + if c[x] and c[x][y] then + if self.debugMode then + term.setCursorPos(x,y) + term.setBackgroundColor(2^self.i) + term.write(" ") + else + local cxy = c[x][y] + l1,l2,l3 = get(cxy,x,y) + _G.debugPix = {l1,l2,l3} + table.insert(line[1],l1) + table.insert(line[2],l2) + table.insert(line[3],l3) + end + else + if self.debugMode then + term.setCursorPos(x,y) + term.setBackgroundColor(2^self.i) + term.setTextColor(2^((self.i+1)%16)) + term.write("0") + else + table.insert(line[1]," ") + table.insert(line[2],bg) + table.insert(line[3],bg) + end + end + end + if not self.debugMode then + term.setCursorPos(p.x1,y) + term.blit(table.concat(line[1]),table.concat(line[2]),table.concat(line[3])) + end + end + break + end + end + end + changeCache.previous[o] = cur + for y=y1,y2 do + local line = {{},{},{}} + for x=x1,x2 do + if changePos then + if not c[x] then + c[x] = {} + end + if not c[x][y] then + c[x][y] = {} + end + local cxy = c[x][y] + table.insert(cxy,o) + -- compare equal parent levels but FUCK how do I do that + -- OH I KNOW + -- if parent and parent does NOT equal go up a level + -- if not parent or parent equals then bigger ID wins + -- wait fuck im dumb, what if b IS the parent + local function isOnTop(a, b) + if a.parent == b.parent then + return a.id > b.id + elseif a.parent == b then + return true + elseif a == b.parent then + return false + else + if a.parent then + if b.parent then + local r = isOnTop(a.parent, b.parent) + if r ~= nil then + return r + end + end + local r = isOnTop(a.parent, b) + if r ~= nil then + return r + end + end + if b.parent then + local r = isOnTop(a, b.parent) + if r ~= nil then + return r + end + end + end + end + table.sort(cxy,function(a,b) return isOnTop(a,b) end) -- bruh + end + if c[x] and c[x][y] then + if self.debugMode then + term.setCursorPos(x,y) + term.setBackgroundColor(2^self.i) + term.write(" ") + else + local cxy = c[x][y] + l1,l2,l3 = get(cxy,x,y) + _G.debugPix = {l1,l2,l3,cxy=cxy} + table.insert(line[1],l1) + table.insert(line[2],l2) + table.insert(line[3],l3) + end + else + if self.debugMode then + term.setCursorPos(x,y) + term.setBackgroundColor(2^self.i) + term.setTextColor(2^((self.i+1)%16)) + term.write("0") + else + table.insert(line[1]," ") + table.insert(line[2],bg) + table.insert(line[3],bg) + end + end + end + if not self.debugMode then + term.setCursorPos(x1,y) + term.blit(table.concat(line[1]),table.concat(line[2]),table.concat(line[3])) + end + end + end + end + if self.debugMode and #debugRenderList > 0 then + self.i = (self.i+1)%16 + end + changeCache[self] = {} + end + term.redirect(oterm) +end + +slide.destroy = function(self) + local scape = self:getScape() + for k,v in pairs(scape.slides) do + if v == self then + table.remove(scape.slides,k) + end + end + for k,v in ipairs(scape.slides) do + v.id = k + end + for k,v in pairs(self) do + self[k] = nil + end + setmetatable(self,{__index=deleted,__newindex=deleted}) +end + + +-- [[ SCAPE API ]] +local scape = shapescape.scape + +scape.initialize = function(self,env,args) + table.insert(shapescape.debug.scapes,self) + self.env = env or _ENV + self.env.shapescape = { + getEvent = function() + return unpack(self.event) + end, + getSlide = function() + return self.slides[self.currentSlide] + end, + setSlide = function(slideID) + -- if string search actually not necessary + if self.slides[slideID] then + self.currentSlide = self.slides[slideID].id + os.pullEvent() + end + end, + getSlides = function() + return self.slides + end, + getScape = function() + return self + end, + exit = function(...) + self.returnValues = table.pack(...) + self.status = "dead" + end, + } + for k,v in pairs(shapescape) do + self.env.shapescape[k] = v + end + if not self.variables then + self.variables = {} + end + setmetatable(self.variables,{__index=_G}) + self.status = "running" + self.log = {} + self.args = args or {} + self.currentSlide = 1 + self.active = true + --_G.debugSelf = self + for s=1,#self.slides do + -- dont initialize slides until update + local sl = self.slides[s] + if sl.name and not self.slides[sl.name] then + self.slides[sl.name] = sl + end + end + for c=1,#self.classes do + self.classes[c]:initialize() + end +end + +scape.loadSlide = function(self,tSlide) + local w,h = term.getSize() + width = tSlide.width or w + height = tSlide.height or h + setmetatable(tSlide,{__index=slide}) + scapeRef[tSlide] = self + for o=1,#tSlide.objects do + local obj = tSlide.objects[o] + tSlide:loadObject(obj) + end + return tSlide +end + +scape.newSlide = function(self,width,height,backgroundColor) + local w,h = term.getSize() + width = width or w + height = height or h + local sl = {width=width,height=height,visible=true,objects={},id=#self.slides+1} + if backgroundColor then + sl.background = {color=backgroundColor} + end + --sl.window = window.create(term.current(),1,1,width,height,false) + table.insert(self.slides,sl) + return self:loadSlide(sl) +end + +scape.removeSlide = function(self, slide) + if type(slide) ~= "table" then + for k,v in pairs(self.slides) do + if k == slide then + slide = v + break + end + end + if type(slide) ~= "table" then + return false + end + end + for k,v in pairs(self.slides) do + if v == slide then + table.remove(self.slides,k) + end + end + return true +end + +scape.setSlide = function(self,nSlide) + if type(nSlide) == "table" and nSlide.id then + self.currentSlide = nSlide.id + elseif type(nSlide) == "number" then + self.currentSlide = nSlide + else + error("bad argument #2 to 'scape.setSlide' (expected slide, got "..type(nSlide)..")",2) + end +end + +scape.update = function(self, ...) + -- i dont feel like this anymore + -- maybe instead of just mouse_click, make it self.mouse_click or .mouse_click + -- and then mouse_click will execute on every mouse click, no matter where was clicked + local e = table.pack(...) + self.event = e + local s = self.slides[self.currentSlide] -- idiot + if not s.active then + s:initialize() + end + for o=#s.objects,1,-1 do + if not s.objects[o].active then + s.objects[o]:initialize() + end + s.objects[o]:update(...) + end + local secEvent + -- NOTE: transparent objects with children let mouse clicks through when click doesn't collide with any child + local function checkCollisions(objs,prefix) + local clickedObj + for o=#objs,1,-1 do + if objs[o]:isVisible() and objs[o]:isInside(e[3],e[4]) then + if objs[o].children then + local pre + if objs[o].name and prefix then + pre = prefix..objs[o].name.."." + end + clickedObj = checkCollisions(objs[o].children,pre) + end + if not (objs[o].children and objs[o]:getBackgroundColor() == 0 and not clickedObj) then -- only passes through if a child wasn't clicked + local newEvent = utils.instantiate(e) + newEvent[1] = "self."..e[1] + objs[o]:update(unpack(newEvent)) -- will NOT update parent, meaning parent should NEVER pass self.mouse events to children + if not clickedObj then + clickedObj = objs[o] + if objs[o].name and prefix then + secEvent = utils.instantiate(e) + secEvent[1] = (prefix or "")..objs[o].name.."."..e[1] + end + end + return clickedObj + end + end + end + end + + if e[1]:find("mouse") == 1 and e[3] and e[4] then + checkCollisions(s.objects,"") + end + if secEvent then + for o=1,#s.objects do + s.objects[o]:update(unpack(secEvent)) + end + end +end + +scape.render = function(self) + term.setCursorBlink(false) + self.slides[self.currentSlide]:render() + if self.log then + local cursor = captureCursor() + local warns = {} + for l=#self.log,1,-1 do + local entry = self.log[l] + if os.epoch("utc") <= entry.time+5000 then + table.insert(warns,entry) + else + break + end + end + local warnCols = { + ["WARNING"] = colors.orange, + ["ERROR"] = colors.red, + ["MSG"] = colors.lime, + } + local y = 2 + local w,h = term.getSize() + for i,warn in ipairs(warns) do + term.setCursorPos(2,y) + local prefix = "["..warn.type.."] " + local lines = utils.wordwrap(prefix..tostring(warn.text),w-4) + width = 0 + for l=1,#lines do + width = math.max(width,#lines[l]+2) + end + term.setBackgroundColor(colors.gray) + term.write(string.rep(" ",width)) + term.setCursorPos(2,y) + term.setTextColor(warnCols[warn.type]) + term.write("\149") + term.setTextColor(colors.white) + term.write(prefix) + term.setTextColor(warnCols[warn.type]) + term.write(lines[1]:sub(#prefix+1).." ") + y = y+1 + for l=2,#lines do + term.setCursorPos(2,y) + term.write("\149"..lines[l]..string.rep(" ",width-#lines[l]-3)) + y = y+1 + end + y = y+1 + end + restoreCursor(cursor) + end +end + +scape.run = function(self,...) + local w,h = term.getSize() + for s=1,#self.slides do + self.slides[s]:resize(w,h) + end + if not self.active then + self:initialize(_ENV, table.pack(...)) + end + self:render() + while self.status ~= "dead" do + local e = table.pack(os.pullEvent()) + if e[1] == "term_resize" then + local w,h = term.getSize() + for s=1,#self.slides do + self.slides[s]:resize(w,h) + end + end + self:update(table.unpack(e,1,e.n)) + self:render() + end + return unpack(self.returnValues or {}) +end + +scape.createSlide = function(self,objects,width,height) + sl = self:newSlide(width,height) + for o=1,#objects do + local obj = objects[o] + sl:createObject(obj.type,obj.x1,obj.y1,obj.x2,obj.y2,obj) + end +end + +scape.addScript = function(self, sCode, sName) + self.assets[self.lastAssetID+1] = {content=sCode,name=sName,id=self.lastAssetID+1} + self.lastAssetID = self.lastAssetID+1 +end + +scape.getAssetByName = function(self, sName) + for k,v in pairs(self.assets) do + if v.name == sName then + return v + end + end +end + +scape.getAssetByID = function(self, nID) + return self.assets[nID] +end + +scape.getAPI = function(self) + return shapescape +end + +scape.message = function(self,message,type) + type = type or "MSG" + local types = {["MSG"]=true,["WARNING"]=true,["ERROR"]=true} + if not types[type] then + error("Invalid type '"..type.."'",2) + end + for e=#self.log,1,-1 do + if os.epoch("utc") > self.log[e].time+5000 then + break + elseif self.log[e].text == message and self.log[e].type == type then + table.remove(self.log,e) + end + end + table.insert(self.log,{time=os.epoch("local"),text=message,type=type}) +end + +scape.warn = function(self,warning) + self:message(warning,"WARNING") +end + +-- [[ GLOBAL API ]] +shapescape.loadScape = function(tScape) + local sc + if type(tScape) == "table" then + sc = tScape + elseif type(tScape) == "string" and fs.exists(tScape) then + local content = utils.fread(tScape) + sc = textutils.unserialize(content) + end + setmetatable(sc,{__index=scape}) + for s=1,#sc.slides do + sc:loadSlide(sc.slides[s]) + end + local function classChildren(obj) + for i,o in ipairs(obj.children) do + scapeRef[o] = scapeRef[obj] + if o.children then + classChildren(o) + end + end + end + if not sc.classes then + if sc.templates then + sc.classes = sc.templates + sc.templates = nil + else + sc.classes = {} + end + end + for c=1,#sc.classes do + scapeRef[sc.classes[c]] = sc + if sc.classes[c].children then + classChildren(sc.classes[c]) + end + shapescape.class.load(sc.classes[c]) + end + return sc +end + +shapescape.createScape = function(slides,assets,classes) + local sc = {slides=slides or {},assets=assets or {},classes=classes or {},variables={}} -- maybe width/height or something idk + -- ooh metatables + sc.lastAssetID = 0 + for k,v in pairs(sc.assets) do + sc.lastAssetID = math.max(sc.lastAssetID,v.id+1) + end + return shapescape.loadScape(sc) +end + +shapescape.loadObject = function(obj) + if type(obj.fill) == "number" then + if obj.fill == 0 then + obj.fill = nil + else + obj.fill = {color=obj.fill} + end + end + if type(obj.border) == "number" then + if obj.border == 0 then + obj.border = nil + else + obj.border = {color=obj.border,weight=1,padding=2} + end + end + if type(obj.text) == "number" then + if obj.text == 0 then + obj.text = nil + else + obj.text = {color=obj.text,content=""} + end + elseif type(obj.text) == "string" then + obj.text = {color=obj.txtcolor or obj.textColor or colors.black,content=obj.text} + end + if not obj.eventListeners then + obj.eventListeners = {} + end + if not obj.pointers then + obj.pointers = {} + end + if obj.type == "group" then + setmetatable(obj,{__index=group}) + else + setmetatable(obj,{__index=generic}) + end + if obj.children then + for i,child in ipairs(obj.children) do + shapescape.loadObject(child) + child.parent = obj + child.id = i + end + end + local x1,y1,x2,y2 = obj:getAbsolutePosition() + changeCache[obj] = {x1=x1,y1=y1,x2=x2,y2=y2} + return obj +end + +shapescape.createObject = function(oType,x1,y1,x2,y2,properties) + properties = properties or {} + local obj = {x1=x1,y1=y1,x2=x2,y2=y2,type=oType} + for k,v in pairs(properties) do + obj[k] = v + end + return shapescape.loadObject(obj) +end + +shapescape.createRectangle = function(x1,y1,x2,y2,fill,border,properties) + properties = properties or {} + properties.fill = fill + properties.border = border + return shapescape.createObject("rect",x1,y1,x2,y2,properties) +end + +shapescape.createWindow = function(x1,y1,x2,y2,fill,textColor,border,properties) + properties = properties or {} + properties.fill = fill + properties.border = border + properties.textColor = textColor + return shapescape.createObject("window",x1,y1,x2,y2,properties) +end + +shapescape.createGroup = function(objects,properties) + properties = properties or {} + properties.children = objects + if not objects or not objects[1] then return end + local x1,y1,x2,y2 = objects[1].x1,objects[1].y1,objects[1].x2,objects[1].y2 + for i=2,#objects do + --print(x1,y1,x2,y2) + local o = objects[i] + x1 = math.min(x1,o.x1) + y1 = math.min(y1,o.y1) + x2 = math.max(x2,o.x2) + y2 = math.max(y2,o.y2) + --print("after object: "..o.x1..","..o.y1..","..o.x2..","..o.y2..":",x1,y1,x2,y2) + --os.sleep(0.5) + end + --os.sleep(2) + local obj = shapescape.createObject("group",x1,y1,x2,y2,properties) + setmetatable(obj,{__index=group}) + for i,o in ipairs(objects) do + o.x1,o.y1,o.x2,o.y2 = o.x1-x1+1,o.y1-y1+1,o.x2-x1+1,o.y2-y1+1 + o.parent = obj + o.id = i + end + return obj +end + +--return {utils=utils,generic=generic,scape=scape,createObject=createObject,createRectangle=createRectangle,createGroup=createGroup} +return shapescape \ No newline at end of file diff --git a/LevelOS/modules/shapescape/input.lua b/LevelOS/modules/shapescape/input.lua new file mode 100644 index 0000000..43da43a --- /dev/null +++ b/LevelOS/modules/shapescape/input.lua @@ -0,0 +1,1506 @@ +local input = {} + +local shapescape +local utils + +input.init = function(api) + shapescape = api + utils = api.utils +end + +local function isInside(x,y,object) + local function n(var) + return type(var) == "number" + end + local x1,y1,x2,y2 + if n(object.x1) and n(object.y1) and n(object.x2) and n(object.y2) then + x1,y1,x2,y2 = object.x1,object.y1,object.x2,object.y2 + elseif n(object.x) and n(object.y) then + x1,y1 = object.x,object.y + if n(object.w) and n(object.h) then + x2,y2 = x1+(object.w-1),y1+(object.h-1) + else + x2,y2 = x1,y1 + end + else + error("Invalid input: "..textutils.serialize(object,{compact=true}),2) + end + if x2 < x1 then + x1,x2 = x2,x1 + end + if y2 < y1 then + y1,y2 = y2,y1 + end + if x >= x1 and y >= y1 and x <= x2 and y <= y2 then + return true + else + return false + end +end + +local function getLines(str) + local lines = {} + local w = 0 + for potato in str:gmatch("([^\n]*)\n?") do + table.insert(lines,potato) + if #potato > w then + w = #potato + end + end + return lines,w +end + +local function cRestore(restore) + if not restore then + return {bg=term.getBackgroundColor(),fg=term.getTextColor(),cursor={term.getCursorPos()}} + else + term.setBackgroundColor(restore.bg) + term.setTextColor(restore.fg) + term.setCursorPos(unpack(restore.cursor)) + end +end + +function input.box(x1,y1,x2,y2,tOptions,sReplaceChar,tShape) + local holdTbl = {} + local isHolding + if lUtils then + isHolding = lUtils.isHolding + else + isHolding = function(key) + if type(key) == "string" then + key = keys[key] + end + return not not holdTbl + end + end + local oCursorA + local s + if not tShape then + s = {x1=x1,y1=y1,x2=x2,y2=y2,cursor={x=1,y=1,a=1},scr=0,ref={}} + else + s = tShape + s.cursor={x=1,y=1,a=1} + s.scr=0 + s.ref={} + end + s.history = {} + s.rhistory = {} + s.changed = false + local opt = {} + if tOptions then + opt = tOptions + elseif s.opt then + opt = s.opt + else + opt = {} + end + if not opt.overflow and not opt.overflowX and not opt.overflowY then + opt.overflow = "scroll" + opt.overflowX = "scroll" + opt.overflowY = "none" + end + opt["overflow"] = opt.overflow or "scroll" -- none, stretch, scroll or wrap + opt["overflowX"] = opt.overflowX or opt.overflow + opt["overflowY"] = opt.overflowY or opt.overflow + if opt.overflowY == "wrap" then + opt.overflowY = "none" + end + opt["cursorColor"] = opt.cursorColor or s.txtcolor or term.getTextColor() + opt["replaceChar"] = sReplaceChar or opt.replaceChar + + opt["minWidth"] = opt.minWidth or s.x2-(s.x1-1) + opt["minHeight"] = opt.minHeight or s.y2-(s.y1-1) + + opt["tabSize"] = opt.tabSize or 4 + opt["indentChar"] = opt.indentChar or " " + + if opt.overflowX == "scroll" then + s.scrollX = 0 + end + + if opt.overflowY == "scroll" then + s.scrollY = 0 + end + + --[[opt["maxWidth"] = opt.maxWidth + opt["maxHeight"] = opt.maxHeight]] + + s.opt = opt + + s.color = s.color or opt.backgroundColor or term.getBackgroundColor() + s.txtcolor = s.txtcolor or opt.textColor or term.getTextColor() + + local txtcolor = s.txtcolor + s.txt = opt.text or "" + --s.lines = {s.txt} + s.lines = {} -- real text input, for example password if censored + s.dLines = {} -- rendered text input, buncha asterisks if censored, spaces instead of tab + s.blit = {} -- foreground and background colors + s.state = false + local ref = s.ref + local syntaxes = { + ["lua"] = { + lexer="lex", + whitespace=colors.white, + comment=colors.green, + string=colors.red, + escape=colors.orange, + keyword=colors.yellow, + value=colors.yellow, + ident=colors.white, + number=colors.purple, + symbol=colors.orange, + operator=colors.yellow, + unidentified=colors.white, + }, + ["lua-light"] = { + lexer="lex", + whitespace=colors.black, + comment=colors.lightGray, + string=colors.red, + escape=colors.orange, + keyword=colors.blue, + value=colors.purple, + ident=colors.black, + number=colors.lightBlue, + symbol=colors.orange, + operator=colors.gray, + unidentified=colors.black, + } + } + local syntax + local uservars = {} + local scope = 0 + local function lineLn(line) + local findTab = line:find("\t") + local offset = 0 + local t = s.opt.tabSize + while findTab do + local l = t-(findTab+offset-1)%t + offset = offset+(l-1) + findTab = line:find("\t",findTab+1) + end + return #line+offset + end + local function fillTable(tTable,tbl,prefix) + --local type = rtype + local docs = s.opt.complete.docs + while tTable do + for k,v in pairs(tTable) do + if type(k) == "string" and not tbl[k] then + if type(v) == "table" then + tbl[k] = {type="table",data={},name=prefix..k} + if docs and docs[tbl[k].name] then + tbl[k].docs = docs[tbl[k].name] + end + --[[scope = scope+1 + fillTable(v,tbl[k].data,prefix..k..".") + scope = scope-1]] + elseif type(v) == "string" or type(v) == "number" or type(v) == "boolean" then + tbl[k] = {type=type(v),data=v,name=prefix..k} + if docs and docs[tbl[k].name] then + tbl[k].docs = docs[tbl[k].name] + end + elseif type(v) == "function" then + local obj = {type="function",data=v,name=prefix..k} + local args = {} + if rtype(v) == "table" then + local mt = getmetatable(v) + if rtype(mt) == "table" and rtype(mt._call) == "function" then + v = mt._call + end + end + if rtype(v) == "function" then + local info = debug.getinfo(v) + for t=1,info.nparams do + table.insert(args,debug.getlocal(v,t)) + end + if info.isvararg then + table.insert(args,"...") + end + obj.args = args + obj.source = info.short_src + end + if not obj.args then obj.args = {} end + if not obj.source then obj.source = "" end + if docs and docs[obj.name] then + obj.docs = docs[obj.name] + end + tbl[k] = obj + end + end + end + local tMetatable = getmetatable(tTable) + if tMetatable and type(tMetatable.__index) == "table" then + tTable = tMetatable.__index + else + tTable = nil + end + end + end + local keywords = {"and", "break", "do", "else", "elseif", "end", "for", "function", "if", "in", "local", "not", "or", "repeat", "return", "then", "until", "while"} + local function complete(sTxt) + local docs = s.opt.complete.docs + if not sTxt then return {} end + local sSearchText + local fComplete = false + local sSearchText = sTxt:match("^[a-zA-Z0-9_%.:]+") + if not sSearchText then return end + if #sSearchText < #sTxt then + fComplete = true + end + s.opt.complete.selected = nil + local env = s.opt.complete.env + fillTable(env,uservars,"") + local nStart = 1 + local nDot = string.find(sSearchText, ".", nStart, true) + local tTable = uservars + while nDot do + local sPart = string.sub(sSearchText, nStart, nDot - 1) + if not tTable[sPart] then return {} end + local value = tTable[sPart].data + if type(value) == "table" then + tTable = value + if type(env[sPart]) == "table" then + fillTable(env[sPart],tTable,"") + env = env[sPart] + end + nStart = nDot + 1 + nDot = string.find(sSearchText, ".", nStart, true) + else + return {} + end + end + local nColon = string.find(sSearchText, ":", nStart, true) + if nColon then + local sPart = string.sub(sSearchText, nStart, nColon - 1) + if not tTable[sPart] then return {} end + local value = tTable[sPart].data + if type(value) == "table" then + tTable = value + if type(env[sPart]) == "table" then + fillTable(env[sPart],tTable,"") + env = env[sPart] + end + nStart = nColon + 1 + else + return {} + end + end + + local sPart = string.sub(sSearchText, nStart) + local prefix = string.sub(sSearchText, 1, nStart-1) + local nPartLength = #sPart + + local tResults = {} + local tStrings = {} + local tSorted = {} + local tSeen = {} + if fComplete then + tResults.length = #sTxt + else + tResults.length = nPartLength + end + for k,v in pairs(tTable) do + if not tSeen[k] and type(k) == "string" and not (nColon and v.type ~= "function") then + if (string.find(k, sPart, 1, true) == 1 and not fComplete) or (fComplete and k == sPart) then + if string.match(k, "^[%a_][%a%d_]*$") then + local sResult = string.sub(k, nPartLength + 1) + local display = k + local index = sSearchText..sResult + if v.type == "function" then + local vArgs = utils.instantiate(v.args) + if nColon then + table.remove(vArgs,1) + end + if fComplete then + display = index.."("..table.concat(vArgs,",")..")" + else + display = k.."("..table.concat(vArgs,",")..")" + end + sResult = sResult.."(" + end + tStrings[sResult] = {src=(v.source or ""),complete=sResult,type=v.type,display=display} + if fComplete and v.type ~= "function" then return nil end + if docs[index] then + local d = docs[index] + if fComplete then + tStrings[sResult].display = d.name:gsub("^_G%.","") + tStrings[sResult].description = d.summary + else + tStrings[sResult].display = d.name:gsub("^_G%.",""):gsub("^"..prefix,"") + end + end + table.insert(tSorted,sResult) + --tResults[#tResults].id = #tResults + end + end + end + tSeen[k] = true + end + table.sort(tSorted) + for t=1,#tSorted do + tResults[t] = tStrings[tSorted[t]] + tResults[t].id = t + end + --table.sort(tResults) + return tResults + end + local function renderComplete(list) -- make option to use overay like s.opt.complete.overlay = true + local c = s.opt.complete + local LevelOS = c.LevelOS + c.reverse = false + if not c.selected and not list[1].description then + c.selected = list[1] + end + local a = 0 + if s.border and s.border.color ~= 0 then + a = 1 + end + local scrollX = s.scrollX or 0 + local scrollY = s.scrollY or 0 + local x,y = s.x1+(s.cursor.x-1)+a-scrollX,s.y1+(s.cursor.y-1)+a-scrollY + x = x-list.length-4 + y = y+1 + local width = 10 + local height = #list + for t=1,#list do + width = math.max(#list[t].display+#list[t].src+5,width) + if list[t].description then + list[t].lines = utils.wordwrap(list[t].description,width-4) + height = height+#list[t].lines + end + end + if c.overlay and LevelOS then + --local wX,wY = LevelOS.self.window.win.getPosition() + local wX,wY = lUtils.getWindowPos(term.current()) + local w,h = lOS.oldterm.getSize() + x = x+wX-1 + y = y+wY-1 + if x < 1 then + x = 1 + end + if y+(height-1) > h and y-1-height >= 1 then + y = y-2 + c.reverse = true + end + if x+(width-1) > w then + x = w-width+1 + end + else + if x < s.x1 then + x = s.x1 + end + if y+(height-1) > s.y2 and y-1-height >= s.y1 then + c.reverse = true + y = y-2 + end + if x+(width-1) > s.x2 then + x = s.x2-width+1 + end + end + local offset = 1 + if c.reverse then + offset = -1 + end + local x2 = x+(width-1) + abbrevs = {table={"tbl",colors.lime},number={"num",colors.yellow},boolean={"bln",colors.yellow},["function"]={"fnc",colors.cyan},keyword={" ",colors.orange},["nil"]={"nil",colors.red},unknown={"???",colors.pink},string={"str",colors.yellow}} + local function theRender() + if not s.opt.complete.colors then + s.opt.complete.colors = {} + end + local col = s.opt.complete.colors + col.selectedbg = col.selectedbg or colors.lightBlue -- green + col.backgroundColor = col.backgroundColor or colors.gray -- magenta + col.txtcolor = col.txtcolor or colors.white -- white + col.typebg = col.typebg or colors.black -- light gray + col.selectedtypebg = col.selectedtypebg or colors.blue -- green + col.sourcetxtcolor = col.sourcetxtcolor or colors.lightGray -- pink + local cY = y + for t=1,#list do + if c.selected == list[t] then + term.setBackgroundColor(col.selectedtypebg) + else + term.setBackgroundColor(col.typebg) + end + local a = abbrevs[list[t].type] or abbrevs["unknown"] + term.setTextColor(a[2]) + term.setCursorPos(x,cY) + term.write(a[1]) + if c.selected == list[t] then + term.setBackgroundColor(col.selectedbg) + else + term.setBackgroundColor(col.backgroundColor) + end + term.setTextColor(col.txtcolor) + term.write(" "..list[t].display..string.rep(" ",width-#list[t].display-4-#list[t].src)) + term.setCursorPos(x2-(#list[t].src-1),cY) + term.setTextColor(col.sourcetxtcolor) + term.write(list[t].src) + cY = cY+offset + if list[t].lines then + for i,l in ipairs(list[t].lines) do + term.setCursorPos(x,cY) + term.setTextColor(col.sourcetxtcolor) + term.write(" "..l..string.rep(" ",width-#l-4)) + cY = cY+offset + end + end + end + end + local x1,y1,x2,y2 + if c.overlay and LevelOS then + LevelOS.overlay = theRender + local wX,wY = LevelOS.self.window.win.getPosition() + x1 = x-wX+1 + x2 = x1+width-1 + y1 = y-wY+1 + y2 = y1+((#list-1)*offset) + else + x1 = x + y1 = y + y2 = y1+((#list-1)*offset) + theRender() + end + list.x1 = x1 + list.y1 = math.min(y1,y2) + list.y2 = math.max(y1,y2) + list.x2 = x2 + end + local function replaceText(txt,pos1,pos2,replace) + return txt:sub(1,pos1-1)..replace..txt:sub(pos2+1,#txt) + end + local function genLines(t) + local syn = s.opt.syntax + if type(syn) == "string" and syntaxes[syn] then + syntax = syntaxes[syn] + syntax.type = syn + elseif type(syn) == "table" and syn.lexer and type(syn.lexer) == "function" then + syntax = syn + elseif type(syn) == "table" and syntaxes[syn.type] then + syntax = syntaxes[syn.type] + for k,v in pairs(syn) do + syntax[k] = v + end + elseif type(syn) == "table" and syn.lexer and ((type(syn.lexer) == "string" and fs.exists(syn.lexer)) or type(syn.lexer) == "function") then + syntax = syn + else + syntax = nil + end + local blit = {} + if s.opt.complete and syntax then + uservars = {} + for k=1,#keywords do + uservars[keywords[k]] = {type="keyword"} + end + uservars["true"] = {type="boolean"} + uservars["false"] = {type="boolean"} + uservars["nil"] = {type="nil"} + _G.debuguservars = uservars + --[[scope = 0 + local tTable = s.opt.complete.env + fillTable(tTable,uservars,"")]] + end + if syntax and ((type(syntax.lexer) == "string" and fs.exists(syntax.lexer)) or type(syntax.lexer) == "function") then + if type(syntax.lexer) ~= "function" then + syntax.lexer = dofile(syntax.lexer) + end + local lex = syntax.lexer + local sublines = lex(t) + local line = 1 + local l = 0 + local ref = 0 + --for li in t:gmatch("([^\n]*)\n?") do -- this aint workin and dont forget to add newlines to the blit lines + blit[1] = "" + blit[2] = "" + _G.debugsublines = sublines + for l=1,#sublines do + local elements = sublines[l] + for t=1,#elements do + local col = utils.toBlit(syntax[elements[t].type] or s.txtcolor) + blit[1] = blit[1]..elements[t].data + blit[2] = blit[2]..string.rep(col,#elements[t].data) + if s.opt.complete and #s.txt < 40000 and syntax.type:find("^lua") then + if elements[t].type == "nfunction" then + local el = t+1 + local args = {} + while true do + local e = elements[el] + if not e then + break + elseif e.type == "function" or e.type == "whitespace" or e.data == "(" or e.data == "," or e.data == "=" then + el = el+1 + elseif e.type == "arg" or e.data == "..." then + table.insert(args,e.data) + el = el+1 + else + break + end + end + local parent = {data=uservars} + local el = t-1 + local children = {} + while true do + local e = elements[el] + if not e then + break + elseif e.data == "." then + el = el-1 + elseif e.type == "ident" then + table.insert(children,1,e.data) + el = el-1 + else + break + end + end + for t=1,#children do + if parent.data[children[t]] then + local child = parent.data[children[t]] + if type(child.data) ~= "table" then child.data = {} child.type = "table" end + parent = child + else + parent.data[children[t]] = {data={},type="table",source="Ln "..l} + parent = parent.data[children[t]] + end + end + parent.data[elements[t].data] = {args=args,type="function",source="Ln "..l} + elseif elements[t].type == "ident" then + local el = t+1 + local foundEquals = false + local foundValue = false + local naming = true + local prefix = "" + local parent = {data=uservars} + local children = {elements[t].data} + while true do + local e = elements[el] + if not e then + break + elseif e.data == "." and naming then + el = el+1 + elseif e.type == "whitespace" or (e.data == "=" and not foundEquals) then + naming = false + el = el+1 + if e.data == "=" then + local stop = false + for c=1,#children-1 do + local child = parent.data[children[c]] + if not child then parent.data[children[c]] = {data={},type="table",source="Ln "..l} child = parent.data[children[c]] end + if type(child.data) ~= "table" then child.data = {} child.type = "table" end + parent = child + prefix = prefix..children[c].."." + end + if stop then break end + foundEquals = true + end + elseif e.type == "ident" then + el = el+1 + if naming then + table.insert(children,e.data) + else + -- idk do later + foundValue = true + -- add table support + if uservars[e.data] then + end + end + elseif foundEquals and (e.type == "string" or e.type == "number" or (e.type == "operator" and e.data == "-") or e.type == "value" or (e.type == "symbol" and e.data:sub(1,1) == "{") or e.type == "function") then + local vType = e.type + if e.type == "operator" then + vType = "number" + elseif e.type == "value" then + if e.data == "nil" then + vType = "nil" + elseif e.data == "true" or e.data == "false" then + vType = "boolean" + else + vType = "unknown" + end + elseif e.type == "symbol" then + vType = "table" + elseif e.type == "function" then + vType = "unknown" + end + local obj = {name=prefix..children[#children],type=vType,source="Ln "..l} + if vType == "table" then + obj.data = {} + end + parent.data[children[#children]] = obj + break + else + break + end + end + end + end + end + if l < #sublines then + blit[1] = blit[1].."\n" + blit[2] = blit[2].."\n" + end + end + _G.debugBlit2 = {blit[1],blit[2]} + end + -- check for transparency for third blit line + for a=1,#s.lines do + s.lines[a] = nil + s.dLines[a] = nil + s.blit[a] = nil + ref[a] = nil + end + s.lines[1] = "" + s.dLines[1] = "" + local width = s.x2-(s.x1-1) + local height = s.y2-(s.y1-1) + if s.border and s.border.color ~= 0 then + width = width-2 + height = height-2 + end + local c = 1 + local l = s.lines + local dl = s.dLines + local pl = 0 + if opt.overflowX == "scroll" then + --local b,e = t:find() + l[1] = "" + dl[1] = "" + while true do + -- line + local b,e = t:find("[^\n]*\n?") + if not b or e == 0 then break end + local line = t:sub(b,e) + l[c] = line + if #blit > 0 then + dl[c] = line + local findTab = dl[c]:find("\t") + local blit2 = blit[2] + if not s.opt.tabSize then + s.opt.tabSize = 4 + end + local t = s.opt.tabSize + while findTab do + local l = t-(findTab-1)%t + dl[c] = replaceText(dl[c],findTab,findTab,string.sub(s.opt.indentChar..string.rep(" ",t-#s.opt.indentChar),t-l+1,t)) + char = utils.toBlit(syntax.whitespace) + blit2 = replaceText(blit2,findTab,findTab,string.rep(char,l)) + findTab = dl[c]:find("\t") + end + local tabArea = string.rep(" ",t) + local findSpace,findSpace2 = dl[c]:find(tabArea) + while findSpace do + dl[c] = replaceText(dl[c],findSpace,findSpace2,s.opt.indentChar..string.rep(" ",t-#s.opt.indentChar)) + findSpace,findSpace2 = dl[c]:find(tabArea,findSpace2+1) + end + dl[c] = dl[c]:sub(1+s.scrollX,width+s.scrollX) + s.blit[c] = {dl[c],blit2:sub(b+s.scrollX,b+s.scrollX+(#dl[c]-1)),string.rep(utils.toBlit(s.color),#dl[c])} + blit[1] = blit[1]:sub(e+1,#blit[1]) + blit[2] = blit[2]:sub(e+1,#blit[2]) + else + dl[c] = line:sub(1+s.scrollX,width+s.scrollX) + s.blit[c] = {dl[c],string.rep(utils.toBlit(s.txtcolor),#dl[c]),string.rep(utils.toBlit(s.color),#dl[c])} + end + if line:sub(#line) == "\n" then + if c+1 > height then + if opt.overflowY == "stretch" then + s.y2 = s.y2+1 + elseif opt.overflowY == "none" then + return false + end + end + l[c+1] = "" + dl[c+1] = "" + end + t = t:sub(e+1,#t) + c = c+1 + end + else + while true do + local b,e = t:find("%S*%s?") + if not b or e < 1 then break end + local w = e-(b-1) + c = #l + if not dl[c] then dl[c] = l[c] end + if string.find(t:sub(b,e),"\n",nil,true) then + if (opt.overflowY == "stretch" or opt.overflowY == "scroll") or c+1 <= height then + if opt.overflowY == "stretch" and c+1 > height then + s.y2 = s.y2+1 + end + b2,e2 = t:sub(b,e):find("\n",nil,true) + e = e2 + l[c+1] = "" + dl[c+1] = "" + else + return false + end + end + local of = opt.overflowX + local tW,tH = term.getSize() + if opt.overflowX == "stretch" then + if #dl[c]+w > tW and #dl[c]+w > width then + of = "wrap" + end + end + if #dl[c]+w > width then + if opt.overflowX == "wrap" then + if (opt.overflowY == "stretch" or opt.overflowY == "scroll") or c+1 <= height then + if opt.overflowY == "stretch" and c+1 > height then + s.y2 = s.y2+1 + end + if not dl[c]:find("%S") then + e = width-#l[c] + l[c] = l[c]..t:sub(b,e) + l[c+1] = "" + else + --l[c+1] = t:sub(b,e) + e = b-1 + l[c+1] = "" + end + else + -- oh no, stop typing + return false + end + elseif opt.overflowX == "stretch" then + s.x2 = s.x2+1 + l[c] = l[c]..t:sub(b,e) + dl[c] = dl[c]..t:sub(b,e):gsub("\9"," ") + elseif opt.overflowX == "none" then + return false + end + else + l[c] = l[c]..t:sub(b,e) + dl[c] = dl[c]..t:sub(b,e):gsub("\9"," ") + end + t = t:sub(e+1,#t) + end + end + return true + end + genLines(s.txt) + local function genText() + local txt = "" + ref[1] = 1 + for l=1,#s.lines do + txt = txt..s.lines[l] + ref[l+1] = ref[l]+#s.lines[l] + if s.select and s.select[1] < ref[l+1] and s.select[2] >= ref[l] and s.blit and s.blit[l] then + local line = s.lines[l] + local c = utils.toBlit(s.opt.selectColor or colors.blue) + local sel1 = s.select[1]-ref[l] + local sel2 = s.select[2]-ref[l]+2 + local findTab = s.lines[l]:find("\t") + local off1 = 0 + local off2 = 0 + local offT = 0 + while findTab do + line = replaceText(line,findTab,findTab," ") + local t = s.opt.tabSize + local a = t-(findTab+offT-1)%t + if sel1 >= findTab then + off1 = off1+a-1 + end + if sel2 > findTab then + off2 = off2+a-1 + end + if findTab > sel1 and findTab < sel2 then + s.blit[l][1] = replaceText(s.blit[l][1],offT+findTab,offT+findTab+(a-1),string.sub(string.rep("\140",t-1).."\132",t-a+1,t)) + end + offT = offT+a-1 + findTab = line:find("\t") + end + sel1 = sel1+off1-s.scrollX + sel2 = sel2+off2-s.scrollX + local pos1 = math.max(0,sel1) + local pos2 = math.min(#s.blit[l][1]+1,sel2) + s.blit[l][3] = s.blit[l][3]:sub(1,pos1)..string.rep(c,pos2-(pos1+1))..s.blit[l][3]:sub(pos2,#s.blit[l][1]) + if s.opt.selectTxtColor then + local c2 = utils.toBlit(s.opt.selectTxtColor) + s.blit[l][2] = s.blit[l][2]:sub(1,pos1)..string.rep(c2,pos2-(pos1+1))..s.blit[l][2]:sub(pos2,#s.blit[l][1]) + end + s.blit[l][1] = s.blit[l][1]:sub(1,pos1)..s.blit[l][1]:sub(pos1+1,pos2-1):gsub(" ","\183")..s.blit[l][1]:sub(pos2,#s.blit[l][1]) + end + end + return txt + end + genText() + local function calcCursor() + local width = s.x2-(s.x1-1) + local height = s.y2-(s.y1-1) + if s.border and s.border.color ~= 0 then + width = width-2 + height = height-2 + end + for r=1,#s.lines do + if ref[r+1] > s.cursor.a or r == #s.lines then + s.cursor.y = r + s.cursor.x = s.cursor.a-(ref[r]-1) + local _,tabs = s.txt:sub(ref[r],s.cursor.a-1):gsub("\9","") + s.cursor.x = s.cursor.x+((s.opt.tabSize-1)*tabs) + break + end + end + if opt.overflowX == "scroll" then + if s.cursor.x < (1+s.scrollX) then + s.scrollX = s.cursor.x-1 + elseif s.cursor.x > (width+s.scrollX) then + s.scrollX = s.cursor.x-width + end + if s.scrollX > 0 and lineLn(s.lines[s.cursor.y]) < width then + s.scrollX = 0 + end + end + if opt.overflowY == "scroll" then + if s.cursor.y < (1+s.scrollY) then + s.scrollY = s.cursor.y-1 + elseif s.cursor.y > (height+s.scrollY) then + s.scrollY = s.cursor.y-height + end + end + end + local function rCalcCursor() + local x = s.cursor.x + local tx = 0 + local offset = 0 + local line = s.lines[s.cursor.y] + if s.cursor.y == #s.lines then + line = line.."\n" + end + for w in line:gmatch(".") do + tx = tx+1 + if w == "\t" then + local l = s.opt.tabSize-(tx-1)%s.opt.tabSize + tx = tx+l-1 + offset = offset+l-1 + end + if tx >= x then + x = tx-offset + break + end + end + s.cursor.a = ref[s.cursor.y]+(x-1) + end + --s.rCalcCursor = rCalcCursor + local oTxt = "" + local function rAll(nTxt) + if nTxt then + if not genLines(nTxt) then + genLines(s.txt) + s.cursor.a = oCursorA + else + s.txt = nTxt + end + else + genLines(s.txt) + end + genText() + calcCursor() + genLines(s.txt) + genText() + oTxt = s.txt + if s.opt.complete then + s.opt.complete.list = nil + end + end + local uTimer + local function addUndo(event) + if #s.history == 0 then + table.insert(s.history,{txt=s.txt,changed=true,cursor=s.cursor.a}) + end + if event == "paste" then + table.insert(s.history,{txt=s.txt,changed=true,cursor=s.cursor.a,description="Paste"}) + elseif event == "key" or event == "char" then + --if s.history[#s.history] and s.history[#s.history].time and s.history[#s.history].time > os.epoch("utc")-250 + if uTimer then os.cancelTimer(uTimer) end + uTimer = os.startTimer(0.3) + elseif event == "timer" then + table.insert(s.history,{txt=s.txt,changed=s.changed,cursor=s.cursor.a,description="Insert Characters"}) + end + while #s.history > 80 do + table.remove(s.history,1) + end + end + local function undo() + if s.history[#s.history-1] then + local h = s.history[#s.history-1] + s.changed = h.changed + s.cursor.a = h.cursor + rAll(h.txt) + table.insert(s.rhistory,s.history[#s.history]) + table.remove(s.history,#s.history) + end + end + local function redo() + if s.rhistory[#s.rhistory] then + local h = s.history + local r = s.rhistory[#s.rhistory] + s.changed = r.changed + s.cursor.a = r.cursor + rAll(r.txt) + table.insert(s.history,r) + table.remove(s.rhistory,#s.rhistory) + end + end + local function addText(txt) + local a = 0 + if s.border and s.border.color ~= 0 then + a = 1 + end + local ttxt = txt + if txt:find("\n") then + if s.opt.overflowY == "none" and s.cursor.y >= s.y2-s.y1+1-a*2 then + return false + else + ttxt = txt:match("(.-)\n") + end + end + if s.opt.overflowX == "none" and lineLn(s.lines[s.cursor.y]..ttxt) > s.x2-s.x1+1-a*2 then + return false + end + local pos1,pos2 + if s.select then + pos1 = s.select[1]-1 + pos2 = s.select[2]+1 + else + pos1 = s.cursor.a-1 + pos2 = s.cursor.a + end + s.changed = true + s.txt = s.txt:sub(1,pos1)..txt..s.txt:sub(pos2,#s.txt) + s.cursor.a = pos1+#txt+1 + s.select = nil + rAll() + end + local function update(...) -- maybe make it so that click already selects and then the key char etc operations always sub based on select so no if statement needed + opt = s.opt + if not opt.tabSize then + opt.tabSize = 4 + end + oCursorA = s.cursor.a + local e = table.pack(...) + if not lUtils then + if e[1] == "key" then + holdTbl[e[2]] = true + elseif e[1] == "key_up" then + holdTbl[e[2]] = nil + end + end + if s.opt.complete and s.opt.complete.LevelOS and s.opt.complete.LevelOS.self.window.events == "all" and e[1]:find("mouse") and type(e[3]) == "number" and type(e[4]) == "number" then + local wX,wY = s.opt.complete.LevelOS.self.window.win.getPosition() + e[3] = e[3]-(wX-1) + e[4] = e[4]-(wY-1) + end + if e[1] == "mouse_click" and s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and not isInside(e[3],e[4],s.opt.complete.list) then + s.opt.complete.list = nil + rAll() + end + if e[1] == "timer" and e[2] == uTimer then + addUndo(e[1]) + end + if not s.state then + if e[1] == "mouse_click" and e[3] >= s.x1 and e[4] >= s.y1 and e[3] <= s.x2 and e[4] <= s.y2 then + s.state = true + elseif e[1] == "term_resize" then + rAll() + elseif s.txt ~= oTxt then + rAll() + end + else + if e[1] == "mouse_click" then + if (e[3] < s.x1 or e[3] > s.x2 or e[4] < s.y1 or e[4] > s.y2) and not (s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and isInside(e[3],e[4],s.opt.complete.list)) then -- add support for autocomplete click + term.setCursorBlink(false) + s.state = false + end + end + if s.txt ~= oTxt then + rAll() + end + if e[1] == "char" then + if #s.history == 0 then + table.insert(s.history,{txt=s.txt,changed=false,cursor=s.cursor.a}) + end + --[[s.changed = true + s.cursor.a = s.cursor.a+1 + rAll(s.txt:sub(1,s.cursor.a-2)..e[2]..s.txt:sub(s.cursor.a-1,#s.txt))]] + addText(e[2]) + addUndo(e[1]) + if s.opt.complete then + s.opt.complete.complete = complete + s.opt.complete.render = renderComplete + s.opt.complete.list = complete(string.match(s.txt:sub(1,s.cursor.a-1), "[a-zA-Z0-9_%.:]+$")) + end + elseif e[1] == "key" then + local dirs = { + [keys.left] = true, + [keys.right] = true, + [keys.up] = true, + [keys.down] = true, + [keys["end"]] = true, + [keys.home] = true, + } + local deletes = { + [keys.delete] = true, + [keys.backspace] = true, + } + if isHolding(keys.leftCtrl) and dirs[e[2]] then -- e fuck implement ur own isholdign + -- nothing + elseif s.select and dirs[e[2]] then + s.select = nil + rAll() + elseif s.select and deletes[e[2]] then + addText("") + elseif e[2] == keys.left and s.cursor.a > 1 then + s.cursor.a = s.cursor.a-1 + --calcCursor() + rAll() + elseif e[2] == keys.right and s.cursor.a <= #s.txt then + s.cursor.a = s.cursor.a+1 + --calcCursor() + rAll() + elseif e[2] == keys.up then + local c = s.opt.complete + if c and c.list and #c.list > 0 and c.selected then + local offset = 1 + if c.reverse then + offset = -1 + end + local sID = c.selected.id-offset + if sID < 1 then + sID = #c.list + elseif sID > #c.list then + sID = 0 + end + c.selected = c.list[sID] + elseif s.cursor.y > 1 then + s.cursor.y = s.cursor.y-1 + if opt.overflowX == "scroll" then + li = s.lines + else + li = s.dLines + end + local ln = lineLn(li[s.cursor.y]) + if s.cursor.x > ln then + s.cursor.x = ln + if s.cursor.y == #s.lines then + s.cursor.x = s.cursor.x+1 + end + end + rCalcCursor() + rAll() + end + elseif e[2] == keys.down then + local c = s.opt.complete + if c and c.list and #c.list > 0 and c.selected then + local offset = 1 + if c.reverse then + offset = -1 + end + local sID = c.selected.id+offset + if sID < 1 then + sID = #c.list + elseif sID > #c.list then + sID = 0 + end + c.selected = c.list[sID] + elseif s.cursor.y < #s.lines then + s.cursor.y = s.cursor.y+1 + if opt.overflowX == "scroll" then + li = s.lines + else + li = s.dLines + end + local ln = lineLn(li[s.cursor.y]) + if s.cursor.x > ln then + s.cursor.x = ln + if s.cursor.y == #s.lines then + s.cursor.x = s.cursor.x+1 + end + end + rCalcCursor() + rAll() + end + elseif e[2] == keys.pageUp then + local h = s.y2-(s.y1-1) + s.cursor.y = math.max(s.cursor.y-h,1) + if opt.overflowX == "scroll" then + li = s.lines + else + li = s.dLines + end + local ln = lineLn(li[s.cursor.y]) + if s.cursor.x > ln then + s.cursor.x = ln + if s.cursor.y == #s.lines then + s.cursor.x = s.cursor.x+1 + end + end + rCalcCursor() + rAll() + elseif e[2] == keys.pageDown then + local h = s.y2-(s.y1-1) + s.cursor.y = math.min(s.cursor.y+h,#s.lines) + if opt.overflowX == "scroll" then + li = s.lines + else + li = s.dLines + end + local ln = lineLn(li[s.cursor.y]) + if s.cursor.x > ln then + s.cursor.x = ln + if s.cursor.y == #s.lines then + s.cursor.x = s.cursor.x+1 + end + end + rCalcCursor() + rAll() + elseif e[2] == keys["end"] then + if isHolding(keys.leftCtrl) then + s.cursor.a = #s.txt+1 + else + if opt.overflowX == "scroll" then + local ln = lineLn(s.lines[s.cursor.y]) + + s.cursor.x = ln + else + s.cursor.x = #s.dLines[s.cursor.y] + end + if s.cursor.y == #s.lines then + s.cursor.x = s.cursor.x+1 + end + rCalcCursor() + end + rAll() + elseif e[2] == keys.home then + if isHolding(keys.leftCtrl) then + s.cursor.a = 1 + else + local wp = (s.lines[s.cursor.y]:match("^[\t ]+") or ""):gsub("\t",string.rep(" ",s.opt.tabSize)) -- always beginning of string so always 4 per tab + if s.cursor.x == 1+#wp then + s.cursor.x = 1 + else + s.cursor.x = 1+#wp + end + rCalcCursor() + end + rAll() + elseif e[2] == keys.backspace and s.cursor.a > 1 then + s.changed = true + s.txt = s.txt:sub(1,s.cursor.a-2)..s.txt:sub(s.cursor.a,#s.txt) + s.cursor.a = s.cursor.a-1 + rAll() + addUndo(e[1]) + elseif e[2] == keys.delete and s.cursor.a <= #s.txt then + s.changed = true + s.txt = s.txt:sub(1,s.cursor.a-1)..s.txt:sub(s.cursor.a+1,#s.txt) + rAll() + addUndo(e[1]) + elseif e[2] == keys.enter then + local wp = s.lines[s.cursor.y]:match("^[\t ]+") or "" + addText("\n"..wp) + addUndo(e[1]) + elseif (isHolding(keys.leftCtrl) and (e[2] == keys.leftBracket or e[2] == keys.rightBracket)) or (s.select and e[2] == keys.tab) then + local sLine = s.cursor.y + local eLine = s.cursor.y + if s.select then + for l=1,#s.lines do + local encountered = false + if (s.select[1] >= s.ref[l] and s.select[1] < s.ref[l+1]) then -- if begin of select is in line + sLine = math.min(sLine,l) + end + if (s.select[2] >= s.ref[l] and s.select[2] < s.ref[l+1]) then -- if end of select is in line + eLine = math.max(eLine,l) + encountered = true + elseif encountered then -- if it already encountered the select and is no longer in selected area it doesnt need to search any further + break + end + end + end + for l=sLine,eLine do + if e[2] == keys.rightBracket or e[2] == keys.tab then + s.lines[l] = "\t"..s.lines[l] + if s.select then + if s.select[1] >= s.ref[l] then + s.select[1] = s.select[1]+1 + end + if s.select[2] >= s.ref[l] then + s.select[2] = s.select[2]+1 + end + end + if s.cursor.a >= s.ref[l] then + s.cursor.a = s.cursor.a+1 + end + calcCursor() + elseif e[2] == keys.leftBracket then + if s.lines[l]:find("^\t") then + s.lines[l] = s.lines[l]:gsub("^\t","") + if s.select then + if s.select[1] > s.ref[l] then + s.select[1] = s.select[1]-1 + end + if s.select[2] > s.ref[l] then + s.select[2] = s.select[2]-1 + end + end + if s.cursor.a > s.ref[l] then + s.cursor.a = s.cursor.a-1 + end + elseif s.lines[l]:find("^ ") then + s.lines[l] = s.lines[l]:gsub("^ ","") + if s.select then + if s.select[1] > s.ref[l] then + s.select[1] = math.max(s.select[1]-4,s.ref[l]) + end + if s.select[2] > s.ref[l] then + s.select[2] = math.max(s.select[2]-4,s.ref[l]) + end + end + if s.cursor.a > s.ref[l] then + s.cursor.a = math.max(s.cursor.a-4,s.ref[l]) + end + end + end + s.txt = genText() + rAll() + end + elseif e[2] == keys.tab then + local c = s.opt.complete + if c and c.list and #c.list > 0 and c.selected then + addText(c.selected.complete) + addUndo("char") + else + addText("\t") + addUndo(e[1]) + end + elseif isHolding(keys.leftCtrl) then + if e[2] == keys.z then + undo() + elseif e[2] == keys.y then + redo() + elseif e[2] == keys.a then + s.select = {1,#s.txt} + s.cursor.a = #s.txt+1 + rAll() + elseif (e[2] == keys.c or e[2] == keys.x) and ccemux and s.select then + local txt = s.txt:sub(s.select[1],s.select[2]) + ccemux.setClipboard(txt) + lOS.clipboard = txt + if e[2] == keys.x then + addText("") + addUndo(e[1]) + end + end + end + elseif e[1] == "paste" then + if s.opt.complete then + s.opt.complete.list = nil + end + if #s.history == 0 then + table.insert(s.history,{txt=s.txt,changed=false,cursor=s.cursor.a}) + end + local txt = e[2] + if lOS.clipboard then + local nline = lOS.clipboard:find("(.)\n") or #lOS.clipboard + local checkCB = lOS.clipboard:sub(1,nline):gsub("\t","") + if txt == checkCB then + txt = lOS.clipboard + end + end + addText(txt) + addUndo(e[1]) + elseif s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and s.opt.complete.selected and (e[1] == "mouse_click" or e[1] == "mouse_scroll" or e[1] == "mouse_up") and isInside(e[3],e[4],s.opt.complete.list) then + local c = s.opt.complete + --local x,y = e[3]-(s.x1-1),e[4]-(s.y1-1) + local el = e[4]-(s.opt.complete.list.y1-1) + if e[1] == "mouse_click" then + c.selected = c.list[el] + elseif e[1] == "mouse_up" and c.selected.id == el then + s.changed = true + s.txt = s.txt:sub(1,s.cursor.a-1)..c.selected.complete..s.txt:sub(s.cursor.a,#s.txt) + s.cursor.a = s.cursor.a+#c.selected.complete + rAll() + addUndo("char") + end + elseif e[1] == "mouse_click" or e[1] == "mouse_scroll" then + s.select = nil + if e[3] >= s.x1 and e[4] >= s.y1 and e[3] <= s.x2 and e[4] <= s.y2 then + if e[1] == "mouse_click" then + local scrollX = s.scrollX or 0 + local scrollY = s.scrollY or 0 + local x,y = e[3]-(s.x1-1)+scrollX,e[4]-(s.y1-1)-s.scr+scrollY + if not s.lines[y] then + y = #s.lines + end + if x > #s.dLines[y] then + if opt.overflowX == "scroll" then + x = lineLn(s.lines[y]) + else + x = #s.dLines[y] + end + if y == #s.lines then + x = x+1 + end + end + s.cursor.x,s.cursor.y = x,y + rCalcCursor() + rAll() + elseif opt.overflowY == "scroll" and e[1] == "mouse_scroll" then + local width = s.x2-(s.x1-1) + local height = s.y2-(s.y1-1) + if s.border and s.border.color ~= 0 then + width = width-2 + height = height-2 + end + if s.scrollY+e[2] >= 0 and s.scrollY+e[2] <= #s.lines-height then + s.scrollY = s.scrollY+e[2] + end + end + end + elseif e[1] == "mouse_drag" then + local pos1 + if s.select then + pos1 = s.select[1] + else + pos1 = s.cursor.a + s.select = {pos1,pos1} + end + local scrollX = s.scrollX or 0 + local scrollY = s.scrollY or 0 + local x,y = e[3]-(s.x1-1)+scrollX,e[4]-(s.y1-1)-s.scr+scrollY + if not s.lines[y] then + y = #s.lines + end + if x > #s.dLines[y] then + if opt.overflowX == "scroll" then + x = lineLn(s.lines[y]) + else + x = #s.dLines[y] + end + if y == #s.lines then + x = x+1 + end + end + s.cursor.x,s.cursor.y = x,y + rCalcCursor() + local pos2 = s.cursor.a + if s.select then + if pos2 < s.select[1] and not s.select.reversed then + s.select.reversed = true + s.select[2] = s.select[1]-1 + elseif pos2 > s.select[2] and s.select.reversed then + s.select.reversed = false + s.select[1] = s.select[2]+1 + end + if s.select.reversed then + s.select[1] = pos2 + else + s.select[2] = pos2-1 + end + end + rAll() + elseif e[1] == "term_resize" then + rAll() + end + end + local lChar = s.txt:sub(s.cursor.a-1,s.cursor.a-1) + if s.opt.complete and (e[1] == "key" or e[1] == "char") and (lChar == "(" or lChar == ",") then + s.opt.complete.list = complete(string.match(s.txt:sub(1,s.cursor.a-1), "[a-zA-Z0-9_%.:%(]+[^%)%(]*$")) + end + end + local function render() + if s.color == 0 then s.color = colors.white end + local restore = cRestore() + term.setBackgroundColor(s.color or term.getBackgroundColor()) + local a = 0 + if s.border and s.border.color ~= 0 then + a = 1 + end + lOS.boxClear(s.x1+a,s.y1+a,s.x2-a,s.y2-a) + local scrollX = s.scrollX or 0 + local scrollY = s.scrollY or 0 + + for y=s.y1+a,s.y2-a do + local l = y-(s.y1-1+a)+s.scr + scrollY + if s.lines[l] then + term.setCursorPos(s.x1+a,y) + term.setBackgroundColor(s.color or term.getBackgroundColor()) + term.setTextColor(s.txtcolor or txtcolor) + local line + if s.blit[l] then + line = s.blit[l][1] + else + line = s.dLines[l] + end + if type(opt.replaceChar) == "string" and #opt.replaceChar > 0 then + local pattern = "." + for t=1,#opt.replaceChar-1 do + pattern = pattern..".?" + end + local nLine = line:gsub(pattern,opt.replaceChar) + line = nLine:sub(1,#line) + end + if s.blit[l] then + --s.blit[l][3] = string.rep(lUtils.toBlit(s.color or term.getBackgroundColor()),#s.blit[l][1]) + term.blit(line,s.blit[l][2],s.blit[l][3]) + else + term.write(line) + end + end + end + if s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 then + if s.opt.complete.overlay and s.opt.complete.LevelOS then + s.opt.complete.LevelOS.self.window.events = "all" + lOS.noEvents = 2 + end + lOS.noEvents = 2 + local ok,err = pcall(renderComplete,s.opt.complete.list) + if not ok then + _G.theterribleerrorrighthere = err + end + elseif s.opt.complete and s.opt.complete.LevelOS and s.opt.complete.LevelOS.self.window.events == "all" then + if s.opt.complete.overlay then + s.opt.complete.LevelOS.self.window.events = nil + lOS.noEvents = false + end + s.opt.complete.LevelOS.overlay = nil + end + if s.state then + local x,y = s.x1+(s.cursor.x-1)+a-scrollX,s.y1+(s.cursor.y-1)+a-scrollY + if isInside(x,y,s) then + term.setTextColor(opt.cursorColor or txtcolor) + term.setCursorPos(x,y) + term.setCursorBlink(true) + else + term.setCursorBlink(false) + end + else + cRestore(restore) + end + end + if not tShape then + s.update = update + s.render = render + else + s.fUpdate = update + s.fRender = render + end + return s +end + +function input.read(_sReplaceChar) + local x1,y1 = term.getCursorPos() + local x2,_ = term.getSize() + local box = input.box(x1,y1,x2,y1,{overflowX="scroll",overflowY="none",replaceChar=_sReplaceChar}) + box.state = true + while true do + local e = {os.pullEvent()} + if e[1] == "key" and e[2] == keys.enter then + box.state = false + print("") + return box.txt + else + box.update(unpack(e)) + box.state = true + box.render() + end + end +end + +return input \ No newline at end of file diff --git a/LevelOS/modules/shapescape/physics.lua b/LevelOS/modules/shapescape/physics.lua new file mode 100644 index 0000000..e69de29 diff --git a/LevelOS/modules/shapescape/transition.lua b/LevelOS/modules/shapescape/transition.lua new file mode 100644 index 0000000..e69de29 diff --git a/LevelOS/settings.lua b/LevelOS/settings.lua new file mode 100644 index 0000000..84b946b --- /dev/null +++ b/LevelOS/settings.lua @@ -0,0 +1,121 @@ +--ccvs = mwm.newCvs() -- config canvas aka settings canvas +btns = {} +function _G.btn(x,y,width,lines) + btns[#btns+1] = {} + thebtn = btns[#btns] + thebtn.x = x + thebtn.y = y + thebtn.w = width + thebtn.h = #lines+2 + function thebtn.render(px,py) + if px == nil then + px,py = thebtn.x,thebtn.y + end + thebtn.blit = {{"\151",tostring(lUtils.toBlit(colors.gray)),tostring(lUtils.toBlit(term.getBackgroundColor()))}} + for t=1,thebtn.w-2 do + thebtn.blit[1][1] = thebtn.blit[1][1].."\131" + thebtn.blit[1][2] = thebtn.blit[1][2]..lUtils.toBlit(colors.gray) + thebtn.blit[1][3] = thebtn.blit[1][3]..lUtils.toBlit(term.getBackgroundColor()) + end + thebtn.blit[1][1] = thebtn.blit[1][1].."\148" + thebtn.blit[1][2] = thebtn.blit[1][2]..lUtils.toBlit(term.getBackgroundColor()) + thebtn.blit[1][3] = thebtn.blit[1][3]..lUtils.toBlit(colors.gray) + for t=1,#lines do + thebtn.blit[t+1] = {"\149",tostring(lUtils.toBlit(colors.gray)),tostring(lUtils.toBlit(term.getBackgroundColor()))} + for w=1,width-2 do + thebtn.blit[t+1][1] = thebtn.blit[t+1][1].." " + if t == 1 then + thebtn.blit[t+1][2] = thebtn.blit[t+1][2]..lUtils.toBlit(term.getTextColor()) + else + thebtn.blit[t+1][2] = thebtn.blit[t+1][2]..lUtils.toBlit(colors.lightGray) + end + thebtn.blit[t+1][3] = thebtn.blit[t+1][3]..lUtils.toBlit(term.getBackgroundColor()) + end + local thetxt = "" + if string.len(lines[t]) > thebtn.w-2 then + thetxt = string.sub(lines[t],thebtn.w-2) + else + thetxt = lines[t] + end + thebtn.blit[t+1][1] = "\149"..thetxt..string.sub(thebtn.blit[t+1][1],2+string.len(thetxt),string.len(thebtn.blit[t+1][1])) + thebtn.blit[t+1][1] = thebtn.blit[t+1][1].."\149" + thebtn.blit[t+1][2] = thebtn.blit[t+1][2]..tostring(lUtils.toBlit(term.getBackgroundColor())) + thebtn.blit[t+1][3] = thebtn.blit[t+1][3]..lUtils.toBlit(colors.gray) + end + thebtn.blit[#thebtn.blit+1] = {"\138",tostring(lUtils.toBlit(term.getBackgroundColor())),tostring(lUtils.toBlit(colors.gray))} + local tempblit = thebtn.blit[#thebtn.blit] + for t=1,thebtn.w-2 do + tempblit[1] = tempblit[1].."\143" + tempblit[2] = tempblit[2]..lUtils.toBlit(term.getBackgroundColor()) + tempblit[3] = tempblit[3]..lUtils.toBlit(colors.gray) + end + tempblit[1] = tempblit[1].."\133" + tempblit[2] = tempblit[2]..lUtils.toBlit(term.getBackgroundColor()) + tempblit[3] = tempblit[3]..lUtils.toBlit(colors.gray) + for t=1,#thebtn.blit do + term.setCursorPos(px,py+(t-1)) + term.blit(table.unpack(thebtn.blit[t])) + end + end + return thebtn +end +lSettings = {{"Peripherals","Monitors, Speakers,","Printers"},{"Personal Settings","Background, Welcome","Screen"},{"E","Fookin nonsense","aha"},{"Hello","How are you","Mr Valentine"},{"Hey","I am good thank",":)"}} +btnW = 21 +btnH = 5 +scrl = 0 +function setrender(scr) -- settings render, scroll (Y) + if scr == nil then + scr = 0 + end + term.setBackgroundColor(colors.black) + term.clear() + local w,h = term.getSize() + term.setCursorPos(math.ceil(w/2)-math.floor(string.len("Settings")/2),2-scr) + term.setTextColor(colors.white) + term.write("Settings") + local cX = math.ceil(w/2)+1 + local cY = 4 + while cX-(btnW+1) > 1 do + cX = cX-(btnW+1) + end + local OGcX = cX + btns = {} + for t=1,#lSettings do + --term.setCursorPos(cX,cY-scr) + btn(cX,cY-scr,btnW,lSettings[t]).render() + if cX+(btnW*2+2) <= w then + cX = cX+(btnW+1) + else + cX = OGcX + cY = cY+btnH+1 + end + end +end +setrender() +local aw,ah = term.getSize() +local ow,oh = aw,ah +while true do + e = {os.pullEvent()} + if e[1] == "mouse_scroll" then + scrl = scrl+e[2] + setrender(scrl) + elseif e[1] == "mouse_click" then + for t=1,#btns do + if e[3] >= btns[t].x and e[4] >= btns[t].y and e[3] <= btns[t].x+(btns[t].w-1) and e[4] <= btns[t].y+(btns[t].h-1) then + term.setCursorPos(1,1) + print("Yey") + term.setBackgroundColor(colors.gray) + btns[t].render(btns[t].x,btns[t].y) + end + end + elseif e[1] == "mouse_up" then + term.setBackgroundColor(colors.black) + setrender(scrl) + end + aw,ah = term.getSize() + if aw ~= ow or ah ~= oh then + setrender(scrl) + ow,oh = aw,ah + end +end +os.sleep(3) \ No newline at end of file diff --git a/LevelOS/startup/MwMRender b/LevelOS/startup/MwMRender new file mode 100644 index 0000000..89c7fca --- /dev/null +++ b/LevelOS/startup/MwMRender @@ -0,0 +1,176 @@ +if mwm == nil then _G.mwm = {} end +function tabletext(text) + local tTable = {} + for a=1,string.len(text) do + tTable[a] = string.sub(text,a,a) + end + return tTable +end +to_colors, to_blit = {}, {} +for i = 1, 16 do + to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i) + to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1) +end +function mwm.newCvs(tX,tY) + local cvs = {} + cvs.rCvs = {} + cvs.oCvs = {} + cvs.orCvs = {} + cvs.color = colors.white + if tX == nil or tY == nil then + tX,tY = term.getSize() + end + for x=1,tX do + cvs.rCvs[x] = {} + for y=1,tY do + cvs.rCvs[x][y] = {"",term.getBackgroundColor()} -- empty pixel = "" + end + end + cvs.width = tX + cvs.height = tY + local function genCvs() + cvs.rCvs = {} + local tX,tY = cvs.width,cvs.height + for x=1,tX do + cvs.rCvs[x] = {} + for y=1,tY do + cvs.rCvs[x][y] = {"",term.getBackgroundColor()} -- empty pixel = "" + end + end + end + function cvs.setColor(color) + cvs.color = color + end + function cvs.rect(x,y,w,h,text,txtcolor) + if tonumber(x) == nil or tonumber(y) == nil or tonumber(w) == nil or tonumber(h) == nil then + return + end + if text ~= nil then + if txtcolor == nil then + txtcolor = colors.white + end + if type(text) == "string" then + text = {text} + end + for t=1,#text do + if string.len(text[t]) > w then + string.sub(text[t],1,w) + end + end + end + cvs.oCvs[#cvs.oCvs+1] = {type="rect",x=x,y=y,width=w,height=h,color=cvs.color,txt=text,txtcolor=txtcolor,ref=#cvs.oCvs+1} + return cvs.oCvs[#cvs.oCvs],true + end + function cvs.print(text,x,y) + if text == nil or tonumber(x) == nil or tonumber(y) == nil then + return + end + cvs.oCvs[#cvs.oCvs+1] = {type="text",txt=text,x=x,y=y,color=cvs.color} + return cvs.oCvs[#cvs.oCvs],true + end + cvs.write = cvs.print + function cvs.sprite(file,x,y,w,h) + if fs.exists(file) == false then + return + end + opensprite = fs.open(file,"r") + local sprite = opensprite.readAll() + opensprite.close() + sprite = textutils.unserialize(sprite) + if sprite == nil then return end + cvs.oCvs[#cvs.oCvs+1] = {type="sprite",texture=sprite,x=x,y=y,width=w,height=h} + return cvs.oCvs[#cvs.oCvs] + end + -- Support circle rendering. This can be done by calculating with math.cos how many pixels it is from one side of one y coord to the other side of the same y coord and putting that in a string. repeat this for every y coord then draw + function cvs.clear() + cvs.oCvs = {} + end + function cvs.render(trans,x1,y1,x2,y2) + if x1 == nil then x1 = 1 end + if y1 == nil then y1 = 1 end + if x2 == nil then x2 = cvs.width end + if y2 == nil then y2 = cvs.height end + genCvs() + bgcolor = term.getBackgroundColor() + for a=1,#cvs.oCvs do + local o = cvs.oCvs[a] + cvs.oCvs[a].ref = a + o.x = math.floor(o.x+0.5) + o.y = math.floor(o.y+0.5) + if o.type == "rect" then + for b=1,o.height do + for c=1,o.width do + local tX,tY = term.getSize() + if o.x+(c-1) <= tX and o.y+(b-1) <= tY and o.x+(c-1) > 0 and o.y+(b-1) > 0 then + cvs.rCvs[o.x+(c-1)][o.y+(b-1)] = {" ",o.color} + end + end + end + if o.txt ~= nil and o.txtcolor ~= nil then + for b=1,o.height do + if o.txt[b] ~= nil then + dObj = tabletext(o.txt[b]) + for c=1,string.len(o.txt[b]) do + cvs.rCvs[o.x+(c-1)][o.y+(b-1)] = {dObj[c],o.color,o.txtcolor} + end + end + end + end + elseif o.type == "sprite" then + for b=1,o.height do + for c=1,o.width do + if o.texture[c] ~= nil then + local tX,tY = term.getSize() + if o.x+(c-1) <= tX and o.y+(b-1) <= tY and o.x+(c-1) > 0 and o.y+(b-1) > 0 and o.texture[c][b] ~= nil then + cvs.rCvs[o.x+(c-1)][o.y+(b-1)] = o.texture[c][b] + end + end + end + end + elseif o.type == "text" then + dObj = tabletext(o.txt) + for b=1,#dObj do + local tX,tY = term.getSize() + if o.x+(b-1) <= tX and o.y <= tY and o.x+(b-1) > 0 and o.y > 0 then + cvs.rCvs[o.x+(b-1)][o.y][1] = dObj[b] + cvs.rCvs[o.x+(b-1)][o.y][3] = o.color + end + end + end + end + for y=y1,y2 do + bl1 = "" + bl2 = "" + bl3 = "" + for x=x1,x2 do + if not (cvs.orCvs[x] ~= nil and cvs.orCvs[x][y] == cvs.rCvs[x][y]) then + if cvs.rCvs[x][y][1] == "" then + if term.current().getLine and trans then + local theline = {term.current().getLine(y)} + bl1 = bl1..string.sub(theline[1],x,x) + bl2 = bl2..string.sub(theline[2],x,x) + bl3 = bl3..string.sub(theline[3],x,x) + else + bl1 = bl1.." " + bl2 = bl2..0 + bl3 = bl3..to_blit[cvs.rCvs[x][y][2]] + end + else + if cvs.rCvs[x][y][3] ~= nil then + bl2 = bl2..to_blit[cvs.rCvs[x][y][3]] + else + bl2 = bl2..0 + end + bl1 = bl1..tostring(cvs.rCvs[x][y][1]) + bl3 = bl3..to_blit[cvs.rCvs[x][y][2]] + end + end + end + term.setCursorPos(x1,y) + term.blit(bl1,bl2,bl3) + end + cvs.orCvs = cvs.rCvs + term.setBackgroundColor(bgcolor) + end + return cvs +end \ No newline at end of file diff --git a/LevelOS/startup/lUtils.lua b/LevelOS/startup/lUtils.lua index e0cd826..fa9924b 100644 --- a/LevelOS/startup/lUtils.lua +++ b/LevelOS/startup/lUtils.lua @@ -4384,9 +4384,9 @@ function lUtils.openWin(title,filepath,x,y,width,height,canresize,canmaximize) end term.setTextColor(colors.white) if canmaximize == false then - term.write(" ├ù ") + term.write(" × ") else - term.write(" + ├ù ") + term.write(" + × ") end term.setCursorPos(x+1,y) term.write(title) @@ -4501,7 +4501,7 @@ function lUtils.openWin(title,filepath,x,y,width,height,canresize,canmaximize) term.setTextColor(colors.white) term.setBackgroundColor(colors.red) term.setCursorPos(x+(width-3),y) - term.write(" ├ù ") + term.write(" × ") elseif e[1] == "mouse_up" then stop = true return false @@ -4618,7 +4618,7 @@ function lUtils.popup(title,msg,width,height,buttons,redrawscreen,colorScheme) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") -- The line below is unreadable now but it just makes the text box for the popup message and then prints it term.setBackgroundColor(bg) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1) @@ -4698,7 +4698,7 @@ function lUtils.popup(title,msg,width,height,buttons,redrawscreen,colorScheme) term.setTextColor(colors.white) term.setBackgroundColor(colors.red) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") elseif event == "mouse_up" then if redrawscreen ~= nil and redrawscreen == true then OGwin.render() @@ -4721,7 +4721,7 @@ function lUtils.popup(title,msg,width,height,buttons,redrawscreen,colorScheme) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") os.sleep(0.1) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) term.setBackgroundColor(tbbg) @@ -4730,7 +4730,7 @@ function lUtils.popup(title,msg,width,height,buttons,redrawscreen,colorScheme) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") os.sleep(0.1) end end @@ -4772,7 +4772,7 @@ function lUtils.inputbox(title,msg,width,height,buttons) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") -- The line below is unreadable now but it just makes the text box for the popup message and then prints it term.setBackgroundColor(colors.white) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1) @@ -4871,7 +4871,7 @@ function lUtils.inputbox(title,msg,width,height,buttons) term.setTextColor(colors.white) term.setBackgroundColor(colors.red) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") elseif event == "mouse_up" then return false,0,"" end @@ -4892,7 +4892,7 @@ function lUtils.inputbox(title,msg,width,height,buttons) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") os.sleep(0.1) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) term.setBackgroundColor(colors.gray) @@ -4901,7 +4901,7 @@ function lUtils.inputbox(title,msg,width,height,buttons) term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)) write(" "..title) term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2)) - write(" ├ù ") + write(" × ") os.sleep(0.1) end end @@ -5377,4 +5377,4 @@ function lUtils.isInside(x,y,object) else return false end -end +end \ No newline at end of file diff --git a/LevelOS/system.lua b/LevelOS/system.lua new file mode 100644 index 0000000..e41461d --- /dev/null +++ b/LevelOS/system.lua @@ -0,0 +1,1505 @@ +if lOS and lOS.started then + error("LevelOS is already running, silly!") +end + +changecolors = false +purecolors = false +local ranstartup = false +if purecolors == true then + term.setPaletteColor(colors.white,1,1,1) + term.setPaletteColor(colors.orange,1,0.5,0) + -- magenta + -- light blue + term.setPaletteColor(colors.yellow,1,1,0) + term.setPaletteColor(colors.lime,0,1,0) + term.setPaletteColor(colors.pink,1,0.5,1) + -- gray + -- light gray + term.setPaletteColor(colors.cyan,0,1,1) + term.setPaletteColor(colors.purple,0.8,0,1) + term.setPaletteColor(colors.blue,0,0,1) + -- brown + term.setPaletteColor(colors.green,0,0.6,0) + term.setPaletteColor(colors.red,1,0,0) + term.setPaletteColor(colors.black,0,0,0) +end +if changecolors == true then + allcolors = {} + for t=0,15,1 do + allcolors[#allcolors+1] = {term.getPaletteColor(2^t)} + term.setPaletteColor(2^t,0,0,0) + end +end +if not lOS then + _G.lOS = {} +end +lOS.started = false +if fs.exists("LevelOS/data/settings.lconf") then + local f = fs.open("LevelOS/data/settings.lconf","r") + local s = f.readAll() + f.close() + lOS.settings = textutils.unserialize(s) +else + lOS.settings = {maxAll=false} -- load from file usually +end +lOS.tbSize = 0 +lOS.lMenu = false +lOS.log = "" +lOS.doasearch = false +lOS.notifications = {} +if fs.exists("AppData") == false then + fs.makeDir("AppData") +end + +if fs.exists("AppData") and fs.exists("AppData/Shapescape") and fs.exists("AppData/Shapescape/temp") then + local f = fs.list("AppData/Shapescape/temp") + for t=1,#f do + fs.delete(fs.combine("AppData/Shapescape/temp",f[t])) + end +end +lOS.oldterm = term.current() +local oldterm = lOS.oldterm +local w,h = term.getSize() + +local function log(txt) + --lOS.log = lOS.log..txt.."\n" +end + + +local function fread(file) + local f = fs.open(file,"r") + local o = f.readAll() + f.close() + return o +end + +lOS.processes = {} + + +lOS.noEvents = false + + +lOS.wins = {} + +lOS.wAll = window.create(oldterm,1,1,w,h,false) -- window all (window object containing all window objects) +local wAll = lOS.wAll +lOS.depWin = window.create(wAll,1,1,w,h,false) -- window where windowless processes will draw objects to. (Does not get saved) + +shell.run("LevelOS/startup/lUtils") +local function login() + if fs.exists("LevelOS/data/account.txt") then + while not lOS.userID do pcall(function() dofile("LevelOS/Login_screen.sgui") end) end + elseif fs.exists("LevelOS/startup/LevelCloud.lua") then + --while not lOS.userID do pcall(function() dofile("LevelOS/Global_Login.lua") end) end + --while not lOS.userID do pcall(function() lOS.userID,lOS.username = dofile("LevelOS/login.lua") end) end + end +end +local boterm = term.current() +local buffer = window.create(boterm,1,1,w,h,false) +local bufcor = coroutine.create(login) +if lOS.checkinternet() ~= 0 then + os.startTimer(0) + while coroutine.status(bufcor) ~= "dead" do + local e = table.pack(os.pullEventRaw()) + term.redirect(buffer) + coroutine.resume(bufcor, table.unpack(e, 1, e.n)) + term.redirect(boterm) + buffer.setVisible(true) + buffer.setVisible(false) + end +end +term.redirect(boterm) +for c=0,15,1 do + wAll.setPaletteColor(2^c,buffer.getPaletteColor(2^c)) +end + +os.startTimer(0) + +local proc = lOS.processes +--proc[0] = {coroutine.create(function() shell.run("LevelOS/desktop.lua") end),title="Desktop",win=window.create(wAll,1,1,w,h,false),winMode="background"} +proc[0] = {title="Desktop",win=window.create(wAll,1,1,w,h,false),winMode="screen"} +proc[0][1] = coroutine.create(function() local ok,err = lOS.run("LevelOS/desktop.lua",proc[0]) if not ok then lOS.bsod(err) end end) +if fs.exists("LevelOS/data/changelog.lconf") then + local w,h = term.getSize() + local offset = 2 + local offsetY = 2 + if w > 60 then + offset = 2+math.ceil((w-60)/3) + end + if h > 40 then + offsetY = 2+math.ceil((h-40)/3) + end + proc[2] = {coroutine.create(function() shell.run("LevelOS/Changelog.lua") end),title="Changelog",win=window.create(wAll,1+offset,1+offsetY,w-(offset*2),(h-2)-(offsetY*2),false),winMode="widget"} +end + +-- FOR DEBUGGING PURPOSES ONLY!!!!! +--proc[2] = {coroutine.create(function() shell.run("LevelOS/Task_Manager.lua") end),title="Task Manager",win=window.create(wAll,3,3,35,14,false),winMode="windowed"} +--proc[3] = {coroutine.create(function() shell.run("User/Scripts/log.lua") end),title="Log",win=window.create(wAll,5,5,35,14,false),winMode="windowed"} +--proc[3] = {coroutine.create(function() shell.run("rom/programs/shell.lua") end),title="Shell",win=window.create(wAll,7,7,35,14,false),winMode="windowed"} +--proc[4] = {coroutine.create(function() shell.run("rom/programs/shell.lua") end),win=window.create(wAll,1,1,51,17,false),winMode="borderless"} +--proc[4] = {coroutine.create(function() shell.run("rom/programs/shell.lua") end),title="Pastebin",win=window.create(wAll,9,9,35,14,false),winMode="borderless"} + +_G.wButton = 0 + +lOS.wins[0] = proc[0] +lOS.focusWin = lOS.wins[0] +lOS.wins[1] = proc[2] +--lOS.wins[1] = proc[2] +--lOS.wins[2] = proc[3] +--lOS.wins[3] = proc[3] +--lOS.wins[4] = proc[4] +--table.insert(lOS.wins,1,proc[4]) + + +--[[for t=1,#lOS.wins do + if lOS.wins[t].snap == nil then + lOS.wins[t].snap = "none" + end +end]] + + + +local lTime = 0 + +local sysErr +local sysEnabled = false + +function lOS.bsod(msg) + sysErr = msg +end + +function lOS.sysUpdate(v) + if v then + sysEnabled = true + else + sysEnabled = false + end +end + +local sysTimer + +local function system() + local timer = 0 + lOS.sysUI = lOS.execute("LevelOS/SystemUI.lua","background") + + local response,err = http.post("https://old.leveloper.cc/sGet.php","path="..textutils.urlEncode("").."&name="..textutils.urlEncode("LevelOS_Beta_1251278571250123"),{Cookie=lOS.userID}) + if response then + local f = response.readAll() + local vtemp = lUtils.getField(f,"version") + local servertimestamp = tonumber(vtemp) + local clienttimestamp + if fs.exists("LevelOS/data/version.txt") then + clienttimestamp = tonumber(fread("LevelOS/data/version.txt")) + end + if not clienttimestamp then + clienttimestamp = 0 + end + if servertimestamp > clienttimestamp then + if fs.exists("LevelOS/Beta_Updater.lua") then + fs.delete("LevelOS/Beta_Updater.lua") + end + shell.run("lStore get Beta_Updater LevelOS/Beta_Updater.lua") + os.sleep(1) + if fs.exists("LevelOS/Beta_Updater.lua") then + lOS.execute("LevelOS/Beta_Updater.lua") + end + end + end + + --[[if not fs.exists("rom/apis/http/http.lua") then + local function wrapRequest(_url, ...) + local ok, err = http.request(...) + if ok then + while true do + local e = {os.pullEvent()} + if e[1] == "http_success" and e[2] == _url then + return e[3] + elseif event == "http_failure" and e[2] == _url then + return nil, e[3], e[4] + end + end + end + return nil, err + end + + function http.post(_url, _post, _headers, _binary) + if type(_url) == "table" then + return wrapRequest(_url.url, _url) + else + return wrapRequest(_url, _url, _post, _headers, _binary) + end + end + + function http.get(_url, _headers, _binary) + if type(_url) == "table" then + return wrapRequest(_url.url, _url) + else + return wrapRequest(_url, _url, nil, _headers, _binary) + end + end + end]] + + while true do + if ranstartup == false and lOS.fadeComplete then + local sProgs = fs.list("LevelOS/startup") + for t=1,#sProgs do + if sProgs[t] ~= "lUtils.lua" and not string.find(sProgs[t],"updater") then + --print("Running "..sProgs[t].."...") + lOS.execute(fs.combine("LevelOS/startup",sProgs[t]),"background") + end + end + ranstartup = true + end + if sysEnabled then + local nsysTimer = os.startTimer(0.1) + local ev = {} + while ev[1] ~= "timer" or ev[2] ~= nsysTimer do + ev = table.pack(os.pullEventRaw()) + end + sysTimer = nsysTimer + lTime = lTime+0.02 + else + coroutine.yield() + end + if not lOS.sysUI.env then + error("SystemUI: CRITICAL_PROCESS_DIED",0) + end + if sysErr then + error(tostring(sysErr),0) + end + end +end + + + +proc[1] = {coroutine.create(system),title="System",path="LevelOS/system.lua",win=window.create(wAll,1,1,51,19,false),winMode="background"} +lOS.system = proc[1] + + +local function getPixel(win,x,y) + theline = {win.getLine(y)} + return string.sub(theline[1],x,x),string.sub(theline[2],x,x),string.sub(theline[3],x,x) +end + +local set = lOS.settings + +local previewrect + + +--for p=0,#proc do + --coroutine.resume(process[1]) +--end + +local cProc = 0 + +function lOS.execute(path,mode,wX,wY,wW,wH,focus) + local aliases = { + ["LevelOS/explorer.lua"] = "Program_Files/LevelOS/Explorer", + ["LevelOS/LevelCloud.lua"] = "Program_Files/LevelOS/Cloud", + ["LevelOS/notepad.lua"] = "Program_Files/LevelOS/Notepad", + ["LevelOS/Pigeon.lua"] = "Program_Files/Pigeon", + } + _G.debugpath = path + local tempargs + if type(path) == "table" then + tempargs = path + path = tempargs[1] + table.remove(tempargs,1) + end + + local tPath = "" + local rPath + local args + if not tempargs then + rPath = string.sub(path,string.find(path,"%S+")) + local b,e = string.find(path,"%S+") + tPath = string.sub(path,e+2,string.len(path)) + args = {} + for i in string.gmatch(tPath,"%S+") do + if i ~= nil and i ~= "" then + args[#args+1] = i + end + end + else + rPath = path + args = tempargs + end + _G.debugargs = args + if lUtils.getFileType(rPath) == ".llnk" then + local link = lUtils.asset.load(rPath) + if link[1] and fs.exists(link[1]) then + local cmd = link[1] + local args2 = link.args + if args2 and #args > 0 then + cmd = cmd.." "..table.concat(args2, " ") + end + if args and #args > 0 then + cmd = cmd.." "..table.concat(args," ") + end + return lOS.execute(cmd,mode,wX,wY,wW,wH,focus) + end + end + if aliases[rPath] then + rPath = aliases[rPath] + end + if fs.isDir(rPath) then + local files = fs.list(rPath) + local runfile = false + if fs.exists(fs.combine(rPath,"main.lua")) then + runfile = true + rPath = fs.combine(rPath,"main.lua") + else + for f=1,#files do + local fP = fs.combine(rPath,files[f]) + if not fs.isDir(fP) then + if lUtils.getFileType(files[f]) == ".lua" then + rPath = fP + runfile = true + break + end + end + end + end + if not runfile then + return false,"No lua file found in this folder." + end + end + + local w,h = oldterm.getSize() + local thewin + if mode == "maximized" then + local tw,th = lOS.wAll.getSize() + thewin = window.create(oldterm,1,2,tw,th-1-lOS.tbSize) + mode = "windowed" + elseif wX and wY and wW and wH then + thewin = window.create(oldterm,wX,wY,wW,wH,false) + elseif w <= 60 or h < 21 or (set.maxAll == true) then + thewin = window.create(oldterm,1,2,w,h-3,false) + else + local x,y = 7,7 + local w,h = 51,19 + local tw,th = lOS.wAll.getSize() + if lOS.tbSize then + th = th-(lOS.tbSize-1) + end + for i,proc in ipairs(lOS.wins) do + if proc.path == rPath then + local nx,ny,nw,nh + if not proc.snap then + nx,ny = proc.win.getPosition() + nw,nh = proc.win.getSize() + else + nx,ny = unpack(proc.snap.oPos) + nw,nh = unpack(proc.snap.oSize) + end + w,h = nw,nh + if nx+nw >= tw or ny+nh >= th then + x,y = 3,3 + else + x,y = nx+2,ny+1 + end + end + end + thewin = window.create(oldterm,x,y,w,h,false) + end + local nPath = rPath + if fs.getName(nPath) == "main.lua" then + nPath = fs.getDir(nPath) + end + local t = lUtils.getFileName(nPath) + t = t:sub(1,1):upper()..t:sub(2) + local process = {title=t,win=thewin,winMode=mode or "windowed",path=rPath} + local function func() + local oWin = thewin + local a = {lOS.run(rPath,process,table.unpack(args))} + if a[1] == false then + if process.winMode ~= "background" then + term.redirect(oWin) + local b = {lUtils.popup(fs.getName(rPath),fs.getName(rPath).." has stopped working.",29,9,{"OK","View Error"})} + if b[3] == "View Error" then + lUtils.popup(fs.getName(rPath),a[2],31,nil,{"OK"}) + end + else + lOS.notification(fs.getName(rPath).." has stopped working.") + end + end + end + process[1] = coroutine.create(func) + for i = 0, 15 do process.win.setPaletteColor(2^i, lOS.wins[0].win.getPaletteColor(2^i)) end + table.insert(lOS.processes,process) + local oterm = term.current() + term.redirect(process.win) + local oldcproc = cProc + cProc = #lOS.processes + process.id = cProc + coroutine.resume(process[1]) + cProc = oldcproc + term.redirect(oterm) + if mode ~= "background" then + os.queueEvent("window_open",#lOS.processes,tostring(process),focus) + end + return process +end + +term.redirect(proc[0].win) +function lOS.getRunningProcess() + return proc[cProc] +end +coroutine.resume(proc[0][1]) +term.redirect(oldterm) + +local oldwin = {} +local isFullscreen = false +local tbSize +lOS.nYieldTime = 0 +lOS.eventsPassed = 0 +local oYieldTime = os.epoch("utc") + +local forceResumeID = 0 +function lOS.queueForceResume() + forceResumeID = forceResumeID + 1 + os.queueEvent("levelos_force_resume", forceResumeID) + return forceResumeID +end + +local function hook(event) + local proc = lOS.getRunningProcess() + if proc then + local cStat = coroutine.status(proc[1]) + if cStat == "running" or cStat == "normal" then + if os.epoch("utc") > proc.lastYield+1000 then + if os.epoch("utc") > proc.lastYield+5000 then + proc.unresponsive = true + end + if not proc.eventQueue then + proc.eventQueue = {} + end + if not proc.systemYield then + proc.systemYield = lOS.queueForceResume() + end + proc.lastInterrupt = os.epoch("utc") + while true do + local e = table.pack(coroutine.yield()) + if e[1] == "levelos_force_resume" then + proc.systemYield = nil + proc.unresponsive = nil + break + else + table.insert(proc.eventQueue, e) + end + end + end + end + end +end + +local requestCache +if fs.exists("rom/apis/http/http.lua") then + local nativeHTTPRequest = http.request + + requestCache = {} + lOS.requestCache = requestCache + + function http.request(_url, _post, _headers, _binary) + local data + if type(_url) == "table" then + data = _url + else + data = {url=_url, body=_post, headers=_headers, binary=_binary} + end + local oldURL = data.url + while requestCache[data.url] do + if data.url:find("#", nil, true) then + data.url = data.url.."0" + else + data.url = data.url.."#" + end + end + requestCache[data.url] = {url=oldURL, binary=data.binary, process=lOS.getRunningProcess()} + + data.binary = true + local ok, err = nativeHTTPRequest(data) + if not ok then + os.queueEvent("http_failure", data.url, err) + end + + return ok, err + end + + local restore = { + checkURL = http.checkURL, + websocket = http.websocket, + websocketAsync = http.websocketAsync, + } + os.loadAPI("rom/apis/http/http.lua") + for key, value in pairs(restore) do + http[key] = value + end +else + +end + +local function resumeProcess(process, e) + if not (process.sFilter and process.sFilter ~= e[1]) then + if process.eventQueue and not process.systemYield then + local queue = process.eventQueue + process.eventQueue = nil + for i, event in ipairs(queue) do + process.lastYield = os.epoch("utc") + resumeProcess(process, event) + end + end + + if not process.systemYield then + process.lastYield = os.epoch("utc") + end + + local cSuccess,cFilter = coroutine.resume(process[1],table.unpack(e, 1, e.n)) + if cSuccess then + process.sFilter = cFilter + elseif process == lOS.system or process == lOS.systemUI then + _G.theSystemError = cFilter + error(cFilter,0) + end + end +end + +local function increaseBrightness(colors, brightnessFactor) + local brightenedColors = {} + + for _, color in ipairs(colors) do + local brightenedColor = { + math.min(color[1] + brightnessFactor, 1), + math.min(color[2] + brightnessFactor, 1), + math.min(color[3] + brightnessFactor, 1) + } + table.insert(brightenedColors, brightenedColor) + end + + return brightenedColors +end + +local function colorDistance(color1, color2) + if type(color1) == "number" then + color1 = {term.getPaletteColor(color1)} + end + if type(color2) == "number" then + color2 = {term.getPaletteColor(color2)} + end + local rDiff = color1[1]+0.2 - color2[1] + local gDiff = color1[2]+0.2 - color2[2] + local bDiff = color1[3]+0.2 - color2[3] + return (rDiff * rDiff + gDiff * gDiff + bDiff * bDiff) ^ 0.5 +end + +local function findClosestColor(targetColor, colorArray) + local closestColor = nil + local minDistance = math.huge + + for _, color in ipairs(colorArray) do + local distance = colorDistance(targetColor, color) + if distance < minDistance then + minDistance = distance + closestColor = color + end + end + + return closestColor +end + +local function genGrayscale(win) + local replace = {} + local grays = {colors.black, colors.gray, colors.lightGray, colors.white} + for i=0,15 do + replace[lUtils.toBlit(2^i)] = lUtils.toBlit(findClosestColor(2^i, grays)) + end + + local lines = {} + local w,h = win.getSize() + for y=1,h do + local line = {win.getLine(y)} + for i=2,3 do + line[i] = line[i]:gsub(".", function(str) return replace[str] end) + end + table.insert(lines, line) + end + return lines +end + +_G.genGrayscale = genGrayscale + +local function refreshProc(e) + term.redirect(wAll) + local w,h = term.getSize() + --for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end + local topwin + local oTime = os.epoch("utc") + local nTime = oTime + for p=0,#proc do + cProc = p + local oProcTime = os.epoch("utc") + if proc[p] == nil then break end + proc[p].id = p + local process = proc[p] + if not process.hasHook then + debug.sethook(process[1], hook, "", 50) + end + if process.win ~= nil then + if not process.owin then + process.owin = process.win + end + term.redirect(process.owin) + if process.winMode == "fullscreen" and process.minimized then + process.win.setVisible(false) + end + else + term.redirect(lOS.depWin) + end + -- filter events + local cWin + lOS.cWin = nil + local n = #lOS.wins + while lOS.wins[n] ~= nil do + if lOS.wins[n] == process then + cWin = n + lOS.cWin = cWin + break + else + n = n-1 + end + end + local alreadydone = false + if e[1] == "levelos_force_resume" then + alreadydone = true + --_G.debugforceresume = e + if process.systemYield == e[2] then + --_G.debugforceres = e + local cSuccess, cFilter = coroutine.resume(process[1], table.unpack(e, 1, e.n)) + if cSuccess then + process.sFilter = cFilter + elseif process == lOS.system or process == lOS.systemUI then + _G.theSystemError = cFilter + error(cFilter, 0) + end + end + elseif (e[1] == "http_success" or e[1] == "http_failure") and requestCache then + if not requestCache[e[2]] then + lOS.notification("Error!", "Invalid request to "..e[2]) + break + else + --alreadydone = true + break + end + elseif process == lOS.focusWin or not (e[1] == "key" or e[1] == "key_up" or e[1] == "char" or e[1] == "paste" or e[1] == "terminate" or e[1] == "term_resize" or (sysTimer and e[1] == "timer" and e[2] == sysTimer and process ~= lOS.system)) then + local a = lUtils.instantiate(e) + if string.find(a[1],"mouse") and ((a[1] ~= "mouse_up" and a[1] ~= "mouse_drag") or process == lOS.focusWin) and process.winMode ~= "background" and cWin and e[4] and e[4] < h-(lOS.tbSize-1) then + local winX,winY = process.win.getPosition() + local winW,winH = process.win.getSize() + if (e[3] >= winX and e[4] >= winY and e[3] < winX+winW and e[4] < winY+winH) or (process.winMode == "windowed" and e[3] >= winX-1 and e[4] >= winY-1 and e[3] < winX+winW+1 and e[4] < winY+winH+1) then -- actually it does work + if topwin == nil then + topwin = process + if not (e[3] >= winX and e[4] >= winY and e[3] < winX+winW and e[4] < winY+winH) then + topwin = nil + end + else + for b=0,#lOS.wins do + if lOS.wins[b] == process then + break + elseif lOS.wins[b] == topwin then + topwin = process + if not (e[3] >= winX and e[4] >= winY and e[3] < winX+winW and e[4] < winY+winH) then + --lOS.notification("Detected border press! Topwin canceled.") + topwin = nil + end + break + end + end + end + elseif process.winMode == "widget" and e[1] == "mouse_click" then + process[1] = coroutine.create(function() return end) + end + elseif not string.find(a[1],"mouse") and not (lOS.noEvents and process == lOS.focusWin and (e[1] == "key" or e[1] == "key_up" or e[1] == "char" or e[1] == "paste" or e[1] == "terminate" or e[1] == "term_resize")) then + if not (process.events and process.events == "all") then + alreadydone = true + resumeProcess(process, e) + end + end + end + if e[1] ~= "levelos_force_resume" and process.events and process.events == "all" and not alreadydone then + if not (process.sFilter and process.sFilter ~= e[1]) then + resumeProcess(process, e) + end + end + if process.win then + process.owin = term.current() + end + local nProcTime = os.epoch("utc") + if topwin ~= process then + if not process.timeCounter then + process.timeCounter = 0 + end + process.timeCounter = process.timeCounter+(nProcTime-oProcTime) + nTime = nTime+(nProcTime-oProcTime) + end + end + if lOS.noEvents then + topwin = nil + end + if requestCache and (e[1] == "http_success" or e[1] == "http_failure") and requestCache[e[2]] then + local req = requestCache[e[2]] + requestCache[e[2]] = nil + local ev = table.pack(table.unpack(e, 1, e.n)) + ev[2] = req.url + if e[1] == "http_success" and not req.binary then + ev[3] = {} + for k,v in pairs(e[3]) do + ev[3][k] = function(...) return lOS.utf8.decodeAll(v(lOS.utf8.encodeAll(...))) end + end + end + cProc = req.process.id + local process = req.process + local oterm = term.current() + if process.win ~= nil then + if not process.owin then + process.owin = process.win + end + term.redirect(process.owin) + if process.winMode == "fullscreen" and process.minimized then + process.win.setVisible(false) + end + else + term.redirect(lOS.depWin) + end + resumeProcess(process, ev) + term.redirect(oterm) + if not lOS.reqlog then lOS.reqlog = {} end + req.event = ev + table.insert(lOS.reqlog, req) + elseif e[1] ~= "levelos_force_resume" and topwin ~= nil then + term.redirect(topwin.owin) + local winX,winY = topwin.win.getPosition() + if topwin.owin ~= topwin.win and topwin.owin.getPosition then + local winX2,winY2 = topwin.owin.getPosition() + winX = winX+(winX2-1) + winY = winY+(winY2-1) + end + local winW,winH = topwin.owin.getSize() + local a = lUtils.instantiate(e) + a[3] = a[3]-winX+1 + a[4] = a[4]-winY+1 + if not (topwin.events and topwin.events == "all") then + if not (topwin.sFilter and topwin.sFilter ~= a[1] and topwin.sFilter ~= "terminate") then + local cWin + lOS.cWin = nil + local n = #lOS.wins + while lOS.wins[n] ~= nil do + if lOS.wins[n] == topwin then + cWin = n + lOS.cWin = cWin + break + else + n = n-1 + end + end + local oProcTime = os.epoch("utc") + cProc = topwin.id + resumeProcess(topwin, a) + topwin.owin = term.current() + local nProcTime = os.epoch("utc") + if not topwin.timeCounter then + topwin.timeCounter = 0 + end + topwin.timeCounter = topwin.timeCounter+(nProcTime-oProcTime) + nTime = nTime+(nProcTime-oProcTime) + end + end + if topwin.title ~= nil then + --lOS.notification("You clicked on process "..topwin.title) + end + end + cProc = 0 + for w=0,#lOS.wins do + while lOS.wins[w] ~= nil and lOS.wins[w].winMode == "background" do + table.remove(lOS.wins,w) + end + end + + if lOS.focusWin and lOS.focusWin.winMode == "fullscreen" then + local win = lOS.focusWin.win + term.redirect(oldterm) + local winW,winH = win.getSize() + local winX,winY = win.getPosition() + local totW,totH = oldterm.getSize() + if not lOS.focusWin.fullscreen then + lOS.focusWin.fullscreen = {pos={winX,winY},size={winW,winH}} + end + if winX ~= 1 or winY ~= 1 or winW ~= totW or winH ~= totH or isFullscreen == false then + win.reposition(1,1,totW,totH,oldterm) + win.setVisible(true) + os.queueEvent("term_resize") + if not isFullscreen then + win.redraw() + isFullscreen = true + tbSize = lOS.tbSize + lOS.tbSize = 0 + end + end + else + if tbSize then + lOS.tbSize = tbSize + tbSize = nil + end + term.redirect(wAll) + --[[local twrite = term.write + local tblit = term.blit + function term.write(...) + if ({term.getCursorPos()})[2] <= ({term.getSize()})[2] then + return twrite(...) + end + end + function term.blit(...) + if ({term.getCursorPos()})[2] <= ({term.getSize()})[2] then + return tblit(...) + end + end]] + for w=0,#lOS.wins do + log("Processing window "..w) + local winW,winH = lOS.wins[w].win.getSize() + local winX,winY = lOS.wins[w].win.getPosition() + if w == lOS.focusWin and previewrect ~= nil then + term.setCursorPos(previewrect.x,previewrect.y) + term.setTextColor(colors.lightBlue) + for a=1,previewrect.w do + term.setBackgroundColor(lUtils.toColor(({getPixel(wAll,previewrect.x+(a-1),previewrect.y)})[3])) + term.write("\129") + end + for a=1,previewrect.h do + term.setBackgroundColor(lUtils.toColor(({getPixel(wAll,previewrect.x,previewrect.y+(a-1))})[3])) + term.setCursorPos(previewrect.x,previewrect.y+(a-1)) + term.write("\132") + term.setBackgroundColor(lUtils.toColor(({getPixel(wAll,previewrect.x+(previewrect.w-1),previewrect.y+(a-1))})[3])) + term.setCursorPos(previewrect.x+(previewrect.w-1),previewrect.y+(a-1)) + term.write("\136") + end + term.setCursorPos(previewrect.x,previewrect.y+(previewrect.h-1)) + for a=1,previewrect.w do + term.setBackgroundColor(lUtils.toColor(({getPixel(wAll,previewrect.x+(a-1),previewrect.y+(previewrect.h-1))})[3])) + term.write("\144") + end + end + if lOS.wins[w].winMode == "fullscreen" and w ~= lOS.focusWin then + -- minimize + os.queueEvent("window_minimize",w,tostring(lOS.wins[w])) + elseif lOS.wins[w].winMode == "screen" then + for l=1,winH do + term.setCursorPos(1,l) + term.blit(lOS.wins[w].win.getLine(l)) + end + elseif lOS.wins[w].winMode == "borderless" or lOS.wins[w].winMode == "widget" then + --log("Drawing borderless window "..w.." from line "..(winY-winY+1).." to "..((winY+(winH-1))-winY+1)) + for l=winY,winY+(winH-1) do + term.setCursorPos(winX,l) + term.blit(lOS.wins[w].win.getLine(l-winY+1)) + end + elseif lOS.wins[w].winMode == "windowed" then + local width = winW+2 + local height = winH+2 + winX = winX-1 + winY = winY-1 + local bPos = 1 + --local w,h = wAll.getSize() + local wW,wH = wAll.getSize() + if (winX + (width-1)) > wW then + --if lOS.wins[w].maximized == true then + bPos = 0 + end + local wincolor = colors.lightGray + if lOS.wins[w].winColor then + wincolor = lOS.wins[w].winColor + elseif lOS.wins[w] == lOS.focusWin and not (lOS.noEvents and lOS.noEvents ~= 2) then + wincolor = colors.gray + end + term.setBackgroundColor(wincolor) + term.setCursorPos(winX,winY) + for t=1,width-(10-bPos) do + term.write(" ") + end + term.setTextColor(colors.white) + if lOS.wins[w].resizable == nil then + lOS.wins[w].resizable = true + end + term.setTextColor(colors.white) + if lOS.wins[w] == lOS.focusWin and not (lOS.noEvents and lOS.noEvents ~= 2) then + if wButton == 1 then + term.setBackgroundColor(colors.lightGray) + else + term.setBackgroundColor(wincolor) + end + term.write(" - ") + if wButton == 2 then + term.setBackgroundColor(colors.lightGray) + else + term.setBackgroundColor(wincolor) + end + if lOS.wins[w].resizable == false then + term.setTextColor(colors.lightGray) + else + term.setTextColor(colors.white) + end + term.write(" + ") + term.setTextColor(colors.white) + if wButton == 3 then + term.setBackgroundColor(colors.red) + else + term.setBackgroundColor(wincolor) + end + term.write(" × ") + term.setBackgroundColor(wincolor) + else + term.setBackgroundColor(wincolor) + term.setTextColor(colors.gray) + term.write(" - + × ") + end + if bPos == 0 then + term.write(" ") + end + term.setCursorPos(winX+1,winY) + if lOS.wins[w].icon ~= nil then + if type(lOS.wins[w].icon) == "table" then + term.blit(lOS.wins[w].icon[1],(lOS.wins[w].icon[2] or lUtils.toBlit(term.getBackgroundColor())),(lOS.wins[w].icon[3] or lUtils.toBlit(term.getBackgroundColor()))) + elseif type(lOS.wins[w].icon) == "string" then + term.write(lOS.wins[w].icon) + end + term.write(" ") + end + if lOS.wins[w].title ~= nil then + term.write(lOS.wins[w].title) + end + if lOS.wins[w].unresponsive then + term.write(" (Not responding)") + end + term.setTextColor(wincolor) + local progWin = lOS.wins[w].win + + local lines = {} + if lOS.wins[w].unresponsive then + lines = genGrayscale(progWin) + _G.debuglines = lines + else + for i=1,height-2 do + lines[i] = {progWin.getLine(i)} + end + end + + local function pixel(x, y) + return lines[y][1]:sub(x,x), lines[y][2]:sub(x,x), lines[y][3]:sub(x,x) + end + + + for i=1,height-2 do + term.setCursorPos(winX,winY+i) + term.blit(string.char(149),lUtils.toBlit(wincolor),({pixel(1,i)})[3]) + term.blit(table.unpack(lines[i])) + term.blit(string.char(149),({pixel(width-2,i)})[3],lUtils.toBlit(wincolor)) + end + local bottomline = {string.char(138),({pixel(1,height-2)})[3],lUtils.toBlit(wincolor)} + for i=1,width-2 do + bottomline[1] = bottomline[1]..string.char(143) + bottomline[2] = bottomline[2]..({pixel(i,height-2)})[3] + bottomline[3] = bottomline[3]..lUtils.toBlit(wincolor) + end + bottomline[1] = bottomline[1]..string.char(133) + bottomline[2] = bottomline[2]..({pixel(width-2,height-2)})[3] + bottomline[3] = bottomline[3]..lUtils.toBlit(wincolor) + term.setCursorPos(winX,winY+(height-1)) + term.blit(table.unpack(bottomline)) + else + log("Something went wrong with window "..w) + end + end + for w=0,#proc do + if proc[w].env ~= nil and proc[w].env.LevelOS ~= nil and proc[w].env.LevelOS.overlay ~= nil then + coroutine.resume(coroutine.create(proc[w].env.LevelOS.overlay)) + end + end + local w,h = term.getSize() + term.setCursorPos(1,1) + --[[if #lOS.notifications > 0 then + term.setBackgroundColor(colors.orange) + term.setTextColor(colors.white) + lUtils.textbox(lOS.notifications[1].txt,w-20,h-7,w,h-5) + end]] + term.redirect(oldterm) + if lOS.focusWin then + for i = 0, 15 do term.setPaletteColor(2^i, lOS.focusWin.win.getPaletteColor(2^i)) end + end + --local w,h = oldterm.getSize() + --term.setBackgroundColor(colors.orange) + --term.setTextColor(colors.white) + --lOS.boxClear(w-20,h-7,w,h-5) + --term.setCursorPos(w-19,h-6) + --term.write(textutils.serialize(lOS.wins)) + --term.write("Yooo") + --term.redirect(oldterm) + for l=1,({wAll.getSize()})[2] do + if isFullscreen or oldwin[l] == nil or lUtils.compare(oldwin[l],{wAll.getLine(l)}) == false then + term.setCursorPos(1,l) + oldwin[l] = {wAll.getLine(l)} + term.blit(unpack(oldwin[l])) + end + end + if isFullscreen then + isFullscreen = false + end + --[[for t=1,h do + oldwin[t] = {wAll.getLine(t)} + end]] + + --term.setCursorPos(1,1) + --term.setBackgroundColor(colors.black) + --term.setTextColor(colors.white) + --write(lOS.log) + --[[taskbar(e)]] + --for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end + end + lOS.nYieldTime = lOS.nYieldTime+(nTime-oTime) + lOS.eventsPassed = lOS.eventsPassed+1 + if os.epoch("utc") >= oYieldTime+5000 then + lOS.yieldTime = lOS.nYieldTime/lOS.eventsPassed + for i,process in ipairs(lOS.processes) do + if process.timeCounter then + process.yieldTime = process.timeCounter/lOS.eventsPassed + process.timeCounter = 0 + end + end + lOS.nYieldTime = 0 + lOS.eventsPassed = 0 + oYieldTime = os.epoch("utc") + end +end + +local holding = {} + +if lUtils then + lUtils.isHolding = nil +end + +local function manage() + if lUtils ~= nil and lUtils.isHolding == nil then + function lUtils.isHolding(key) + if type(key) == "string" then + key = keys[key] + end + if holding[key] == nil or holding[key] == false then + return false + else + return true + end + end + end + local dragging + local dragon = false -- i can not come up with more variable names ffs + local dragSide + local dragX,dragY = false,false + local DRx,DRy + local OGw,OGh + local OGx,OGy + while true do + if lOS.focusWin then + local tempX,tempY = lOS.focusWin.win.getPosition() + local tempW,tempH = lOS.focusWin.win.getSize() + local tempX2,tempY2 = (tempX-1)+({lOS.focusWin.win.getCursorPos()})[1],(tempY-1)+({lOS.focusWin.win.getCursorPos()})[2] + if tempY2 <= ({term.getSize()})[2] and tempX2 >= tempX and tempY2 >= tempY and tempX2 <= tempX+(tempW-1) and tempY2 <= tempY+(tempH-1) then + lOS.focusWin.win.restoreCursor() + term.setCursorBlink(lOS.focusWin.win.getCursorBlink()) + term.setCursorPos(tempX2,tempY2) + term.setTextColor(lOS.focusWin.win.getTextColor()) + else + term.setCursorBlink(false) + end + end + for t=1,#lOS.processes do + if coroutine.status(lOS.processes[t][1]) == "dead" then + local win + for n=1,#lOS.wins do + if lOS.wins[n] == lOS.processes[t] then + --table.remove(lOS.wins,n) + win = n + break + end + end + if win and not lOS.wins[win].isClosing then + if not lOS.sysUIlog then + lOS.sysUIlog = {} + end + table.insert(lOS.sysUIlog,"Closed win "..t.." because the process died.") + os.queueEvent("window_close",win,tostring(lOS.wins[win]),"system closed cuz ded") + lOS.wins[win].isClosing = true + else + table.remove(lOS.processes,t) + break + end + end + end + if lOS.focusWin then + for i = 0, 15 do term.setPaletteColor(2^i, lOS.focusWin.win.getPaletteColor(2^i)) end + end + local e = table.pack(os.pullEventRaw()) + + local systemPresent = false + local sysUIPresent = false + for p=1,#lOS.processes do + if lOS.processes[p] == lOS.system then + systemPresent = true + elseif lOS.processes[p] == lOS.sysUI then + sysUIPresent = true + end + end + if not systemPresent then + error("CRITICAL_PROCESS_DIED",0) + elseif lOS.sysUI and lOS.sysUI.env and not sysUIPresent then + error("SystemUI: CRITICAL_PROCESS_DIED",0) + end + if e[1] == "term_resize" then + local w1,h1 = term.getSize() + local w2,h2 = lOS.wAll.getSize() + if w1 ~= w2 or h1 ~= h2 then + lOS.wAll.reposition(1,1,w1,h1) + lOS.depWin.reposition(1,1,w1,h1) + oldwin = {} + local totalW,totalH = lOS.wAll.getSize() + if lOS.tbSize then + totalH = totalH-lOS.tbSize + end + lOS.wins[0].win.reposition(1,1,w1,h1) + for i,win in ipairs(lOS.wins) do + local oldX, oldY = win.win.getPosition() + local oldWidth, oldHeight = win.win.getSize() + local newX, newY + local newWidth, newHeight + + if win.snap then + if win.snap.x then + newWidth = totalW + end + + if win.snap.y then + if win.winMode == "windowed" then + newHeight = totalH-1 + else + newHeight = totalH + end + end + end + + local width, height = newWidth or oldWidth, newHeight or oldHeight + + if oldX >= totalW or oldY >= totalH or oldX+width-1 <= 1 or oldY+height-1 <= 1 then + newX, newY = 4,4 + end + + win.win.reposition(newX or oldX, newY or oldY, width, height) + end + end + end + if e[1] == "paste" and e[2] == " " then + e[2] = "" + end + if e[1] == "key" then + holding[e[2]] = true + elseif e[1] == "key_up" then + holding[e[2]] = false + end + --for i = 0, 15 do term.setPaletteColor(2^i, term.nativePaletteColor(2^i)) end + if e[1] == "mouse_move" then + hX,hY = e[3],e[4] + end + local tempW,tempH = term.getSize() + if string.find(e[1],"mouse") and (e[3] ~= nil and e[4] <= (tempH-lOS.tbSize)) and not lOS.noEvents then + local focusWin = 0 + for t=1,#lOS.wins do + local winX,winY = lOS.wins[t].win.getPosition() + local winW,winH = lOS.wins[t].win.getSize() + local w,h = wAll.getSize() + if lOS.tbSize then + h = h-lOS.tbSize + end + if (e[3] >= winX and e[4] >= winY and e[3] < winX+winW and e[4] < winY+winH) or (lOS.wins[t].winMode == "windowed" and e[3] >= winX-1 and e[4] >= winY-1 and e[3] < winX+winW+1 and e[4] < winY+winH+1) then -- fixed + focusWin = t + end + end + local bPos = 1 + local tempwin = lOS.wins[focusWin] + local winX,winY = tempwin.win.getPosition() + local winW,winH = tempwin.win.getSize() + local totalW,totalH = lOS.wAll.getSize() + if lOS.tbSize then + totalH = totalH-lOS.tbSize + end + if winX+(winW-1) >= totalW --[[tempwin.maximized ~= nil and tempwin.maximized == true]] then + bPos = 0 + end + if e[1] == "mouse_click" and focusWin > 0 and (tempwin ~= lOS.focusWin) then + table.remove(lOS.wins,focusWin) + lOS.wins[#lOS.wins+1] = tempwin + lOS.focusWin = tempwin + end + if e[1] == "mouse_drag" and dragging ~= nil then + local winX,winY = dragging.win.getPosition() + local winW,winH = dragging.win.getSize() + if dragon == true then + if dragging.snap and (dragging.snap.x and dragging.snap.y) then + local rx = (DRx-(winX-1))/(winW-9) + winX = e[3]-math.ceil(rx*(dragging.snap.oSize[1]-9)) + winW = dragging.snap.oSize[1] + winH = dragging.snap.oSize[2] + dragging.snap = nil + OGx,OGy = winX,winY + else + winX = OGx-(DRx-e[3]) + end + if dragging.snap and dragging.snap.y then + if DRy-e[4] <= -2 then + winY = OGy-(DRy-e[4]) + dragging.snap = nil + dragging.win.reposition(winX,winY,winW,h-7) + end + else + winY = OGy-(DRy-e[4]) + end + + local w,h = totalW,totalH + + -- ALL THE RIGHT SIDE STUFF IS TEMP, TO SEE WHAT POSITION IT SHOULD BE ON. IT SHOULD BE RELATIVE TO THE SIZE OF A POTENTIAL LEFT SIDE SPLITSCREENED WINDOW + + if e[3] == w and e[4] == 1 then + previewrect = {x=math.ceil(w/2)+1,y=2,w=math.floor(w/2)-1,h=math.floor((h)/2)-2} + elseif e[3] == w and e[4] == h then + previewrect = {x=math.ceil(w/2)+1,y=math.floor((h)/2)+2,w=math.floor(w/2)-1,h=math.floor((h)/2)-2} + elseif e[3] == w then + previewrect = {x=math.ceil(w/2)+1,y=2,w=math.floor(w/2)-1,h=h-2} + + + elseif e[3] == 1 and e[4] == 1 then + previewrect = {x=2,y=2,w=math.floor(w/2)-2,h=math.floor((h)/2)-2} + elseif e[3] == 1 and e[4] == h then + previewrect = {x=2,y=math.floor((h)/2)+2,w=math.floor(w/2)-2,h=math.floor((h)/2)-2} + elseif e[3] == 1 then + previewrect = {x=2,y=2,w=math.floor(w/2)-2,h=h-2} + elseif e[4] == 1 then + --previewrect = {x=2,y=2,w=w-2,h=h-4} + else + previewrect = nil + end + end + if dragX == true then + if dragSide == 1 then + if OGw+(DRx-e[3]) >= 10 then + winW = OGw+(DRx-e[3]) + winX = OGx-(DRx-e[3]) + end + else + if OGw-(DRx-e[3]) >= 10 then + winW = OGw-(DRx-e[3]) + end + end + end + if dragY == true then + if OGh-(DRy-e[4]) >= 4 then + winH = OGh-(DRy-e[4]) + end + end + local wW,wH = dragging.win.getSize() + local resized = false + if wW ~= winW or wH ~= winH then + resized = true + end + dragging.win.reposition(winX,winY,winW,winH) + if resized then + os.queueEvent("term_resize") + end + end + local winX,winY = tempwin.win.getPosition() + local winW,winH = tempwin.win.getSize() + if focusWin > 0 then + if tempwin.winMode == "windowed" and (e[1] == "mouse_click" or e[1] == "mouse_up") then + if e[4] == winY-1 then + if e[3] <= bPos+winX+winW-1 and e[3] >= bPos+winX+winW-3 then + if e[1] == "mouse_click" then + wButton = 3 + else + os.queueEvent("window_close",focusWin,tostring(tempwin),"system closed cuz button click") + end + elseif e[3] <= bPos+winX+winW-4 and e[3] >= bPos+winX+winW-6 then + if e[1] == "mouse_click" then + wButton = 2 + elseif (tempwin.resizable == nil or tempwin.resizable == true) then + if tempwin.snap then + --tempwin.win.reposition(tempwin.snap.oPos[1],tempwin.snap.oPos[2],unpack(tempwin.snap.oSize)) + os.queueEvent("window_reposition",focusWin,tostring(tempwin),tempwin.snap.oPos[1],tempwin.snap.oPos[2],unpack(tempwin.snap.oSize)) + tempwin.snap = nil + else + tempwin.snap = {x=true,y=true,oPos={tempwin.win.getPosition()},oSize={tempwin.win.getSize()}} + --tempwin.win.reposition(1,2,totalW,totalH-1) + os.queueEvent("window_reposition",focusWin,tostring(tempwin),1,2,totalW,totalH-1) + end + --os.queueEvent("term_resize") + end + elseif e[3] <= bPos+winX+winW-7 and e[3] >= bPos+winX+winW-9 then + if e[1] == "mouse_click" then + wButton = 1 + else + --oop im dumb + + -- why tf would u do this + --[[local n=-1 + while lOS.wins[n] ~= nil do + n = n-1 + end + lOS.wins[n] = tempwin]] + --lOS.wins[#lOS.wins] = nil + os.queueEvent("window_minimize",focusWin,tostring(tempwin)) + -- plz work + end + elseif e[1] == "mouse_click" then + if tempwin.dragtimestamp and tempwin.dragtimestamp+500 > os.epoch("utc") then + if tempwin.snap then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),tempwin.snap.oPos[1],tempwin.snap.oPos[2],unpack(tempwin.snap.oSize)) + else + tempwin.snap = {x=true,y=true,oPos={tempwin.win.getPosition()},oSize={tempwin.win.getSize()}} + --tempwin.win.reposition(1,2,totalW,totalH-1) + os.queueEvent("window_reposition",focusWin,tostring(tempwin),1,2,totalW,totalH-1) + end + else + dragging = tempwin + dragging.dragtimestamp = os.epoch("utc") + dragon = true + DRx,DRy = e[3],e[4] + OGx,OGy = winX,winY + end + end + end + if e[4] >= winY and e[4] <= winY+winH and (e[3] == winX-1 or e[3] == winX+winW) and e[1] == "mouse_click" and (tempwin.resizable == nil or tempwin.resizable == true) then + dragging = tempwin + DRx = e[3] + OGw = winW + OGx = winX + dragX = true + if e[3] == winX-1 then + dragSide = 1 + else + dragSide = 2 + end + end + if e[4] == winY+(winH) and e[3] >= winX-1 and e[3] <= winX+(winW) and e[1] == "mouse_click" and (tempwin.resizable == nil or tempwin.resizable == true) then + dragY = true + DRy = e[4] + OGh = winH + dragging = tempwin + end + end + end + if e[1] == "mouse_up" then + if dragon == true then + local w,h = totalW,totalH + dragging.snap = {x=false,y=true,oPos={dragging.win.getPosition()},oSize={dragging.win.getSize()}} + if e[3] == w and e[4] == 1 then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),math.ceil(w/2)+1,2,math.floor(w/2),math.floor((h)/2)-2) + -- whenever im not super lazy, make this window 1 pixel higher on the bottom and then don't draw the bottom line. that way it connects nicely to the window below. + elseif e[3] == w and e[4] == h then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),math.ceil(w/2)+1,math.floor((h)/2)+2,math.floor(w/2),math.floor((h)/2)-1) + elseif e[3] == w then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),math.ceil(w/2)+1,2,math.floor(w/2),h-1) + elseif e[3] == 1 and e[4] == 1 then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),1,2,math.floor(w/2)-1,math.floor((h)/2)-2) + elseif e[3] == 1 and e[4] == h then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),1,math.floor((h)/2)+2,math.floor(w/2)-1,math.floor((h)/2)-1) + elseif e[3] == 1 then + os.queueEvent("window_reposition",focusWin,tostring(tempwin),1,2,math.floor(w/2)-1,h-1) + else + dragging.snap = nil + end + os.queueEvent("term_resize") + end + previewrect = nil + dragon = false + dragging = nil + dragX,dragY = false,false + wButton = 0 + end + end + if e[1] == "key_up" and lUtils.isHolding(keys.leftCtrl) and e[2] == keys.w and #lOS.wins > 0 and lOS.focusWin and lOS.focusWin ~= lOS.wins[0] and not lOS.focusWin.noShortcuts then + local focusWin + for t=#lOS.wins,1,-1 do + if lOS.wins[t] == lOS.focusWin then + focusWin = t + break + end + end + os.queueEvent("window_close",focusWin,tostring(lOS.focusWin),"system closed cuz ctrl + w") + elseif e[1] ~= "mouse_move" or e[3] ~= nil then + refreshProc(e) + end + end +end + + + + +local hover = false +function lOS.isHover() + return hover +end + + + +local function hoverevent() + local timerID = os.startTimer(3) + while true do + e = table.pack(os.pullEventRaw("mouse_move")) + if e[1] == "mouse_move" then + os.cancelTimer(timerID) + timerID = os.startTimer(3) + hX,hY = e[3],e[4] + hover = false + elseif e[1] == "timer" and e[2] == timerID then + os.queueEvent("mouse_hover",1,hX,hY) + log("Hovering at "..hX..","..hY) + hover = true + end + end +end +lOS.notifWins = {} +function lOS.notification(title,txt,programpath,duration) + lOS.notifications[#lOS.notifications+1] = {title,txt,programpath,duration} + + local w,h = lOS.wAll.getSize() + local y = h-5-lOS.tbSize + local continue = false + while not continue do + continue = true + for k,v in pairs(lOS.notifWins) do + if coroutine.status(v[1]) ~= "dead" then + local c1 = false + for k1,v1 in pairs(lOS.processes) do + if v1 == v then + c1 = true + end + end + if c1 == false then + lOS.notifWins[k] = nil + else + local tx,ty = v.win.getPosition() + if ty-6 <= y then + y = ty-6 + end + end + else + lOS.notifWins[k] = nil + end + end + end + lOS.execute({"LevelOS/Notification.lua",title,txt,programpath,duration},"widget",w-31,y,32,5,false) +end +--parallel.waitForAny(manage,notifications,hoverevent) +manage() \ No newline at end of file diff --git a/Program_Files/Common_Files/oop.lua b/Program_Files/Common_Files/oop.lua new file mode 100644 index 0000000..481fc3d --- /dev/null +++ b/Program_Files/Common_Files/oop.lua @@ -0,0 +1,648 @@ +------------------------------------------------ LevelOOPer Library 2.2.6 ------------------------------------------------ +function _G.pairs(t) + local mt = getmetatable(t) + if mt and type(mt.__pairs) == "function" then + return mt.__pairs(t) + else + return next, t, nil + end +end + +local function fread(file) + local f = fs.open(file,"r") + local txt = f.readAll() + f.close() + return txt +end + +local env = _ENV +local oop = {} + +local classCache = {} +local privCache = {} +local publicCache = {} +local objectCache = {} + +local dir = fs.getDir(({...})[1] or shell.getRunningProgram()) + +local function typeRestriction(index, sType, value) + if not oop.isType(value, sType) then + return false, "cannot convert "..sType.." '"..index.."' to '"..oop.type(value, true).."'" + else + return true + end +end + +local function getNumberType(number) + if number%1 == 0 then + return "int" + else + return "double" + end +end + +oop.setEnv = function(newEnv) + env = newEnv +end + +oop.setDir = function(newDir) + oop.expect(1, newDir, "string") + dir = newDir +end + +oop.getDir = function() + return dir +end + +local default = { + int = 0, + double = 0, + number = 0, + string = "", + ["function"] = function() end, + table = {}, + boolean = false, +} + +local varTypes = { + int = "int", + double = "double", + num = "number", + str = "string", + func = "function", + tbl = "table", + bool = "boolean", +} + +local newVar = { + var = function(name) + local tbl = {name=name} + setmetatable(tbl, { + __call = function(self, value) + self.value = value + return self + end + }) + return tbl + end, + obj = function(class) + oop.expect(1, class, "class") + local var = {type=classCache[class].name} + setmetatable(var, { + __call = function(self, name) + self.name = name + setmetatable(var, { + __call = function(self, value) + local ok,err = typeRestriction(name, var.type, value) + if not ok then + error(err, 2) + else + self.value = value + return self + end + end, + }) + return self + end, + }) + return var + end, +} + +local const = function(name) + if type(name) == "function" then + return function(realname) + local var = name(realname) + var.const = true + return var + end + elseif type(name) == "string" then + local var = {name=name, const=true} + setmetatable(var, { + __call = function(self, value) + self.value = value + return self + end, + }) + return var + end +end + +for k,v in pairs(varTypes) do + newVar[k] = function(name) + local tbl = {name=name, type=varTypes[k]} + setmetatable(tbl, { + __call = function(self, value) + local stat,err = typeRestriction(name, tbl.type, value) + if not stat then + error(err, 2) + else + self.value = value + return self + end + end, + }) + return tbl + end +end + +oop.injectEnv = function(env) + env.oop = oop + env.class = oop.class + env.const = const + env.import = oop.import + env.type = oop.type + env.getPublicObject = oop.getPublicObject + for k,v in pairs(newVar) do + env[k] = v + end +end + +local metamethods = { + ["__add"] = true, + ["__sub"] = true, + ["__mul"] = true, + ["__div"] = true, + ["__mod"] = true, + ["__pow"] = true, + ["__unm"] = true, + ["__concat"] = true, + ["__len"] = true, + ["__eq"] = true, + ["__lt"] = true, + ["__le"] = true, + ["__index"] = true, + ["__newindex"] = true, + ["__call"] = true, + ["__tostring"] = true, + ["__metatable"] = true, + ["__pairs"] = true, + ["__ipairs"] = true +} + +local function sanitizeArgs(...) + local args = table.pack(...) + for k,v in pairs(args) do + if publicCache[v] then + args[k] = publicCache[v] + end + end + return table.unpack(args, nil, args.n) +end + +local function instantiate(orig) + local function deepCopy(o, seen) + seen = seen or {} + if seen[o] then + return seen[o] + end + local copy + if type(o) == 'table' then + copy = {} + seen[o] = copy + for k,v in pairs(o) do + copy[deepCopy(k, seen)] = deepCopy(v, seen) + end + setmetatable(copy, deepCopy(getmetatable(o), seen)) + else + copy = o + end + return copy + end + return deepCopy(orig) +end + +local function makeClass(name, classTbl, inherit) + local class = { + static = { + }, + private = { + }, + public = { + }, + properties = { + static = { + }, + private = { + }, + public = { + }, + types = { + }, + consts = { + }, + }, + metatable = { + public = { + }, + private = { + }, + }, + name = name, + } + + class.metatable.static = { + __index = class.static, + __newindex = function(tbl, k, v) + if class.properties.static[k] then + if class.properties.consts[k] then + error("cannot modify const '"..tostring(k).."'", 2) + elseif class.properties.types[k] then + local stat,err = typeRestriction(k, class.properties.types[k], v) + if not stat then + error(err, 2) + else + class.static[k] = v + end + end + elseif class.properties.private[k] then + error("cannot modify private property '"..tostring(k).."' outside of class", 2) + elseif class.properties.public[k] then + error("cannot modify public property '"..tostring(k).."' outside of object", 2) + end + end, + __call = function(self, ...) + local privObj = {} + local obj = {} + + privCache[obj] = privObj + publicCache[privObj] = obj + + local properties = {} + local privProperties = {} + + setmetatable(properties, {__index=class.public}) + setmetatable(privProperties, { + __index=function(t, k) + return properties[k] or class.private[k] + end + }) + + local mt = { + __index = function(t, k) + local p = properties + if t == privObj then + p = privProperties + end + local v = p[k] + if type(v) == "function" then + local tEnv = {self=privObj} + oop.injectEnv(tEnv) + setmetatable(tEnv, {__index=env}) + setfenv(v, tEnv) + end + return v + end + } + + local inConstructor = false + + mt.__pairs = function(tbl) + local loopThroughTables = {properties, class.public, class.static} + if tbl == privObj then + table.insert(loopThroughTables, 2, class.private) + end + local cls = class + while cls.parent do -- this WONT WORK + cls = cls.parent + if tbl == privObj then + table.insert(loopThroughTables, cls.private) + end + table.insert(loopThroughTables, cls.public) + table.insert(loopThroughTables, cls.static) + end + + local currentIndex = 1 + + local had = {} + + local function customIterator(_, prevKey) + local key, value + + while currentIndex <= #loopThroughTables do + key, value = next(loopThroughTables[currentIndex], prevKey) + while key ~= nil and had[key] do -- skip keys that were already found + key, value = next(loopThroughTables[currentIndex], key) + end + + if key ~= nil then + had[key] = true + return key, tbl[key] + else + prevKey = nil + currentIndex = currentIndex + 1 + end + end + + return nil + end + + return customIterator, tbl, nil + end + + mt.__newindex = function(tbl, k, v) + if class.properties.consts[k] and not inConstructor then + error("cannot modify const '"..tostring(k).."'", 2) + end + if tbl ~= privObj and class.properties.private[k] then + error("cannot modify private property '"..tostring(k).."' outside of class", 2) + end + if class.properties.types[k] then + local stat,err = typeRestriction(k, class.properties.types[k], v) + if not stat then + error(err, 2) + end + end + if class.properties.static[k] then + -- change in whole class + class.static[k] = v + elseif class.properties.private[k] then + privProperties[k] = v + else + properties[k] = v + end + end + + local mt2 = {} + + for k,v in pairs(mt) do + mt2[k] = v + end + + for k,v in pairs(class.metatable.public) do + mt[k] = function(...) + local tEnv = {self=privObj} + oop.injectEnv(tEnv) + setmetatable(tEnv, {__index=env}) + setfenv(v, tEnv) + return v(sanitizeArgs(...)) + end + end + + for k,v in pairs(class.metatable.private) do + mt2[k] = function(...) + local tEnv = {self=privObj} + oop.injectEnv(tEnv) + setmetatable(tEnv, {__index=env}) + setfenv(v, tEnv) + return v(sanitizeArgs(...)) + end + end + + setmetatable(obj, mt) + setmetatable(privObj, mt2) + + local constructors = {class.constructor} + local cls = class + while cls.parent do + cls = cls.parent + if cls.constructor then + table.insert(constructors, 1, cls.constructor) + end + end + for t=1,#constructors do + inConstructor = true + local tEnv = {self=privCache[obj] or obj} + oop.injectEnv(tEnv) + setmetatable(tEnv, {__index=env}) + setfenv(constructors[t], tEnv) + local status,tObj = pcall(constructors[t],sanitizeArgs(...)) + if not status then + error(tObj, 2) + end + if tObj == false then + return false + elseif tObj then + obj = publicCache[tObj] or tObj + end + inConstructor = false + end + + objectCache[obj] = class + objectCache[privObj] = class + + for k,v in pairs(privObj) do + if type(v) == "table" and v == class.private[k] then + privObj[k] = instantiate(v) + end + end + + return obj + end, + } + + local iClass + if inherit then + iClass = classCache[inherit] + class.parent = iClass + setmetatable(class.static, {__index=iClass.static}) + setmetatable(class.public, { + __index = function(tbl, key) + if iClass.public[key] ~= nil then + return iClass.public[key] + else + return class.static[key] + end + end, + }) + + setmetatable(class.private, { + __index = function(tbl, key) + if iClass.private[key] ~= nil then + return iClass.private[key] + else + return class.public[key] + end + end, + }) + + for k,v in pairs(class.properties) do + setmetatable(v, {__index=iClass.properties[k]}) + end + + setmetatable(class, {__index=iClass}) + else + setmetatable(class.public, {__index=class.static}) + setmetatable(class.private, {__index=class.public}) + end + + loopThrough = {"static","private","public"} + for i,l in ipairs(loopThrough) do + if classTbl[l] then + for k,v in pairs(classTbl[l]) do + if type(k) == "number" then + if type(v) == "table" and v.name then + if v.const then + class.properties.consts[v.name] = true + end + if v.type then + class.properties.types[v.name] = v.type + end + if v.value or v.type then + class[l][v.name] = v.value or default[v.type] + end + class.properties[l][v.name] = true -- now values can be generic typed starting with nil but still recognized + elseif type(v) == "string" then + class.properties[l][v] = true + end + elseif type(k) == "string" then + if l == "public" and k == name then -- constructor + if type(v) ~= "function" then + error("invalid constructor: expected function, got "..type(v),2) + end + class.constructor = v + elseif metamethods[k] then + class.metatable[l][k] = v + else + class[l][k] = v + class.properties[l][k] = true + end + end + end + end + end + + local classObj = {} + + classCache[classObj] = class + class.obj = classObj + + setmetatable(classObj, class.metatable.static) + + env[name] = classObj +end + +function oop.class(name) + return function(class) + if classCache[class] then -- a class object was passed meaning we're gonna inherit + return function(realclass) + makeClass(name, realclass, class) + end + else + makeClass(name, class) + end + end +end + +local oType = type +function oop.type(value, numberType) + if oType(value) == "table" then + if objectCache[value] then + return objectCache[value].name + elseif classCache[value] then + return "class" + else + return "table" + end + elseif numberType and oType(value) == "number" then + return getNumberType(value) + else + return oType(value) + end +end + +function oop.getClassName(class) + oop.expect(1, class, "class") + return classCache[class].name +end + +function oop.isType(value, ...) + local types = {oop.type(value), oType(value), oop.type(value, true)} + if types[1] ~= types[2] and types[3] ~= "class" then + types[3] = "object" + end + local allowedTypes = {...} + for i,t in ipairs(types) do + for i=1, #allowedTypes do + if allowedTypes[i] == t or (allowedTypes[i] == "double" and t == "number") then + return true + end + end + end + return false +end + +function oop.expect(index, value, ...) + local level = 3 + local allowedTypes = {...} + if type(allowedTypes[1]) == "number" then + level = allowedTypes[1] + table.remove(allowedTypes, 1) + end + if type(allowedTypes[1]) ~= "string" then + error("bad argument #3: expected string", 2) + end + if oop.isType(value, ...) then + return value + end + + local expectlist + local numbertype = false + for t=1,#allowedTypes do + if allowedTypes[t] == "int" then + numbertype = true + end + end + if #allowedTypes > 1 then + local lastType = allowedTypes[#allowedTypes] + allowedTypes[#allowedTypes] = nil + expectlist = table.concat(allowedTypes, ", ").." or "..lastType + else + expectlist = allowedTypes[1] + end + local t = oop.type(value, numbertype) + + error("bad argument #"..index..": expected "..expectlist.." got "..t.." ("..tostring(value)..")", level) +end + +function oop.getPublicObject(privateObject) + return publicCache[privateObject] or privateObject +end + +function oop.import(filepath) + oop.expect(1, filepath, "string") + + local makePackage = require("cc.require") + local tEnv = setmetatable({shell=shell, multishell=multishell},{__index=env}) + tEnv.require, tEnv.package = makePackage.make(tEnv, dir) + tEnv.oop = oop + + oop.injectEnv(tEnv) + + local pathlog = filepath + local filepath2 = filepath..".lua" + pathlog = pathlog.."\n"..filepath2 + + if filepath:sub(1,1) ~= "/" then + pathlog = pathlog.."\n"..fs.combine(dir, filepath) + pathlog = pathlog.."\n"..fs.combine(dir, filepath2) + if fs.exists(fs.combine(dir, filepath)) then + filepath = fs.combine(dir, filepath) + elseif fs.exists(fs.combine(dir, filepath2)) then + filepath = fs.combine(dir, filepath2) + end + end + + if fs.exists(filepath2) and not fs.exists(filepath) then + filepath = filepath2 + end + + if not fs.exists(filepath) then + error("Could not find file:"..pathlog,2) + end + + local f = fread(filepath) + + local func, err = load(f, "@"..filepath, nil, tEnv) + + if not func then + error(err, 2) + else + local ok,err = pcall(func) + if not ok then + error(err, 2) + end + end +end + +return oop \ No newline at end of file diff --git a/Program_Files/LevelOS/CMD/latch.lua b/Program_Files/LevelOS/CMD/latch.lua new file mode 100644 index 0000000..46481c6 --- /dev/null +++ b/Program_Files/LevelOS/CMD/latch.lua @@ -0,0 +1,723 @@ +local tokens = { + whitespace = { + ["\t"] = true, + [" "] = true, + ["\n"] = true, + [","] = true, + }, + + line = { + ["\n"] = true, + [","] = true, + }, + + label = { + [":"] = true, + }, + + indexVar = { + ["%"] = true, + }, + + operator = {}, + op = { + add = { + ["+"] = true, + }, + subtract = { + ["-"] = true, + }, + multiply = { + ["*"] = true, + }, + modulo = { + ["%"] = true, + }, + power = { + ["^"] = true, + }, + divide = { + ["/"] = true, + }, + }, + + assign = { + ["="] = true, + }, + + comparison = {}, + comp = { + equals = { + ["=="] = true, + }, + notEquals = { + ["~="] = true, + }, + bigger = { + [">"] = true, + }, + biggerOrEquals = { + [">="] = true, + }, + smaller = { + ["<"] = true, + }, + smallerOrEquals = { + ["<="] = true, + }, + }, + + logic = {}, + logicAnd = { + ["and"] = true, + ["&&"] = true, + }, + logicOr = { + ["or"] = true, + ["||"] = true, + }, + logicNot = { + ["not"] = true, + ["!"] = true, + }, + + keyword = {}, + ifStatement = { + ["if"] = true, + }, + whileStatement = { + ["while"] = true, + }, + forStatement = { + ["for"] = true, + }, + elseStatement = { + ["else"] = true, + }, + localStatement = { + ["local"] = true, + }, + eof = { + ["end"] = true, + ["exit"] = true, + }, + + boolean = {}, + boolTrue = { + ["true"] = true, + }, + boolFalse = { + ["false"] = true, + }, + + ident = { + ["_"] = true, + }, + digits = { + }, + + string = { + ["\""] = true, + ["'"] = true, + }, + + symbol = { + ["("] = true, + [")"] = true, + }, + + escape = { + ["\\"] = true, + } +} + +for c=("a"):byte(), ("z"):byte() do + tokens.ident[string.char(c)] = true + tokens.ident[string.char(c):upper()] = true +end + +for c=("0"):byte(), ("9"):byte() do + tokens.ident[string.char(c)] = true + tokens.digits[string.char(c)] = true +end + +local function addTo(root, ...) + if not root then error("invalid argument #1: expected table, got nil",2) end + local tbls = {...} + for i,t in ipairs(tbls) do + for k,v in pairs(t) do + root[k] = v + end + end +end + +for k,v in pairs(tokens.comp) do + addTo(tokens.comparison, v) +end + +for k,v in pairs(tokens.op) do + addTo(tokens.operator, v) +end + +addTo(tokens.logic, tokens.logicAnd, tokens.logicOr, tokens.logicNot) + +addTo(tokens.keyword, tokens.logic, tokens.ifStatement, tokens.whileStatement, tokens.forStatement, tokens.elseStatement, tokens.localStatement, tokens.eof) + +addTo(tokens.boolean, tokens.boolTrue, tokens.boolFalse) + +local statementValues = { + ["string"] = true, + ["number"] = true, + ["variable"] = true, + ["arg"] = true, + ["value"] = true, +} + +local validVarTypes = { + ["function"] = true, + ["arg"] = true, + ["variable"] = true, + ["escape"] = true, +} + +local function lex(text) + local char = 1 + local function get(p) + local tPos = p or char + return text:sub(tPos,tPos) + end + local beginline = true + local inStatement = false + local isLabel = false + local data = {{}} + local line = data[1] + local posOffset = 0 + + local function newline() + posOffset = char + table.insert(data,{}) + line = data[#data] + end + + local function addData(txt,type) + if not txt then error("no txt",2) end + if line[#line] and line[#line].type == type then + line[#line].data = line[#line].data..txt + line[#line].posLast = line[#line].posLast+#txt + else + table.insert(line,{data=txt,posFirst=(char-posOffset),posLast=(char-posOffset)+(#txt-1),type=type}) + end + end + + local function readWhitespace(acceptNewline) + local txt = get() + local oldchar = char + while tokens.whitespace[txt] and (acceptNewline or not tokens.line[txt]) do + char = char + 1 + addData(txt,"whitespace") + isLabel = false + txt = get() + end + if oldchar == char then + return false + else + return true + end + end + + while char <= #text do + local txt = get() + if tokens.whitespace[txt] and not tokens.line[txt] then + addData(txt,"whitespace") + isLabel = false + + elseif tokens.escape[txt] then + addData(txt..text:sub(char+1,char+1),"escape") + char = char+1 + if tokens.whitespace[get(char+1)] then + isLabel = false + beginline = false + end + + elseif tokens.string[txt] then + local bchar = txt + local echar = bchar + local stype = "string" + if isLabel then + stype = "label" + elseif beginline then + stype = "function" + end + isLabel = false + beginline = false + addData(txt,stype) + char = char+1 + while char <= #text do + local c = text:sub(char,char) + if tokens.escape[c] then + addData(c..get(char+1),"escape") + char = char+1 + if tokens.digits[get()] then + char = char+1 + for i=1,2 do + if tokens.digits[get()] then + addData(get(),"escape") + char = char+1 + else + break + end + end + else + char = char+1 + end + elseif tokens.indexVar[c] then + local b2,e2 = text:find("^%%[A-Za-z0-9_]+%%", char) + if b2 then + addData(text:sub(b2,e2), "variable") + char = e2 + else + local b3,e3 = text:find("^%%[0-9]+", char) + if not b3 then + b3,e3 = text:find("^%%~[0-9]+", char) + end + if b3 then + addData(text:sub(b3,e3), "arg") + char = e3 + else + addData(c, stype) + end + end + char = char+1 + elseif c == echar then + addData(c, stype) + break + else + if c == "\n" then + newline() + else + addData(c, stype) + end + char = char+1 + end + end + + elseif tokens.indexVar[txt] then + if inStatement == 1 and line[#line] and line[#line].type == "whitespace" and line[#line-1] and statementValues[line[#line-1].type] then + inStatement = false + beginline = true + end + local b2,e2 = text:find("^%%[A-Za-z0-9_]+%%", char) + if b2 then + if isLabel and beginline then + addData(text:sub(b2,e2), "unidentified") + else + addData(text:sub(b2,e2), "variable") + end + char = e2 + else + local b3,e3 = text:find("^%%[0-9]+", char) + if not b3 then + b3,e3 = text:find("^%%~[0-9]+", char) + end + if b3 then + if isLabel and beginline then + addData(text:sub(b3,e3), "unidentified") + else + addData(text:sub(b3,e3), "arg") + end + char = e3 + elseif isLabel then + addData(txt, "label") + elseif beginline then + addData(txt, "function") + elseif not inStatement then + addData(txt, "string") + elseif tokens.operator[txt] then + addData(txt, "operator") + else + addData(txt, "unidentified") + end + end + if tokens.whitespace[get(char+1)] then + beginline = false + end + + elseif tokens.ident[txt] or (not inStatement and not (tokens.line[txt] or tokens.label[txt] or tokens.assign[txt] or tokens.symbol[txt])) then + local b,e + if inStatement then + b,e = text:find("^[A-Za-z0-9_.]+", char) + else + b,e = char,char+1 + local c = get(e) + while e <= #text and not (tokens.line[c] or tokens.label[c] or tokens.assign[c] or tokens.indexVar[c] or tokens.whitespace[c] or tokens.symbol[c] or tokens.escape[c]) do + e = e + 1 + c = get(e) + end + e = e - 1 + end + local token = text:sub(b,e) + local ltoken = token:lower() + local statementValue = false + if inStatement then + if line[#line] and line[#line].type == "whitespace" and line[#line-1] and statementValues[line[#line-1].type] then + if inStatement == 1 then + inStatement = 0 + beginline = true + end + statementValue = true + end + end + char = e + if isLabel then + addData(token, "label") + if tokens.whitespace[get(char+1)] then + isLabel = false + beginline = false + end + + elseif tokens.localStatement[ltoken] and not inStatement then + if not beginline then + addData(token, "string") + else + addData(token, "keyword") + end + + elseif tokens.eof[ltoken] and (inStatement == 0 or not inStatement) then + addData(token, "keyword") + + elseif (tokens.ifStatement[ltoken] or tokens.whileStatement[ltoken]) and (inStatement == 0 or not inStatement) then + addData(token, "keyword") + inStatement = 1 + + elseif (tokens.elseStatement[ltoken] or tokens.eof[ltoken]) and (inStatement == 0 or not inStatement) then + addData(token, "keyword") + + elseif (tokens.logicAnd[ltoken] or tokens.logicOr[ltoken]) and statementValue then + addData(token, "logic") + if inStatement == 0 then + inStatement = 1 + end + + elseif tokens.logicNot[ltoken] and inStatement and not statementValue then + addData(token, "logic") + if inStatement == 0 then + inStatement = 1 + end + + elseif tokens.forStatement[ltoken] and (inStatement == 0 or not inStatement) then + -- other handling + + elseif tokens.boolean[ltoken] and inStatement and not statementValue then + addData(token, "value") + + elseif beginline and (inStatement == 0 or not inStatement) then + addData(token, "function") + if tokens.whitespace[get(char+1)] then + beginline = false + end + + elseif tonumber(token) then + addData(token, "number") + if tokens.whitespace[get(char+1)] then + beginline = false + end + + else + addData(token, "string") + + end + if inStatement == 0 then + inStatement = false + end + + elseif tokens.label[txt] then + if char+1 > #text or tokens.whitespace[get(char+1)] then + addData(txt, "string") + else + addData(txt, "label") + isLabel = true + end + + elseif tokens.line[txt] then + beginline = true + inStatement = false + if txt ~= "\n" then + addData(txt, "symbol") + end + + elseif tokens.symbol[txt] then + addData(txt, "symbol") + + elseif txt:find("%p") then + local b,e = text:find("^%p+", char) + local token = text:sub(b,e) + char = e + if inStatement then + if tokens.operator[token] then + addData(token, "operator") + elseif tokens.comparison[token] then + addData(token, "comparison") + elseif tokens.logic[token] then + addData(token, "logic") + else + addData(token, "string") + end + else + if tokens.assign[token] then + local v = line[#line] + if v and v.type == "whitespace" then + v = line[#line-1] + end + if v and (validVarTypes[v.type]) then + local it = #line-1 + while v and (validVarTypes[v.type]) do + if v.type == "function" then + v.type = "variable" + end + it = it-1 + v = line[it] + end + addData(token, "assign") + inStatement = true + elseif beginline then + addData(token, "unidentified") + else + addData(token, "string") + end + elseif beginline then + addData(token, "unidentified") + else + addData(token, "string") + end + end + + elseif txt ~= "\n" then + addData(txt, "unidentified") + end + if txt == "\n" then + newline() + end + char = char+1 + end + return data +end + +local function parse(data, filename) + filename = filename or "latch" + if type(data) == "string" then + data = lex(data) + end + + local function err(lineNum, txt, txtafter) + local err = filename..": "..txt.." on line "..lineNum + if txtafter then + err = err.." ("..txtafter..")" + end + error(err, 0) + end + + local function unexpectedToken(line, token, ...) + _G.debugtoken = token + local exp = {...} + local expstr + if #exp > 0 then + expstr = "expected "..table.concat(exp, ", ") + end + err(line, "unexpected token '"..tostring(token.data).."' ("..tostring(token.type)..")", expstr) + end + + local function locateEntry(tbl, value) + for k,v in pairs(tbl) do + if v == value then + return true + end + end + return false + end + + local function parseToken(line, i, allowed, stopAt) + local token = line[i] + local data = {} + + stopAt = stopAt or {"whitespace"} + + while true do + if locateEntry(allowed, token.type) then + if token.type == "number" then + table.insert(data, {type="number", data=token.data}) + elseif token.type == "operator" then + table.insert(data, {type="operator", data=token.data}) + elseif token.type == "variable" and token.data:sub(1,1) == "%" then + table.insert(data, {type="variable", data=token.data:sub(2,#token.data-1)}) + elseif token.type == "variable" then + table.insert(data, {type="string", data=token.data}) + elseif token.type == "arg" then + table.insert(data, {type="argument", data=token.data:sub(2)}) + elseif token.type == "label" then + if tokens.string[token.data:sub(2,2)] then + table.insert(data, {type="label", data=token.data:sub(2):gsub(token.data:sub(2,2),"")}) + else + table.insert(data, {type="label", data=token.data:sub(2)}) + end + elseif tokens.string[token.data:sub(1,1)] then + table.insert(data, {type="string", data=token.data:gsub(token.data:sub(1,1),"")}) + else + table.insert(data, {type="string", data=token.data}) + end + elseif locateEntry(stopAt, token.type) then + i = i - 1 + break + else + local expected = {} + addTo(expected, stopAt) + addTo(expected, allowed) + unexpectedToken(ln, token, unpack(expected)) + end + i = i + 1 + token = line[i] + if not token then break end + end + + return data, i + end + + local function parseExpression(line, i, whitespaceConcat) -- this is all wrong FUCK, value (x+5) > comparison (x+5 == 8) > expression(x+5 == 8 and %potato%) + -- expression structure + local token = line[i] + local conditions = { + { + {} -- current condition (value) + -- more AND conditions + } + -- more OR conditions + } + local condition = conditions[1][1] + local isValid = false + while true do + token = line[i] + if not token then break end + if token.type == "whitespace" then -- optional token + elseif token.type == "logic" and tokens.logicNot[token.data] then -- optional token + table.insert(condition, token) + isValid = false + elseif statementValues[token.type] then -- REQUIRED token, expression is invalid without it + table.insert(condition, token) + isValid = true + i = i + 1 + token = line[i] + if not token then break end + if token.type == "whitespace" then + i = i + 1 + token = line[i] + end + if not token then break end + if token.type == "logic" and tokens.logicAnd[token.data] then + condition = {} + table.insert(conditions[#conditions], condition) + isValid = false + elseif token.type == "logic" and tokens.logicOr[token.data] then + condition = {} + table.insert(conditions, {}) + table.insert(conditions[#conditions], condition) + isValid = false + elseif token.type == "operator" then + table.insert(condition, token) + elseif whitespaceConcat then + i = i - 1 + else + i = i - 1 + break -- end of statement + end + else + unexpectedToken(i, token, "expression") + end + i = i + 1 + end + if not isValid then + err(i, "invalid statement") + else + return conditions, i + end + end + + + local program = { + arguments = {}, + env = {}, + commands = {}, + labels = {}, + code = {}, + } + local scope = program.code + for ln,line in ipairs(data) do + local token + local i = 1 + local beginline = true + local function nextToken(includeWhitespace) + if line[i+1] and (includeWhitespace or line[i+1].type ~= "whitespace") then + return line[i+1] + elseif line[i+2] then + return line[i+2] + end + end + local function nextTokenType(includeWhitespace) + local nt = nextToken(includeWhitespace) + if nt then + return nt.type + end + end + while i <= #line do + token = line[i] + if beginline then + if token.type == "function" or token.type == "variable" or token.type == "arg" then + local data + data, i = parseToken(line, i, {"function", "variable", "arg", "escape"}, {"whitespace", "symbol", "assign"}) + local l + if nextTokenType() == "assign" then + l = {type="assign", data=data, value={}} + while nextTokenType() == "assign" do + i = i + 1 + end + i = i + 1 + l.value, i = parseExpression(line, i, true) + else + l = {type="command", data=data, line=ln, params={}} + beginline = false + end + table.insert(scope, l) + + elseif token.type == "label" then + program.labels[token.data] = {scope=scope, commandNum=#scope+1} + + elseif token.type ~= "whitespace" then + --unexpectedToken(ln, token, "whitespace", "function", "variable", "arg") + end + + else + if token.type == "label" then + if scope[#scope].type == "command" then + local data + data, i = parseToken(line, i, {"label", "variable", "arg", "escape"}, {"whitespace", "symbol"}) + table.insert(scope[#scope].params, data) + else + unexpectedToken(ln, token) + end + + elseif token.type == "string" or token.type == "number" then + if scope[#scope].type == "command" then + local data + data, i = parseToken(line, i, {"string", "number", "variable", "arg", "escape"}, {"whitespace", "symbol"}) + table.insert(scope[#scope].params, data) + end + + elseif token.type ~= "whitespace" then + + end + end + i = i + 1 + end + end + return program +end + +return {lex=lex,parse=parse} \ No newline at end of file diff --git a/Program_Files/LevelOS/Cloud/icon.limg b/Program_Files/LevelOS/Cloud/icon.limg new file mode 100644 index 0000000..61b0ba5 --- /dev/null +++ b/Program_Files/LevelOS/Cloud/icon.limg @@ -0,0 +1 @@ +{{{"Ÿ€"," b ","b ",},{"ƒ","bb9"," 9 ",},{"€€€"," "," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Cloud/main.lua b/Program_Files/LevelOS/Cloud/main.lua new file mode 100644 index 0000000..68b09a5 --- /dev/null +++ b/Program_Files/LevelOS/Cloud/main.lua @@ -0,0 +1,1493 @@ +local assets = { + [ "init.lua" ] = { + id = 0, + content = "if not self.init then\ + if not fs.exists(\"LevelOS/startup/LevelCloud.lua\") then\ + --LevelOS.maximize()\ + term.setBackgroundColor(colors.white)\ + term.clear()\ + os.queueEvent(\"term_resize\")\ + os.pullEvent()\ + local a = {lUtils.popup(\"LevelCloud\",\"Do you want to install LevelCloud?\",27,9,{\"Install\",\"Cancel\"})}\ + if a[1] and a[3] == \"Install\" then\ + local oterm = term.current()\ + local nterm = window.create(term.current(),1,1,51,19,false)\ + term.redirect(nterm)\ + shell.run(\"lStore get Sm0f1bwQ LevelOS/startup/LevelCloud.lua\")\ + term.redirect(oterm)\ + local b = {lUtils.popup(\"LevelCloud\",\"LevelCloud installed. Please reboot to synchronize.\",27,9,{\"Reboot\",\"Later\"})}\ + if b[1] and b[3] == \"Reboot\" then\ + lOS.save()\ + os.reboot()\ + end\ + end\ + shapescape.exit()\ + end\ + local w,h = lOS.wAll.getSize()\ + if lOS.tbSize then\ + h = h-lOS.tbSize\ + end\ + local th = 14\ + if h > 32 then\ + th = 25\ + end\ + LevelOS.self.window.win.reposition(w-30,h-(th-1),25,th)\ + LevelOS.setWin(25,th,\"borderless\")\ + LevelOS.setTitle(\"Cloud\")\ + \ + self.init = true\ +end", + name = "init.lua", + }, + [ "open_folder.lua" ] = { + id = 1, + content = "lOS.execute(\"LevelOS/explorer.lua User/Cloud\")", + name = "open_folder.lua", + }, + [ "sync_click.lua" ] = { + id = 7, + content = "while true do\ + e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" or e[1] == \"mouse_up\" then\ + if e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then\ + if e[1] == \"mouse_click\" then\ + self.oldcolor = self.txtcolor\ + self.txtcolor = colors.lightBlue\ + else\ + --shell.run(\"pastebin run 4DaaHGRP\")\ + if not lOS.cloudSync then\ + lOS.cloudTimer = 30\ + end\ + end\ + end\ + if e[1] == \"mouse_up\" and self.oldcolor then\ + self.txtcolor = self.oldcolor\ + self.oldcolor = nil\ + end\ + end\ +end", + name = "sync_click.lua", + }, + colorsync2 = { + id = 8, + content = "if not lOS.userID then\ + self.color = colors.red\ + self.txt = \" Offline\"\ +elseif lOS.cloudSync then\ + self.color = colors.orange\ + self.txt = \" Syncing...\"\ +elseif self.color == colors.orange then\ + self.color = colors.blue\ + self.txt = \"LevelCloud\"\ +end", + name = "colorsync2", + }, + [ "log.lua" ] = { + id = 3, + content = "local c = 0\ +local scr = 0\ +local scroll = 0\ +local lasty = 0\ +term.setBackgroundColor(colors.gray)\ +term.clear()\ +os.sleep(0.1)\ +local isConflict\ +local objs = {}\ +local doRender = true\ +local sl = shapescape.getSlides()\ +while true do\ + isConflict = false\ + local w,h = term.getSize()\ + if lOS.cloudLog and (scr ~= scroll or #lOS.cloudLog ~= c or lOS.cloudSync or doRender) then\ + doRender = false\ + c = #lOS.cloudLog\ + if c > 40 then\ + for t=1,c-40 do\ + table.remove(lOS.cloudLog,t)\ + end\ + end\ + scr = scroll\ + c = #lOS.cloudLog\ + local l = lOS.cloudLog\ + term.setBackgroundColor(colors.gray)\ + term.clear()\ + local y = 2-scroll\ + if lOS.cloud and lOS.cloud.conflicts and lOS.cloud.conflicts then\ + for k,v in pairs(lOS.cloud.conflicts) do\ + isConflict = true\ + -- will only run if theres an entry, but also only once\ + -- big brain moment :sunglasses:\ + term.setBackgroundColor(colors.red)\ + local txt = \"There are multiple versions of a file. Resolve >\"\ + local lines = lUtils.wordwrap(txt,w-2)\ + lOS.boxClear(1,y,w,y+2+#lines)\ + objs.conflict = {x1=1,y1=y,x2=w,y2=y+2+#lines}\ + term.setCursorPos(2,y+1)\ + term.setTextColor(colors.white)\ + term.write(\"There is a sync issue\")\ + term.setTextColor(colors.lightGray)\ + for i=1,#lines do\ + term.setCursorPos(2,y+i+1)\ + term.write(lines[i])\ + end\ + y = y+4+#lines\ + term.setBackgroundColor(colors.gray)\ + break\ + end\ + end\ + for t=c,1,-1 do\ + term.setCursorPos(1,y)\ + if l[t].action == \"Upload\" then\ + term.setTextColor(colors.blue)\ + term.write(\"\\24 \")\ + elseif l[t].action == \"Download\" then\ + term.setTextColor(colors.lime)\ + term.write(\"\\25 \")\ + elseif l[t].action == \"Deleted\" then\ + term.setTextColor(colors.red)\ + term.write(\"× \")\ + elseif l[t].action == \"Conflict\" then\ + term.setTextColor(colors.orange)\ + term.write(\"! \")\ + end\ + term.setTextColor(colors.white)\ + term.write(fs.getName(l[t].destination))\ + y = y+1\ + local dif = os.epoch(\"utc\")-l[t].timestamp\ + dif = math.floor(dif/1000)\ + term.setTextColor(colors.lightGray)\ + term.setCursorPos(1,y)\ + if dif <= 0 then\ + term.write(\"Just now\")\ + elseif dif == 1 then\ + term.write(\"1 second ago\")\ + elseif dif < 60 then\ + term.write(dif..\" seconds ago\")\ + elseif dif < 3600 then\ + term.write(math.floor(dif/60)..\" minute(s) ago\")\ + elseif dif < 86400 then\ + term.write(math.floor(dif/3600)..\" hour(s) ago\")\ + else\ + term.write(math.floor(dif/86400)..\" day(s) ago\")\ + end\ + y = y+1\ + term.setCursorPos(1,y)\ + term.setTextColor(colors.lightGray)\ + term.write(\"In \")\ + term.setTextColor(colors.cyan)\ + local loc = fs.getDir(l[t].destination)\ + if loc == \"\" then loc = \"Cloud\" end\ + if #loc+4 > w then\ + loc = \"..\"..loc:sub(#loc-(w-6))\ + end\ + term.write(loc)\ + term.setTextColor(colors.white)\ + y = y+2\ + end\ + lasty = y\ + end\ + e = {os.pullEvent()}\ + if lOS.cloudLog and e[1] == \"mouse_scroll\" and e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then\ + if scroll+e[2] >= 0 then\ + scroll = scroll+e[2]\ + end\ + elseif e[1] == \"mouse_click\" and objs.conflict and lUtils.isInside(e[3],e[4],self) and lUtils.isInside(e[3],e[4],objs.conflict) then\ + local objs = {}\ + local stop = false\ + local function render()\ + local y = 1\ + objs = {}\ + term.setBackgroundColor(colors.gray)\ + term.clear()\ + term.setTextColor(colors.white)\ + term.setCursorPos(1,y)\ + term.write(\"< Sync issues\")\ + table.insert(objs,{x1=1,y1=y,x2=2,y2=y,func=function() stop = true end})\ + y = y+2\ + for path,v in pairs(lOS.cloud.conflicts) do\ + term.setBackgroundColor(colors.gray)\ + lOS.explorer.drawIcon(path,-1,y-1,true,true)\ + term.setTextColor(colors.white)\ + term.setCursorPos(5,y)\ + term.write(fs.getName(path))\ + y = y+1\ + --[[term.setCursorPos(5,y)\ + term.setTextColor(colors.lightGray)\ + term.write(\"In \")\ + term.setTextColor(colors.cyan)\ + local loc = fs.getDir(path)\ + if loc == \"\" then loc = \"Cloud\" end\ + if #loc+8 > w then\ + loc = \"..\"..loc:sub(#loc-(w-10)):gsub(\"^User/Cloud/\",\"\")\ + end\ + term.write(loc)]]\ + local btxt = \"Resolve\"\ + term.setTextColor(colors.cyan)\ + lUtils.border(5,y,5+#btxt+1,y+2,nil,1)\ + table.insert(objs,{x1=5,y1=y+1,x2=5+#btxt+1,y2=y+1,func=\ + function()\ + local tW,tH = lOS.wAll.getSize()\ + if tW <= 51 or tH <= 19 then\ + LevelOS.setWin(\"fullscreen\")\ + else\ + LevelOS.setWin(math.floor(tW/2-51/2)+1,math.floor(tH/2-19/2)+1,51,19,\"borderless\")\ + LevelOS.self.window.resizable = false\ + end\ + sl.resolvepath=path\ + shapescape.setSlide(2)\ + stop=true\ + end\ + })\ + term.setCursorPos(6,y+1)\ + term.setBackgroundColor(colors.cyan)\ + term.setTextColor(colors.white)\ + term.write(btxt)\ + y = y+3\ + end\ + end\ + render()\ + while not stop do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" then\ + for o=1,#objs do\ + if lUtils.isInside(e[3],e[4],objs[o]) then\ + objs[o].func()\ + end\ + end\ + end\ + end\ + doRender = true\ + end\ +end", + name = "log.lua", + }, + [ "resolveaction.lua" ] = { + id = 12, + content = "local sl = shapescape.getSlides()\ +if self.isSel then\ + self.border.color = colors.white\ + self.isSel = false\ + local mdpth = \"AppData/LevelCloud/\"..lOS.username\ + local cpath = sl.resolvepath:gsub(\"^User/Cloud\",\"\")\ + local mdfile = fs.combine(mdpth,cpath)\ + if self.txt:find(\"local\") then\ + -- delete online file\ + -- how tf u even do that bro\ + local h = http.post(\"https://os.leveloper.cc/cDelete.php\",\"path=\"..textutils.urlEncode(cpath),{Cookie=lOS.userID})\ + if h then\ + fs.delete(mdfile)\ + lOS.cloudTimer = 30\ + lOS.cloud.conflicts[sl.resolvepath] = nil\ + end\ + elseif self.txt:find(\"online\") then\ + -- delete local file\ + fs.delete(mdfile)\ + fs.delete(sl.resolvepath)\ + lOS.cloudTimer = 30\ + lOS.cloud.conflicts[sl.resolvepath] = nil\ + elseif self.txt:find(\"both\") then\ + local name = fs.getName(sl.resolvepath)\ + local pth = fs.getDir(sl.resolvepath)\ + while fs.exists(fs.combine(pth,name)) do\ + local temp,con = lUtils.inputbox(\"LevelCloud\",\"Please enter a new filename for \"..fs.getName(sl.resolvepath),25,11,{\"OK\"})\ + if not con then\ + return\ + end\ + if temp then\ + name = temp\ + end\ + end\ + local npth = fs.combine(pth,name)\ + fs.delete(mdfile)\ + fs.move(sl.resolvepath,npth)\ + lOS.cloud.conflicts[sl.resolvepath] = nil\ + end\ + sl.resolvepath = nil\ + local w,h = lOS.wAll.getSize()\ + if lOS.tbSize then\ + h = h-lOS.tbSize\ + end\ + local th = 14\ + if h > 32 then\ + th = 25\ + end\ + self.render()\ + LevelOS.self.window.win.reposition(w-30,h-(th-1),25,th)\ + LevelOS.setWin(25,th,\"borderless\")\ + LevelOS.setTitle(\"Cloud\")\ + shapescape.setSlide(1)\ +end", + name = "resolveaction.lua", + }, + [ "resync.lua" ] = { + id = 5, + content = "if not lOS.cloudSync then\ + --shell.run(\"pastebin run 4DaaHGRP\")\ + lOS.cloudTimer = 30\ +end", + name = "resync.lua", + }, + [ "coroclick.lua" ] = { + id = 2, + content = "while true do\ + e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" or e[1] == \"mouse_up\" then\ + if e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then\ + if e[1] == \"mouse_click\" then\ + self.oldcolor = self.txtcolor\ + self.txtcolor = colors.lightBlue\ + else\ + -- stupid bitch\ + --self.color = self.oldcolor or self.color\ + end\ + end\ + if e[1] == \"mouse_up\" and self.oldcolor then\ + self.txtcolor = self.oldcolor\ + self.oldcolor = nil\ + end\ + end\ +end", + name = "coroclick.lua", + }, + [ "close_click.lua" ] = { + id = 9, + content = "local function dClose()\ +while true do\ + local e = {os.pullEvent()}\ + --local x1,y1 = LevelOS.self.window.win.getPosition()\ + --local w,h = LevelOS.self.window.win.getSize()\ + --local x2,y2 = x1+(w-1),y1+(h-1)\ + if e[1] == \"mouse_up\" and e[3] == self.x1 and e[4] == self.y1 then\ + shapescape.exit()\ + --elseif e[1] == \"mouse_click\" and (e[3] < x1 or e[4] < y1 or e[3] > x2 or e[4] > y2) then\ + --elseif lOS.wins[#lOS.wins] ~= LevelOS.self.window then\ + --shapescape.exit()\ + end\ +end\ +end\ +local ok,err = pcall(dClose)\ +self.txt = err", + name = "close_click.lua", + }, + [ "closebutton.lua" ] = { + id = 11, + content = "local isSel = false\ +local sl = shapescape.getSlides()\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) and not isSel then\ + self.otxtcolor = self.txtcolor\ + self.txtcolor = colors.lightGray\ + isSel = true\ + elseif e[1] == \"mouse_up\" and isSel then\ + isSel = false\ + self.txtcolor = self.otxtcolor\ + self.otxtcolor = nil\ + if lUtils.isInside(e[3],e[4],self) then\ + sl.resolvepath = nil\ + local w,h = lOS.wAll.getSize()\ + if lOS.tbSize then\ + h = h-lOS.tbSize\ + end\ + local th = 14\ + if h > 32 then\ + th = 25\ + end\ + LevelOS.self.window.win.reposition(w-30,h-(th-1),25,th)\ + LevelOS.setWin(25,th,\"borderless\")\ + LevelOS.setTitle(\"Cloud\")\ + shapescape.setSlide(1)\ + end\ + end\ +end", + name = "closebutton.lua", + }, + [ "log_out.lua" ] = { + id = 4, + content = "if not lOS.cloudSync then\ + lUtils.logout()\ + lOS.cloudTimer = 30\ +end", + name = "log_out.lua", + }, + colorsync = { + id = 6, + content = "if not lOS.userID then\ + self.color = colors.red\ + self.border.color = colors.black\ +elseif lOS.cloudSync then\ + self.color = colors.orange\ + self.border.color = colors.red\ +elseif self.color == colors.orange then\ + self.color = colors.blue\ + self.border.color = colors.cyan\ +end", + name = "colorsync", + }, + [ "resolve.lua" ] = { + id = 10, + content = "local function func()\ +local sl = shapescape.getSlides()\ +self.isSel = false\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + self.border.color = colors.cyan\ + self.isSel = true\ + elseif e[1] == \"mouse_up\" and self.isSel then\ + self.border.color = colors.white\ + if lUtils.isInside(e[3],e[4],self) then\ + -- nothing anymore\ + end\ + end\ +end\ +end\ +local ok,err = pcall(func)\ +if not ok then\ + _G.clickerror = err\ +end", + name = "resolve.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + color = 0, + y2 = 17, + y1 = 17, + x1 = 27, + txt = "helo am veri cool", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + x2 = 44, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 51, + y2 = 19, + y1 = 1, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 0, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 128, + border = { + color = 256, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + x2 = 50, + y2 = 16, + border = { + color = 0, + type = 1, + }, + x1 = 2, + type = "window", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 3, + }, + }, + ox2 = 1, + color = 32768, + y1 = 4, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 3, + }, + { + x2 = 51, + y2 = 3, + border = { + color = 512, + type = 1, + }, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 2048, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 1, + }, + { + x2 = 32, + y2 = 2, + x1 = 21, + ox1 = 5, + txt = "LevelCloud", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 8, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + ox2 = -6, + border = { + color = 0, + type = 1, + }, + input = false, + color = 2048, + snap = { + Top = "Snap top", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap top", + }, + y1 = 2, + }, + { + color = 128, + y2 = 19, + border = { + color = 256, + type = 1, + }, + x1 = 1, + oy1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + y1 = 17, + x2 = 51, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + x2 = 13, + y2 = 18, + x1 = 3, + oy1 = 1, + txt = "Open folder", + type = "text", + oy2 = 1, + txtcolor = 512, + border = { + color = 0, + type = 1, + }, + event = { + mouse_up = { + [ 2 ] = 1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + }, + input = false, + color = 128, + snap = { + Top = "Snap bottom", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 18, + }, + { + color = 128, + y2 = 18, + x1 = 43, + ox1 = 8, + oy1 = 1, + txt = "Log out", + type = "text", + event = { + mouse_up = { + [ 2 ] = 4, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + }, + ox2 = 2, + txtcolor = 512, + border = { + color = 0, + type = 1, + }, + x2 = 49, + input = false, + y1 = 18, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 1, + }, + { + x2 = 49, + y2 = 2, + x1 = 49, + ox1 = 2, + txt = "", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 7, + }, + }, + txtcolor = 1, + ox2 = 2, + y1 = 2, + input = false, + color = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 3, + y2 = 2, + y1 = 2, + x1 = 3, + txt = "×", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + selected = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 9, + }, + }, + txtcolor = 1, + input = false, + color = 0, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + }, + c = 1, + }, + { + y = 21, + x = 66, + h = 19, + w = 51, + objs = { + { + x2 = 30, + y2 = 2, + y1 = 2, + x1 = 6, + txt = "Resolve the file conflict", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 2048, + input = false, + color = 0, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 50, + y2 = 4, + y1 = 3, + x1 = 6, + txt = "The online version and the local version have changes that couldn't be merged.", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 50, + y2 = 8, + y1 = 5, + x1 = 2, + txt = " Keep local file", + type = "text", + event = { + mouse_up = { + [ 2 ] = 12, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 10, + }, + }, + txtcolor = 2048, + input = false, + color = 0, + border = { + color = 1, + type = 1, + }, + }, + { + x2 = 49, + y2 = 7, + y1 = 7, + x1 = 5, + txt = "Overwrite the online file with the local one", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + color = 0, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 12, + y1 = 9, + x1 = 2, + txt = " Keep online file", + type = "text", + event = { + mouse_up = { + [ 2 ] = 12, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 10, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 2048, + input = false, + x2 = 50, + border = { + color = 1, + type = 1, + }, + }, + { + color = 0, + y2 = 11, + y1 = 11, + x1 = 5, + txt = "Overwrite the local file with the online one", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + x2 = 49, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 50, + y2 = 16, + border = { + color = 1, + type = 1, + }, + x1 = 2, + txt = " Keep both files", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = 12, + }, + Coroutine = { + [ 2 ] = 10, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 2048, + input = false, + color = 0, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 13, + }, + { + color = 0, + y2 = 15, + y1 = 15, + x1 = 5, + txt = "Rename the local file", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 32768, + input = false, + x2 = 49, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 51, + y2 = 19, + y1 = 17, + x1 = 1, + oy1 = 2, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + border = { + color = 0, + type = 1, + }, + color = 256, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + x2 = 50, + y2 = 18, + x1 = 44, + ox1 = 7, + oy1 = 1, + txt = " Close", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 11, + }, + }, + ox2 = 1, + y1 = 18, + border = { + color = 0, + type = 1, + }, + color = 1, + input = false, + txtcolor = 32768, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 1, + }, + { + color = 0, + y2 = 17, + y1 = 1, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + border = { + color = 256, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 2, + }, + { + x2 = 51, + y2 = 1, + border = { + color = 0, + type = 1, + }, + x1 = 1, + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 128, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 1, + }, + { + x2 = 11, + y2 = 1, + y1 = 1, + x1 = 2, + txt = "LevelCloud", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + input = false, + color = 0, + border = { + color = 0, + type = 1, + }, + }, + { + color = 0, + y2 = 1, + x1 = 49, + ox1 = 2, + txt = " ×", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 11, + }, + }, + txtcolor = 1, + ox2 = 0, + y1 = 1, + input = false, + x2 = 51, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + type = "rect", + x2 = 4, + y2 = 4, + y1 = 2, + x1 = 2, + image = { + { + "Ÿ€", + " b ", + "b ", + }, + { + "ƒ", + "bb9", + " 9 ", + }, + { + "€€€", + " ", + " ", + }, + }, + border = { + color = 0, + type = 1, + }, + color = 0, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + }, + }, + c = 2, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end +end + + local tArgs = {...} +if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} +end + + +return lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/Program_Files/LevelOS/Cloud/taskbar.limg b/Program_Files/LevelOS/Cloud/taskbar.limg new file mode 100644 index 0000000..9e2eec6 --- /dev/null +++ b/Program_Files/LevelOS/Cloud/taskbar.limg @@ -0,0 +1 @@ +{{{"‡‹"," 0","00 ",},{"ƒ‡","000"," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Explorer/icon.limg b/Program_Files/LevelOS/Explorer/icon.limg new file mode 100644 index 0000000..64f6287 --- /dev/null +++ b/Program_Files/LevelOS/Explorer/icon.limg @@ -0,0 +1 @@ +{{{"ƒ€€","1 ","444",},{"—Œ”","499","944",},{"€€€"," "," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Explorer/main.lua b/Program_Files/LevelOS/Explorer/main.lua new file mode 100644 index 0000000..96fc85f --- /dev/null +++ b/Program_Files/LevelOS/Explorer/main.lua @@ -0,0 +1,3685 @@ +local assets = { + [ "bgwindow.lua" ] = { + id = 8, + content = "local sl = shapescape.getSlides()\ +local win = sl[1].win\ +\ +local function draw()\ + local w,h = term.getSize()\ + for y=1,h do\ + term.setCursorPos(1,y)\ + term.blit(win.getLine(y))\ + end\ +end\ +\ +draw()\ +\ +while true do\ + local e = {os.pullEvent()}\ + draw()\ +end", + name = "bgwindow.lua", + }, + [ "cancel.lua" ] = { + id = 16, + content = "shapescape.exit()", + name = "cancel.lua", + }, + [ "viewlist.lua" ] = { + id = 21, + content = "local e = {shapescape.getEvent()}\ +local sl = shapescape.getSlides()\ +local scolor = colors.lightGray\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + self.ocolor = self.txtcolor\ + self.txtcolor = scolor\ +elseif e[1] == \"mouse_up\" then\ + if self.txtcolor == scolor and lUtils.isInside(e[3],e[4],self) then\ + sl.rMode = 1\ + sl.config.modes[sl.folder] = nil\ + sl.saveConfig()\ + os.queueEvent(\"explorer_reload\")\ + end\ + if self.ocolor then\ + self.txtcolor = self.ocolor\ + self.ocolor = nil\ + end\ +end\ +if sl.rMode == 1 then\ + self.color = colors.lightGray\ +else\ + self.color = 0\ +end", + name = "viewlist.lua", + }, + [ "sidedragthingy.lua" ] = { + id = 4, + content = "local drag = false\ +local s = shapescape.getSlide()\ +local sl = shapescape.getSlides()\ +while not sl.config do os.pullEvent() end\ +s.sidebar.x2 = sl.config.sidebar.width\ +self.x1 = s.sidebar.x2-1\ +self.x2 = self.x1+2\ +s.main.x1 = self.x2+1\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + drag = true\ + elseif e[1] == \"mouse_drag\" and drag then\ + self.x1,self.x2 = e[3]-2,e[3]\ + s.sidebar.x2 = self.x2-1\ + s.main.x1 = self.x2+1\ + elseif e[1] == \"mouse_up\" and drag then\ + sl.config.sidebar.width = s.sidebar.x2\ + sl.saveConfig()\ + drag = false\ + end\ +end", + name = "sidedragthingy.lua", + }, + [ "bottombar.lua" ] = { + id = 11, + content = "local sl = shapescape.getSlides()\ +local str = {}\ +if sl.files then\ + if #sl.files == 1 then\ + table.insert(str,\"1 item | \")\ + else\ + table.insert(str,#sl.files)\ + table.insert(str,\" items | \")\ + end\ +end\ +if sl.selected then\ + local size = 0\ + local count = 0\ + for k,v in pairs(sl.selected) do\ + if size and not v.isDir then\ + size = size+v.size\ + else\ + size = nil\ + end\ + count = count+1\ + end\ + if count > 0 then\ + if count == 1 then\ + table.insert(str,\"1 item selected\")\ + else\ + table.insert(str,count)\ + table.insert(str,\" items selected\")\ + end\ + if size then\ + table.insert(str,string.format(\" %.1f kB\",math.floor(size/100+0.5)/10))\ + end\ + table.insert(str,\" | \")\ + end\ +end\ +self.txt = table.concat(str)", + name = "bottombar.lua", + }, + [ "hPrevious.lua" ] = { + id = 5, + content = "local sl = shapescape.getSlides()\ +local e = {shapescape.getEvent()}\ +if not sl.history then return end\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) and #sl.history > 1 then\ + self.txtcolor = colors.blue\ +elseif e[1] == \"mouse_up\" and #sl.history > 1 then\ + self.txtcolor = colors.white\ + if lUtils.isInside(e[3],e[4],self) then\ + sl.folder = sl.history[#sl.history-1]\ + table.insert(sl.rhistory,sl.history[#sl.history])\ + sl.history[#sl.history] = nil\ + sl.ofolder = sl.folder\ + end\ +elseif e[1] == \"mouse_move\" and #sl.history > 1 then\ + if e[3] and e[4] and lUtils.isInside(e[3],e[4],self) then\ + self.txtcolor = colors.lightBlue\ + else\ + self.txtcolor = colors.white\ + end\ +end\ +if self.txtcolor ~= colors.blue and self.txtcolor ~= colors.lightBlue then\ + if #sl.history <= 1 then\ + self.txtcolor = colors.gray\ + else\ + self.txtcolor = colors.white\ + end\ +end", + name = "hPrevious.lua", + }, + [ "searchrender.lua" ] = { + id = 3, + content = "local x,y = term.getCursorPos()\ +term.setBackgroundColor(colors.black)\ +term.setTextColor(colors.gray)\ +lUtils.border(self.x1-1,self.y1-1,self.x2+1,self.y2+1,nil,2)\ +if self.txt == \"\" then\ + term.setCursorPos(self.x1,self.y1)\ + term.setTextColor(colors.gray)\ + term.write(\"Search...\")\ +end\ +term.setCursorPos(x,y)\ +term.setTextColor(colors.white)", + name = "searchrender.lua", + }, + [ "hList.lua" ] = { + id = 12, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +local e = {shapescape.getEvent()}\ +if not sl.history then return end\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) and (#sl.history > 1 or #sl.rhistory > 0) then\ + self.txtcolor = colors.blue\ +elseif e[1] == \"mouse_up\" and (#sl.history > 1 or #sl.rhistory > 0) then\ + self.txtcolor = colors.white\ + if lUtils.isInside(e[3],e[4],self) then\ + --[[sl.folder = sl.history[#sl.history-1]\ + table.insert(sl.rhistory,sl.history[#sl.history])\ + sl.history[#sl.history] = nil\ + sl.ofolder = sl.folder]]\ + local opt = {}\ + local function jump(obj)\ + local to = obj.id\ + sl.rhistory = {}\ + sl.history = {}\ + for t=1,#opt do\ + if t < to then\ + table.insert(sl.rhistory,opt[t].file)\ + elseif t >= to then\ + table.insert(sl.history,1,opt[t].file)\ + end\ + end\ + sl.folder = obj.file\ + sl.ofolder = obj.file\ + end\ + for i=1,#sl.rhistory do\ + table.insert(opt,{txt=\" \"..sl.getName(sl.rhistory[i]),file=sl.rhistory[i],action=jump})\ + end\ + table.insert(opt,{txt=\"\\7 \"..sl.getName(sl.history[#sl.history]),file=sl.history[#sl.history],action=jump})\ + for i=#sl.history-1,1,-1 do\ + table.insert(opt,{txt=\" \"..sl.getName(sl.history[i]),file=sl.history[i],action=jump})\ + end\ + for i=1,#opt do\ + opt[i].id = i\ + end\ + lOS.contextmenu(2,self.y2+2,0,opt,{fg=colors.white,bg=colors.gray,txt=colors.white,divider=colors.gray,selected=colors.lightGray},true)\ + end\ +elseif e[1] == \"mouse_move\" and (#sl.history > 1 or #sl.rhistory > 0) then\ + if e[3] and e[4] and lUtils.isInside(e[3],e[4],self) then\ + self.txtcolor = colors.lightBlue\ + else\ + self.txtcolor = colors.white\ + end\ +end\ +if self.txtcolor ~= colors.blue and self.txtcolor ~= colors.lightBlue then\ + if #sl.history <= 1 and #sl.rhistory <= 0 then\ + self.txtcolor = colors.gray\ + else\ + self.txtcolor = colors.white\ + end\ +end", + name = "hList.lua", + }, + [ "owtxt.lua" ] = { + id = 10, + content = "local sl = shapescape.getSlides()\ +local file = sl.OWfile\ +if file and file.type then\ + self.txt = \"Always use this app for .\"..file.type..\" files\"\ +end", + name = "owtxt.lua", + }, + [ "viewicons.lua" ] = { + id = 22, + content = "local e = {shapescape.getEvent()}\ +local sl = shapescape.getSlides()\ +local scolor = colors.lightGray\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + self.ocolor = self.txtcolor\ + self.txtcolor = scolor\ +elseif e[1] == \"mouse_up\" then\ + if self.txtcolor == scolor and lUtils.isInside(e[3],e[4],self) then\ + sl.rMode = 2\ + sl.config.modes[sl.folder] = 2\ + sl.saveConfig()\ + os.queueEvent(\"explorer_reload\")\ + end\ + if self.ocolor then\ + self.txtcolor = self.ocolor\ + self.ocolor = nil\ + end\ +end\ +if sl.rMode == 2 then\ + self.color = colors.lightGray\ +else\ + self.color = 0\ +end", + name = "viewicons.lua", + }, + [ "select.lua" ] = { + id = 15, + content = "local sl = shapescape.getSlides()\ +sl.fpathbox.state = true\ +os.queueEvent(\"key\",keys.enter)", + name = "select.lua", + }, + [ "dropdown.lua" ] = { + id = 13, + content = "self.txt = \"\"\ +local sl = shapescape.getSlides()\ +if sl.dropdown then\ + lOS.contextmenu(unpack(sl.dropdown))\ + sl.dropdown = nil\ +end", + name = "dropdown.lua", + }, + [ "owok.lua" ] = { + id = 19, + content = "local s = shapescape.getSlide()\ +local sl = shapescape.getSlides()\ +local e = {shapescape.getEvent()}\ +if s.selected then\ + if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + self.isSel = true\ + elseif e[1] == \"mouse_up\" then\ + if lUtils.isInside(e[3],e[4],self) and self.isSel then\ + self.isSel = false\ + -- run the thang\ + sl.OWprogram = s.selected\ + fTypes = lUtils.asset.load(\"LevelOS/data/formats.lconf\")\ + local t = sl.OWfile.type\ + local p = sl.OWprogram.path\ + if s.checkbox.isOn then\ + local deskIcons = lUtils.asset.load(\"LevelOS/assets/Desktop_Icons.limg\")\ + if not fTypes[t] then\ + fTypes[t] = {name=t:upper()..\"-File\",program=p,contextMenu={{\"Open\"},{p}},openWith={p},icon=deskIcons[4]}\ + else\ + fTypes[t].program = p\ + if fTypes[t].openWith and not sl.locateEntry(fTypes[t].openWith,p) then\ + table.insert(fTypes[t].openWith,1,p)\ + end\ + end\ + lUtils.asset.save(fTypes,\"LevelOS/data/formats.lconf\",false)\ + sl.fullReload = true\ + else\ + if fTypes[t] and fTypes[t].openWith and not sl.locateEntry(fTypes[t].openWith,p) then\ + table.insert(fTypes[t].openWith,p)\ + lUtils.asset.save(fTypes,\"LevelOS/data/formats.lconf\",false)\ + sl.fullReload = true\ + end\ + end\ + s.reset = true\ + sl.OWfile = nil\ + shapescape.setSlide(1)\ + end\ + self.isSel = false\ + end\ + if self.isSel then\ + self.color = colors.black\ + else\ + self.color = colors.gray\ + end\ + self.txtcolor = colors.white\ +else\ + self.color = colors.lightGray\ + self.txtcolor = colors.gray\ +end", + name = "owok.lua", + }, + [ "folderUp.lua" ] = { + id = 7, + content = "local sl = shapescape.getSlides()\ +local e = {shapescape.getEvent()}\ +if not sl.folder then return end\ +local cPath = sl.split_path(sl.folder)\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) and #cPath > 0 then\ + self.txtcolor = colors.blue\ +elseif e[1] == \"mouse_up\" and #cPath > 0 then\ + self.txtcolor = colors.white\ + if lUtils.isInside(e[3],e[4],self) then\ + sl.folder = fs.getDir(sl.folder)\ + end\ +elseif e[1] == \"mouse_move\" and #cPath > 0 then\ + if e[3] and e[4] and lUtils.isInside(e[3],e[4],self) then\ + self.txtcolor = colors.lightBlue\ + else\ + self.txtcolor = colors.white\ + end\ +end\ +if self.txtcolor ~= colors.blue and self.txtcolor ~= colors.lightBlue then\ + if #cPath <= 0 then\ + self.txtcolor = colors.gray\ + else\ + self.txtcolor = colors.white\ + end\ +end", + name = "folderUp.lua", + }, + [ "filepathbox.lua" ] = { + id = 14, + content = "local sl = shapescape.getSlides()\ +sl.fpathbox = self\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"key\" and e[2] == keys.enter and self.state then\ + local rtbl = {}\ + if self.changed then\ + if not self.txt:find('\"') then\ + table.insert(rtbl,fs.combine(sl.folder,self.txt))\ + else\ + for pth in str:gmatch('\"([^\"]*)\"') do\ + table.insert(rtbl,fs.combine(sl.folder,pth))\ + end\ + end\ + else\ + for k,v in pairs(sl.ret) do\ + table.insert(rtbl,(k))\ + end\ + end\ + shapescape.exit(rtbl)\ + end\ +end", + name = "filepathbox.lua", + }, + [ "owlist.lua" ] = { + id = 17, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +local programs = {}\ +local porder = {}\ +local dIcons = lUtils.asset.load(\"LevelOS/assets/Desktop_Icons.limg\")\ +local function findPrograms(pth)\ + local list = fs.list(pth)\ + local foundProgram = false\ + local folders = {}\ + if pth ~= \"Program_Files\" and fs.exists(fs.combine(pth,\"main.lua\")) then\ + foundProgram = true\ + else\ + for f=1,#list do\ + local pr = fs.combine(pth,list[f])\ + if fs.isDir(pr) then\ + table.insert(folders,pr)\ + elseif lUtils.getFileType(pr) == \".lua\" or lUtils.getFileType(pr) == \"\" then\ + if pth ~= \"Program_Files\" then\ + foundProgram = true\ + break\ + else\ + local p = {path=pr,name=sl.getName(pr)}\ + local ext = lUtils.getFileType(pr)\ + p.name = p.name:sub(1,#p.name-#ext)\ + p.icon = dIcons[3]\ + programs[p.path] = p\ + end\ + end\ + end\ + end\ + if foundProgram then\ + local p = {path=pth,name=sl.getName(pth)}\ + if fs.exists(fs.combine(pth,\"icon.limg\")) then\ + p.icon = lUtils.asset.load(fs.combine(pth,\"icon.limg\"))[1]\ + end\ + programs[p.path] = p\ + else\ + for t=1,#folders do\ + findPrograms(folders[t])\ + end\ + end\ +end\ +local scrollY\ +local mSelect\ +local selected\ +local function generate()\ + programs = {} -- add lua option with lua icon and stuff\ + programs[\"Lua\"] = {name=\"Lua\",path=\"Lua\"}\ + programs[\"rom/programs/edit.lua\"] = {name=\"Edit\",path=\"rom/programs/edit.lua\"} -- add icon here too wait i dont even have one so make one ig\ + local p = {name=\"Paint\",path=\"rom/programs/fun/advanced/paint.lua\"}\ + programs[p.path] = p\ + programs[\"LevelOS/notepad.lua\"] = {name=\"Notepad\",path=\"LevelOS/notepad.lua\",icon={{\"\\159\\159 \",\" \",\"77 \"},{\"\\157\\141\\149\",\"888\",\"77 \"},{\"\\136\\140\\149\",\"778\",\"88 \"}}}\ + findPrograms(\"Program_Files\")\ + porder = {}\ + for k,v in pairs(programs) do\ + table.insert(porder,k)\ + end\ + table.sort(porder,function(a,b) return programs[a].name:lower() < programs[b].name:lower() end)\ + scrollY = 0\ + mSelect = nil\ + selected = nil\ +end\ +local function render()\ + -- idk\ + term.setBackgroundColor(colors.white)\ + term.clear()\ + local y = 1-scrollY\ + local w,h = term.getSize()\ + for k,v in ipairs(porder) do\ + local p = programs[v]\ + if selected == p then\ + term.setBackgroundColor(colors.gray)\ + term.setTextColor(colors.white)\ + elseif mSelect == p then\ + term.setBackgroundColor(colors.lightGray)\ + term.setTextColor(colors.black)\ + else\ + term.setBackgroundColor(colors.white)\ + term.setTextColor(colors.black)\ + end\ + lOS.boxClear(1,y,w,y+2)\ + if p.icon then\ + lUtils.renderImg(p.icon,2,y)\ + else\ + lUtils.renderImg(dIcons[3],2,y)\ + end\ + term.setCursorPos(6,y+1)\ + term.write(p.name)\ + y = y+3\ + end\ +end\ +generate()\ +render()\ +while true do\ + local e = {os.pullEvent()}\ + if s.reset and sl.OWfile then\ + s.reset = false\ + generate()\ + end\ + local w,h = term.getSize()\ + if e[1] == \"mouse_move\" or e[1] == \"mouse_click\" then\ + mSelect = nil\ + end\ + if e[1]:find(\"mouse\") and e[3] and e[4] and lUtils.isInside(e[3],e[4],{x1=1,y1=1,x2=w,y2=h}) then\ + if e[1] == \"mouse_up\" then\ + selected = nil\ + end\ + if e[1] == \"mouse_scroll\" then\ + -- stuff\ + if (e[2] == -1 and scrollY > 0) or (e[2] == 1 and scrollY < (#porder*3-h)) then\ + scrollY = scrollY+e[2]\ + end\ + render()\ + elseif e[1] == \"mouse_click\" or e[1] == \"mouse_move\" or e[1] == \"mouse_up\" then\ + local y = math.ceil((e[4]+scrollY)/3)\ + if porder[y] then\ + local p = programs[porder[y]]\ + if e[1] == \"mouse_up\" and mSelect == p then\ + selected = p\ + elseif e[1] == \"mouse_click\" or e[1] == \"mouse_move\" then\ + mSelect = p\ + end\ + render()\ + end\ + end\ + end\ + if e[1] == \"mouse_up\" then\ + mSelect = nil\ + end\ + s.selected = selected\ +end", + name = "owlist.lua", + }, + [ "owcheckbox.lua" ] = { + id = 18, + content = "local e = {shapescape.getEvent()}\ +local s = shapescape.getSlide()\ +s.checkbox = self\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) then\ + self.isSel = true\ +elseif e[1] == \"mouse_up\" then\ + if lUtils.isInside(e[3],e[4],self) and self.isSel then\ + self.isOn = not self.isOn\ + end\ + self.isSel = false\ +end\ +if self.isSel then\ + self.color = colors.black\ +elseif self.isOn then\ + self.color = colors.green\ + self.border.color = colors.gray\ +else\ + self.color = colors.white\ + self.border.color = colors.lightGray\ +end", + name = "owcheckbox.lua", + }, + [ "openwithwhite.lua" ] = { + id = 9, + content = "local clickoutside = false\ +local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +while true do\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_click\" then\ + if lUtils.isInside(e[3],e[4],self) then\ + clickoutside = false\ + else\ + clickoutside = true\ + end\ + elseif e[1] == \"mouse_up\" then\ + if clickoutside and not lUtils.isInside(e[3],e[4],self) then\ + clickoutside = false\ + s.reset = true\ + sl.OWfile = nil\ + shapescape.setSlide(1)\ + end\ + end\ +end", + name = "openwithwhite.lua", + }, + [ "hNext.lua" ] = { + id = 6, + content = "local sl = shapescape.getSlides()\ +local e = {shapescape.getEvent()}\ +if not sl.history then return end\ +if e[1] == \"mouse_click\" and lUtils.isInside(e[3],e[4],self) and #sl.rhistory > 0 then\ + self.txtcolor = colors.blue\ +elseif e[1] == \"mouse_up\" and #sl.rhistory > 0 then\ + self.txtcolor = colors.white\ + if lUtils.isInside(e[3],e[4],self) then\ + sl.folder = sl.rhistory[#sl.rhistory]\ + sl.rhistory[#sl.rhistory] = nil\ + sl.ofolder = sl.folder\ + table.insert(sl.history,sl.folder)\ + end\ +elseif e[1] == \"mouse_move\" and #sl.rhistory > 0 then\ + if e[3] and e[4] and lUtils.isInside(e[3],e[4],self) then\ + self.txtcolor = colors.lightBlue\ + else\ + self.txtcolor = colors.white\ + end\ +end\ +if self.txtcolor ~= colors.blue and self.txtcolor ~= colors.lightBlue then\ + if #sl.rhistory <= 0 then\ + self.txtcolor = colors.gray\ + else\ + self.txtcolor = colors.white\ + end\ +end", + name = "hNext.lua", + }, + [ "tree.lua" ] = { + id = 1, + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +s.sidebar = self\ +local objs = {}\ +local tree = {\"Quick access\",\"LevelCloud\",\"This PC\",\"Local disk\",path=\"\"}\ +local function genTree(tEntry)\ + local list = fs.list(tEntry.path)\ + for i,f in ipairs(tEntry) do\ + tEntry[i] = nil\ + end\ + for f=1,#list do\ + if fs.isDir(fs.combine(tEntry.path,list[f])) then\ + tEntry[#tEntry+1] = list[f]\ + end\ + end\ +end\ +tree[\"Quick access\"] = {\ + path=\"Quick access\",\ + nopath=true,\ + open=true,\ +}\ +local function reloadQuickAccess()\ + if not sl.config.quickaccess then sl.config.quickaccess = {} return end\ + local l = sl.config.quickaccess\ + for t=#tree[\"Quick access\"],1,-1 do\ + tree[\"Quick access\"][\"Quick access/\"..tree[\"Quick access\"][t]] = nil\ + table.remove(tree[\"Quick access\"],t)\ + end\ + for t=1,#l do\ + local n = sl.getName(l[t])\ + tree[\"Quick access\"][#tree[\"Quick access\"]+1] = n\ + tree[\"Quick access\"][\"Quick access/\"..n] = {\ + path = l[t],\ + open = false,\ + }\ + end\ +end\ +reloadQuickAccess()\ +sl.reloadQuickAccess = reloadQuickAccess\ +--[[tree[\"Quick access\"][\"Quick access/LevelOS\"] = {\ + path=\"User/Cloud/Public/LevelOS\",\ + open=false,\ +}]]\ +if fs.exists(\"User/Cloud\") then\ + tree[\"LevelCloud\"] = {\ + path=\"User/Cloud\",\ + open=false,\ + auto=true,\ + }\ +end\ +tree[\"This PC\"] = {\ + path=\"User\",\ + open=true,\ + \"Desktop\",\ + \"Documents\",\ + \"Downloads\",\ + \"Games\",\ + \"Images\",\ + \"Music\",\ + \"Scripts\",\ +}\ +--[[for t=#tree[\"This PC\"]-2,1,-1 do\ + if not fs.exists(fs.combine(tree[\"This PC\"].path,tree[\"This PC\"][t])) then\ + table.remove(tree[\"This PC\"],t)\ + end\ +end]]\ +tree[\"Local disk\"] = {\ + path=\"\",\ + open=false,\ + auto=true,\ +}\ +for k=#sl.disks,1,-1 do\ + local v = sl.disks[k]\ + local dPath = v.getMountPath()\ + local dName = sl.getName(dPath)\ + tree[\"This PC\"][#tree[\"This PC\"]+1] = dName\ + tree[\"This PC\"][\"User/\"..dName] = {path=dPath,open=false,auto=true}\ + genTree(tree[\"This PC\"][\"User/\"..dName])\ +end\ +table.insert(tree,\"rom\")\ +tree[\"rom\"] = {\ + path=\"rom\",\ + open=false,\ +}\ +if tree[\"LevelCloud\"] then\ + genTree(tree[\"LevelCloud\"])\ +end\ +genTree(tree[\"Local disk\"])\ +genTree(tree[\"rom\"])\ +--local cList = fs.list(dir)\ +local x,y = 0,1\ +local symbols = {\ + [\"Quick access\"] = {symbol=\"*\",color=colors.blue},\ + [\"User/Cloud\"] = {symbol=\"\",color=colors.blue},\ + [\"\"] = {symbol=\"\",color=colors.lightGray},\ + [\"rom\"] = {symbol=\"\",color=colors.red},\ + [\"User\"] = {symbol=\"\",color=colors.lightBlue},\ + [\"User/Desktop\"] = {symbol=\"\",color=colors.lightBlue},\ + [\"User/Documents\"] = {symbol=\"\",color=colors.lightGray},\ + [\"User/Downloads\"] = {symbol=\"\",color=colors.blue},\ + [\"User/Images\"] = {symbol=\"\",color=colors.lightBlue},\ + [\"User/Scripts\"] = {symbol=\"\",color=colors.cyan},\ + [\"User/Games\"] = {symbol=\"\",color=colors.lightGray},\ + [\"User/Music\"] = {symbol=\"\",color=colors.blue},\ +}\ +sl.symbols = symbols\ +local selected1\ +local selected2\ +local scrollY = 0\ +local hadselected = false\ +local function updateTree(tree)\ + for i=#tree,1,-1 do\ + local f = tree[i]\ + local file = fs.combine(tree.path,f)\ + if not fs.exists(file) then\ + tree[file] = nil\ + end\ + tree[i] = nil\ + end\ + local ls = fs.list(tree.path)\ + for f=1,#ls do\ + local file = fs.combine(tree.path,ls[f])\ + if fs.isDir(file) then\ + tree[#tree+1] = ls[f]\ + if tree[file] and tree.auto then\ + updateTree(tree[file])\ + end\ + end\ + end\ +end\ +local function updateFullTree()\ + for k,v in ipairs(tree) do\ + if tree[v] and tree[v].auto then\ + updateTree(tree[v])\ + end\ + end\ +end\ +sl.updateFullTree = updateFullTree\ +local function renderTree(tree)\ + x = x+1\ + for i,f in ipairs(tree) do\ + local file = fs.combine(tree.path,f)\ + if not tree[file] then\ + if not fs.exists(file) then\ + --error(\"No tree[\"..file..\"]!\")\ + else\ + tree[file] = {path=file,open=false,auto=true}\ + genTree(tree[file])\ + end\ + end\ + local t = tree[file]\ + if t and (fs.exists(t.path) or t.nopath) then\ + t.x,t.y = x,y\ + objs[#objs+1] = t\ + term.setCursorPos(x,y)\ + local selected\ + if sl.folder and not hadselected then\ + selected = ((t.open and t.path == sl.folder) or (not t.open and string.find(sl.folder..\"/\",t.path..\"/\",nil,true) == 1))\ + end\ + if selected then\ + term.setBackgroundColor(colors.gray)\ + hadselected = true\ + else\ + term.setBackgroundColor(colors.black)\ + end\ + term.clearLine()\ + term.setCursorPos(x,y)\ + y = y+1\ + if t.open then\ + if selected2 == t then\ + term.setTextColor(colors.blue)\ + elseif selected then\ + term.setTextColor(colors.white)\ + else\ + term.setTextColor(colors.lightGray)\ + end\ + term.write(\" \")\ + elseif #t > 0 then\ + if selected2 == t then\ + term.setTextColor(colors.blue)\ + elseif selected then\ + term.setTextColor(colors.lightGray)\ + else\ + term.setTextColor(colors.gray)\ + end\ + term.write(\" \")\ + else\ + term.write(\" \")\ + end\ + if symbols[t.path] then\ + term.setTextColor(symbols[t.path].color)\ + term.write(symbols[t.path].symbol..\" \")\ + elseif sl.disks[t.path] then\ + term.setTextColor(colors.blue)\ + term.write(\" \")\ + else\ + term.setTextColor(colors.yellow)\ + term.write(\" \")\ + end\ + if selected1 == t then\ + term.setTextColor(colors.lightGray)\ + elseif fs.isReadOnly(t.path) then\ + term.setTextColor(colors.lightGray)\ + else\ + term.setTextColor(colors.white)\ + end\ + term.write(sl.getName(t.path))\ + if t.open then\ + renderTree(t)\ + end\ + if x == 1 then\ + y = y+1\ + end\ + end\ + end\ + x = x-1\ +end\ +local function render()\ + objs = {}\ + hadselected = false\ + term.setBackgroundColor(colors.black)\ + term.clear()\ + x,y = 0,1+scrollY\ + renderTree(tree)\ +end\ +render()\ +local ofolder\ +while true do\ + local e = {os.pullEvent()}\ + local w,h = term.getSize()\ + if e[1] == \"mouse_scroll\" and lUtils.isInside(e[3]+self.x1-1,e[4]+self.y1-1,self) then\ + if (e[2] == -1 and scrollY < 0) or (e[2] == 1 and y > h) then\ + scrollY = scrollY-e[2]\ + render()\ + end\ + elseif e[1]:find(\"mouse\") then\ + if e[1] == \"mouse_move\" or e[1] == \"mouse_click\" or e[1] == \"mouse_up\" then selected1 = nil selected2 = nil end\ + if e[3] and e[4] and lUtils.isInside(e[3]+self.x1-1,e[4]+self.y1-1,self) then\ + for k,v in pairs(objs) do\ + if e[4] == v.y then\ + if e[3] == v.x and #v > 0 then\ + if e[1] == \"mouse_move\" or e[1] == \"mouse_click\" then\ + selected2 = v\ + elseif e[1] == \"mouse_up\" then\ + v.open = not v.open\ + end\ + else\ + if e[1] == \"mouse_move\" or e[1] == \"mouse_click\" then\ + selected1 = v\ + elseif e[1] == \"mouse_up\" and not v.nopath then\ + if e[2] == 1 then\ + sl.folder = v.path\ + os.queueEvent(\"explorer_change_folder\")\ + elseif e[2] == 2 then\ + sl.cMenu(v.path,e[3],e[4])\ + end\ + end\ + end\ + break\ + end\ + end\ + end\ + render()\ + elseif ofolder ~= sl.folder then\ + ofolder = sl.folder\ + render()\ + end\ +end", + name = "tree.lua", + }, + [ "mainscreen.lua" ] = { + id = 0, + content = "if not blittle then\ + os.loadAPI(\"blittle\")\ +end\ +local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +local imgcache\ +local nimgcache\ +local shortcache\ +s.main = self\ +if not lOS.fCut then\ + lOS.fCut = {}\ +end\ +if not lOS.fClipboard then\ + lOS.fClipboard = {}\ +end\ +local tW,tH = lOS.wAll.getSize()\ +if tW > 85+12 and tH > 33+12 and LevelOS and LevelOS.self and LevelOS.self.window then\ + LevelOS.setWin(85,33)\ + os.queueEvent(\"term_resize\")\ + os.queueEvent(\"term_resize\")\ +end\ +local tArgs = {...}\ +local cSlide = sl.cSlide\ +if tArgs[2] then\ + if tArgs[2] == \"SelFile\" and cSlide ~= 3 then\ + sl.ret = {}\ + shapescape.setSlide(3)\ + elseif tArgs[2] == \"SelFolder\" and cSlide ~= 4 then\ + sl.ret = {}\ + shapescape.setSlide(4)\ + elseif tArgs[2] == \"SelSave\" and cSlide ~= 5 then\ + sl.ret = {}\ + shapescape.setSlide(5)\ + end\ +end\ +local deskIcons = lUtils.asset.load(\"LevelOS/assets/Desktop_Icons.limg\")\ +\ +local config\ +if fs.exists(\"LevelOS/data/explorer.lconf\") then\ + config = lUtils.asset.load(\"LevelOS/data/explorer.lconf\")\ +end\ +local function saveConfig()\ + lUtils.asset.save(config,\"LevelOS/data/explorer.lconf\")\ +end\ +\ +local fTypes = lUtils.asset.load(\"LevelOS/data/formats.lconf\")\ +sl.saveConfig = saveConfig\ +if not config then\ + config = {\ + _VERSION=\"1.0\",\ + tabs = {\ + {\"Name\",w=18,on=true},\ + {\"Status\",w=8,on=false},\ + {\"Type\",w=10,on=true},\ + {\"Size\",w=9,on=true},\ + {\"Date Modified\",w=16,on=true},\ + {\"Date Created\",w=16,on=false},\ + },\ + modes = {[\"User/Cloud/Images\"]=2,[\"User/Images\"]=2},\ + sidebar = {width=13}\ + }\ + saveConfig()\ +end\ +\ +sl.config = config\ +\ +if tArgs[1] and fs.exists(tArgs[1]) then\ + sl.folder = tArgs[1]\ +else\ + sl.folder = \"\"\ +end\ +\ +local function uFP(filepath2) -- unique filepath\ + if fs.exists(filepath2) == true then\ + t = 1\ + while fs.exists(string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2)))..\"_(\"..t..\")\"..lUtils.getFileType(filepath2)) == true do\ + t = t+1\ + end\ + filepath2 = string.sub(filepath2,1,string.len(filepath2)-string.len(lUtils.getFileType(filepath2)))..\"_(\"..t..\")\"..lUtils.getFileType(filepath2)\ + end\ + return filepath2\ +end\ +sl.uFP = uFP\ +\ +local disks = {peripheral.find(\"drive\",function(name,object) return object.hasData() end)}\ +for t=1,#disks do\ + disks[disks[t].getMountPath()] = disks[t]\ +end\ +sl.disks = disks\ +\ +local function getName(path)\ + local isDisk = false\ + if sl.disks[path] and sl.disks[path].getDiskLabel() then\ + str = sl.disks[path].getDiskLabel()..\" (\"..string.gsub(path,\"_\",\" \")..\")\"\ + isDisk = true\ + elseif path == \"\" then\ + str = \"Local disk\"\ + else\ + str = string.gsub(fs.getName(path),\"_\",\" \")\ + if lUtils.getFileType(str) == \".llnk\" then\ + str = str:sub(1,#str-5)\ + end\ + end\ + return str,isDisk\ +end\ +sl.getName = getName\ +\ +local files -- includes all entries from fs.attributes\ +local scrollY = 0\ +\ +function locateEntry(tTable,tEntry)\ + if type(tTable) ~= \"table\" then\ + error(\"Invalid input #1\",2)\ + end\ + for t=1,#tTable do\ + if tTable[t] == tEntry then\ + return true,t\ + end\ + end\ + return false,0\ +end\ +sl.locateEntry = locateEntry\ +\ +local function openWith(file)\ + -- open with GUI\ + --[[term.setBackgroundColor(colors.white)\ + local w,h = term.getSize()\ + lOS.boxClear(]]\ + sl.OWfile = file\ + shapescape.setSlide(2)\ + coroutine.yield()\ + return sl.OWprogram\ +end\ +sl.openWith = openWith\ +\ +local function getIcon(path)\ + if sl.symbols and sl.symbols[path] then\ + --term.setTextColor(sl.symbols[file.path].color)\ + --term.write(sl.symbols[file.path].symbol)\ + return {sl.symbols[path].symbol,lUtils.toBlit(sl.symbols[path].color)}\ + else\ + if fs.isDir(path) then\ + if sl.disks[path] then\ + return {\"\",lUtils.toBlit(colors.blue)}\ + else\ + return {\"\",lUtils.toBlit(colors.yellow)}\ + end\ + else\ + return {\"\",lUtils.toBlit(colors.white)}\ + end\ + --term.write(\"\")\ + end\ +end\ +sl.getIcon = getIcon\ +\ +local NAME = {name=\"Name\",width=18}\ +sl.NAME = NAME\ +function NAME.render(file)\ + local path = file.path\ + if shortcache[file.path] then\ + path = shortcache[file.path][1]\ + end\ + local isCut = locateEntry(lOS.fCut,file.path)\ + if not isCut then\ + if sl.symbols and sl.symbols[path] then\ + term.setTextColor(sl.symbols[path].color)\ + term.write(sl.symbols[path].symbol)\ + else\ + if fs.isDir(path) then\ + if disks[path] then\ + term.setTextColor(colors.blue)\ + term.write(\"\")\ + else\ + term.setTextColor(colors.yellow)\ + term.write(\"\")\ + end\ + else\ + term.setTextColor(colors.white)\ + term.write(\"\")\ + end\ + end\ + if fs.isReadOnly(file.path) then\ + term.setTextColor(colors.lightGray)\ + else\ + term.setTextColor(colors.white)\ + end\ + else\ + term.setTextColor(colors.lightGray)\ + term.write(\" \")\ + end\ + local str = \" \"..sl.getName(file.path)\ + if #str >= NAME.width-1 then\ + str = str:sub(1,NAME.width-4)..\"..\"\ + end\ + local x,y = term.getCursorPos()\ + term.write(str)\ + if sl.search then\ + local b,e = sl.getName(file.path):lower():find(sl.search:lower(),nil,true)\ + if b < NAME.width-2 then\ + if e >= NAME.width-2 then\ + e = NAME.width-3\ + end\ + local nx,ny = term.getCursorPos()\ + local bg = term.getBackgroundColor()\ + term.setCursorPos(x+b,y)\ + term.setBackgroundColor(colors.orange)\ + term.write(str:sub(b+1,e+1))\ + term.setBackgroundColor(bg)\ + term.setCursorPos(nx,ny)\ + end\ + end\ + term.setTextColor(colors.white)\ +end\ +\ +local TYPE = {name=\"Type\",width=10}\ +function TYPE.func(file)\ + if fs.isDir(file.path) then\ + if disks[file.path] then\ + return \"Disk\"\ + else\ + return \"Folder\"\ + end\ + elseif fTypes[file.type] then\ + return fTypes[file.type].name\ + else\ + return file.type:upper()..\"-file\"\ + end\ +end\ +\ +local SIZE = {name=\"Size\",width=9}\ +function SIZE.func(file)\ + if not fs.isDir(file.path) then\ + return string.format(\"%.1f kB\",math.floor(file.size/100+0.5)/10)\ + else\ + return \"\"\ + end\ +end\ +\ +local MODIFIED = {name=\"Date Modified\",width=16}\ +function MODIFIED.func(file)\ + if not file.modification then return \"\" end\ + local t = file.modification\ + return os.date(\"%d-%m-%y %H:%M\",t/1000)\ +end\ +\ +local CREATED = {name=\"Date Created\",width=16}\ +function CREATED.func(file)\ + if not file.created then return \"\" end\ + local t = file.created\ + return os.date(\"%d-%m-%y %H:%M\",t/1000)\ +end\ +\ +local STATUS = {name=\"Status\",width=8}\ +function STATUS.render(file)\ + -- nothing yet, cloud doesn't support this yet\ + -- IF timestamp of file is HIGHER than cloud reported timestamp, file is NOT SYNCED YET\ + -- IF timestamp of file is LOWER than cloud reported timestamp or file only exists in CLOUD TABLE, file is AVAILABLE ONLINE\ + -- IF timestamp of file is EQUAL to cloud reported timestamp, file is AVAILABLE COMPLETELY\ + if lOS.cloud.lastSync and lOS.cloud.files[file.path] then\ + if lOS.cloud.conflicts[file.path] then\ + term.setTextColor(colors.red)\ + term.write(\"×\")\ + elseif file.modification > lOS.cloud.lastSync then\ + term.setTextColor(colors.blue)\ + term.write(\"\\24\")\ + else\ + term.setTextColor(colors.lime)\ + term.write(\"\\7\")\ + end\ + end\ + term.setTextColor(colors.white)\ +end\ +local tabRef = {}\ +local aTabs = {NAME,TYPE,SIZE,MODIFIED,CREATED,STATUS}\ +for k,v in ipairs(config.tabs) do\ + for i,t in pairs(aTabs) do\ + if v[1] == t.name then\ + t.id = k\ + t.width = v.w\ + tabRef[k] = t\ + table.remove(aTabs,i)\ + break\ + end\ + end\ +end\ +aTabs = nil\ +local tabs = {NAME,TYPE,SIZE,MODIFIED}\ +sl.rMode = 1\ +local selected = {}\ +sl.selected = selected\ +local mSelected = nil\ +local renamebox\ +local function render()\ + shortcache = lOS.explorer.shortcache\ + imgcache = lOS.explorer.imgcache\ + nimgcache = lOS.explorer.nimgcache\ + local oterm = term.current()\ + if oterm ~= self.window then\ + term.redirect(self.window)\ + end\ + if not files then return end\ + local w,h = term.getSize()\ + if sl.rMode == 1 then\ + while #files+scrollY <= h-3 and scrollY < 0 do\ + scrollY = scrollY+1\ + end\ + term.setBackgroundColor(colors.black)\ + term.clear()\ + tabs[1].x = 2\ + term.setBackgroundColor(colors.black)\ + term.setTextColor(colors.lightGray)\ + for i=1,#tabs do\ + term.setCursorPos(tabs[i].x,1)\ + term.write(tabs[i].name..string.rep(\" \",tabs[i].width-#tabs[i].name-1)..\"\\149\")\ + if tabs[i+1] then\ + tabs[i+1].x = tabs[i].x+tabs[i].width\ + end\ + end\ + term.setTextColor(colors.white)\ + for i=1,math.min(#files+scrollY,h-2) do\ + local file = files[i-scrollY]\ + file.x1 = tabs[1].x\ + file.y1 = i+1+1\ + file.y2 = file.y1\ + file.x2 = tabs[#tabs].x+tabs[#tabs].width-1\ + local fc = fs.combine\ + local rPath = file.path\ + local isShort = false\ + if shortcache[file.path] then\ + isShort = true\ + rPath = shortcache[file.path][1]\ + elseif file.type == \"llnk\" then\ + local info = lUtils.asset.load(file.path)\ + if info and type(info[1]) == \"string\" then\ + isShort = true\ + rPath = info[1] -- if linked to folder, assume user wants to go to folder, if linked to main.lua it links to program (do context menu shizz)\ + imgcache[file.path] = info.icon\ + shortcache[file.path] = info\ + end\ + end\ + if selected[file.path] then\ + term.setBackgroundColor(colors.gray)\ + term.setCursorPos(file.x1,file.y1)\ + term.write(string.rep(\" \",file.x2-(file.x1-1)))\ + elseif mSelected and lUtils.isInside(mSelected[1],mSelected[2],file) then\ + term.setBackgroundColor(colors.pink)\ + term.setCursorPos(file.x1,file.y1)\ + term.write(string.rep(\" \",file.x2-(file.x1-1)))\ + end\ + for t=1,#tabs do\ + term.setCursorPos(tabs[t].x,i+1+1)\ + if tabs[t].render then\ + tabs[t].render(file)\ + elseif tabs[t].func then\ + local str = tabs[t].func(file)\ + if str and #str >= tabs[t].width then\ + str = str:sub(1,tabs[t].width-3)..\"..\"\ + end\ + term.write(tostring(str))\ + end\ + end\ + if renamebox and sl.renaming == file.path then\ + renamebox.render()\ + end\ + term.setBackgroundColor(colors.black)\ + end\ + elseif sl.rMode == 2 then\ + term.setBackgroundColor(colors.black)\ + term.clear()\ + local fx,fy = 2,1+scrollY\ + local fw,fh = 7,5\ + for i=1,#files do\ + local file = files[i]\ + if selected[file.path] then\ + term.setBackgroundColor(colors.gray)\ + elseif mSelected and file.x1 and lUtils.isInside(mSelected[1],mSelected[2],file) then\ + term.setBackgroundColor(colors.pink)\ + else\ + term.setBackgroundColor(colors.black)\ + end\ + lineH = lOS.explorer.drawIcon(file.path,fx,fy,false,false,4,{\"\\24\",\"3\",\"0\"})-4\ + fh = math.max(lineH+4,fh)\ + file.x1,file.y1 = fx,fy\ + file.x2,file.y2 = fx+7-1,fy+5-1+lineH-1\ + fx = fx+fw+1\ + if fx+fw > w then\ + fx = 2\ + fy = fy+fh\ + fh = 5\ + end\ + end\ + end\ + if oterm ~= self.window then\ + term.redirect(oterm)\ + end\ +end\ +\ +local function genFile(f)\ + local file = {path=f,name=fs.getName(f),type=lUtils.getFileType(f):sub(2),readOnly=fs.isReadOnly(f),isDir=fs.isDir(f)}\ + if not ((file.isDir and file.readOnly) or sl.disks[file.path]) then\ + local a = fs.attributes(f)\ + for k,v in pairs(a) do\ + file[k] = v\ + end\ + end\ + return file\ +end\ +sl.genFile = genFile\ +\ +local function updateFiles()\ + while not fs.exists(sl.folder) do\ + sl.folder = fs.getDir(sl.folder)\ + end\ + local inCloud = sl.folder:find(\"User/Cloud\",nil,true) == 1\ + tabs = {}\ + for t=1,#tabRef do\ + if (tabRef[t] == STATUS and inCloud) or config.tabs[tabRef[t].id].on then\ + table.insert(tabs,tabRef[t])\ + end\ + end\ + if config.modes[sl.folder] then\ + sl.rMode = config.modes[sl.folder]\ + else\ + sl.rMode = 1\ + end\ + if LevelOS.self and LevelOS.self.window then\ + if sl.search then\ + LevelOS.setTitle(sl.search..\" - Search Results in \"..sl.getName(sl.folder))\ + LevelOS.self.window.icon = {\"\",lUtils.toBlit(colors.lightBlue)}\ + else\ + LevelOS.setTitle(sl.getName(sl.folder))\ + LevelOS.self.window.icon = sl.getIcon(sl.folder)\ + end\ + end\ + local fi = fs.list(sl.folder)\ + table.sort(fi,function(a,b) return a:lower() < b:lower() end)\ + local folders = {}\ + local nonfolders = {}\ + for t=1,#fi do\ + local f = fs.combine(sl.folder,fi[t])\ + local file = genFile(f)\ + if not (sl.search and not file.name:lower():find(sl.search:lower(),nil,true)) then\ + if file.isDir then\ + table.insert(folders,file)\ + else\ + table.insert(nonfolders,file)\ + end\ + end\ + end\ + files = folders\ + for k,v in ipairs(nonfolders) do\ + table.insert(files,v)\ + end\ + sl.files = files\ + sl.updateFullTree()\ +end\ +while not sl.symbols do\ + os.sleep(0.1)\ +end\ +updateFiles()\ +render()\ +sl.ofolder = sl.folder\ +local ofolder = sl.folder\ +sl.history = {sl.folder}\ +sl.rhistory = {}\ +local mousemove = false\ +local tID = os.startTimer(10)\ +local timerTimeout = os.epoch(\"utc\")+15000\ +local function count(tbl)\ + local c = 0\ + for k,v in pairs(tbl) do\ + c = c+1\ + end\ + return c\ +end\ +local contextcache = {}\ +--[[if not fs.isDir(file) and b[2] == 1 and fTypes[f.type] and fTypes[f.type].c then\ + local found,num = locateEntry(fTypes[f.type].c[1],b[3])\ + local p = fTypes[f.type].c[2][num]\ + if p == \"Lua\" then\ + lOS.execute(f.path)\ + else\ + lOS.execute(p..\" \"..f.path)\ + end\ +elseif b[3] == \"Open\" and fs.isDir(file) then\ + selected = {}\ + sl.folder = f.path\ + updateFiles()\ + scrollY = 0\ +elseif b[3] == \"Delete\" then\ + for k,v in pairs(selected) do\ + fs.delete(k)\ + end\ + selected = {}\ + updateFiles()\ + scrollY = 0\ +end]]\ +local function cMenu(f, x, y)\ + local options = lOS.explorer.genContextMenu(f,s,sl,selected,updateFiles,render,LevelOS,false)\ + local output = {lOS.contextmenu(x,y,30,options,nil,true)}\ + renamebox = sl.renamebox\ + return unpack(output)\ +end\ +sl.cMenu = cMenu\ +local function updateReturn()\ + local tbl = {}\ + for k,v in pairs(sl.ret) do\ + if not selected[k] then\ + sl.ret[k] = nil\ + else\ + table.insert(tbl,k)\ + end\ + end\ + if #tbl > 1 then\ + sl.fpathbox.txt = \"\"\ + for t=1,#tbl do\ + sl.fpathbox.txt = sl.fpathbox.txt..'\"'..fs.getName(tbl[t])..'\" '\ + end\ + sl.fpathbox.changed = false\ + elseif #tbl == 1 then\ + sl.fpathbox.txt = fs.getName(tbl[1])\ + sl.fpathbox.changed = false\ + else\ + sl.fpathbox.changed = true\ + end\ +end\ +\ +local function fPaste(obj)\ + local tFolder = sl.folder\ + for c=1,#lOS.fClipboard do\ + if fs.exists(lOS.fClipboard[c]) == true then\ + fs.copy(lOS.fClipboard[c],uFP(fs.combine(tFolder,fs.getName(lOS.fClipboard[c]))))\ + end\ + if locateEntry(lOS.fCut,lOS.fClipboard[c]) == true then\ + fs.delete(lOS.fClipboard[c])\ + table.remove(lOS.fCut,({locateEntry(lOS.fCut,lOS.fClipboard[c])})[2])\ + end\ + end\ + lOS.fClipboard = {}\ + lOS.fCut = {}\ + updateFiles()\ +end\ +\ +local function fRename(obj)\ + lOS.explorer.fRename(obj,s,sl,render,LevelOS)\ + renamebox = sl.renamebox\ +end\ +\ +local function fOpenWith(obj)\ + if LevelOS and LevelOS.self and LevelOS.self.window then\ + LevelOS.self.window.events = nil\ + end\ + LevelOS.overlay = nil\ + lOS.noEvents = false\ + local p = sl.openWith(obj.file)\ + if p then\ + p = p.path\ + local f = obj.file\ + if p == \"Lua\" then\ + lOS.execute(f.path)\ + else\ + lOS.execute(p..\" \"..f.path)\ + end\ + end\ +end\ +\ +local function fNew(obj)\ + local pth = uFP(fs.combine(sl.folder,\"New_\"..obj.txt:gsub(\" \",\"_\")..\".\"..obj.format))\ + lUtils.fwrite(pth,obj.preset)\ + term.redirect(self.window)\ + updateFiles()\ + render()\ + fRename(pth)\ + -- rename\ +end\ +\ +local function fNewFolder(obj)\ + local pth = uFP(fs.combine(sl.folder,\"New_Folder\"))\ + fs.makeDir(pth)\ + term.redirect(self.window)\ + updateFiles()\ + render()\ + fRename(pth)\ + -- rename\ +end\ +\ +local dragmenu\ +while true do\ + local w,h = term.getSize()\ + local sObj = {x1=1,y1=1,x2=w,y2=h}\ + local e = {os.pullEvent()}\ + if sl.fullReload then\ + os.queueEvent(\"explorer_full_reload\")\ + sl.fullReload = false\ + elseif sl.reload then\ + os.queueEvent(\"explorer_reload\")\ + sl.reload = false\ + end\ + if e[1] == \"explorer_full_reload\" then\ + term.setBackgroundColor(colors.black)\ + term.clear()\ + fTypes = lUtils.asset.load(\"LevelOS/data/formats.lconf\")\ + contextcache = {}\ + imgcache = {}\ + nimgcache = {}\ + shortcache = {}\ + updateFiles()\ + os.sleep(0.2)\ + render()\ + end\ + if dragmenu and (e[1] == \"mouse_drag\" or e[1] == \"mouse_up\") then\ + --dragmenu.x = e[3]-dragmenu.width+1\ + dragmenu.width = e[3]-dragmenu.x+1\ + if e[1] == \"mouse_up\" then\ + config.tabs[dragmenu.id].w = dragmenu.width\ + saveConfig()\ + dragmenu = nil\ + end\ + render()\ + elseif renamebox then\ + if e[1] == \"char\" and e[2] == \" \" then\ + e[2] = \"_\"\ + end\ + if e[1] == \"key\" and e[2] == keys.enter then\ + renamebox.state = false\ + --sl.renaming = nil\ + else\ + renamebox.update(unpack(e))\ + if sl.rMode == 1 then\ + renamebox.x2 = math.min(math.max(NAME.width,#renamebox.txt+5),w)\ + end\ + render()\ + renamebox.render()\ + end\ + if not renamebox.state then\ + local pth = fs.combine(sl.ofolder,renamebox.txt)\ + if renamebox.isShortcut then\ + pth = pth..\".llnk\"\ + end\ + if pth == sl.renaming or fs.combine(pth,\"\") == sl.ofolder then\ + -- nothing\ + elseif fs.exists(pth) then\ + local newPth = uFP(pth)\ + local r = {lUtils.popup(\"Rename File\",\"Do you want to rename \\\"\"..fs.getName(sl.renaming):gsub(\"_\",\" \")..\"\\\" to \\\"\"..fs.getName(newPth):gsub(\"_\",\" \")..\"\\\"?\\n\\nThere is already a file with the same name in this location.\",41,11,{\"Yes\",\"No\"})}\ + if r[1] and r[3] == \"Yes\" then\ + fs.move(sl.renaming,newPth)\ + end\ + else\ + fs.move(sl.renaming,pth)\ + end\ + renamebox = nil\ + sl.renamebox = nil\ + sl.renaming = nil\ + term.setCursorBlink(false)\ + s.win.setPaletteColor(colors.pink,unpack(sl.opink))\ + updateFiles()\ + render()\ + end\ + else\ + if e[1] == \"mouse_move\" or e[1] == \"mouse_up\" or e[1] == \"mouse_click\" then\ + mSelected = nil\ + end\ + if sl.ofolder ~= sl.folder then\ + scrollY = 0\ + sl.ofolder = sl.folder\ + ofolder = sl.folder\ + table.insert(sl.history,sl.folder)\ + sl.rhistory = {}\ + sl.search = nil\ + updateFiles()\ + render()\ + elseif ofolder ~= sl.folder then\ + ofolder = sl.folder\ + sl.search = nil\ + updateFiles()\ + render()\ + elseif (e[1] == \"timer\" and tID == e[2]) or os.epoch(\"utc\") > timerTimeout then\ + if not (e[1] == \"timer\" and tID == e[2]) then\ + _G.expLog = \"Timer timed out at \"..os.date()\ + end\ + updateFiles()\ + render()\ + tID = os.startTimer(4)\ + timerTimeout = os.epoch(\"utc\")+15000\ + end\ + if e[1] == \"mouse_scroll\" and lUtils.isInside(e[3],e[4],sObj) then\ + if sl.rMode == 1 and ((e[2] > 0 and #files+scrollY > h-2) or (e[2] < 0 and scrollY < 0)) then\ + scrollY = scrollY-e[2]\ + elseif sl.rMode == 2 and ((e[2] > 0 and files[#files].y2+1 > h) or (e[2] < 0 and scrollY < 0)) then\ + scrollY = scrollY-e[2]\ + end\ + render()\ + elseif (e[1] == \"mouse_click\" or e[1] == \"mouse_up\" or e[1] == \"mouse_move\" or e[1] == \"mouse_drag\") and e[3] and e[4] and lUtils.isInside(e[3],e[4],sObj) then\ + if e[1] == \"mouse_move\" then\ + if not mousemove then\ + shapescape.getSlide().win.setPaletteColor(colors.pink,0.15,0.15,0.15)\ + mousemove = true\ + end\ + mSelected = {e[3],e[4]}\ + render()\ + elseif not (sl.rMode == 1 and e[4] <= 1) then\ + local clickedfile = false\ + if not (sl.rMode == 1 and e[4] == 2) then\ + for i,f in ipairs(files) do\ + if f.x1 and lUtils.isInside(e[3],e[4],f) then\ + clickedfile = true\ + if e[1] == \"mouse_drag\" and e[2] == 2 then\ + selected[f.path] = f\ + if sl.ret then\ + if (f.isDir and tArgs[2] == \"SelFolder\") or (tArgs[2] == \"SelFile\" and not f.isDir) then\ + sl.ret[f.path] = f\ + updateReturn()\ + end\ + end\ + elseif e[1] == \"mouse_click\" then\ + if e[2] == 1 then\ + if lUtils.isHolding(keys.leftShift) then\ + local startSel = math.huge\ + local endSel = -1\ + for i1,f1 in ipairs(files) do\ + if selected[f1.path] then\ + if i1 < startSel then\ + startSel = i1\ + end\ + if i1 > endSel then\ + endSel = i1\ + end\ + end\ + end\ + if i > startSel and i > endSel then\ + if startSel < 0 then\ + startSel = i\ + end\ + endSel = i\ + elseif i >= startSel and i < endSel then\ + if startSel < 0 then\ + startSel = i\ + end\ + endSel = i\ + elseif i < startSel then\ + startSel,endSel = i,startSel\ + end\ + selected = {}\ + for i1=startSel,endSel do\ + selected[files[i1].path] = files[i1]\ + if (files[i1].isDir and tArgs[2] == \"SelFolder\") or (tArgs[2] == \"SelFile\" and not files[i1].isDir) then\ + sl.ret[files[i1].path] = files[i1]\ + updateReturn()\ + end\ + end\ + elseif lUtils.isHolding(keys.leftCtrl) then\ + if selected[f.path] then\ + selected[f.path] = nil\ + if sl.ret then\ + if count(sl.ret) > 1 then\ + sl.ret[f.path] = nil\ + updateReturn()\ + end\ + end\ + else\ + selected[f.path] = f\ + if (f.isDir and tArgs[2] == \"SelFolder\") or (tArgs[2] == \"SelFile\" and not f.isDir) then\ + sl.ret[f.path] = f\ + updateReturn()\ + end\ + end\ + elseif selected[f.path] and count(selected) == 1 and selected[f.path].lastClick and selected[f.path].lastClick > os.epoch(\"utc\")-1000 then\ + local nf = f\ + local cmd = nf.path\ + local invalidShort = false\ + if shortcache[f.path] then\ + local rPath = shortcache[f.path][1]\ + f.sPath = rPath\ + f.sName = fs.getName(rPath)\ + if not fs.exists(rPath) then\ + invalidShort = true\ + else\ + nf = genFile(shortcache[f.path][1])\ + end\ + local args = shortcache[f.path].args\ + if args and #args > 0 then\ + cmd = cmd..\" \"..table.concat(args, \" \")\ + end\ + end\ + if invalidShort then\ + lOS.explorer.fInvShort({file=f},updateFiles,render)\ + else\ + if nf.isDir then\ + selected = {}\ + sl.folder = nf.path\ + updateFiles()\ + scrollY = 0\ + break\ + elseif (tArgs[2] == \"SelFile\") then\ + sl.ret = {[f.path]=f}\ + updateReturn()\ + shapescape.exit({f.path})\ + elseif shortcache[f.path] and shortcache[f.path].program then -- if needs to be executed with special program\ + local p = shortcache[f.path].program\ + if p == \"Lua\" then\ + lOS.execute(cmd)\ + else\ + lOS.execute(p..\" \"..cmd)\ + end\ + elseif fTypes[nf.type] and fTypes[nf.type].program then\ + local p = fTypes[nf.type].program\ + if p == \"Lua\" then\ + lOS.execute(f.path)\ + else\ + lOS.execute(p..\" \"..nf.path)\ + end\ + elseif nf.type == \"\" then\ + lOS.execute(f.path)\ + else\ + fOpenWith({file=nf})\ + -- open with prompt\ + end\ + end\ + else\ + selected = {[f.path]=f}\ + if (f.isDir and tArgs[2] == \"SelFolder\") or (tArgs[2] == \"SelFile\" and not f.isDir) then\ + sl.ret = {[f.path] = f}\ + updateReturn()\ + end\ + f.lastClick = os.epoch(\"utc\")\ + end\ + elseif e[2] == 2 then\ + if count(selected) > 0 and not selected[f.path] then\ + selected = {[f.path]=f}\ + if (f.isDir and tArgs[2] == \"SelFolder\") or (tArgs[2] == \"SelFile\" and not f.isDir) then\ + sl.ret = {[f.path] = f}\ + updateReturn()\ + end\ + end\ + end\ + elseif e[1] == \"mouse_up\" then\ + if e[2] == 2 then\ + if count(selected) > 0 then\ + cMenu(f,e[3],e[4])\ + else\ + clickedfile = false\ + end\ + end\ + end\ + break\ + end\ + end\ + end\ + if not clickedfile then\ + selected = {}\ + if e[1] == \"mouse_up\" and e[2] == 2 then\ + cMenu(nil, e[3], e[4])\ + end\ + end\ + render()\ + elseif sl.rMode == 1 and e[4] == 1 then\ + if e[1] == \"mouse_click\" and e[2] == 1 then\ + for k,v in ipairs(tabs) do\ + if e[3] == v.x+v.width-1 then\ + dragmenu = v\ + end\ + end\ + elseif e[1] == \"mouse_up\" and e[2] == 2 then\ + local opt = {}\ + for t=1,#config.tabs do\ + local o = {}\ + if config.tabs[t].on then\ + o.txt = {\" \\7 \"..config.tabs[t][1],\"\",\"888\"}\ + else\ + o.txt = \" \"..config.tabs[t][1]\ + end\ + o.action = function() config.tabs[t].on = not config.tabs[t].on saveConfig() sl.reload = true os.queueEvent(\"explorer_reload\") os.queueEvent(\"explorer_reload\") end\ + table.insert(opt,o)\ + end\ + lOS.contextmenu(e[3],e[4],20,opt)\ + end\ + end\ + elseif e[1] == \"term_resize\" or e[1] == \"explorer_reload\" then\ + updateFiles()\ + render()\ + saveConfig()\ + end\ + end\ + sl.selected = selected\ +end", + name = "mainscreen.lua", + }, + [ "topbar.lua" ] = { + id = 2, + content = "local sl = shapescape.getSlides()\ +local function split(str, pat)\ + local t = {} -- NOTE: use {n = 0} in Lua-5.0\ + local fpat = \"(.-)\" .. pat\ + local last_end = 1\ + local s, e, cap = str:find(fpat, 1)\ + while s do\ + if s ~= 1 or cap ~= \"\" then\ + table.insert(t,cap)\ + end\ + last_end = e+1\ + s, e, cap = str:find(fpat, last_end)\ + end\ + if last_end <= #str then\ + cap = str:sub(last_end)\ + table.insert(t, cap)\ + end\ + return t\ +end\ +local function split_path(str)\ + return split(str,'[\\\\/]+')\ +end\ +sl.split_path = split_path\ +local robjs = {}\ +local function render()\ + local w,h = term.getSize()\ + term.setBackgroundColor(colors.black)\ + term.setTextColor(colors.gray)\ + lUtils.border(1,1,w,h,\"fill\",2)\ + robjs = {w=1}\ + local olist = {}\ + local cPath = split_path(sl.folder)\ + table.insert(cPath,1,\"\")\ + local isDisk\ + local isRom\ + local ico = sl.getIcon(sl.folder)\ + if sl.search then\ + local str = \"Search Results in \"..sl.getName(sl.folder)\ + if robjs.w+#str+3 > w-2 then\ + robjs[1] = {\" \\171 \"}\ + else\ + table.insert(robjs,{\" > \"})\ + table.insert(robjs,{str})\ + robjs.w = robjs.w+#str+3\ + ico = {\"\",lUtils.toBlit(colors.lightBlue)}\ + end\ + else\ + for i=#cPath,1,-1 do\ + local str\ + if i == 2 and sl.disks[cPath[2]] and sl.disks[cPath[i]].getDiskLabel() then\ + str = string.gsub(cPath[2],\"_\",\" \")..\" (\"..sl.disks[cPath[2]].getDiskLabel()..\")\"\ + isDisk = true\ + elseif i == 1 then\ + str = \"Local disk\"\ + else\ + str = string.gsub(cPath[i],\"_\",\" \")\ + end\ + if i == 2 and str == \"rom\" then\ + isRom = true\ + end\ + if robjs.w+#str+3 > w-2 then\ + for t=1,i do\ + olist[t] = cPath[t]\ + end\ + if olist[2] and (sl.disks[olist[2]] or olist[2] == \"rom\") then\ + table.remove(olist,1)\ + end\ + robjs[1] = {\" \\171 \",list=olist}\ + break\ + else\ + table.insert(robjs,1,{str,path=fs.combine(table.unpack(cPath,1,i))})\ + table.insert(robjs,1,{\" > \"})\ + robjs.w = robjs.w+#str+3\ + end\ + if isDisk or isRom then\ + break\ + end\ + end\ + end\ + term.setCursorPos(2,2)\ + term.blit(ico[1],ico[2],lUtils.toBlit(term.getBackgroundColor()))\ + robjs[1].x = 3\ + term.setTextColor(colors.lightGray)\ + term.write(robjs[1][1])\ + \ + for i=2,#robjs do\ + robjs[i].x = robjs[i-1].x+#robjs[i-1][1]\ + if robjs[i][1] == \" > \" then\ + term.setTextColor(colors.lightGray)\ + else\ + term.setTextColor(colors.white)\ + end\ + term.write(robjs[i][1])\ + end\ +end\ +\ +render()\ +os.sleep(0.5)\ +render()\ +local ofolder = sl.folder\ +local function gotofolder(obj)\ + sl.folder = obj.path\ + os.queueEvent(\"explorer_update\")\ + os.queueEvent(\"explorer_update\")\ +end\ +local getIcon = sl.getIcon\ +local ow,oh = term.getSize()\ +local osearch = sl.search\ +while true do\ + local e = {os.pullEvent()}\ + local w,h = term.getSize()\ + if e[1] == \"term_resize\" or ofolder ~= sl.folder or ow ~= w or oh ~= h or osearch ~= sl.search then\ + ow,oh = w,h\ + ofolder = sl.folder\ + osearch = sl.search\ + render()\ + elseif e[1]:find(\"mouse\") and e[3] and e[4] and e[4] == 2 and e[3] > 1 and e[3] < w then\ + render()\ + local cObj = false\ + for k,v in ipairs(robjs) do\ + if e[3] >= v.x and e[3] <= v.x+(#v[1]-1) then\ + cObj = true\ + local int = 1\ + local a = v\ + if a[1] == \" > \" and k > 1 then\ + int = 2\ + a = robjs[k-1]\ + k = k-1\ + end\ + if e[1] == \"mouse_click\" then\ + term.setBackgroundColor(colors.gray)\ + term.setCursorPos(a.x,2)\ + term.setTextColor(colors.white)\ + term.write(a[1])\ + if robjs[k+1] and k > 1 then\ + term.setTextColor(colors.lightGray)\ + term.write(\" > \")\ + end\ + elseif e[1] == \"mouse_up\" then\ + if k == 1 and a.list then\ + -- idk\ + if a.list then\ + --sl.dropdown = {self.x1+a.x-3,self.y2,0,a.list}\ + local opt = {}\ + for f=#a.list,1,-1 do\ + local pth = fs.combine(unpack(a.list,1,f))\ + local ico = getIcon(pth)\ + table.insert(opt,{txt={ico[1]..\" \"..sl.getName(a.list[f]),ico[2]},path=pth,action=gotofolder})\ + end\ + sl.dropdown = {self.x1+a.x-3,self.y2,0,opt,{bg=colors.white,fg=colors.lightGray,txt=colors.black}}\ + end\ + elseif int == 1 and a.path then\ + sl.folder = a.path\ + os.queueEvent(\"explorer_change_folder\")\ + elseif a.path then\ + term.setCursorPos(a.x,2)\ + term.setBackgroundColor(colors.gray)\ + term.setTextColor(colors.white)\ + term.write(a[1])\ + term.setTextColor(colors.lightGray)\ + term.write(\" \\31 \")\ + os.sleep(0)\ + local opt = {}\ + local ls = fs.list(a.path)\ + for f=1,#ls do\ + local pth = fs.combine(a.path,ls[f])\ + if fs.isDir(pth) then\ + local ico = getIcon(pth)\ + table.insert(opt,{txt={ico[1]..\" \"..ls[f],ico[2]},path=pth,action=gotofolder})\ + if robjs[k+2] and robjs[k+2][1] == ls[f] then\ + opt[#opt].color = colors.black\ + --table.insert(opt,{txt=\"\\7 \"..ls[f],path=fs.combine(a.path,ls[f]),color=colors.black,action=gotofolder})\ + --else\ + --table.insert(opt,{txt=\" \"..ls[f],path=fs.combine(a.path,ls[f]),action=gotofolder})\ + end\ + end\ + end\ + sl.dropdown = {self.x1+a.x+#a[1]-3,self.y2,0,opt,{bg=colors.white,fg=colors.lightGray,txt=colors.gray}}\ + render()\ + --os.pullEvent()\ + -- dropdown menu\ + end\ + end\ + end\ + end\ + if not cObj then\ + if e[1] == \"mouse_up\" then\ + local w,h = term.getSize()\ + term.setBackgroundColor(colors.black)\ + term.setTextColor(colors.gray)\ + lUtils.border(1,1,w,h,\"fill\",2)\ + local ico = sl.getIcon(sl.folder)\ + term.setCursorPos(2,2)\ + term.blit(ico[1],ico[2],lUtils.toBlit(term.getBackgroundColor()))\ + term.setTextColor(colors.white)\ + local ibox = lUtils.input(4,2,w-1,2,{text=sl.folder,overflowY=\"none\",overflowX=\"scroll\",selectColor=colors.blue})\ + ibox.state = true\ + --ibox.txt = sl.folder\ + ibox.select = {1,#ibox.txt}\ + ibox.cursor.a = #ibox.txt+1\ + ibox.cursor.x = #ibox.txt+1\ + ibox.update(\"timer\")\ + ibox.render()\ + while ibox.state do\ + local e = {os.pullEvent()}\ + if e[1] == \"char\" and e[2] == \" \" then\ + e[2] = \"_\"\ + end\ + if e[1] == \"key\" and e[2] == keys.enter then\ + break\ + else\ + ibox.update(unpack(e))\ + ibox.render()\ + end\ + end\ + term.setCursorBlink(false)\ + if fs.exists(ibox.txt) then\ + sl.folder = fs.combine(ibox.txt,\"\")\ + end\ + render()\ + end\ + end\ + end\ +end", + name = "topbar.lua", + }, + [ "searchupdate.lua" ] = { + id = 20, + content = "local sl = shapescape.getSlides()\ +while true do\ + local e = {os.pullEvent()}\ + if self.state and e[1] == \"key\" and e[2] == keys.enter then\ + self.state = false\ + if #self.txt >= 1 then\ + sl.search = self.txt\ + os.queueEvent(\"explorer_reload\")\ + end\ + end\ +end", + name = "searchupdate.lua", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + y = 22, + x = 65, + h = 19, + w = 51, + objs = { + { + type = "rect", + x2 = 14, + y2 = 19, + y1 = 3, + x1 = 12, + color = 32768, + oy2 = 0, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + }, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + border = { + color = 128, + type = 1, + }, + }, + { + type = "rect", + color = 32768, + y2 = 3, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + x2 = 51, + border = { + color = 0, + type = 1, + }, + x1 = 1, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + y1 = 1, + }, + { + txt = " ", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 2, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 5, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 1, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + x2 = 5, + y2 = 2, + y1 = 2, + txtcolor = 128, + color = 0, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + render = { + [ 2 ] = -1, + }, + }, + x1 = 4, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 6, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 12, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 6, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 8, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 7, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + x1 = 8, + border = { + color = 0, + type = 1, + }, + }, + { + x2 = 51, + y2 = 19, + border = { + color = 0, + type = 1, + }, + x1 = 1, + oy1 = 0, + txt = " ?? items |", + type = "text", + oy2 = 0, + txtcolor = 1, + ox2 = 0, + color = 128, + y1 = 19, + input = false, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 11, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + }, + { + lines = { + "", + }, + color = 32768, + y2 = 2, + border = { + color = 0, + type = 1, + }, + x1 = 34, + scr = 0, + history = {}, + ox1 = 17, + changed = false, + x2 = 49, + txt = "", + type = "input", + blit = {}, + txtcolor = 1, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = 3, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 20, + }, + }, + rhistory = {}, + ox2 = 2, + state = false, + dLines = { + "", + }, + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 256, + indentChar = " ", + tabSize = 4, + minWidth = 16, + overflow = "scroll", + minHeight = 1, + }, + cursor = { + y = 1, + x = 1, + a = 1, + }, + scrollX = 0, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + y1 = 2, + }, + { + type = "window", + color = 32768, + y2 = 18, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 0, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + oy2 = 1, + x2 = 51, + border = { + color = 0, + type = 1, + }, + input = false, + x1 = 15, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 4, + }, + { + type = "window", + color = 128, + y2 = 18, + oy2 = 1, + x1 = 1, + x2 = 13, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 1, + }, + update = { + [ 2 ] = -1, + }, + }, + input = false, + y1 = 4, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + border = { + color = 0, + type = 1, + }, + }, + { + type = "window", + color = 256, + y2 = 3, + y1 = 1, + ox2 = 20, + x2 = 31, + x1 = 10, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + update = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "dropdown", + type = "text", + color = 0, + y2 = 1, + border = { + color = 0, + type = 1, + }, + txtcolor = 1, + x2 = 8, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 13, + }, + }, + x1 = 1, + y1 = 1, + }, + { + x2 = 48, + y2 = 19, + x1 = 48, + ox1 = 3, + oy1 = 0, + txt = "=", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 21, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 3, + y1 = 19, + border = { + color = 0, + type = 1, + }, + color = 0, + input = false, + txtcolor = 1, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + x2 = 50, + y2 = 19, + x1 = 50, + ox1 = 1, + oy1 = 0, + txt = "", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 22, + }, + Coroutine = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + y1 = 19, + border = { + color = 0, + type = 1, + }, + color = 0, + input = false, + txtcolor = 8, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + }, + c = 1, + }, + { + y = 22, + x = 65, + h = 19, + w = 51, + objs = { + { + type = "window", + color = 32768, + y2 = 19, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 8, + }, + update = { + [ 2 ] = -1, + }, + }, + x1 = 1, + x2 = 51, + ox2 = 0, + oy2 = 0, + border = { + color = 0, + type = 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 1, + }, + { + type = "rect", + oy2 = -9, + color = 1, + y2 = 19, + border = { + color = 0, + type = 1, + }, + ox2 = -12, + x2 = 38, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 9, + }, + update = { + [ 2 ] = -1, + }, + }, + x1 = 14, + ox1 = 12, + oy1 = 9, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + y1 = 1, + }, + { + x2 = 37, + y2 = 3, + x1 = 15, + ox1 = 11, + oy1 = 8, + txt = "How do you want to open this file?", + type = "text", + oy2 = 7, + txtcolor = 32768, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + border = { + color = 0, + type = 1, + }, + color = 0, + input = false, + y1 = 2, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + ox2 = -11, + }, + { + type = "window", + oy2 = -2, + color = 1, + y2 = 12, + y1 = 4, + ox2 = -12, + x2 = 38, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 17, + }, + update = { + [ 2 ] = -1, + }, + }, + x1 = 14, + ox1 = 12, + oy1 = 6, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + border = { + color = 256, + type = 1, + }, + }, + { + color = 0, + y2 = 16, + x1 = 19, + ox1 = 7, + oy1 = -4, + txt = "Always use this app for ??? files", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 10, + }, + }, + txtcolor = 32768, + oy2 = -6, + y1 = 14, + x2 = 37, + input = false, + ox2 = -11, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + border = { + color = 0, + type = 1, + }, + }, + { + color = 256, + y2 = 18, + x1 = 28, + ox1 = -2, + oy1 = -6, + txt = " OK", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 19, + }, + }, + ox2 = -11, + txtcolor = 32768, + y1 = 16, + x2 = 37, + input = false, + oy2 = -8, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + border = { + color = 1, + type = 1, + }, + }, + { + type = "rect", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 18, + }, + }, + x2 = 17, + y2 = 15, + y1 = 14, + ox2 = 9, + color = 0, + oy2 = -5, + x1 = 15, + ox1 = 11, + oy1 = -4, + snap = { + Top = "Snap center", + Right = "Snap center", + Left = "Snap center", + Bottom = "Snap center", + }, + border = { + color = 256, + type = 1, + }, + }, + }, + c = 2, + }, + { + y = 21, + x = 65, + h = 19, + w = 51, + objs = { + { + type = "rect", + x2 = 14, + y2 = 15, + border = { + color = 128, + type = 1, + }, + x1 = 12, + color = 32768, + oy2 = 4, + y1 = 3, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + }, + { + type = "rect", + color = 32768, + y2 = 3, + y1 = 1, + x1 = 1, + x2 = 51, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + txt = " ", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 2, + input = false, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 5, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 1, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + x2 = 5, + y2 = 2, + y1 = 2, + txtcolor = 128, + color = 0, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 6, + }, + }, + x1 = 4, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 6, + input = false, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 12, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 6, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "", + type = "text", + color = 0, + y2 = 2, + y1 = 2, + txtcolor = 128, + x2 = 8, + input = false, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 7, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 8, + border = { + color = 0, + type = 1, + }, + }, + { + lines = { + "", + }, + color = 32768, + y2 = 2, + y1 = 2, + x1 = 34, + scrollX = 0, + cursor = { + y = 1, + x = 1, + a = 1, + }, + ox1 = 17, + x2 = 49, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 256, + indentChar = " ", + tabSize = 4, + minWidth = 16, + overflow = "scroll", + minHeight = 1, + }, + ox2 = 2, + state = false, + history = {}, + event = { + render = { + [ 2 ] = 3, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + rhistory = {}, + txtcolor = 1, + blit = {}, + dLines = { + "", + }, + type = "input", + scr = 0, + changed = false, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + type = "window", + color = 32768, + y2 = 14, + oy2 = 5, + x1 = 15, + border = { + color = 0, + type = 1, + }, + x2 = 51, + ox2 = 0, + input = false, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 0, + }, + update = { + [ 2 ] = -1, + }, + }, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 4, + }, + { + type = "window", + x2 = 13, + y2 = 14, + y1 = 4, + x1 = 1, + color = 128, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 1, + }, + render = { + [ 2 ] = -1, + }, + }, + input = false, + oy2 = 5, + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + border = { + color = 0, + type = 1, + }, + }, + { + type = "window", + x2 = 31, + y2 = 3, + y1 = 1, + x1 = 10, + color = 256, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + update = { + [ 2 ] = -1, + }, + }, + input = false, + ox2 = 20, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + }, + { + txt = "dropdown", + type = "text", + color = 0, + y2 = 1, + border = { + color = 0, + type = 1, + }, + txtcolor = 1, + x2 = 8, + input = false, + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 13, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + }, + x1 = 1, + y1 = 1, + }, + { + type = "rect", + color = 128, + y2 = 19, + border = { + color = 0, + type = 1, + }, + x1 = 1, + y1 = 15, + x2 = 51, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + oy1 = 4, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + oy2 = 0, + }, + { + color = 32768, + y2 = 18, + x1 = 43, + ox1 = 8, + oy1 = 1, + txt = " Cancel", + type = "text", + event = { + mouse_up = { + [ 2 ] = 16, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + txtcolor = 1, + border = { + color = 0, + type = 1, + }, + x2 = 50, + input = false, + y1 = 18, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 1, + }, + { + x2 = 41, + y2 = 18, + x1 = 34, + ox1 = 17, + oy1 = 1, + txt = " Select", + type = "text", + event = { + render = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = 15, + }, + }, + txtcolor = 1, + y1 = 18, + border = { + color = 0, + type = 1, + }, + color = 32768, + input = false, + ox2 = 10, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 1, + }, + { + color = 0, + y2 = 16, + x1 = 4, + oy1 = 3, + txt = "File Name:", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txtcolor = 1, + oy2 = 3, + border = { + color = 0, + type = 1, + }, + input = false, + x2 = 13, + snap = { + Top = "Snap bottom", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 16, + }, + { + lines = { + "", + }, + x2 = 32, + y2 = 16, + cursor = { + y = 1, + x = 1, + a = 1, + }, + x1 = 15, + state = false, + scr = 0, + changed = false, + oy1 = 3, + txtcolor = 1, + txt = "", + opt = { + overflowX = "scroll", + overflowY = "none", + cursorColor = 1, + indentChar = " ", + tabSize = 4, + minWidth = 9, + overflow = "scroll", + minHeight = 1, + }, + oy2 = 3, + history = {}, + scrollX = 0, + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 14, + }, + update = { + [ 2 ] = -1, + }, + }, + rhistory = {}, + ox2 = 19, + blit = {}, + dLines = { + "", + }, + border = { + color = 0, + type = 1, + }, + type = "input", + color = 32768, + ref = { + 1, + 1, + }, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + y1 = 16, + }, + { + color = 32768, + y2 = 16, + x1 = 34, + ox1 = 17, + oy1 = 3, + txt = "All files (*.*) ", + type = "text", + event = { + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + render = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 1, + txtcolor = 1, + border = { + color = 0, + type = 1, + }, + x2 = 50, + input = false, + y1 = 16, + snap = { + Top = "Snap bottom", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + oy2 = 3, + }, + }, + c = 3, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end +end + + local tArgs = {...} +if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} +end + + +return lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/Program_Files/LevelOS/Explorer/taskbar.limg b/Program_Files/LevelOS/Explorer/taskbar.limg new file mode 100644 index 0000000..9a6fd50 --- /dev/null +++ b/Program_Files/LevelOS/Explorer/taskbar.limg @@ -0,0 +1 @@ +{{{"—‹"," 0","00 ",},{"Š…","000"," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Lua/icon.limg b/Program_Files/LevelOS/Lua/icon.limg new file mode 100644 index 0000000..629ee39 --- /dev/null +++ b/Program_Files/LevelOS/Lua/icon.limg @@ -0,0 +1 @@ +{{{"Ÿˆ"," b","bb ",},{"€‚•"," 0b","bb ",},{"‚ƒ€","bb "," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Lua/main.lua b/Program_Files/LevelOS/Lua/main.lua new file mode 100644 index 0000000..904630e --- /dev/null +++ b/Program_Files/LevelOS/Lua/main.lua @@ -0,0 +1,172 @@ +local tArgs = {...} +if type(tArgs[1]) == "string" and fs.exists(tArgs[1]) then + lOS.execute(table.concat(tArgs," ")) + return +end +if LevelOS then LevelOS.self.window.icon = {"\3","b"} end +local lex = "LevelOS/modules/lex.lua" +local docs = "Program_Files/Slime_Text/highlight/lua/docs.json" +local docstable +if not fs.exists(lex) then + -- oh no +end +if not fs.exists(docs) then + local webres = http.get("http://tweaked.cc/index.json") + if webres then + docstable = textutils.unserializeJSON(webres.readAll()) + end + --[[if webres then + lUtils.fwrite(docs,webres.readAll()) + end]] +end +if fs.exists(docs) then + docstable = textutils.unserializeJSON(lUtils.fread(docs)) +end +local pretty = require "cc.pretty" + +local tCommandHistory = {} +local hCur = 0 +local bRunning = true +local tEnv = { + ["exit"] = setmetatable({}, { + __tostring = function() return "Call exit() to exit." end, + __call = function() bRunning = false end, + __type = "function", + }), + ["_echo"] = function(...) + return ... + end, +} +setmetatable(tEnv, { __index = _ENV }) +term.setTextColor(colors.yellow) +print("LevelOS interactive Lua prompt.\nCall exit() to exit.") +term.setTextColor(colors.white) +while bRunning do + hCur = #tCommandHistory+1 + write("lua> ") + local w,h = term.getSize() + local x,y = term.getCursorPos() + local self = lUtils.input(x,y,w,y) + self.opt.cursorColor = colors.white + self.opt.overflowY = "scroll" + self.scrollY = 0 + self.opt.syntax = { + type="lua", + keyword=colors.yellow, + comment=colors.green, + string=colors.red, + number=colors.purple, + symbol=colors.white, + operator=colors.lightGray, + value=colors.purple, + ident=colors.white, + ["function"]=colors.cyan, + nfunction=colors.lime, + arg=colors.orange, + lexer=lex, + whitespace=colors.lightGray, + } + self.opt.complete = { + docs = docstable, + env = tEnv, + overlay = true, + LevelOS = LevelOS, + } + self.opt.selectColor=colors.gray + self.opt.overflowY="stretch" + self.opt.overflowX="scroll" + _G.debugeditor = self + while true do + tCommandHistory[hCur] = self.txt + self.state = true + local doUpdate = true + local e = {os.pullEvent()} + if e[1] == "term_resize" then + local w,h = term.getSize() + self.x2 = w + elseif e[1] == "key" and (e[2] == keys.up or e[2] == keys.down) and not self.opt.complete.list then + doUpdate = false + local o = 1 + if e[2] == keys.up then + o = -1 + end + if tCommandHistory[hCur+o] then + hCur = hCur+o + self.txt = tCommandHistory[hCur] + self.cursor.x = #self.txt+1 + self.cursor.a = #self.txt+1 + end + self.update("term_resize") + elseif e[1] == "key" and e[2] == keys.enter and not lUtils.isHolding(keys.leftShift) then + if hCur < #tCommandHistory then + hCur = #tCommandHistory + tCommandHistory[hCur] = self.txt + end + doUpdate = false + term.setCursorBlink(false) + LevelOS.overlay = nil + print("") + local s = self.txt + local nForcePrint = 0 + local func, e = load(s, "=lua", "t", tEnv) + local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv) + if not func then + if func2 then + func = func2 + e = nil + nForcePrint = 1 + end + else + if func2 then + func = func2 + end + end + + if func then + local tResults = table.pack(pcall(func)) + if tResults[1] then + local n = 1 + while n < tResults.n or n <= nForcePrint do + local value = tResults[n + 1] + local ok, serialised = pcall(pretty.pretty, value, { + function_args = settings.get("lua.function_args"), + function_source = settings.get("lua.function_source"), + }) + if ok then + pretty.print(serialised) + else + print(tostring(value)) + end + n = n + 1 + end + else + printError(tResults[2]) + end + else + printError(e) + end + break + elseif e[1] == "key" or e[1] == "char" or e[1] == "paste" then + if hCur < #tCommandHistory then + hCur = #tCommandHistory + tCommandHistory[hCur] = self.txt + end + end + if doUpdate then + self.update(unpack(e)) + end + local w,h = term.getSize() + while self.y2 > h do + if self.y1 > 1 then + term.scroll(1) + self.y2 = self.y2-1 + self.y1 = self.y1-1 + else + self.y1 = 1 + self.y2 = h + self.opt.overflowY = "scroll" + end + end + self.render() + end +end \ No newline at end of file diff --git a/Program_Files/LevelOS/Lua/taskbar.limg b/Program_Files/LevelOS/Lua/taskbar.limg new file mode 100644 index 0000000..17f2c1f --- /dev/null +++ b/Program_Files/LevelOS/Lua/taskbar.limg @@ -0,0 +1 @@ +{{{"Ÿœ’"," 00","0 ",},{"‚","000"," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Notepad/icon.limg b/Program_Files/LevelOS/Notepad/icon.limg new file mode 100644 index 0000000..e73e57f --- /dev/null +++ b/Program_Files/LevelOS/Notepad/icon.limg @@ -0,0 +1 @@ +{{{"ŸŸ€"," ","77 ",},{"•","888","77 ",},{"ˆŒ•","778","88 ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Notepad/main.lua b/Program_Files/LevelOS/Notepad/main.lua new file mode 100644 index 0000000..7fce64c --- /dev/null +++ b/Program_Files/LevelOS/Notepad/main.lua @@ -0,0 +1,273 @@ +local tArgs = {...} +local tFilepath = "" +if tArgs[1] ~= nil then + tFilepath = tArgs[1] +end +local isReadOnly = false +if tArgs[2] ~= nil and tArgs[2] == "true" then + isReadOnly = true +end +local tCol = {bg=colors.white,txt=colors.black,misc=colors.lightGray,misc2=colors.gray} +local btns = {{"File",{{"New","New window","Open...","Save","Save as..."},"Print","Quit"}},{"Edit",{"Undo",{"Search","Replace"},{"Time"}}},{"View",{"Dark Mode"}}} +local function topbar() + local w,h = term.getSize() + theline = "" + for t=1,w-1 do + theline = theline.."\131" + end + term.setCursorPos(1,1) + term.setBackgroundColor(tCol.bg) + term.setTextColor(tCol.misc) + term.clearLine() + term.setCursorPos(1,2) + term.write(theline) + term.setCursorPos(1,1) + term.setTextColor(tCol.txt) + for t=1,#btns do + btns[t].x = ({term.getCursorPos()})[1] + btns[t].w = string.len(btns[t][1])+2 + term.write(" "..btns[t][1].." ") + end +end +local w,h = term.getSize() +local a = {} +local function txt() + a = {{width=w-1,height=h-4,sTable={},filepath="",lines={""},changed=false},0,0,1,1} + if tFilepath ~= "" then + if fs.exists(tFilepath) == true then + local openfile = fs.open(tFilepath,"r") + a[1].lines = {} + for line in openfile.readLine do + a[1].lines[#a[1].lines+1] = line + end + openfile.close() + if a[1].lines[1] == nil then + a[1].lines[1] = "" + end + end + end + while true do + local w,h = term.getSize() + a[1].width = w-1 + a[1].height = h-4 + a[1].sTable = {background={tCol.bg},text={tCol.txt},cursor={colors.red}} + term.setCursorPos(1,5) + term.setBackgroundColor(colors.white) + term.setTextColor(colors.black) + --print("I am present.") + local changesAllowed = true + if isReadOnly == true or (tFilepath ~= "" and fs.isReadOnly(tFilepath) == true) then + changesAllowed = false + end + a = {lUtils.drawEditBox(a[1],1,3,a[2],a[3],a[4],a[5],true,true,nil,changesAllowed)} + --term.setCursorPos(1,3) + --term.clearLine() + --term.write("HIII: ") + --term.write(textutils.serialize(a)) + os.sleep(0) + end +end +_G.thetxtfunction = txt +local function save() + if tFilepath == "" then + while true do + local i = {lUtils.inputbox("Filepath","Please enter a new filepath:",29,10,{"Done","Cancel"})} + if i[2] == false or i[4] == "Cancel" then + return false + end + if fs.exists(i[1]) == true then + lUtils.popup("Error","This path already exists!",29,9,{"OK"}) + else + tFilepath = i[1] + break + end + end + end + local savefile = fs.open(tFilepath,"w") + for t=1,#a[1].lines do + savefile.writeLine(a[1].lines[t]) + end + savefile.close() + return true +end +function uwansave() + local name = "" + if tFilepath == "" then + name = "Untitled" + else + name = fs.getName(tFilepath) + end + local c = {lUtils.popup("Notepad","Do you want to save your changes in "..name.."?",30,8,{"Save","Don't save","Cancel"})} + if c[1] == false then return false + elseif c[3] == "Save" then + if tFilepath == "" then + -- select file path with explorer asap + return false + end + local ayyy = fs.open(tFilepath,"w") + for t=1,#a[1].lines do + ayyy.writeLine(a[1].lines[t]) + end + end + if c[3] ~= "Cancel" then + return true + end +end +local function scrollbars() + local w,h = term.getSize() + term.setCursorPos(1,h-1) + term.setBackgroundColor(tCol.misc) + term.setTextColor(tCol.misc2) + term.clearLine() + term.setCursorPos(1,h-1) + term.write("\17") + term.setCursorPos(w-1,h-1) + term.write("\16") + for t=2,h-2 do + term.setCursorPos(w,t) + if t == 3 then + term.write("\30") + elseif t == h-2 then + term.write("\31") + else + term.write(" ") + end + end + term.setCursorPos(1,h) + for t=1,w do + term.write("\131") + end +end +function LevelOS.close() + local u = true + if a[1].changed == true then + u = uwansave() + end + if u == true then + return + else + regevents() + end +end +function regevents() + scrollbars() + local txtcor = coroutine.create(txt) + topbar() + coroutine.resume(txtcor) + while true do + e = {os.pullEvent()} + scrollbars() + if not ((e[1] == "mouse_click" or e[1] == "mouse_up") and e[4] == 1) then + coroutine.resume(txtcor,table.unpack(e)) + end + --term.setCursorPos(1,3) + --term.setBackgroundColor(colors.white) + --term.setTextColor(colors.black) + --term.clearLine() + --term.setCursorPos(1,3) + --term.write(coroutine.status(txtcor).." "..textutils.serialize(a[1])) + if e[1] == "term_resize" then + topbar() + scrollbars() + coroutine.resume(txtcor,"mouse_click",1,1,1) + elseif e[1] == "mouse_click" then + if e[4] == 1 then + topbar() + term.setCursorBlink(false) + local oldcursorpos = {term.getCursorPos()} + for t=1,#btns do + if e[3] >= btns[t].x and e[3] <= btns[t].x+btns[t].w-1 then + -- open menu and color button light blue + term.setCursorPos(btns[t].x,1) + term.setBackgroundColor(colors.blue) + term.write(" "..btns[t][1].." ") + local disabled = {} + if btns[t][1] == "File" then + if a[1].changed == false then + disabled = {"Save","Save as..."} + end + end + local b = {lUtils.clickmenu(btns[t].x,2,20,btns[t][2],true,disabled)} + if b[1] ~= false then + if b[3] == "New" then + local d = true + if a[1].changed == true then + d = uwansave() + end + if d == true then + tFilepath = "" + txtcor = coroutine.create(txt) + os.startTimer(0.1) + end + elseif b[3] == "New window" then + lOS.execute(LevelOS.path) + elseif b[3] == "Open..." then + local u = true + if a[1].changed == true then + u = uwansave() + end + if u == true then + local d = {lUtils.explorer("/","SelFile false")} + if d[1] ~= nil then + if fs.exists(d[1]) == true then + a = {} + txtcor = coroutine.create(txt) + coroutine.resume(txtcor) + tFilepath = d[1] + local openfile = fs.open(tFilepath,"r") + a[1].lines = {} + for line in openfile.readLine do + a[1].lines[#a[1].lines+1] = line + end + openfile.close() + if a[1].lines[1] == nil then + a[1].lines[1] = "" + end + end + end + end + elseif b[3] == "Save" then + if save() == true then + a[1].changed = false + end + elseif b[3] == "Save as..." then + local oldF = tFilepath + tFilepath = "" + if save() == false then + tFilepath = oldF + else + a[1].changed = false + end + elseif b[3] == "Quit" then + local u = true + if a[1].changed == true then + u = uwansave() + end + if u == true then + return + end + elseif b[3] == "Dark Mode" then + tCol = {bg=colors.black,txt=colors.white,misc=colors.gray,misc2=colors.lightGray} + topbar() + scrollbars() + coroutine.resume(txtcor,"mouse_click",1,1,1) + btns[3][2][1] = "Light Mode" + elseif b[3] == "Light Mode" then + tCol = {bg=colors.white,txt=colors.black,misc=colors.lightGray,misc2=colors.gray} + topbar() + scrollbars() + coroutine.resume(txtcor,"mouse_click",1,1,1) + btns[3][2][1] = "Dark Mode" + end + end + end + end + topbar() + term.setCursorPos(table.unpack(oldcursorpos)) + term.setTextColor(colors.red) + term.setCursorBlink(true) + end + end + end +end +regevents() \ No newline at end of file diff --git a/Program_Files/LevelOS/Notepad/taskbar.limg b/Program_Files/LevelOS/Notepad/taskbar.limg new file mode 100644 index 0000000..e61ad0e --- /dev/null +++ b/Program_Files/LevelOS/Notepad/taskbar.limg @@ -0,0 +1 @@ +{{{"•ƒ•"," 0","00 ",},{"Š…","000"," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Store/icon.limg b/Program_Files/LevelOS/Store/icon.limg new file mode 100644 index 0000000..b270ede --- /dev/null +++ b/Program_Files/LevelOS/Store/icon.limg @@ -0,0 +1 @@ +{{{"‡Œ‹"," ","999",},{"€Š€"," 9 ","909",},{"","999"," ",},},} \ No newline at end of file diff --git a/Program_Files/LevelOS/Store/main.lua b/Program_Files/LevelOS/Store/main.lua new file mode 100644 index 0000000..8fac176 --- /dev/null +++ b/Program_Files/LevelOS/Store/main.lua @@ -0,0 +1,1538 @@ +local assets = { + [ "background.lua" ] = { + id = 8, + name = "background.lua", + content = "-- render bg\ +\ +os.sleep(0.5)\ +ogP = {}\ +local s = shapescape.getSlide()\ +for a=2,4 do\ + ogP[a] = s.objs[a].oy1\ +end\ +\ +while true do\ + -- render bg if app changes\ + local e = {os.pullEvent()}\ + if e[1] == \"mouse_scroll\" then\ + for a=2,4 do\ + if s.objs[a].oy1+e[2] >= ogP[a] then\ + s.objs[a].oy1 = s.objs[a].oy1+e[2]\ + if a ~= 2 then\ + s.objs[a].oy2 = s.objs[a].oy2+e[2]\ + end\ + end\ + end\ + end\ +end\ + ", + }, + [ "updatething.lua" ] = { + id = 10, + name = "updatething.lua", + content = "local s = shapescape.getSlide()\ +if s.updateFunc then\ +\9s.updateFunc()\ +\9s.updateFunc = nil\ +end", + }, + [ "mainscreen.lua" ] = { + id = 2, + name = "mainscreen.lua", + content = "function doYourThing()\ +local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +local hpost = http.post\ +local function download(name,pth,saveto,run)\ +\9local f = hpost(\"https://old.leveloper.cc/sGet.php\",\"path=\"..textutils.urlEncode(pth)..\"&\"..rType..\"=\"..textutils.urlEncode(name),{Cookie=lOS.userID}).readAll()\ +\9if f and f ~= \"409\" and f ~= \"403\" and f ~= \"401\" then\ +\9\9if run then\ +\9\9\9return f\ +\9\9else\ +\9\9\9lUtils.fwrite(saveto,f)\ +\9\9\9return true\ +\9\9end\ +\9else\ +\9\9return false\ +\9end\ +end\ +local function get(name)\ +\ +\9--write(\"Connecting to LevelStore... \")\ +\ +\9rType = \"code\"\ +\9local response, err = http.post(\"https://old.leveloper.cc/sGet.php\",\"path=\"..textutils.urlEncode(\"\")..\"&code=\"..textutils.urlEncode(name),{Cookie=lOS.userID})\ +\ +\9if not response then\ +\9\9rType = \"name\"\ +\9\9response, err = http.post(\"https://old.leveloper.cc/sGet.php\",\"path=\"..textutils.urlEncode(\"\")..\"&name=\"..textutils.urlEncode(name),{Cookie=lOS.userID})\ +\9end\ +\ +\9if response then\ +\9\9local tree = {}\ +\9\9local folders = {}\ +\9\9local function searchFolder(folder)\ +\9\9\9--print(\"Searching folder root/\"..folder)\ +\9\9\9local f = hpost(\"https://old.leveloper.cc/sGet.php\",\"path=\"..textutils.urlEncode(folder)..\"&\"..rType..\"=\"..textutils.urlEncode(name),{Cookie=lOS.userID}).readAll()\ +\9\9\9--print(f)\ +\9\9\9local f2 = f\ +\9\9\9while true do\ +\9\9\9\9local file = nil\ +\9\9\9\9file,f = lUtils.getField(f,\"file\")\ +\9\9\9\9if not file then\ +\9\9\9\9\9break\ +\9\9\9\9else\ +\9\9\9\9\9local name = lUtils.getField(file,\"name\")\ +\9\9\9\9\9tree[#tree+1] = fs.combine(folder,name)\ +\9\9\9\9\9--print(\"Found \"..fs.combine(folder,name))\ +\9\9\9\9end\ +\9\9\9end\ +\9\9\9f = f2\ +\9\9\9while true do\ +\9\9\9\9local file = nil\ +\9\9\9\9file,f = lUtils.getField(f,\"folder\")\ +\9\9\9\9if not file then\ +\9\9\9\9\9break\ +\9\9\9\9else\ +\9\9\9\9\9local name = lUtils.getField(file,\"name\")\ +\9\9\9\9\9--if not fs.exists(fs.combine(folder,name)) then\ +\9\9\9\9\9\9--fs.makeDir(fs.combine(folder,name))\ +\9\9\9\9\9--end\ +\9\9\9\9\9folders[#folders+1] = fs.combine(folder,name)\ +\9\9\9\9\9searchFolder(fs.combine(folder,name))\ +\9\9\9\9end\ +\9\9\9end\ +\9\9\9return true\ +\9\9end\ +\9\9searchFolder(\"\")\ +\9\9--print(\"Success.\")\ +\ +\9\9return tree,folders\ +\9else\ +\9\9printError(\"Failed.\")\ +\9\9print(err)\ +\9end\ +end\ +\ +local circ = lUtils.asset.load(\"LevelOS/assets/circProgress.limg\")\ +\ +bigfont.writeOn(term.current(),1,\"Loading...\")\ +local pth = \"Program_Files\"\ +local appdata = \"AppData/lStore\"\ +if not fs.exists(appdata) then\ +\9fs.makeDir(appdata)\ +end\ +local apps = {} -- retrieve from servers\ +_G.debugApps = apps\ +local response,err = http.get(\"https://old.leveloper.cc/sList.php\",{Cookie=lOS.userID})\ +if not response then\ +\9term.setBackgroundColor(colors.black)\ +\9term.clear()\ +\9bigfont.writeOn(term.current(),1,err)\ +\9return\ +end\ +local res = response.readAll()\ +--local gf = lUtils.getField\ +local cols = {}\ +for k,v in pairs(colors) do\ +\9if type(v) == \"number\" and v ~= colors.gray and v ~= colors.white and v ~= colors.black then\ +\9\9table.insert(cols,v)\ +\9end\ +end\ +local selected = {}\ +while true do\ +\9local p\ +\9p,res = lUtils.getField(res,\"project\")\ +\9if p ~= nil then\ +\9\9local function gf(field)\ +\9\9\9return (lUtils.getField(p,field))\ +\9\9end\ +\9\9local app = {code=gf(\"code\"),name=string.gsub(gf(\"title\"),\"_\",\" \"),creator=gf(\"creator\"),timestamp=gf(\"version\"),listing=gf(\"listing\")}\ +\9\9if gf(\"icon\") then\ +\9\9\9local icon = gf(\"icon\")\ +\9\9\9if icon and textutils.unserialize(icon) then\ +\9\9\9\9app.icon = textutils.unserialize(icon)[1]\ +\9\9\9end\ +\9\9end\ +\9\9if gf(\"iconBig\") then\ +\9\9\9local ico = gf(\"iconBig\")\ +\9\9\9if ico and textutils.unserialize(ico) then\ +\9\9\9\9app.ico = textutils.unserialize(ico)[1]\ +\9\9\9end\ +\9\9end\ +\9\9if gf(\"background\") then\ +\9\9\9app.background = gf(\"background\")\ +\9\9end\ +\9\9if gf(\"description\") then\ +\9\9\9app.description = gf(\"description\")\ +\9\9else\ +\9\9\9app.description = \"No description.\"\ +\9\9end\ +\9\9app.creatorName = gf(\"creator_name\")\ +\9\9if gf(\"rating\") and gf(\"ratings\") then\ +\9\9\9app.rating = tonumber(gf(\"rating\"))\ +\9\9\9app.ratings = tonumber(gf(\"ratings\"))\ +\9\9else\ +\9\9\9app.rating = 0\ +\9\9\9app.ratings = 0\ +\9\9end\ +\9\9if gf(\"fname\") then\ +\9\9\9app.fname = gf(\"fname\")\ +\9\9else\ +\9\9\9app.fname = string.gsub(app.name,\"%W\",\"_\")\ +\9\9end\ +\9\9app.verified = gf(\"verified\")\ +\9\9if app.verified == \"true\" then\ +\9\9\9app.verified = true\ +\9\9else\ +\9\9\9app.verified = false\ +\9\9end\ +\9\9if app.creator == \"2\" then\ +\9\9\9app.verified = true\ +\9\9end\ +\9\9app.bg = cols[math.random(1,#cols)]\ +\9\9table.insert(apps,app)\ +\9else\ +\9\9break\ +\9end\ +end\ +if not fs.exists(pth) then\ +\9fs.makeDir(pth)\ +end\ +local tCol = {bg=colors.black,txt=colors.white,txt2=colors.white,app=colors.gray,topbar=colors.black,sel=colors.blue,heart=colors.red}\ +local aW,aH = 14,11\ +local function path(app)\ +\9return fs.combine(pth,app.fname)\ +end\ +sl.api = {get=get,download=download,path=path,circ=circ,tCol=tCol}\ +local function textbox(txt,x1,y1,x2,y2)\ +\9local x,y = x1,y1\ +\9local w,h = x2-(x-1),y2-(y-1)\ +\9local bg,fg = term.getBackgroundColor(),term.getTextColor()\ +\9local win = window.create(term.current(),x,y,w,h,false)\ +\9win.setBackgroundColor(bg)\ +\9win.setTextColor(fg)\ +\9win.clear()\ +\9win.setCursorPos(1,1)\ +\9local oterm = term.current()\ +\9term.redirect(win)\ +\9write(txt)\ +\9term.redirect(oterm)\ +\9for y=y1,y2 do\ +\9\9term.setCursorPos(x1,y)\ +\9\9term.blit(win.getLine(y-(y1-1)))\ +\9end\ +end\ +local function bigWrite(txt)\ +\9local x,y = term.getCursorPos()\ +\9bigfont.writeOn(term.current(),1,txt,x,y)\ +end\ +local function rApp(app,x,y)\ +\9app.x1,app.y1 = x,y\ +\9app.x2,app.y2 = x+(aW-1),y+(aH-1)\ +\9if app.ico then\ +\9\9lUtils.renderImg(app.ico,x,y)\ +\9elseif app.icon then\ +\9\9term.setBackgroundColor(app.bg)\ +\9\9--local w,h = term.getSize()\ +\9\9for q=0,4 do\ +\9\9\9term.setCursorPos(x,y+q)\ +\9\9\9term.write(string.rep(\" \",aW))\ +\9\9end\ +\9\9lUtils.renderImg(app.icon,x+math.floor(aW/2 - 3/2),y+1)\ +\9else\ +\9\9term.setBackgroundColor(app.bg)\ +\9\9--local w,h = term.getSize()\ +\9\9for q=0,4 do\ +\9\9\9term.setCursorPos(x,y+q)\ +\9\9\9term.write(string.rep(\" \",aW))\ +\9\9end\ +\9\9term.setTextColor(colors.white)\ +\9\9term.setCursorPos(x+math.ceil(aW/2)-3,y+1)\ +\9\9bigWrite(string.upper(string.sub(app.name,1,2)))\ +\9end\ +\9if app.listing == \"unlisted\" then\ +\9\9term.setTextColor(colors.lightGray)\ +\9elseif app.listing == \"premium\" then\ +\9\9term.setTextColor(colors.orange)\ +\9else\ +\9\9term.setTextColor(tCol.txt)\ +\9end\ +\9if selected == app then\ +\9\9term.setBackgroundColor(colors.lightGray)\ +\9else\ +\9\9term.setBackgroundColor(tCol.app)\ +\9end\ +\9for q=5,aH-1 do\ +\9\9term.setCursorPos(x,y+q)\ +\9\9term.write(string.rep(\" \",aW))\ +\9end\ +\9--textbox(app.name,x+1,y+6,x+aW-2,y+7)\ +\9local txt = lUtils.wordwrap(app.name, aW-2)\ +\9term.setCursorPos(x+1, y+6)\ +\9term.write(txt[1])\ +\9term.setCursorPos(x+1, y+7)\ +\9if txt[2] then\ +\9\9term.write(txt[2])\ +\9\9term.setCursorPos(x+1, y+8)\ +\9end\ +\9local creatorName = app.creatorName\ +\9\ +\9if creatorName == \"Noodle\" then\ +\9\9creatorName = \"Leveloper\"\ +\9\9term.setTextColor(colors.cyan)\ +\9else\ +\9\9term.setTextColor(colors.lightGray)\ +\9end\ +\9local creatorName = creatorName:sub(1, aW-2)\ +\9term.write(creatorName)\ +\9\ +\9term.setCursorPos(x+1,y+aH-2)\ +\9for q=1,5 do\ +\9\9if math.floor(app.rating+0.5) >= q then\ +\9\9\9term.setTextColor(tCol.heart)\ +\9\9else\ +\9\9\9term.setTextColor(colors.lightGray)\ +\9\9end\ +\9\9term.write(\"\\3\")\ +\9end\ +\9term.setTextColor(colors.lightGray)\ +\9term.write(\" \"..app.ratings)\ +\9term.setCursorPos(x+aW-3,y+aH-2)\ +\9if fs.exists(path(app)) then\ +\9\9term.setTextColor(colors.red)\ +\9\9term.write(\"×\")\ +\9\9term.setCursorPos(x+1,y+aH-1)\ +\9\9term.setTextColor(colors.lightGray)\ +\9\9term.write(\"Installed\")\ +\9else\ +\9\9term.setTextColor(colors.blue)\ +\9\9term.write(\"\\25\")\ +\9end\ +end\ +local scroll = 0\ +\ +local function matchSearch(app)\ +\9if not s.var.search or s.var.search == \"\" then\ +\9\9return true\ +\9elseif app.name:lower():find(s.var.search:lower(), nil, true) then\ +\9\9return true\ +\9else\ +\9\9return false\ +\9end\ +end\ +\ +local function render()\ +\9term.setBackgroundColor(colors.black)\ +\9term.clear()\ +\9local w,h = term.getSize()\ +\9local cX = 3\ +\9local cY = 2\ +\9for a=1,#apps do\ +\9\9if (s.var.menu.id == \"home\" or (s.var.menu.id == \"verified\" and apps[a].verified) or (s.var.menu.id == \"installed\" and fs.exists(path(apps[a])))) and matchSearch(apps[a]) then\ +\9\9\9--rApp(apps[a],cX,cY)\ +\9\9\9if cX + (aW) >= w then\ +\9\9\9\9cX = 3\ +\9\9\9\9cY = cY+aH+1\ +\9\9\9end\ +\9\9\9rApp(apps[a],cX,cY-scroll)\ +\9\9\9cX = cX+aW+2\ +\9\9end\ +\9end\ +end\ +\ +function s.var.resetRender()\ +\9scroll = 0\ +\9os.queueEvent(\"store_render\")\ +end\ +\ +render()\ +while true do\ +\9local e = {os.pullEvent()}\ +\9if e[1] == \"store_render\" then\ +\9\9render()\ +\9elseif (e[1] == \"mouse_click\" or e[1] == \"mouse_up\") and s.var.sb and e[3] >= s.var.sidebar.x1 then\ +\9\9-- nothing\ +\9elseif e[1] == \"term_resize\" then\ +\9\9render()\ +\9elseif e[1] == \"mouse_click\" then\ +\9\9local x,y = e[3],e[4]\ +\9\9for k,a in ipairs(apps) do\ +\9\9\9if x >= a.x1 and y >= a.y1 and x <= a.x2 and y <= a.y2 then\ +\9\9\9\9selected = a\ +\9\9\9\9render()\ +\9\9\9end\ +\9\9end\ +\9elseif e[1] == \"mouse_up\" then\ +\9\9local x,y = e[3],e[4]\ +\9\9if selected.x2 and selected.y2 then\ +\9\9\9local app = selected\ +\9\9\9if x == selected.x2-2 and y == selected.y2-1 then\ +\9\9\9\9local o = {lUtils.popup(\"LevelStore\",\"Do you want to install \"..selected.name..\"?\",25,9,{\"Install\",\"Cancel\"})}\ +\9\9\9\9--local app = selected\ +\9\9\9\9selected = {}\ +\9\9\9\9render()\ +\9\9\9\9if o[1] and o[3] == \"Install\" then\ +\9\9\9\9\9lUtils.renderImg(circ[1],x-1,y-1)\ +\9\9\9\9\9local tree,folders = get(app.code)\ +\9\9\9\9\9--if not tree then return end\ +\9\9\9\9\9--term.write(\"Downloading... \")\ +\9\9\9\9\9--term.write(\"0%\")\ +\9\9\9\9\9local sPath = path(app)\ +\9\9\9\9\9if #tree > 0 or #folders > 0 then\ +\9\9\9\9\9\9fs.makeDir(sPath)\ +\9\9\9\9\9\9for f=1,#folders do\ +\9\9\9\9\9\9\9fs.makeDir(fs.combine(sPath,folders[f]))\ +\9\9\9\9\9\9end\ +\9\9\9\9\9\9for f=1,#tree do\ +\9\9\9\9\9\9\9download(app.code,tree[f],fs.combine(sPath,tree[f]))\ +\9\9\9\9\9\9\9term.setBackgroundColor(tCol.app)\ +\9\9\9\9\9\9\9term.setTextColor(tCol.txt)\ +\9\9\9\9\9\9\9lUtils.renderImg(circ[math.floor(8*(f/#tree)+1.5)],x-1,y-1)\ +\9\9\9\9\9\9\9--term.setCursorPos(x,y)\ +\9\9\9\9\9\9\9--term.write(math.floor(99*(f/#tree)))\ +\9\9\9\9\9\9end\ +\9\9\9\9\9\9local b = {lUtils.popup(\"LevelStore\",\"Do you want to create a shortcut to \"..app.name..\"?\",25,9,{\"Yes\",\"No\"})}\ +\9\9\9\9\9\9if b[1] and b[3] == \"Yes\" then\ +\9\9\9\9\9\9\9lOS.genIco(sPath)\ +\9\9\9\9\9\9end\ +\9\9\9\9\9end\ +\9\9\9\9\9lUtils.renderImg(circ[9],x-1,y-1)\ +\9\9\9\9\9render()\ +\9\9\9\9end\ +\9\9\9elseif x >= app.x1 and y >= app.y1 and x <= app.x2 and y <= app.y2 then\ +\9\9\9\9local slides = shapescape.getSlides()\ +\9\9\9\9slides.app = app\ +\9\9\9\9shapescape.setSlide(2)\ +\9\9\9\9--lUtils.popup(\"LevelStore\",\"Click: \"..x..\",\"..y..\" vs \"..(selected.x2-2)..\",\"..(selected.y2-2),27,9,{\"OK\"})\ +\9\9\9end\ +\9\9end\ +\9\9selected = {}\ +\9\9render()\ +\9elseif e[1] == \"mouse_scroll\" then\ +\9\9if scroll+e[2] >= 0 then\ +\9\9\9scroll = scroll+e[2]\ +\9\9\9render()\ +\9\9end\ +\9end\ +end\ +end\ +local ok,err = pcall(doYourThing)\ +if not ok then\ +\9printError(err)\ +end\ +--os.sleep(10)", + }, + [ "Top_menu.lua" ] = { + id = 0, + name = "Top_menu.lua", + content = "local s = shapescape.getSlide()\ +if not s.var then\ + s.var = {}\ +end\ +if not s.var.theme then\ + s.var.theme = {txt=colors.white,txt2=colors.lightGray,bg=colors.gray,app=colors.gray,bb=colors.blue}\ +end\ +local th = s.var.theme\ +local acc = {{\"\\159\\131\\139 \",\"----\",\"bbb-\"},{\" \\144\\133\\149\",\"-b0b\",\"b0b-\"},{\"\\130\\143\\135 \",\"000-\",\"----\"}}\ +local src = {{\" \\159\\140\\144\",\"--00\",\"-0--\"},{\" \\154 \\154\",\"---0\",\"-0--\"},{\"\\136\\129\\131 \",\"000-\",\"----\"}}\ +local function g(str)\ + return (str:gsub(\"-\",lUtils.toBlit(th.bg)))\ +end\ +local function blit(p)\ + term.blit(p[1],g(p[2]),g(p[3]))\ +end\ +s.var.menus = {{name=\"Home\", id=\"home\"},{name=\"Verified\", id=\"verified\"},{name=\"Installed\", id=\"installed\"}}\ +s.var.menu = s.var.menus[1]\ +local bline = {x1=2,x2=3}\ +local function render()\ + term.setBackgroundColor(th.bg)\ + term.clear()\ + term.setCursorPos(1,2)\ + for t=1,#s.var.menus do\ + local m = s.var.menus[t]\ + if s.var.menu == m then\ + term.setTextColor(th.txt)\ + else\ + term.setTextColor(th.txt2)\ + end\ + term.write(\" \")\ + m.x1 = ({term.getCursorPos()})[1]\ + term.write(m.name)\ + m.x2 = ({term.getCursorPos()})[1]-1\ + term.write(\" \")\ + end\ + local m = s.var.menu\ + local function b()\ + term.setCursorPos(1,3)\ + term.setBackgroundColor(th.bg)\ + term.clearLine()\ + term.setCursorPos(bline.x1,3)\ + term.setTextColor(th.bb)\ + term.write(string.rep(\"\\131\",bline.x2-(bline.x1-1)))\ + local w,h = term.getSize()\ + for a=1,#acc do\ + --w,h = term.getSize()\ + term.setCursorPos(w-4,a)\ + blit(acc[a])\ + end\ + if not src.x then src.x = 10 end\ + for a=1,#src do\ + term.setCursorPos(w-src.x,a)\ + blit(src[a])\ + end\ + if src.x > 11 then\ + if th.bg == colors.black then\ + term.setBackgroundColor(colors.gray)\ + else\ + term.setBackgroundColor(colors.black)\ + end\ + term.setTextColor(th.bg)\ + lUtils.border((w-src.x)+4,1,w-7,3)\ + term.setCursorPos((w-src.x)+5,2)\ + term.write(string.rep(\" \",src.x-12))\ + if s.var.search then\ + term.setCursorPos((w-src.x)+5,2)\ + term.setTextColor(th.txt2)\ + term.write(string.sub(s.var.search,1,src.x-12))\ + end\ + end\ + term.setBackgroundColor(th.bg)\ + end\ + local v = 5\ + while bline.x2 < m.x2 do\ + bline.x2 = bline.x2+math.ceil((m.x2-bline.x2)/v)\ + b()\ + os.sleep(0.05)\ + end\ + while bline.x1 < m.x1 do\ + bline.x1 = bline.x1+math.ceil((m.x1-bline.x1)/v)\ + b()\ + os.sleep(0.05)\ + end\ + while bline.x1 > m.x1 do\ + bline.x1 = bline.x1-math.ceil((bline.x1-m.x1)/v)\ + b()\ + os.sleep(0.05)\ + end\ + while bline.x2 > m.x2 do\ + bline.x2 = bline.x2-math.ceil((bline.x2-m.x2)/v)\ + b()\ + os.sleep(0.05)\ + end\ + b()\ +end\ +render()\ +local ow,oh = term.getSize()\ +while true do\ + local e = {os.pullEvent()}\ + local w,h = term.getSize()\ + if e[1] == \"mouse_up\" and e[2] == 1 and e[4] == 2 then\ + for t=1,#s.var.menus do\ + local m = s.var.menus[t]\ + if e[3] >= m.x1 and e[3] <= m.x2 then\ + \9s.var.menu = {}\ + \9--s.var.resetRender()\ + s.var.menu = m\ + render()\ + --s.var.resetRender()\ + end\ + end\ + if e[3] >= w-4 and e[3] <= w-1 then\ + if s.var.sidebar and not s.var.sb then\ + table.insert(s.objs,s.var.sidebar)\ + s.var.sb = true\ + elseif s.var.sidebar and s.var.sb then\ + for o=1,#s.objs do\ + if s.objs[o] == s.var.sidebar then\ + table.remove(s.objs,o)\ + s.var.sb = false\ + break\ + end\ + end\ + end\ + elseif e[3] >= w-src.x and e[3] <= w-7 then\ + if not src.search then\ + if w > 80 then\ + max = 24\ + else\ + max = 12\ + end\ + for t=1,max,2 do\ + src.x = src.x+2\ + render()\ + os.sleep(0.05)\ + end\ + src.search = true\ + term.setBackgroundColor(colors.black)\ + term.setTextColor(colors.white)\ + src.box = lUtils.input((w-src.x)+5,2,w-8,2,{overflowX=\"scroll\",overflowY=\"none\"})\ + end\ + --[[local txtbox = window.create(term.current(),(w-src.x)+5,2,src.x-12,1)\ + if th.bg == colors.black then\ + txtbox.setBackgroundColor(colors.gray)\ + else\ + txtbox.setBackgroundColor(colors.black)\ + end\ + txtbox.setTextColor(colors.white)\ + txtbox.clear()\ + txtbox.setCursorPos(1,1)\ + local txtcor = coroutine.create(function() s.var.search = lUtils.read() term.setCursorBlink(false) end)\ + local oterm = term.current()\ + while coroutine.status(txtcor) ~= \"dead\" do\ + e = {os.pullEvent()}\ + render()\ + txtbox.redraw()\ + term.redirect(txtbox)\ + coroutine.resume(txtcor,unpack(e))\ + term.redirect(oterm)\ + term.setTextColor(colors.red)\ + term.setCursorBlink(true)\ + end]]\ + \ + local box = src.box\ + box.state = true\ + if #box.txt > 0 then\ + \9box.select = {1, #box.txt}\ + \9box.cursor.a = #box.txt+1\ + end\ + box.update(\"term_resize\")\ + box.render()\ + while box.state do\ + \9local e = {os.pullEvent()}\ + \9if e[1] == \"key\" and e[2] == keys.enter then\ + \9\9box.state = false\ + \9else\ + \9\9box.update(unpack(e))\ + \9\9box.render()\ + \9\9if s.var.search ~= box.txt then\ + \9\9\9s.var.search = box.txt\ + \9\9\9s.var.resetRender()\ + \9\9end\ + \9end\ + end\ + term.setCursorBlink(false)\ + render()\ + s.var.resetRender()\ + --[[else\ + while src.x > 10 do\ + src.x = src.x-1\ + render()\ + os.sleep(0.05)\ + end\ + src.search = false\ + end]]\ + end\ + elseif e[1] == \"term_resize\" or w ~= ow or h ~= oh then\ + render()\ + end\ + ow,oh = w,h\ +end", + }, + [ "appPage_topbar.lua" ] = { + id = 5, + name = "appPage_topbar.lua", + content = "local sl = shapescape.getSlides()\ +local get = sl.api.get\ +local download = sl.api.download\ +local path = sl.api.path\ +local s = shapescape.getSlide()\ +local circSymbol = lUtils.asset.load(\"LevelOS/assets/Circle_Symbols.limg\")\ +if not s.var then s.var = {} end\ +while true do\ + term.setBackgroundColor(colors.lightGray)\ + term.clear()\ + --[[term.setCursorPos(4,3)\ + term.setTextColor(colors.gray)\ + term.write(sl.app.code)]]\ + if fs.exists(sl.api.path(sl.app)) then\ + lUtils.renderImg(circSymbol[2],2,2)\ + term.setBackgroundColor(colors.lightGray)\ + term.setTextColor(colors.white)\ + term.setCursorPos(8,3)\ + term.write(\"Installed\")\ + end\ + local e = {os.pullEvent()}\ + if s.var.download then\ + lUtils.renderImg(circSymbol[1],2,2)\ + term.setCursorPos(8,3)\ + term.write(\"0%\")\ + term.setCursorPos(12,3)\ + term.setTextColor(colors.white)\ + term.setBackgroundColor(colors.lightGray)\ + local w,h = term.getSize()\ + for t=12,w-14 do\ + term.write(\"\\140\")\ + end\ + term.write(\" ×\")\ + --os.sleep(5)\ + local app = sl.app\ + local tree,folders = get(app.code)\ + --if not tree then return end\ + --term.write(\"Downloading... \")\ + --term.write(\"0%\")\ + local sPath = path(app)\ + if #tree > 0 or #folders > 0 then\ + fs.makeDir(sPath)\ + for f=1,#folders do\ + fs.makeDir(fs.combine(sPath,folders[f]))\ + end\ + for f=1,#tree do\ + download(app.code,tree[f],fs.combine(sPath,tree[f]))\ + term.setBackgroundColor(colors.lightGray)\ + term.setCursorPos(8,3)\ + term.write(math.floor((f/#tree)*100+0.5)..\"%\")\ + term.setCursorPos(12,3)\ + for t=12,w-14 do\ + if (t-11)/(w-14-11) <= f/#tree then\ + term.setTextColor(colors.blue)\ + else\ + term.setTextColor(colors.white)\ + end\ + term.write(\"\\140\")\ + end\ + end\ +\9\9\9s.updateFunc = function()\ +\9\9\9\9local b = {lUtils.popup(\"LevelStore\",\"Do you want to create a shortcut to \"..app.name..\"?\",25,9,{\"Yes\",\"No\"})}\ +\9\9\9\9if b[1] and b[3] == \"Yes\" then\ +\9\9\9\9\9lOS.genIco(sPath)\ +\9\9\9\9end\ +\9\9\9end\ +\9\9\9os.pullEvent()\ + end\ + s.var.download = false\ + end\ +end", + }, + [ "toMain.lua" ] = { + id = 3, + name = "toMain.lua", + content = "local s = shapescape.getSlide()\ +if not s.var.download then\ + os.queueEvent(\"term_resize\")\ + shapescape.setSlide(1)\ +end", + }, + [ "sidebar.lua" ] = { + id = 1, + name = "sidebar.lua", + content = "term.setBackgroundColor(colors.gray)\ +term.clear()\ +local s = shapescape.getSlide()\ +for t=1,#s.objs do\ + if s.objs[t] == self then\ + s.var.sidebar = self\ + s.var.sb = false\ + table.remove(s.objs,t)\ + coroutine.yield()\ + end\ +end\ +lUtils.shapescape.run({shapescape.getSlides()[3]})", + }, + [ "getbuttonclick.lua" ] = { + id = 7, + name = "getbuttonclick.lua", + content = "local s = shapescape.getSlide()\ +local sl = shapescape.getSlides()\ +if self.txt == \" Get \" then\ + s.var.download = true\ +elseif self.txt == \" Run \" then\ + local get,path = sl.api.get,sl.api.path\ + local app = sl.app\ + if fs.isDir(path(app)) == false then\ + lOS.execute(path(app))\ + else\ + local file\ + local files = fs.list(path(app))\ + for f=1,#files do\ + local fP = fs.combine(path(app),files[f])\ + if not fs.isDir(fP) then\ + if files[f] == \"main.lua\" then\ + file = fP\ + elseif not file and lUtils.getFileType(files[f]) == \".lua\" then\ + file = fP\ + end\ + end\ + end\ + if not file then\ + lUtils.popup(\"LevelStore\",\"No lua file found in this project. Please contact the developer.\",27,9,{\"Cancel\"})\ + else\ + lOS.execute(file)\ + end\ + end\ +end", + }, + [ "username.lua" ] = { + id = 9, + name = "username.lua", + content = "self.txt = lOS.username or \"Guest\"", + }, + [ "Get_Button.lua" ] = { + id = 6, + name = "Get_Button.lua", + content = "local sl = shapescape.getSlides()\ +local s = shapescape.getSlide()\ +while true do\ + if s.var and s.var.download then\ + self.txt = \" Get \"\ + self.txtcolor = colors.lightGray\ + elseif fs.exists(sl.api.path(sl.app)) then\ + self.txt = \" Run \"\ + self.txtcolor = colors.white\ + else\ + self.txt = \" Get \"\ + self.txtcolor = colors.white\ + end\ + local e = {os.pullEvent()}\ +end", + }, + [ "appPage.lua" ] = { + id = 4, + name = "appPage.lua", + content = "local function find(a,b)\ + return string.find(a,b,nil,true)\ +end\ +local function tag(txt)\ + if not find(txt,\"<\") then return nil end\ + local txt2 = string.sub(txt,({find(txt,\"<\")})[2]+1,#txt)\ + return txt2:match(\"(.-)[^%w_]\")\ +end\ +local function renderdesc(txt)\ + local d = txt\ + while true do\ + local t = tag(d)\ + if not t then\ + return\ + else\ + local content\ + content,d = lUtils.getField(d,t)\ + if t == \"text\" then\ + print(content)\ + end\ + end\ + end\ +end\ +\ +local function wordwrap(str)\ + local x,y = term.getCursorPos()\ + local tW,tH = term.getSize()\ + local words = 0\ + for w in str:gmatch(\"%S+\") do\ + local x1,y1 = term.getCursorPos()\ + if x1+(#w*3) >= tW then\ + if words == 0 then\ + return false,0\ + else\ + bigfont.bigPrint(\" \")\ + end\ + end\ + bigfont.bigWrite(w..\" \")\ + words = words+1\ + end\ + bigfont.bigPrint(\" \")\ + return true,words\ +end\ +\ +while true do\ + term.setBackgroundColor(colors.gray)\ + term.setTextColor(colors.white)\ + term.clear()\ + local w,h = term.getSize()\ + local oterm = term.current()\ + local win = window.create(term.current(),2,2,w-2,h)\ + term.redirect(win)\ + term.setBackgroundColor(colors.gray)\ + term.setTextColor(colors.white)\ + term.clear()\ + term.setCursorPos(1,1)\ + local slides = shapescape.getSlides()\ + local app = slides.app\ + if app.icon then\ + lUtils.renderImg(app.icon,1,1)\ + term.setCursorPos(6,1)\ + end\ + if not wordwrap(app.name) then\ + local x,y = term.getCursorPos()\ + term.setCursorPos(x,2)\ + print(app.name)\ + print(\"\")\ + end\ + print(\"\")\ + renderdesc(app.description)\ + local e = {os.pullEvent()}\ +end", + }, +} + +local nAssets = {} +for key,value in pairs(assets) do nAssets[key] = value nAssets[assets[key].id] = assets[key] end +assets = nAssets +nAssets = nil + +local slides = { + { + h = 19, + x = 65, + y = 22, + objs = { + { + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + x1 = 1, + y1 = 1, + x2 = 51, + y2 = 3, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 0, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + type = "window", + ox2 = 0, + color = 128, + }, + { + oy2 = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + type = "window", + x1 = 1, + y1 = 4, + x2 = 51, + y2 = 19, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 2, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 32768, + border = { + color = 256, + type = 1, + }, + }, + { + oy2 = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap bottom", + }, + type = "window", + x1 = 22, + y1 = 4, + x2 = 51, + y2 = 19, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox1 = 29, + ox2 = 0, + color = 128, + border = { + color = 1, + type = 1, + }, + }, + }, + c = 1, + w = 51, + }, + { + h = 19, + x = 65, + y = 22, + objs = { + { + oy2 = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + border = { + color = 128, + type = 1, + }, + x1 = 1, + y1 = 4, + x2 = 51, + y2 = 19, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 8, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = 10, + }, + }, + ox2 = 0, + color = 32768, + type = "window", + }, + { + oy2 = -15, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + type = "window", + x1 = 9, + y1 = 12, + x2 = 43, + y2 = 34, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 4, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 8, + color = 128, + oy1 = -2, + border = { + color = 1, + type = 1, + }, + }, + { + oy2 = -1, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap center", + }, + type = "window", + x1 = 9, + y1 = 7, + x2 = 43, + y2 = 11, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 5, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 8, + color = 128, + oy1 = 3, + border = { + color = 256, + type = 1, + }, + }, + { + oy2 = 0, + border = { + color = 256, + type = 1, + }, + txtcolor = 1, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 6, + }, + mouse_up = { + [ 2 ] = 7, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + color = 512, + snap = { + Top = "Snap center", + Right = "Snap right", + Left = "Snap right", + Bottom = "Snap center", + }, + x1 = 33, + y1 = 8, + x2 = 41, + y2 = 10, + ox1 = 18, + txt = " Get", + ox2 = 10, + type = "text", + input = false, + oy1 = 2, + }, + { + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 0, + type = 1, + }, + x1 = 1, + y1 = 1, + x2 = 51, + y2 = 3, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 128, + input = false, + type = "rect", + }, + { + type = "text", + txtcolor = 1, + y1 = 1, + x2 = 5, + y2 = 3, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = 3, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txt = " \ + \27", + x1 = 1, + color = 0, + input = false, + border = { + color = 0, + type = 1, + }, + }, + }, + c = 2, + w = 51, + }, + { + h = 19, + x = 65, + y = 22, + objs = { + { + oy2 = 0, + snap = { + Top = "Snap top", + Right = "Snap right", + Left = "Snap left", + Bottom = "Snap bottom", + }, + type = "rect", + x1 = 1, + y1 = 1, + x2 = 51, + y2 = 19, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + ox2 = 0, + color = 128, + border = { + color = 0, + type = 1, + }, + }, + { + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + type = "text", + border = { + color = 0, + type = 1, + }, + x1 = 3, + y1 = 2, + x2 = 15, + y2 = 2, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txt = "Logged in as", + txtcolor = 1, + color = 128, + input = false, + }, + { + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + txtcolor = 2048, + border = { + color = 0, + type = 1, + }, + x1 = 16, + y1 = 2, + x2 = 25, + y2 = 2, + event = { + selected = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = 9, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + Initialize = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + }, + txt = "loading...", + type = "text", + color = 0, + input = false, + }, + { + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + x1 = 3, + type = "text", + txtcolor = 1, + y1 = 8, + x2 = 20, + y2 = 10, + event = { + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + }, + txt = " App Settings", + border = { + color = 128, + type = 1, + }, + color = 32768, + input = false, + }, + { + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 128, + type = 1, + }, + type = "text", + x1 = 3, + y1 = 4, + x2 = 22, + y2 = 6, + event = { + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + }, + txt = " Manage Account", + txtcolor = 1, + color = 32768, + input = false, + }, + { + snap = { + Top = "Snap top", + Right = "Snap left", + Left = "Snap left", + Bottom = "Snap top", + }, + border = { + color = 128, + type = 1, + }, + type = "text", + x1 = 3, + y1 = 12, + x2 = 19, + y2 = 14, + event = { + render = { + [ 2 ] = -1, + }, + update = { + [ 2 ] = -1, + }, + Coroutine = { + [ 2 ] = -1, + }, + mouse_up = { + [ 2 ] = -1, + }, + mouse_click = { + [ 2 ] = -1, + }, + focus = { + [ 2 ] = -1, + }, + }, + txt = " Manage Apps", + txtcolor = 1, + color = 32768, + input = false, + }, + }, + c = 3, + w = 51, + }, + { + h = 19, + x = 61, + y = 22, + objs = {}, + c = 4, + w = 51, + }, +} + +for s=1,#slides do + local slide = slides[s] + for o=1,#slide.objs do + local obj = slide.objs[o] + for key,value in pairs(obj.event) do + if assets[ value[2] ] then + lUtils.shapescape.addScript(obj,value[2],key,assets,LevelOS,slides) + else + obj.event[key] = {function() end,-1} + end + end + end +end + + local tArgs = {...} +if tArgs[1] and tArgs[1] == "load" then + return {assets=assets,slides=slides} +end + + +return lUtils.shapescape.run(slides,...) \ No newline at end of file diff --git a/Program_Files/LevelOS/Store/taskbar.limg b/Program_Files/LevelOS/Store/taskbar.limg new file mode 100644 index 0000000..806c01f --- /dev/null +++ b/Program_Files/LevelOS/Store/taskbar.limg @@ -0,0 +1 @@ +{{{"—Œ”"," 0","00 ",},{"Š…","000"," ",},},} \ No newline at end of file diff --git a/bigfont b/bigfont index 603d3a4..370d24d 100644 --- a/bigfont +++ b/bigfont @@ -1,14 +1,14 @@ ------------------------------------------------------------------------------------- -- Wojbies API 5.0 - Bigfont - functions to write bigger font using drawing sybols -- ------------------------------------------------------------------------------------- --- Copyright (c) 2015-2022 Wojbie (wojbie@wojbie.net) +-- Copyright (c) 2015-2020 Wojbie (wojbie@wojbie.net) -- Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: -- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. -- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. -- 4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -- 5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. --- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY. +-- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --Switch to true to replace generic currency sign "\164" with krist symbol. local krist = false @@ -16,10 +16,10 @@ local krist = false --### Initializing local b = shell and {} or (_ENV or getfenv()) b.versionName = "Bigfont By Wojbie" -b.versionNum = 5.003 --2021-07-21 +b.versionNum = 5.001 --2021-03-12 b.doc = {} -local expect, field if require then expect, field = require "cc.expect".expect, require "cc.expect".field else local ok, did = pcall(dofile,"rom/modules/main/cc/expect.lua") if ok then field, expect = did.field, did.expect else field, expect = function() end, function() end end end +local expect, field if require then expect, field = require "cc.expect".expect, require "cc.expect".field else expect = dofile("rom/modules/main/cc/expect.lua") field = expect.field expect = expect.expect end --### Font database local rawFont = {{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147", "\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132", "\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131", "\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132", "\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32", "\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129", "\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32", "\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32", "\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148", "\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32", "\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32", "\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148", "\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149", "\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32", "\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32", "\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32", "\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132", "\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32", "\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149", "\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32", "\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148", "\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32", "\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32", "\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129", "\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32", "\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32", "\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32", "\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148", "\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32", "\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32", "\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32", "\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132", "\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149", "\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32", "\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32", "\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32", "\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144", "\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149", "\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129" }, {[[000110000110110000110010101000000010000000100101]], [[000000110110000000000010101000000010000000100101]], [[000000000000000000000000000000000000000000000000]], [[100010110100000010000110110000010100000100000110]], [[000000110000000010110110000110000000000000110000]], [[000000000000000000000000000000000000000000000000]], [[000000110110000010000000100000100000000000000010]], [[000000000110110100010000000010000000000000000100]], [[000000000000000000000000000000000000000000000000]], [[010000000000100110000000000000000000000110010000]], [[000000000000000000000000000010000000010110000000]], [[000000000000000000000000000000000000000000000000]], [[011110110000000100100010110000000100000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110000110110000000000000000000010100100010000000]], [[000010000000000000110110000000000100010010000000]], [[000000000000000000000000000000000000000000000000]], [[010110010110100110110110010000000100000110110110]], [[000000000000000000000110000000000110000000000000]], [[000000000000000000000000000000000000000000000000]], [[010100010110110000000000000000110000000010000000]], [[110110000000000000110000110110100000000010000000]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[100100100100100100100100100100100100100100100100]], [[000000110100110110000010000011110000000000011000]], [[000000000100000000000010000011000110000000001000]], [[000000000000000000000000000000000000000000000000]], [[010000100100000000000000000100000000010010110000]], [[000000000000000000000000000000110110110110110000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110000000110110110110110110110110]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000110110000110010000000000000000010010]], [[000010000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110110110110000000000000]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110000000000000000010000]], [[000000000000000000000000100000000000000110000110]], [[000000000000000000000000000000000000000000000000]] }} @@ -250,8 +250,8 @@ b.writeOn = function(tTerminal, nSize, sString, nX, nY) field(tTerminal, "getBackgroundColor", "function") expect(2, nSize, "number") expect(3, sString, "string") - expect(4, nX, "number", "nil") - expect(5, nY, "number", "nil") + expect(3, nX, "number", "nil") + expect(3, nY, "number", "nil") press(tTerminal, makeText(nSize, sString, tTerminal.getTextColor(), tTerminal.getBackgroundColor()), nX, nY) end diff --git a/blittle b/blittle new file mode 100644 index 0000000..8d9eead --- /dev/null +++ b/blittle @@ -0,0 +1,743 @@ +-- +--------------------------------------------------------+ +-- | | +-- | BLittle | +-- | | +-- +--------------------------------------------------------+ + +local version = "Version 1.1.6beta" + +-- By Jeffrey Alexander, aka Bomb Bloke. +-- Convenience functions to make use of ComputerCraft 1.76's new "drawing" characters. +-- http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ + +------------------------------------------------------------- + +if shell then + local arg = {...} + + if #arg == 0 then + print("Usage:") + print("blittle [args]") + return + end + + if not blittle then os.loadAPI(shell.getRunningProgram()) end + local oldTerm = term.redirect(blittle.createWindow()) + shell.run(unpack(arg)) + term.redirect(oldTerm) + + return +end + +local relations = {[0] = {8, 4, 3, 6, 5}, {4, 14, 8, 7}, {6, 10, 8, 7}, {9, 11, 8, 0}, {1, 14, 8, 0}, {13, 12, 8, 0}, {2, 10, 8, 0}, {15, 8, 10, 11, 12, 14}, + {0, 7, 1, 9, 2, 13}, {3, 11, 8, 7}, {2, 6, 7, 15}, {9, 3, 7, 15}, {13, 5, 7, 15}, {5, 12, 8, 7}, {1, 4, 7, 15}, {7, 10, 11, 12, 14}} + +local colourNum, exponents, colourChar = {}, {}, {} +for i = 0, 15 do exponents[2^i] = i end +do + local hex = "0123456789abcdef" + for i = 1, 16 do + colourNum[hex:sub(i, i)] = i - 1 + colourNum[i - 1] = hex:sub(i, i) + colourChar[hex:sub(i, i)] = 2 ^ (i - 1) + colourChar[2 ^ (i - 1)] = hex:sub(i, i) + + local thisRel = relations[i - 1] + for i = 1, #thisRel do thisRel[i] = 2 ^ thisRel[i] end + end +end + +local function getBestColourMatch(usage) + local lastCol = relations[exponents[usage[#usage][1]]] + + for j = 1, #lastCol do + local thisRelation = lastCol[j] + for i = 1, #usage - 1 do if usage[i][1] == thisRelation then return i end end + end + + return 1 +end + +local function colsToChar(pattern, totals) + if not totals then + local newPattern = {} + totals = {} + for i = 1, 6 do + local thisVal = pattern[i] + local thisTot = totals[thisVal] + totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + end + pattern = newPattern + end + + local usage = {} + for key, value in pairs(totals) do usage[#usage + 1] = {key, value} end + + if #usage > 1 then + -- Reduce the chunk to two colours: + while #usage > 2 do + table.sort(usage, function (a, b) return a[2] > b[2] end) + local matchToInd, usageLen = getBestColourMatch(usage), #usage + local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] + for i = 1, 6 do if pattern[i] == matchFrom then + pattern[i] = matchTo + usage[matchToInd][2] = usage[matchToInd][2] + 1 + end end + usage[usageLen] = nil + end + + -- Convert to character. Adapted from oli414's function: + -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ + local data = 128 + for i = 1, #pattern - 1 do if pattern[i] ~= pattern[6] then data = data + 2^(i-1) end end + return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] + else + -- Solid colour character: + return "\128", colourChar[pattern[1]], colourChar[pattern[1]] + end +end + +local function snooze() + local myEvent = tostring({}) + os.queueEvent(myEvent) + os.pullEvent(myEvent) +end + +function shrink(image, bgCol) + local results, width, height, bgCol = {{}, {}, {}}, 0, #image + #image % 3, bgCol or colours.black + for i = 1, #image do if #image[i] > width then width = #image[i] end end + + for y = 0, height - 1, 3 do + local cRow, tRow, bRow, counter = {}, {}, {}, 1 + + for x = 0, width - 1, 2 do + -- Grab a 2x3 chunk: + local pattern, totals = {}, {} + + for yy = 1, 3 do for xx = 1, 2 do + pattern[#pattern + 1] = (image[y + yy] and image[y + yy][x + xx]) and (image[y + yy][x + xx] == 0 and bgCol or image[y + yy][x + xx]) or bgCol + totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 + end end + + cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) + counter = counter + 1 + end + + results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) + end + + results.width, results.height = #results[1][1], #results[1] + + return results +end + +function shrinkGIF(image, bgCol) + if not GIF and not os.loadAPI("GIF") then error("blittle.shrinkGIF: Load GIF API first.", 2) end + + image = GIF.flattenGIF(image) + snooze() + + local prev = GIF.toPaintutils(image[1]) + snooze() + + prev = blittle.shrink(prev, bgCol) + prev.delay = image[1].delay + image[1] = prev + snooze() + + image.width, image.height = prev.width, prev.height + + for i = 2, #image do + local temp = GIF.toPaintutils(image[i]) + snooze() + + temp = blittle.shrink(temp, bgCol) + snooze() + + local newImage = {{}, {}, {}, ["delay"] = image[i].delay, ["width"] = temp.width, ["height"] = 0} + + local a, b, c, pa, pb, pc = temp[1], temp[2], temp[3], prev[1], prev[2], prev[3] + for i = 1, temp.height do + local a1, b1, c1, pa1, pb1, pc1 = a[i], b[i], c[i], pa[i], pb[i], pc[i] + + if a1 ~= pa1 or b1 ~= pb1 or c1 ~= pc1 then + local min, max = 1, #a1 + local a2, b2, c2, pa2, pb2, pc2 = {a1:byte(1, max)}, {b1:byte(1, max)}, {c1:byte(1, max)}, {pa1:byte(1, max)}, {pb1:byte(1, max)}, {pc1:byte(1, max)} + + for j = 1, max do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then + min = j + break + end end + + for j = max, min, -1 do if a2[j] ~= pa2[j] or b2[j] ~= pb2[j] or c2[j] ~= pc2[j] then + max = j + break + end end + + newImage[1][i], newImage[2][i], newImage[3][i], newImage.height = min > 1 and {min - 1, a1:sub(min, max)} or a1:sub(min, max), b1:sub(min, max), c1:sub(min, max), i + end + + snooze() + end + + image[i], prev = newImage, temp + + for j = 1, i - 1 do + local oldImage = image[j] + + if type(oldImage[1]) == "table" and oldImage.height == newImage.height then + local same = true + + for k = 1, oldImage.height do + local comp1, comp2 = oldImage[1][k], newImage[1][k] + + if type(comp1) ~= type(comp2) or + (type(comp1) == "string" and comp1 ~= comp2) or + (type(comp1) == "table" and (comp1[1] ~= comp2[1] or comp1[2] ~= comp2[2])) or + oldImage[2][k] ~= newImage[2][k] or + oldImage[3][k] ~= newImage[3][k] then + same = false + break + end + end + + if same then + newImage[1], newImage[2], newImage[3] = j + break + end + end + + snooze() + end + end + + return image +end + +local function newLine(width, bCol) + local line = {} + for i = 1, width do line[i] = {bCol, bCol, bCol, bCol, bCol, bCol} end + return line +end + +function createWindow(parent, x, y, width, height, visible) + if parent == term or not parent then + parent = term.current() + elseif type(parent) ~= "table" or not parent.write then + error("blittle.newWindow: \"parent\" does not appear to be a terminal object.", 2) + end + + local workBuffer, backBuffer, frontBuffer, window, tCol, bCol, curX, curY, blink, cWidth, cHeight, pal = {}, {}, {}, {}, colours.white, colours.black, 1, 1, false + if type(visible) ~= "boolean" then visible = true end + x, y = x and math.floor(x) or 1, y and math.floor(y) or 1 + + do + local xSize, ySize = parent.getSize() + cWidth, cHeight = (width or xSize), (height or ySize) + width, height = cWidth * 2, cHeight * 3 + end + + if parent.setPaletteColour then + pal = {} + + local counter = 1 + for i = 1, 16 do + pal[counter] = {parent.getPaletteColour(counter)} + counter = counter * 2 + end + + window.getPaletteColour = function(colour) + return unpack(pal[colour]) + end + + window.setPaletteColour = function(colour, r, g, b) + pal[colour] = {r, g, b} + if visible then return parent.setPaletteColour(colour, r, g, b) end + end + + window.getPaletteColor, window.setPaletteColor = window.getPaletteColour, window.setPaletteColour + end + + window.blit = function(_, _, bC) + local bClen = #bC + if curX > width or curX + bClen < 2 or curY < 1 or curY > height then + curX = curX + bClen + return + end + + if curX < 1 then + bC = bC:sub(2 - curX) + curX, bClen = 1, #bC + end + + if curX + bClen - 1 > width then bC, bClen = bC:sub(1, width - curX + 1), width - curX + 1 end + + local colNum, rowNum, thisX, yBump = math.floor((curX - 1) / 2) + 1, math.floor((curY - 1) / 3) + 1, (curX - 1) % 2, ((curY - 1) % 3) * 2 + local firstColNum, lastColNum, thisRow = colNum, math.floor((curX + bClen) / 2), backBuffer[rowNum] + local thisChar = thisRow[colNum] + + for i = 1, bClen do + thisChar[thisX + yBump + 1] = colourChar[bC:sub(i, i)] + + if thisX == 1 then + thisX, colNum = 0, colNum + 1 + thisChar = thisRow[colNum] + if not thisChar then break end + else thisX = 1 end + end + + if visible then + local chars1, chars2, chars3, count = {}, {}, {}, 1 + + for i = firstColNum, lastColNum do + chars1[count], chars2[count], chars3[count] = colsToChar(thisRow[i]) + count = count + 1 + end + + chars1, chars2, chars3 = table.concat(chars1), table.concat(chars2), table.concat(chars3) + parent.setCursorPos(x + math.floor((curX - 1) / 2), y + math.floor((curY - 1) / 3)) + parent.blit(chars1, chars2, chars3) + local thisRow = frontBuffer[rowNum] + frontBuffer[rowNum] = {thisRow[1]:sub(1, firstColNum - 1) .. chars1 .. thisRow[1]:sub(lastColNum + 1), thisRow[2]:sub(1, firstColNum - 1) .. chars2 .. thisRow[2]:sub(lastColNum + 1), thisRow[3]:sub(1, firstColNum - 1) .. chars3 .. thisRow[3]:sub(lastColNum + 1)} + else + local thisRow = workBuffer[rowNum] + + if (not thisRow[firstColNum]) or thisRow[firstColNum] < lastColNum then + local x, newLastColNum = 1, lastColNum + + while x <= lastColNum + 1 do + local thisSpot = thisRow[x] + + if thisSpot then + if thisSpot >= firstColNum - 1 then + if x < firstColNum then firstColNum = x else thisRow[x] = nil end + if thisSpot > newLastColNum then newLastColNum = thisSpot end + end + x = thisSpot + 1 + else x = x + 1 end + end + + thisRow[firstColNum] = newLastColNum + if thisRow.max <= newLastColNum then thisRow.max = firstColNum end + end + end + + curX = curX + bClen + end + + window.write = function(text) + window.blit(nil, nil, string.rep(colourChar[bCol], #tostring(text))) + end + + window.clearLine = function() + local oldX = curX + curX = 1 + window.blit(nil, nil, string.rep(colourChar[bCol], width)) + curX = oldX + end + + window.clear = function() + local t, fC, bC = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth) + for y = 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {t, fC, bC} end + window.redraw() + end + + window.getCursorPos = function() + return curX, curY + end + + window.setCursorPos = function(newX, newY) + curX, curY = math.floor(newX), math.floor(newY) + if visible and blink then window.restoreCursor() end + end + + window.restoreCursor = function() end + window.setCursorBlink = window.restoreCursor + + window.isColour = function() + return parent.isColour() + end + window.isColor = window.isColour + + window.getSize = function() + return width, height + end + + window.scroll = function(lines) + lines = math.floor(lines) + + if lines ~= 0 then + if lines % 3 == 0 then + local newWB, newBB, newFB, line1, line2, line3 = {}, {}, {}, string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth) + for y = 1, cHeight do newWB[y], newBB[y], newFB[y] = workBuffer[y + lines] or {["max"] = 0}, backBuffer[y + lines] or newLine(cWidth, bCol), frontBuffer[y + lines] or {line1, line2, line3} end + workBuffer, backBuffer, frontBuffer = newWB, newBB, newFB + else + local newBB, tRowNum, tBump, sRowNum, sBump = {}, 1, 0, math.floor(lines / 3) + 1, (lines % 3) * 2 + local sRow, tRow = backBuffer[sRowNum], {} + for x = 1, cWidth do tRow[x] = {} end + + for y = 1, height do + if sRow then + for x = 1, cWidth do + local tChar, sChar = tRow[x], sRow[x] + tChar[tBump + 1], tChar[tBump + 2] = sChar[sBump + 1], sChar[sBump + 2] + end + else + for x = 1, cWidth do + local tChar = tRow[x] + tChar[tBump + 1], tChar[tBump + 2] = bCol, bCol + end + end + + tBump, sBump = tBump + 2, sBump + 2 + + if tBump > 4 then + tBump, newBB[tRowNum] = 0, tRow + tRowNum, tRow = tRowNum + 1, {} + for x = 1, cWidth do tRow[x] = {} end + end + + if sBump > 4 then + sRowNum, sBump = sRowNum + 1, 0 + sRow = backBuffer[sRowNum] + end + end + + for y = 1, cHeight do workBuffer[y] = {["max"] = 1, cWidth} end + + backBuffer = newBB + end + + window.redraw() + end + end + + window.setTextColour = function(newCol) + tCol = newCol + end + window.setTextColor = window.setTextColour + + window.setBackgroundColour = function(newCol) + bCol = newCol + end + window.setBackgroundColor = window.setBackgroundColour + + window.getTextColour = function() + return tCol + end + window.getTextColor = window.getTextColour + + window.getBackgroundColour = function() + return bCol + end + window.getBackgroundColor = window.getBackgroundColour + + window.redraw = function() + if visible then + for i = 1, cHeight do + local work, front = workBuffer[i], frontBuffer[i] + local front1, front2, front3 = front[1], front[2], front[3] + + if work.max > 0 then + local line1, line2, line3, lineLen, skip, back, count = {}, {}, {}, 1, 0, backBuffer[i], 1 + + while count <= work.max do if work[count] then + if skip > 0 then + line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count - skip, count - 1), front2:sub(count - skip, count - 1), front3:sub(count - skip, count - 1) + skip, lineLen = 0, lineLen + 1 + end + + for i = count, work[count] do + line1[lineLen], line2[lineLen], line3[lineLen] = colsToChar(back[i]) + lineLen = lineLen + 1 + end + + count = work[count] + 1 + else skip, count = skip + 1, count + 1 end end + + if count < cWidth + 1 then line1[lineLen], line2[lineLen], line3[lineLen] = front1:sub(count), front2:sub(count), front3:sub(count) end + + front1, front2, front3 = table.concat(line1), table.concat(line2), table.concat(line3) + frontBuffer[i], workBuffer[i] = {front1, front2, front3}, {["max"] = 0} + end + + parent.setCursorPos(x, y + i - 1) + parent.blit(front1, front2, front3) + end + + if pal then + local counter = 1 + for i = 1, 16 do + parent.setPaletteColour(counter, unpack(pal[counter])) + counter = counter * 2 + end + end + end + end + + window.setVisible = function(newVis) + newVis = newVis and true or false + + if newVis and not visible then + visible = true + window.redraw() + else visible = newVis end + end + + window.getPosition = function() + return x, y + end + + window.reposition = function(newX, newY, newWidth, newHeight) + x, y = type(newX) == "number" and math.floor(newX) or x, type(newY) == "number" and math.floor(newY) or y + + if type(newWidth) == "number" then + newWidth = math.floor(newWidth) + if newWidth > cWidth then + local line1, line2, line3 = string.rep("\128", newWidth - cWidth), string.rep(colourChar[tCol], newWidth - cWidth), string.rep(colourChar[bCol], newWidth - cWidth) + for y = 1, cHeight do + local bRow, fRow = backBuffer[y], frontBuffer[y] + for x = cWidth + 1, newWidth do bRow[x] = {bCol, bCol, bCol, bCol, bCol, bCol} end + frontBuffer[y] = {fRow[1] .. line3, fRow[2] .. line2, fRow[3] .. line3} + end + elseif newWidth < cWidth then + for y = 1, cHeight do + local wRow, bRow, fRow = workBuffer[y], backBuffer[y], frontBuffer[y] + for x = newWidth + 1, cWidth do bRow[x] = nil end + frontBuffer[y] = {fRow[1]:sub(1, newWidth), fRow[2]:sub(1, newWidth), fRow[3]:sub(1, newWidth)} + + while wRow[wRow.max] and wRow[wRow.max] > newWidth do + wRow[wRow.max] = nil + wRow.max = table.maxn(wRow) + end + end + end + width, cWidth = newWidth * 2, newWidth + end + + if type(newHeight) == "number" then + newHeight = math.floor(newHeight) + if newHeight > cHeight then + local line1, line2, line3 = string.rep("\128", cWidth), string.rep(colourChar[tCol], cWidth), string.rep(colourChar[bCol], cWidth) + for y = cHeight + 1, newHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = {["max"] = 0}, newLine(cWidth, bCol), {line1, line2, line3} end + elseif newHeight < cHeight then + for y = newHeight + 1, cHeight do workBuffer[y], backBuffer[y], frontBuffer[y] = nil, nil, nil end + end + height, cHeight = newHeight * 3, newHeight + end + + window.redraw() + end + + window.clear() + return window +end + +function draw(image, x, y, terminal) + local t, tC, bC = image[1], image[2], image[3] + x, y, terminal = x or 1, y or 1, terminal or term.current() + + for i = 1, image.height do + local tI = t[i] + if type(tI) == "string" then + terminal.setCursorPos(x, y + i - 1) + terminal.blit(tI, tC[i], bC[i]) + elseif type(tI) == "table" then + terminal.setCursorPos(x + tI[1], y + i - 1) + terminal.blit(tI[2], tC[i], bC[i]) + end + end +end + +function save(image, filename) + local output = fs.open(filename, "wb") + if not output then error("Can't open "..filename.." for output.") end + + local writeByte = output.write + + local function writeInt(num) + writeByte(bit.band(num, 255)) + writeByte(bit.brshift(num, 8)) + end + + writeByte(66) -- B + writeByte(76) -- L + writeByte(84) -- T + + local animated = image[1].delay ~= nil + writeByte(animated and 1 or 0) + + if animated then + writeInt(#image) + else + local tempImage = {image[1], image[2], image[3]} + image[1], image[2], image[3] = tempImage, nil, nil + end + + local width, height = image.width, image.height + + writeInt(width) + writeInt(height) + + for k = 1, #image do + local thisImage = image[k] + + if type(thisImage[1]) == "number" then + writeByte(3) + writeInt(thisImage[1]) + else + for i = 1, height do + if thisImage[1][i] then + local rowType, len, thisRow = type(thisImage[1][i]) + + if rowType == "string" then + writeByte(1) + len = #thisImage[1][i] + writeInt(len) + thisRow = {thisImage[1][i]:byte(1, len)} + elseif rowType == "table" then + writeByte(2) + len = #thisImage[1][i][2] + writeInt(len) + writeInt(thisImage[1][i][1]) + thisRow = {thisImage[1][i][2]:byte(1, len)} + else + error("Malformed row record #"..i.." in frame #"..k.." when attempting to save \""..filename.."\", type is "..rowType..".") + end + + for x = 1, len do writeByte(thisRow[x]) end + + local txt, bg = thisImage[2][i], thisImage[3][i] + for x = 1, len do writeByte(colourNum[txt:sub(x, x)] + colourNum[bg:sub(x, x)] * 16) end + else writeByte(0) end + end + end + + if animated then writeInt(thisImage.delay * 20) end + + snooze() + end + + if image.pal then + writeByte(#image.pal) + for i = 0, #image.pal do for j = 1, 3 do writeByte(image.pal[i][j]) end end + end + + if not animated then + image[2], image[3] = image[1][2], image[1][3] + image[1] = image[1][1] + end + + output.close() +end + +function load(filename) + local input = fs.open(filename, "rb") + if not input then error("Can't open "..filename.." for input.") end + + local read = input.read + + local function readInt() + local result = read() + return result + bit.blshift(read(), 8) + end + + if string.char(read(), read(), read()) ~= "BLT" then + -- Assume legacy format. + input.close() + input = fs.open(filename, "rb") + + read = input.read + + function readInt() + local result = input.read() + return result + bit.blshift(input.read(), 8) + end + + local image = {} + image.width, image.height = readInt(), readInt() + + for i = 1, 3 do + local thisSet = {} + for y = 1, image.height do + local thisRow = {} + for x = 1, image.width do thisRow[x] = string.char(input.read()) end + thisSet[y] = table.concat(thisRow) + end + image[i] = thisSet + end + + input.close() + + return image + end + + local image, animated, frames = {}, read() == 1 + if animated then frames = readInt() else frames = 1 end + + local width, height = readInt(), readInt() + image.width, image.height = width, height + + for k = 1, frames do + local thisImage = {["width"] = width, ["height"] = 0} + local chr, txt, bg = {}, {}, {} + + for i = 1, height do + local lineType = read() + + if lineType == 3 then + chr, txt, bg = readInt() + break + elseif lineType > 0 then + local l1, l2, len, bump = {}, {}, readInt() + if lineType == 2 then bump = readInt() end + + for x = 1, len do l1[x] = read() end + chr[i] = string.char(unpack(l1)) + if lineType == 2 then chr[i] = {bump, chr[i]} end + + for x = 1, len do + local thisVal = read() + l1[x], l2[x] = colourNum[bit.band(thisVal, 15)], colourNum[bit.brshift(thisVal, 4)] + end + + txt[i], bg[i], thisImage.height = table.concat(l1), table.concat(l2), i + end + end + + if animated then thisImage["delay"] = readInt() / 20 end + thisImage[1], thisImage[2], thisImage[3] = chr, txt, bg + image[k] = thisImage + + snooze() + end + + local palLength = read() + if palLength and palLength > 0 then + image.pal = {} + for i = 0, palLength do image.pal[i] = {read(), read(), read()} end + end + + if not animated then + image[2], image[3] = image[1][2], image[1][3] + image[1] = image[1][1] + end + + input.close() + + return image +end + +if term.setPaletteColour then + function applyPalette(image, terminal) + terminal = terminal or term + + local col, pal = 1, image.pal + + for i = 0, #pal do + local thisCol = pal[i] + terminal.setPaletteColour(col, thisCol[1] / 255, thisCol[2] / 255, thisCol[3] / 255) + col = col * 2 + end + end +end \ No newline at end of file diff --git a/real_startup.lua b/real_startup.lua new file mode 100644 index 0000000..f04ffb0 --- /dev/null +++ b/real_startup.lua @@ -0,0 +1,847 @@ +-- LevelOS + +term.clear() +term.setCursorPos(1,1) + +if not fs.exists("LevelOS") then + --Let the bios know the system is broken + fs.delete("/real_startup.lua") + os.reboot() + return +end +for k,v in pairs(colors) do + if type(v) == "number" then + term.setPaletteColor(v,term.native().getPaletteColor(v)) + end +end + +if lOS then + error("LevelOS is already running, silly!") +end + +_G.lOS = {} +lOS.path = shell.getRunningProgram() + + +local chunkSize = 32768 +if jit then + chunkSize = 4092 +end + +local function encodeUTF8(asciiText) + local utf8Text = "" + + for i=1, #asciiText, chunkSize do + utf8Text = utf8Text..utf8.char(string.byte(asciiText, i, math.min(i + (chunkSize-1), #asciiText))) + end + + return utf8Text +end + +local function decodeUTF8(utf8Text) + local asciiText = "" + + local ok, t = pcall(function() + return string.char(utf8.codepoint(utf8Text, 1, #utf8Text)) + end) + + if ok then + asciiText = t + else + local ok2, err = pcall(function() + for _, codepoint in utf8.codes(utf8Text) do + if codepoint < 256 then + asciiText = asciiText .. string.char(codepoint) + else + asciiText = asciiText .. "?" + end + end + end) + + if not ok2 then + _G.debugCurrentText = asciiText + _G.debugTextInput = utf8Text + + error(err, 2) + end + end + + return asciiText +end + + +local function encodeAll(...) + local tbl = table.pack(...) + for k,v in pairs(tbl) do + if type(v) == "string" and not utf8.len(v) then + tbl[k] = encodeUTF8(v) + end + end + + return table.unpack(tbl, 1, tbl.n) +end + +local function decodeAll(...) + local tbl = table.pack(...) + for k,v in pairs(tbl) do + if type(v) == "string" then + tbl[k] = decodeUTF8(v) + end + end + + return table.unpack(tbl, 1, tbl.n) +end + +lOS.utf8 = { + decode = decodeUTF8, + encode = encodeUTF8, + decodeAll = decodeAll, + encodeAll = encodeAll, +} + +local function extractVersion(str) + local version = str:match("ComputerCraft (%d+%.%d+%.%d+)") + return version +end + +local function isVersionAbove(version1, version2) + local function splitVersion(version) + local parts = {} + for part in version:gmatch("(%d+)") do + table.insert(parts, tonumber(part)) + end + return parts + end + + local v1Parts = splitVersion(version1) + local v2Parts = splitVersion(version2) + + for i = 1, math.max(#v1Parts, #v2Parts) do + local v1 = v1Parts[i] or 0 + local v2 = v2Parts[i] or 0 + if v1 > v2 then + return true + elseif v1 < v2 then + return false + end + end + + return true -- They are equal if all parts are equal +end + +if isVersionAbove(extractVersion(_HOST), "1.109") then + local fopen = fs.open + + function fs.open(path, mode) + local f = fopen(path, mode) + if not f then return nil end + + local customHandle = {} + + for k,v in pairs(f) do + if mode:find("b") then + customHandle[k] = function(...) return v(...) end + else + customHandle[k] = function(...) return decodeAll(v(encodeAll(...))) end + end + end + + return customHandle + end +end + + +--enable mouse if no color +if not rtype then + _G.rtype = type + _G.type = function(obj) + local mt = getmetatable(obj) + if rtype(mt) == "table" and mt.__type then + if rtype(mt.__type) == "string" then + return mt.__type + elseif rtype(mt.__type) == "function" then + return mt.__type(obj) + end + else + return rtype(obj) + end + end +end + +function _G.pairs(t) + local mt = getmetatable(t) + if mt and type(mt.__pairs) == "function" then + return mt.__pairs(t) + else + return next, t, nil + end +end + +if not hardreboot then + _G.hardreboot = os.reboot +end + +if fs.combine("a","b","c") == fs.combine("a","b") then + local ocombine = fs.combine + function fs.combine(path,...) + local parts = {...} + for p=1,#parts do + path = ocombine(path,parts[p]) + end + return path + end +end + +local w,h = term.getSize() +if h < 19 then return end +local newwin = false + +local therealOGterm = term.current() +local function tokenise( ... ) + local sLine = table.concat( { ... }, " " ) + local tWords = {} + local bQuoted = false + for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do + if bQuoted then + table.insert( tWords, match ) + else + for m in string.gmatch( match, "[^ \t]+" ) do + table.insert( tWords, m ) + end + end + bQuoted = not bQuoted + end + return tWords +end + +if fs.exists("AppData") == false then + fs.makeDir("AppData") +end + +if fs.exists("User") == false then + fs.makeDir("User") + fs.makeDir("User/Documents") + fs.makeDir("User/Images") + fs.makeDir("User/Scripts") + fs.makeDir("User/Downloads") +end + +if not fs.exists("bigfont") then + --Let the bios know the system is broken + fs.delete("/real_startup.lua") + os.reboot() + return +end + +local to_colors, to_blit = {}, {} +for i = 1, 16 do + to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i) + to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1) +end + + + +local function toColor(theblit) + return to_colors[theblit] or nil +end + + + +local function toBlit(thecolor) + return to_blit[thecolor] or nil +end + +local function render(spr,x,y) + local format = "lImg" + if format == "lImg" then + local sW,sH = #spr[1][1],#spr + local w,h = term.getSize() + for l=1,#spr do + if not y then + term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),(math.ceil(h/2)-math.floor(sH/2)+(l-1))+x) + else + term.setCursorPos(x,y+(l-1)) + end + local bl = {} + bl[1] = spr[l][1] + bl[2] = string.gsub(spr[l][2],"T",toBlit(term.getBackgroundColor())) + bl[3] = string.gsub(spr[l][3],"T",toBlit(term.getBackgroundColor())) + term.blit(unpack(bl)) + end + elseif format == "nfp" or format == "nfg" then + local b,e = string.find(spr,"\n") + local sW,sH + local w,h = term.getSize() + local lines,sW = getLines(spr) + sH = #lines + for l=1,sH do + term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),math.ceil(h/2)-math.floor(sH/2)+(l-1)) + term.blit(string.rep(" ",#lines[l]),lines[l],lines[l]) + end + end +end + + +local function fread(file) + local f = fs.open(file,"r") + local o = f.readAll() + f.close() + return o +end + +local function fwrite(file,content) + local f = fs.open(file,"w") + f.write(content) + f.close() + return true +end + +local loadingico = textutils.unserialize(fread("LevelOS/assets/loading.limg")) + + +_G.bigfont = loadfile("bigfont",_ENV)() + + +local progress = 0 + +local function centerText(text,customY,customLen) -- i tried to put indentation but pastebin is being stupid for some reason + local x,y = term.getSize() + local x2,y2 = term.getCursorPos() + if customY then y2 = customY end + local len = customLen or text:len() + term.setCursorPos((math.ceil(x / 2) - math.floor(len / 2)), y2) + term.write(text) + term.setCursorPos(x2,y2+1) +end + +local doUpdate +local bootText = "Initializing" +local dots = 1 +local frame = 1 + +local function bootscreen() + term.setBackgroundColor(colors.black) + term.setTextColor(colors.cyan) + term.clear() + local w,h = term.getSize() + if h > 19 then + if fs.exists("LevelOS/assets/logo_christmas.limg") and os.date("%m") == "12" then + render(textutils.unserialize(fread("LevelOS/assets/logo_christmas.limg"))[1],math.ceil(w/2)-4,h/2-8) + elseif fs.exists("LevelOS/assets/logo_pride.limg") then + render(textutils.unserialize(fread("LevelOS/assets/logo_pride.limg"))[1],math.ceil(w/2)-4,h/2-8) + else + bigfont.writeOn(term.current(),2,"L",nil,h/2-8) + end + else + if fs.exists("LevelOS/assets/logo_christmas.limg") and os.date("%m") == "12" then + render(textutils.unserialize(fread("LevelOS/assets/logo_christmas.limg"))[1],math.ceil(w/2)-4,h/2-5) + elseif fs.exists("LevelOS/assets/logo_pride.limg") then + render(textutils.unserialize(fread("LevelOS/assets/logo_pride.limg"))[1],math.ceil(w/2)-4,h/2-5) + else + bigfont.writeOn(term.current(),2,"L",nil,h/2-5) + end + end + while true do + if doUpdate then + term.setBackgroundColor(colors.blue) + term.setTextColor(colors.white) + term.clear() + local w,h = term.getSize() + term.setCursorPos(1,math.ceil(h/2)) + centerText("Getting ready for updates") + centerText("Do not turn off your computer") + local init = false + while true do + render(loadingico[frame],-5) + if progress > 0 then + if not init then + term.setBackgroundColor(colors.blue) + term.setTextColor(colors.white) + term.clear() + render(loadingico[frame],-5) + init = true + end + term.setCursorPos(1,math.ceil(h/2)) + centerText("Working on updates") + centerText(math.floor(progress + 0.5).."% complete") + centerText("Do not turn off your computer") + end + frame = frame+1 + if frame > #loadingico then + frame = 1 + end + os.sleep(0.1) + end + end + if h > 19 then + render(loadingico[frame],10) + if bootText then + centerText(" "..bootText..string.rep(".",dots).." ",h/2+14,#(" "..bootText.." ")) + end + else + render(loadingico[frame],7) + end + os.pullEvent() + end +end + +local function loadIco() + while true do + frame = frame+1 + if frame > #loadingico then + frame = 1 + end + dots = dots+0.5 + if dots > 3 then dots = 0 end + os.sleep(0.1) + end +end + +local hpost = function(...) + while true do + local ret = table.pack(http.post(...)) + if not ret[1] then + os.sleep(0.5) + else + badConn = false + return table.unpack(ret, 1, ret.n) + end + end +end + +local function getField(thing,fieldname) + if string.find(thing,"<"..fieldname..">",1,true) ~= nil and string.find(thing,"",1,true) ~= nil then + local begin = nil + local ending = nil + local trash,begin = string.find(thing,"<"..fieldname..">",1,true) + local ending,ending2 = string.find(thing,"",begin+1,true) + if begin ~= nil and ending ~= nil then + return string.sub(thing,begin+1,ending-1),string.sub(thing,1,trash-1)..string.sub(thing,ending2+1,string.len(thing)) + end + end + return nil +end + +local function download(pth) + local f = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode(pth).."&code="..textutils.urlEncode("lSlb8kZq"),{Cookie=userID}).readAll() + if f ~= "409" and f ~= "403" and f ~= "401" then + if pth == "startup.lua" and f ~= fread(shell.getRunningProgram()) then + fwrite(shell.getRunningProgram(),f) + os.sleep(1) + os.reboot() + end + if pth ~= "startup.lua" then + fwrite(pth,f) + end + return true + else + return false + end +end + +local step = 0.05 +local function update() + bootText = "Connecting to server" + os.sleep(step) + local opost = http.post + local ping = http.get("https://os.leveloper.cc/ping.php") + if not ping then + bootText = "Trying HTTP" + os.sleep(step) + local ping2 = http.get("http://os.leveloper.cc/ping.php") + if not ping2 then + bootText = "Starting in offline" + os.sleep(step) + -- no internet + return + end + function http.post(...) + local args = table.pack(...) + local r = table.pack(opost(...)) + if not r[1] and string.find(args[1],"https://",nil,true) == 1 then + args[1] = "http"..string.sub(args[1],6,#args[1]) + return opost(table.unpack(args)) + else + return table.unpack(r) + end + end + local oget = http.get + function http.get(...) + local args = table.pack(...) + local r = table.pack(oget(...)) + if not r[1] and string.find(args[1],"https://",nil,true) == 1 then + args[1] = "http"..string.sub(args[1],6,#args[1]) + return oget(table.unpack(args)) + else + return table.unpack(r) + end + end + hpost = http.post + end + + bootText = "Checking client version" + os.sleep(step) + local servertimestamp + local clienttimestamp + if fs.exists("LevelOS/data/version.txt") then + clienttimestamp = tonumber(fread("LevelOS/data/version.txt")) + end + if not clienttimestamp then + clienttimestamp = 0 + end + bootText = "Looking for updates" + os.sleep(step) + local response,err = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("").."&code="..textutils.urlEncode("lSlb8kZq")) + local res2,err2 + if fs.exists("LevelOS/startup/LevelCloud.lua") then + res2,err2 = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("").."&code="..textutils.urlEncode("Sm0f1bwQ")) + end + if res2 then + bootText = "Updating LevelCloud" + os.sleep(step) + local f = res2.readAll() + local sTS = tonumber((getField(f,"version"))) or math.huge + local cTS = fs.attributes("LevelOS/startup/LevelCloud.lua").modification or fs.attributes("LevelOS/startup/LevelCloud.lua").modified or 0 + if sTS > cTS then + local f = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode("LevelCloud.lua").."&code="..textutils.urlEncode("Sm0f1bwQ")) + if f then + fwrite("LevelOS/startup/LevelCloud.lua",f.readAll()) + end + end + end + bootText = "Processing" + os.sleep(step) + if not response then + -- could not connect + --os.sleep(0.5) + -- put "starting in offline mode" + return + end + local f = response.readAll() + servertimestamp = tonumber((getField(f,"version"))) + local tFiles = 0 + local pack = "Full" + if fs.exists("LevelOS/data/settings.lconf") then + local set = textutils.unserialize(fread("LevelOS/data/settings.lconf")) + if set.package then + pack = set.package + end + end + + local tree = {} + local folders = {} + local function searchFolder(folder) + --print("Searching folder root/"..folder) + local f = hpost("https://os.leveloper.cc/sGet.php","path="..textutils.urlEncode(folder).."&code="..textutils.urlEncode("lSlb8kZq")).readAll() + --print(f) + local f2 = f + while true do + local file = nil + file,f = getField(f,"file") + if not file then + break + else + local name = getField(file,"name") + local timestamp = getField(file,"timestamp") + timestamp = tonumber(timestamp) + if pack == "Full" or fs.exists(fs.combine(folder,name)) or folder == "LevelOS/assets" or fs.combine(folder,name) == "startup.lua" then + tree[fs.combine(folder,name)] = {timestamp=timestamp} + tFiles = tFiles+1 + end + --print("Found "..fs.combine(folder,name)) + end + end + f = f2 + while true do + local file = nil + file,f = getField(f,"folder") + if not file then + break + else + local name = getField(file,"name") + if not fs.exists(fs.combine(folder,name)) then + fs.makeDir(fs.combine(folder,name)) + end + folders[fs.combine(folder,name)] = "" + searchFolder(fs.combine(folder,name)) + end + end + return true + end + if servertimestamp > clienttimestamp then + doUpdate = true + local deleteFiles = { + "LevelOS/explorer.lua", + "LevelOS/Pigeon.lua", + "LevelOS/LevelCloud.lua", + "LevelOS/notepad.lua", + "LevelOS/Register.lua", + } + if fs.exists("LevelOS/data/nativelog.txt") then + fs.move("LevelOS/data/nativelog.txt","LevelOS/data/nativelog.lconf") + end + for f=1,#deleteFiles do + if fs.exists(deleteFiles[f]) then + fs.delete(deleteFiles[f]) + end + end + else + bootText = "Checking file integrity" + os.sleep(step) + local icheck = { + --"startup.lua", + "bigfont", + "blittle", + "LevelOS", + "LevelOS/system.lua", + "LevelOS/startup", + "LevelOS/startup/lUtils.lua", + "LevelOS/assets", + "LevelOS/assets/Circle_Symbols.limg", + "LevelOS/assets/circProgress.limg", + "LevelOS/assets/Compact_Icons.limg", + "LevelOS/assets/Desktop_Icons.limg", + "LevelOS/assets/loading.limg", + "LevelOS/assets/logo_pride.limg", + "LevelOS/assets/QR_Code.limg", + "LevelOS/assets/wifi.limg", + "LevelOS/login.lua", + "LevelOS/Changelog.lua", + "LevelOS/lStore.lua", + "LevelOS/SystemUI.lua", + "LevelOS/Task_Manager.lua", + } + local aFiles = {} -- absent files + for f=1,#icheck do + if not fs.exists(icheck[f]) then + table.insert(aFiles,icheck[f]) + end + end + if #aFiles > 0 then + bootText = "Integrity compromised" + os.sleep(0.5) + bootText = "Restoring files" + if not searchFolder("") then return false end + + for f=1,#aFiles do + while not download(aFiles[f]) do + end + end + bootText = "Restarting" + os.sleep(1) + os.reboot() + end + end + + if doUpdate then + if not searchFolder("") then return false end + + if tree["startup.lua"] then + download("startup.lua") + end + + for k,v in pairs(tree) do + if download(k,root,v.timestamp) == true then + progress = progress+(100/tFiles) + end + end + fwrite("LevelOS/data/version.txt",tostring(servertimestamp)) + os.sleep(1) + os.reboot() + end + if not fs.exists("LevelOS/assets/wifi.limg") then + folders["LevelOS/assets"] = "" + if not searchFolder("LevelOS/assets") then return false end + for k,v in pairs(tree) do + if download(k) == true then + end + end + end + bootText = "Loading system" + os.sleep(step) +end + + +parallel.waitForAny(update,bootscreen,loadIco) + +local expect = require "cc.expect".expect + +local function wrap(txt,width) + local lines = {} + for line in txt:gmatch("([^\n]*)\n?") do + table.insert(lines,"") + for word in line:gmatch("%S*%s?") do + if #lines[#lines]+#word > width and #lines[#lines] > 0 then + lines[#lines+1] = "" + end + if #lines[#lines]+#word > width then + local tWord = word + while #lines[#lines]+#tWord > width do + print(tWord:sub(1,width)) + lines[#lines] = tWord:sub(1,width) + table.insert(lines,"") + tWord = tWord:sub(width+1) + end + lines[#lines] = tWord + else + lines[#lines] = lines[#lines]..word + end + end + end + if txt:sub(#txt) == "\n" then + table.insert(lines,"") + end + return lines +end + +local function bPrint(txt) + local x,y = term.getCursorPos() + local w,h = term.getSize() + + local text = wrap(txt,w-(x-1)) + for t=1,#text do + term.write(text[t]) + term.setCursorPos(x,y+t) + end +end + +local function wordwrap(str) + local x,y = term.getCursorPos() + local tW,tH = term.getSize() + for w in str:gmatch("%S+") do + local x1,y1 = term.getCursorPos() + if x1+(#w*3) >= tW then + bigfont.bigPrint(" ") + local x2,y2 = term.getCursorPos() + term.setCursorPos(x,y2) + end + bigfont.bigWrite(w.." ") + end +end + + +if fs.exists("LevelOS/lStore.lua") then + shell.setAlias("lStore","LevelOS/lStore.lua") + + local completion = require "cc.shell.completion" + local function completelStorePut(shell, text, previous) + if previous[2] == "put" then + return fs.complete(text, "User/Cloud", true, false) + end + end + + shell.setCompletionFunction("LevelOS/lStore.lua", completion.build( + { completion.choice, { "put ", "get ", "run " } }, + completelStorePut + )) +end + +if jit then + shell.run("lStore get JITAlert LevelOS/startup/JITAlert2.lua") + if fs.exists("LevelOS/startup/JITAlert2.lua") then + if fs.exists("LevelOS/startup/JITAlert.lua") then + fs.delete("LevelOS/startup/JITAlert.lua") + end + fs.move("LevelOS/startup/JITAlert2.lua","LevelOS/startup/JITAlert.lua") + end +elseif fs.exists("LevelOS/startup/JITAlert.lua") then + fs.delete("LevelOS/startup/JITAlert.lua") +end + +local u + +local u = {pcall(loadfile("LevelOS/system.lua",_ENV))} +_G.whatitreturn = u +local link +local copied = false +local crashwin +function bsodRender() + term.redirect(therealOGterm) + term.setPaletteColor(colors.blue,0,120/255,215/255) + term.setPaletteColor(colors.white,1,1,1) + term.setCursorPos(4,4) + term.setBackgroundColor(colors.blue) + term.clear() + local w,h = term.getSize() + if w > 140 then + crashwin = window.create(therealOGterm,12,8,w/2+10,h-12) + bigfont.writeOn(therealOGterm,3,"L",w-40,math.ceil(h/2)-10) + else + crashwin = window.create(therealOGterm,4,5,w-8,h-5) + end + term.redirect(crashwin) + term.setBackgroundColor(colors.blue) + term.clear() + if w > 110 and h > 40 and fs.exists("LevelOS/assets/QR_Code.limg") then + local qrcode = textutils.unserialize(fread("LevelOS/assets/QR_Code.limg")) + bigfont.hugePrint(":(") + wordwrap("Your PC ran into a problem and needs to restart. Please press space to continue.") + print("\n\n\n\n\n\n") + local x,y = term.getCursorPos() + render(qrcode[1],x,y) + term.setCursorPos(x+22,y) + bPrint("For more information about this issue and possible fixes, visit ") + local txt = "https://discord.gg/vBsjGqy99U" + if copied then + txt = txt.." (copied to clipboard!)" + end + local tx,ty = term.getCursorPos() + local tw,th = term.getSize() + if tx+(#txt-1) > tw then + ty = ty+1 + tx = x+22 + end + if ccemux and ccemux.setClipboard then + link = {x=tx,y=ty,w=#txt,h=1,txt="https://discord.gg/vBsjGqy99U"} + if copied then + term.setTextColor(colors.cyan) + else + term.setTextColor(colors.lightBlue) + term.setCursorPos(link.x,link.y+1) + term.write(string.rep("\131",link.w)) + end + end + term.setCursorPos(tx,ty) + term.write(txt) + term.setTextColor(colors.white) + term.setCursorPos(x+22,y+(#qrcode[1]-3)) + bPrint("If you contact support, give them this info:") + term.setCursorPos(x+22,y+(#qrcode[1]-1)) + bPrint(u[2]) + else + bigfont.bigPrint(":(") + print("\nYour PC ran into a problem and needs to restart. Please press space to continue.") + print("\nError:") + print(u[2]) + end +end +bsodRender() +os.sleep(1) +while true do + local e = {os.pullEventRaw()} + if e[1] == "mouse_click" then + local x,y = crashwin.getPosition() + e[3] = e[3]-(x-1) + e[4] = e[4]-(y-1) + end + if e[1] == "key" and e[2] == keys.space then + hardreboot() + elseif e[1] == "term_resize" then + bsodRender() + elseif link and e[1] == "mouse_click" and e[3] >= link.x and e[4] == link.y and e[3] <= link.x+(link.w-1) then + ccemux.setClipboard(link.txt) + copied = true + bsodRender() + elseif link and e[1] == "mouse_click" then + --[[term.setCursorPos(1,1) + print(textutils.serialize(link)) + print(textutils.serialize(e))]] + elseif e[1] == "terminate" then + term.redirect(therealOGterm) + break + end +end + +-- TEMP FOR DEBUGGING +term.setBackgroundColor(colors.black) term.clear() term.setCursorPos(1,1) +shell.run("shell") \ No newline at end of file