ClientOS/LevelOS/system.lua
2025-10-21 00:40:14 +02:00

1505 lines
57 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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()