Posted 18 February 2013 - 11:58 AM
Im trying to make an api to multitask but it not work
api:
local gotTerminate=false
local active
local loaded=false
local termNative={
restore=term.restore,
redirect=term.redirect,
}
function isActive()
return active
end
local activeRoutines = { }
local eventAssignments = { }
local entryRoutine
local rootRoutine
local passEventTo=nil
local numActiveCoroutines=0
local isRunning=false
function getInternalState()
return active, activeRoutines,eventAssignments,entryRoutine,
rootRoutine,passEventTo,numActiveCoroutines,isRunning
end
if goroutine then
active, activeRoutines,eventAssignments,entryRoutine,
rootRoutine,passEventTo,numActiveCoroutines,isRunning=goroutine.getInternalState()
else
active=false
activeRoutines = { }
eventAssignments = { }
entryRoutine=nil
rootRoutine=nil
passEventTo=nil
numActiveCoroutines=0
isRunning=false
end
loaded=true
local function findCoroutine(co)
for _,routine in pairs(activeRoutines) do
if routine.co==co then
return routine
end
end
return nil
end
function findNamedCoroutine(name)
return activeRoutines[name]
end
function running()
return findCoroutine(coroutine.running())
end
local function validateCaller(funcName)
local callingRoutine=running()
if callingRoutine==nil then
error(funcName.." can only be called by a coroutine running under goroutine!")
end
return callingRoutine
end
function assignEvent(assignTo,event,…)
–get the routine calling this funciton
local callingRoutine=validateCaller("assignEvent")
if callingRoutine~=entryRoutine then
return false, "assignEvent: only main routine, passed to run(..), can assign events!"
end
–get the assignee
local assignee=callingRoutine
if assignTo~=nil and assignTo~=callingRoutine.name then
assignee=findNamedCoroutine(assignTo)
if assignee==nil then
return false, "assignEvent: named coroutine not found!"
end
end
–is this event already assigned elsewhere?
if eventAssignments[event]~=nil then
return false,"This event assignment conflicts with an existing assignment!"
end
–still here? good, no conflict then
eventAssignments[event]={co=assignee,assignedBy=callingRoutine}
return true
end
function passEvent(passTo)
if passTo==nil then
passEventTo=""
else
passEventTo=passTo
end
end
function releaseEvent(event)
local callingRoutine=validateCaller("releaseEvent")
local ass=eventAssignments[event]
if ass~=nil then
if caller.co~=entryRoutine and caller~=ass.assignedBy and caller~=ass.routine then
return false, "Event can only be released by the assigner, assignee, or the entry routine!"
end
table.remove(eventAssignments,i)
return true
end
return false
end
–called by goroutines to wait for an event to occur with some
–set of optional event parameter filters
function waitForEvent(event,…)
co=validateCaller("waitForEvent")
co.filters={event,…}
return coroutine.yield("goroutine_listening")
end
local function matchFilters(params,routine)
if params[1]=="terminate" then
return true
end
for j=1,#params do
if routine==nil or (routine.filters and routine.filters[j]~=nil and routine.filters[j]~=params[j]) then
return false
end
end
return true
end
local function sendEventTo(routine, params)
if routine.dead then
return
end
termNative.redirect(routine.redirect[#routine.redirect])
local succ,r1=coroutine.resume(routine.co,unpack(params))
termNative.restore()
–did it die or terminate?
if succ==false or coroutine.status(routine.co)=="dead" then
–it's dead, remove it from active
–if there's an error, send coroutine_error
if r1~=nil then
os.queueEvent("coroutine_error",routine.name,r1)
end
–send coroutine_end
routine.dead=true
–not dead, is it waiting for an event?
else
–"goroutine_listening" indicates it yielded via coroutine.waitForEvent
–which has had filters set already
if r1~="goroutine_listening" then
–Add to eventListeners
routine.filters={r1}
end
end
end
local function _spawn(name,method,redirect,parent,args)
if activeRoutines[name] then
return nil, "Couldn't spawn; a coroutine with that name already exists!"
end
local routine={name=name,co=coroutine.create(method),redirect={redirect}, parent=parent,children={}}
if routine.co==nil then
error("Failed to create coroutine '"..name.."'!")
end
parent.children[#parent.children+1]=routine
activeRoutines[name]=routine
os.queueEvent("coroutine_start",name)
numActiveCoroutines=numActiveCoroutines+1
–run it a bit..
sendEventTo(routine,args)
return routine
end
function spawnWithRedirect(name,method,redirect,…)
return _spawn(name,method,redirect,running(),{…})
end
local mon=peripheral.wrap("right")
function spawn(name,method,…)
local cur=running()
return _spawn(name,method,cur.redirect[1],cur,{…})
end
local nilRedir = {
write = function() end,
getCursorPos = function() return 1,1 end,
setCursorPos = function() end,
isColor = function() return false end,
scroll = function() end,
setCursorBlink = function() end,
setTextColor = function() end,
getTextColor = function() end,
getTextSize = function() end,
setTextScale = function() end,
clear = function() end,
clearLine = function() end,
}
function spawnBackground(name,method,…)
return _spawn(name,method,nilRedir,rootRoutine,{…})
end
function spawnPeer(name,method,…)
local cur=running()
return _spawn(name,method,cur.redirect[1],cur.parent,{…})
end
function spawnPeerWithRedirect(name,method,redirect,…)
local cur=running()
return _spawn(name,method,redirect,cur.parent,{…})
end
function spawnProgram(name,progName,…)
local cur=running()
return _spawn(name, function(…) os.run({}, …) end,cur.redirect[1],cur,{…})
end
function list()
local l={}
local i=1
for name,_ in pairs(activeRoutines) do
l=name
i=i+1
end
return l
end
function kill(name)
local routine=validateCaller("killCoroutine")
if not routine then
return false, "Must be called from a coroutine. How'd you even manage this?"
end
local target=findNamedCoroutine(name)
if target then
if routine==target then
return false,"You can't commit suicide!"
end
–mark it dead
target.dead=true
return true
end
return false, "coroutine not found"
end
local function logCoroutineErrors()
while true do
local _, name, err=os.pullEventRaw("coroutine_error")
if _~="terminate" then
local file=fs.open("go.log","a")
file.write("coroutine '"..tostring(name).."' crashed with the following error: "..tostring(err).."\n")
file.close()
end
end
end
function run(main,terminable,…)
if isRunning then
–spawn it
local cur=running()
local name="main"
local i=1
while activeRoutines[name] do
i=i+1
name="main"..i
end
if _spawn(name,main,cur.redirect[1],cur,{…}) then
–wait for it to die
while true do
local e={os.pullEventRaw()}
if e[1]=="coroutine_end" and e[2]==name then
return
elseif e[1]=="coroutine_error" and e[2]==name then
error(e[3])
return
end
end
else
error("Couldn't spawn main coroutine "..name.."!")
end
end
–hook term.redirect and term.restore
local function term_redirect(target)
–push redirect to current term's stack
local co=running()
co.redirect[#co.redirect+1]=target
–undo the current redirection then redirect
termNative.restore()
termNative.redirect(target)
end
local function term_restore()
local co=running()
–do nothing unless they've got more than 1 redirect in their stack
if #co.redirect>1 then
table.remove(co.redirect,#co.redirect)
–undo current redirection and restore to new end of stack
termNative.restore()
termNative.redirect(co.redirect[#co.redirect])
end
end
termNative.redirect=term.redirect
termNative.restore=term.restore
term.redirect=term_redirect
term.restore=term_restore
–make the object for the root coroutine (this one)
rootRoutine={
co=coroutine.running(),
name="root",
redirect={term.native},
parent=nil,
children={}
}
isRunning=true
–default terminable to true
if terminable==nil then
terminable=true
end
–start the main coroutine for the process
entryRoutine=_spawn("main",main,term.native,rootRoutine,{…})
–begin with routine 1
–gooo!
local params={}
while numActiveCoroutines>0 do
–grab an event
params={os.pullEventRaw()}
if terminable and params[1]=="terminate" then
gotTerminate=true
end
local assigned=eventAssignments[params[1]]~=nil
local assignedTo=assigned and eventAssignments[params[1]].co or nil
local alreadyHandledBy={}
–set passTo to empty string, meaning anyone listening
passEventTo=""
while assignedTo~=nil do
–set this to nil first
passEventTo=nil
–send to assigned guy, if he matches, else break
if matchFilters(params,assignedTo) then
sendEventTo(assignedTo,params)
else
passEventTo=""
break
end
–add him to the list of guys who've handled this already
alreadyHandledBy[assignedTo]=true
–set assignedTo to whatever passTo was
if passEventTo=="" then
assignedTo=nil
elseif passEventTo~=nil then
assignedTo=findNamedCoroutine(passEventTo)
else
assignedTo=nil
end
end
–if it was assigned to nobody, or they passed to everybody..
if passEventTo=="" then
for _,routine in pairs(activeRoutines) do
–if they haven't handled it already via assignments above..
if not alreadyHandledBy[routine] and not routine.dead then
local match=matchFilters(params,routine)
–if it matched, or this routine has never run…
if match then
sendEventTo(routine,params)
end
end
end
end
–clean up any dead coroutines
local dead={}
local function listChildren(routine,list)
for i=1,#routine.children do
if not routine.children.dead then
list[routine.children.name]=routine.children
listChildren(routine.children,list)
end
end
end
for name,routine in pairs(activeRoutines) do
if routine.dead then
dead[name]=routine
listChildren(routine,dead)
end
end
for name,routine in pairs(dead) do
os.queueEvent("coroutine_end",routine.name)
activeRoutines[name]=nil
numActiveCoroutines=numActiveCoroutines-1
local parent=routine.parent
if not parent.dead then
–find and remove from children
for i=1,#parent.children do
if parent.children==routine then
table.remove(parent.children,i)
break
end
end
end
end
–release all events assigned to dead coroutines
local remove={}
for k,v in pairs(eventAssignments) do
if dead[eventAssignments[k].co.name] then
table.insert(remove,k)
end
end
for i=1,#remove do
eventAssignments[remove]=nil
end
end
–Should I send every remaining process a terminate event, regardless
–of what they were waiting on, so they can do cleanup? Could cause
–errors in some cases…
–[[
for k,v in activeRoutines do
coroutine.resume(v.co,"terminate")
end
–]]
activeRoutines={}
eventAssignments = { }
passEventTo=nil
entryRoutine=nil
rootRoutine=nil
isRunning=false
–remove hooks from term.redirect and .restore
term.redirect=termNative.redirect
term.restore=termNative.restore
end
function launch(sh)
if not active then
active=true
sh=sh or "rom/programs/shell"
term.clear()
term.setCursorPos(1,1)
run(
function()
spawnBackground("errLogger",logCoroutineErrors)
os.run({},sh)
end
)
os.shutdown()
end
end
function drawWindows()
term.clear()
for name, v in pairs(windowsdata) do
x = windowsdata[name]["x"]-1
y = windowsdata[name]["y"]-1
w = windowsdata[name]["w"]+1
h = windowsdata[name]["h"]+1
wx = w+x
running = windowsdata[name]["running"]
dl = paintutils.drawLine
dp = paintutils.drawPixel
dl(x, y, wx, y, colors.blue)
dl(x, h+y, wx, h+y, colors.blue)
dl(x, y, x, h+y, colors.blue)
dl(wx, y, wx, h+y)
dp(wx, y, colors.red)
dp(wx-1, y, colors.lightBlue)
y1 = y
x = nil
w = nil
h = nil
x, y = term.getCursorPos()
term.setCursorPos(wx-1, y1)
term.write("DX")
term.setCursorPos(x, y)
y = nil
wx = nil
y1 = nil
windows[name].makeActive(windowsdata[name]["x"], windowsdata[name]["y"])
end
end
function drag(name, x, y)
windowsdata[name]["x"] = x
windowsdata[name]["y"] = y
drawWindows()
end
function close(name)
goroutine.kill("windows - "..name)
windows[name] = nil
windowsdata[name] = nil
drawWindows()
end
function handleEvents()
while true do
evt, p1, p2, p3 = os.pullEventRaw()
for k, v in pairs(windowsdata) do
if evt=="mouse_click" then
if p1==v["w"] and p2==v["y"] then
close(k)
elseif p1==v["w"]-1 and p2==v["y"] then
while true do
evt2, a1, a2 = os.pullEventRaw()
if evt2=="mouse_click" then break end
if evt2=="mouse_drag" then drag(k, a1, a2) end
end
end
end
end
end
end
function runactual(name)
shell.run(windowsdata[name]["program"])
drawWindows()
end
function run(name)
for k, v in pairs(windowsdata) do
if k==name then
v["running"] = true
spawnWithRedirect("windows - "..name,run,windows[k],name)
end
end
end
windows = {}
windowsdata = {}
function createWindow(name, w, h, x, y, prog)
windows[name] = createRedirectBuffer(w,h, nil, nil, true)
windowsdata[name]["x"] = x
windowsdata[name]["y"] = y
windowsdata[name]["w"] = w
windowsdata[name]["h"] = h
windowsdata[name]["program"] = prog
windowsdata[name]["running"] = false
end
program:
creadits to gopher for gorutine and redirect
api:
local trueCursor={term.getCursorPos()}
local redirectBufferBase = {
write=
function(buffer,...)
local cy=buffer.curY
if cy>0 and cy<=buffer.height then
local text=table.concat({...}," ")
local cx=buffer.curX
local px, py
if buffer.isActive and not buffer.cursorBlink then
term.native.setCursorPos(cx+buffer.scrX, cy+buffer.scrY)
end
for i=1,#text do
if cx<=buffer.width then
buffer[cy][cx]={char=string.char(text:byte(i)),textColor=buffer.textColor,backgroundColor=buffer.backgroundColor}
end
cx=cx+1
end
buffer.curX=cx
buffer[cy].isDirty=true
if buffer.isActive then
buffer.drawDirty()
if not buffer.cursorBlink then
trueCursor={cx+buffer.scrX-1,cy+buffer.scrY-1}
term.native.setCursorPos(unpack(trueCursor))
end
end
end
end,
setCursorPos=
function(buffer,x,y)
buffer.curX=math.floor(x)
buffer.curY=math.floor(y)
if buffer.isActive and buffer.cursorBlink then
term.native.setCursorPos(x+buffer.scrX-1,y+buffer.scrY-1)
trueCursor={x+buffer.scrX-1,y+buffer.scrY-1}
end
end,
getCursorPos=
function(buffer)
return buffer.curX,buffer.curY
end,
scroll=
function(buffer,offset)
for j=1,offset do
local temp=table.remove(buffer,1)
table.insert(buffer,temp)
for i=1,#temp do
temp[i].char=" "
temp[i].textColor=buffer.textColor
temp[i].backgroundColor=buffer.backgroundColor
end
end
if buffer.isActive then
term.redirect(term.native)
buffer.blit()
term.restore()
end
end,
isColor=
function(buffer)
return buffer._isColor
end,
isColour=
function(buffer)
return buffer._isColor
end,
clear=
function(buffer)
for y=1,buffer.height do
for x=1,buffer.width do
buffer[y][x]={char=" ",textColor=buffer.textColor,backgroundColor=buffer.backgroundColor}
end
end
if buffer.isActive then
term.redirect(term.native)
buffer.blit()
term.restore()
end
end,
clearLine=
function(buffer)
local line=buffer[buffer.curY]
local fg,bg = buffer.textColor, buffer.backgroundColor
for x=1,buffer.width do
line[x]={char=" ",textColor=fg,backgroundColor=bg}
end
buffer[buffer.curY].isDirty=true
if buffer.isActive then
buffer.drawDirty()
end
end,
setCursorBlink=
function(buffer,onoff)
buffer.cursorBlink=onoff
if buffer.isActive then
term.native.setCursorBlink(onoff)
if onoff then
term.native.setCursorPos(buffer.curX,buffer.curY)
trueCursor={buffer.curX,buffer.curY}
end
end
end,
getSize=
function(buffer)
return buffer.width, buffer.height
end,
setTextColor=
function(buffer,color)
buffer.textColor=color
if buffer.isActive then
if term.native.isColor() or color==colors.black or color==colors.white then
term.native.setTextColor(color)
end
end
end,
setTextColour=
function(buffer,color)
buffer.textColor=color
if buffer.isActive then
if term.native.isColor() or color==colors.black or color==colors.white then
term.native.setTextColor(color)
end
end
end,
setBackgroundColor=
function(buffer,color)
buffer.backgroundColor=color
if buffer.isActive then
if term.native.isColor() or color==colors.black or color==colors.white then
term.native.setBackgroundColor(color)
end
end
end,
setBackgroundColour=
function(buffer,color)
buffer.backgroundColor=color
if buffer.isActive then
if term.native.isColor() or color==colors.black or color==colors.white then
term.native.setBackgroundColor(color)
end
end
end,
resize=
function(buffer,width,height)
if buffer.width~=width or buffer.height~=height then
local fg, bg=buffer.textColor, buffer.backgroundColor
if width>buffer.width then
for y=1,buffer.height do
for x=#buffer[y]+1,width do
buffer[y][x]={char=" ",textColor=fg,backgroundColor=bg}
end
end
end
if height>buffer.height then
local w=width>buffer.width and width or buffer.width
for y=#buffer+1,height do
local row={}
for x=1,width do
row[x]={char=" ",textColor=fg,backgroundColor=bg}
end
buffer[y]=row
end
end
buffer.width=width
buffer.height=height
end
end,
blit=
function(buffer,sx,sy,dx, dy, width,height)
sx=sx or 1
sy=sy or 1
dx=dx or buffer.scrX
dy=dy or buffer.scrY
width=width or buffer.width
height=height or buffer.height
local h=sy+height>buffer.height and buffer.height-sy or height-1
for y=0,h do
local row=buffer[sy+y]
local x=0
local cell=row[sx]
local fg,bg=cell.textColor,cell.backgroundColor
local str=""
local tx=x
while true do
str=str..cell.char
x=x+1
if x==width or sx+x>buffer.width then
break
end
cell=row[sx+x]
if cell.textColor~=fg or cell.backgroundColor~=bg then
--write
term.setCursorPos(dx+tx,dy+y)
term.setTextColor(fg)
term.setBackgroundColor(bg)
term.write(str)
str=""
tx=x
fg=cell.textColor
bg=cell.backgroundColor
end
end
term.setCursorPos(dx+tx,dy+y)
term.setTextColor(fg)
term.setBackgroundColor(bg)
term.write(str)
end
end,
drawDirty =
function(buffer)
term.redirect(term.native)
for y=1,buffer.height do
if buffer[y].isDirty then
term.redirect(term.native)
buffer.blit(1,y,buffer.scrX,buffer.scrY+y-1,buffer.width,buffer.height)
term.restore()
buffer[y].isDirty=false
end
end
term.restore()
end,
makeActive =
function(buffer,posX, posY)
posX=posX or 1
posY=posY or 1
buffer.scrX=posX
buffer.scrY=posY
term.redirect(term.native)
buffer.blit(1,1,posX,posY,buffer.width,buffer.height)
term.setCursorPos(buffer.curX,buffer.curY)
term.setCursorBlink(buffer.cursorBlink)
term.setTextColor(buffer.textColor)
term.setBackgroundColor(buffer.backgroundColor)
buffer.isActive=true
term.restore()
end,
isBuffer = true,
}
function createRedirectBuffer(width,height,fg,bg,isColor)
bg=bg or colors.black
fg=fg or colors.white
isColor=isColor~=nil and isColor or term.isColor()
local buffer={}
for y=1,height do
local row={}
for x=1,width do
row[x]={char=" ",textColor=fg,backgroundColor=bg}
end
buffer[y]=row
end
buffer.scrX=1
buffer.scrY=1
buffer.width=width
buffer.height=height
buffer.cursorBlink=false
buffer.textColor=fg
buffer.backgroundColor=bg
buffer._isColor=isColor
buffer.curX=1
buffer.curY=1
local meta={}
local function wrap(f,o)
return function(...)
return f(o,...)
end
end
for k,v in pairs(redirectBufferBase) do
if type(v)=="function" then
meta[k]=wrap(v,buffer)
else
meta[k]=v
end
end
setmetatable(buffer,{__index=meta})
return buffer
end
local gotTerminate=false
local active
local loaded=false
local termNative={
restore=term.restore,
redirect=term.redirect,
}
function isActive()
return active
end
local activeRoutines = { }
local eventAssignments = { }
local entryRoutine
local rootRoutine
local passEventTo=nil
local numActiveCoroutines=0
local isRunning=false
function getInternalState()
return active, activeRoutines,eventAssignments,entryRoutine,
rootRoutine,passEventTo,numActiveCoroutines,isRunning
end
if goroutine then
active, activeRoutines,eventAssignments,entryRoutine,
rootRoutine,passEventTo,numActiveCoroutines,isRunning=goroutine.getInternalState()
else
active=false
activeRoutines = { }
eventAssignments = { }
entryRoutine=nil
rootRoutine=nil
passEventTo=nil
numActiveCoroutines=0
isRunning=false
end
loaded=true
local function findCoroutine(co)
for _,routine in pairs(activeRoutines) do
if routine.co==co then
return routine
end
end
return nil
end
function findNamedCoroutine(name)
return activeRoutines[name]
end
function running()
return findCoroutine(coroutine.running())
end
local function validateCaller(funcName)
local callingRoutine=running()
if callingRoutine==nil then
error(funcName.." can only be called by a coroutine running under goroutine!")
end
return callingRoutine
end
function assignEvent(assignTo,event,…)
–get the routine calling this funciton
local callingRoutine=validateCaller("assignEvent")
if callingRoutine~=entryRoutine then
return false, "assignEvent: only main routine, passed to run(..), can assign events!"
end
–get the assignee
local assignee=callingRoutine
if assignTo~=nil and assignTo~=callingRoutine.name then
assignee=findNamedCoroutine(assignTo)
if assignee==nil then
return false, "assignEvent: named coroutine not found!"
end
end
–is this event already assigned elsewhere?
if eventAssignments[event]~=nil then
return false,"This event assignment conflicts with an existing assignment!"
end
–still here? good, no conflict then
eventAssignments[event]={co=assignee,assignedBy=callingRoutine}
return true
end
function passEvent(passTo)
if passTo==nil then
passEventTo=""
else
passEventTo=passTo
end
end
function releaseEvent(event)
local callingRoutine=validateCaller("releaseEvent")
local ass=eventAssignments[event]
if ass~=nil then
if caller.co~=entryRoutine and caller~=ass.assignedBy and caller~=ass.routine then
return false, "Event can only be released by the assigner, assignee, or the entry routine!"
end
table.remove(eventAssignments,i)
return true
end
return false
end
–called by goroutines to wait for an event to occur with some
–set of optional event parameter filters
function waitForEvent(event,…)
co=validateCaller("waitForEvent")
co.filters={event,…}
return coroutine.yield("goroutine_listening")
end
local function matchFilters(params,routine)
if params[1]=="terminate" then
return true
end
for j=1,#params do
if routine==nil or (routine.filters and routine.filters[j]~=nil and routine.filters[j]~=params[j]) then
return false
end
end
return true
end
local function sendEventTo(routine, params)
if routine.dead then
return
end
termNative.redirect(routine.redirect[#routine.redirect])
local succ,r1=coroutine.resume(routine.co,unpack(params))
termNative.restore()
–did it die or terminate?
if succ==false or coroutine.status(routine.co)=="dead" then
–it's dead, remove it from active
–if there's an error, send coroutine_error
if r1~=nil then
os.queueEvent("coroutine_error",routine.name,r1)
end
–send coroutine_end
routine.dead=true
–not dead, is it waiting for an event?
else
–"goroutine_listening" indicates it yielded via coroutine.waitForEvent
–which has had filters set already
if r1~="goroutine_listening" then
–Add to eventListeners
routine.filters={r1}
end
end
end
local function _spawn(name,method,redirect,parent,args)
if activeRoutines[name] then
return nil, "Couldn't spawn; a coroutine with that name already exists!"
end
local routine={name=name,co=coroutine.create(method),redirect={redirect}, parent=parent,children={}}
if routine.co==nil then
error("Failed to create coroutine '"..name.."'!")
end
parent.children[#parent.children+1]=routine
activeRoutines[name]=routine
os.queueEvent("coroutine_start",name)
numActiveCoroutines=numActiveCoroutines+1
–run it a bit..
sendEventTo(routine,args)
return routine
end
function spawnWithRedirect(name,method,redirect,…)
return _spawn(name,method,redirect,running(),{…})
end
local mon=peripheral.wrap("right")
function spawn(name,method,…)
local cur=running()
return _spawn(name,method,cur.redirect[1],cur,{…})
end
local nilRedir = {
write = function() end,
getCursorPos = function() return 1,1 end,
setCursorPos = function() end,
isColor = function() return false end,
scroll = function() end,
setCursorBlink = function() end,
setTextColor = function() end,
getTextColor = function() end,
getTextSize = function() end,
setTextScale = function() end,
clear = function() end,
clearLine = function() end,
}
function spawnBackground(name,method,…)
return _spawn(name,method,nilRedir,rootRoutine,{…})
end
function spawnPeer(name,method,…)
local cur=running()
return _spawn(name,method,cur.redirect[1],cur.parent,{…})
end
function spawnPeerWithRedirect(name,method,redirect,…)
local cur=running()
return _spawn(name,method,redirect,cur.parent,{…})
end
function spawnProgram(name,progName,…)
local cur=running()
return _spawn(name, function(…) os.run({}, …) end,cur.redirect[1],cur,{…})
end
function list()
local l={}
local i=1
for name,_ in pairs(activeRoutines) do
l=name
i=i+1
end
return l
end
function kill(name)
local routine=validateCaller("killCoroutine")
if not routine then
return false, "Must be called from a coroutine. How'd you even manage this?"
end
local target=findNamedCoroutine(name)
if target then
if routine==target then
return false,"You can't commit suicide!"
end
–mark it dead
target.dead=true
return true
end
return false, "coroutine not found"
end
local function logCoroutineErrors()
while true do
local _, name, err=os.pullEventRaw("coroutine_error")
if _~="terminate" then
local file=fs.open("go.log","a")
file.write("coroutine '"..tostring(name).."' crashed with the following error: "..tostring(err).."\n")
file.close()
end
end
end
function run(main,terminable,…)
if isRunning then
–spawn it
local cur=running()
local name="main"
local i=1
while activeRoutines[name] do
i=i+1
name="main"..i
end
if _spawn(name,main,cur.redirect[1],cur,{…}) then
–wait for it to die
while true do
local e={os.pullEventRaw()}
if e[1]=="coroutine_end" and e[2]==name then
return
elseif e[1]=="coroutine_error" and e[2]==name then
error(e[3])
return
end
end
else
error("Couldn't spawn main coroutine "..name.."!")
end
end
–hook term.redirect and term.restore
local function term_redirect(target)
–push redirect to current term's stack
local co=running()
co.redirect[#co.redirect+1]=target
–undo the current redirection then redirect
termNative.restore()
termNative.redirect(target)
end
local function term_restore()
local co=running()
–do nothing unless they've got more than 1 redirect in their stack
if #co.redirect>1 then
table.remove(co.redirect,#co.redirect)
–undo current redirection and restore to new end of stack
termNative.restore()
termNative.redirect(co.redirect[#co.redirect])
end
end
termNative.redirect=term.redirect
termNative.restore=term.restore
term.redirect=term_redirect
term.restore=term_restore
–make the object for the root coroutine (this one)
rootRoutine={
co=coroutine.running(),
name="root",
redirect={term.native},
parent=nil,
children={}
}
isRunning=true
–default terminable to true
if terminable==nil then
terminable=true
end
–start the main coroutine for the process
entryRoutine=_spawn("main",main,term.native,rootRoutine,{…})
–begin with routine 1
–gooo!
local params={}
while numActiveCoroutines>0 do
–grab an event
params={os.pullEventRaw()}
if terminable and params[1]=="terminate" then
gotTerminate=true
end
local assigned=eventAssignments[params[1]]~=nil
local assignedTo=assigned and eventAssignments[params[1]].co or nil
local alreadyHandledBy={}
–set passTo to empty string, meaning anyone listening
passEventTo=""
while assignedTo~=nil do
–set this to nil first
passEventTo=nil
–send to assigned guy, if he matches, else break
if matchFilters(params,assignedTo) then
sendEventTo(assignedTo,params)
else
passEventTo=""
break
end
–add him to the list of guys who've handled this already
alreadyHandledBy[assignedTo]=true
–set assignedTo to whatever passTo was
if passEventTo=="" then
assignedTo=nil
elseif passEventTo~=nil then
assignedTo=findNamedCoroutine(passEventTo)
else
assignedTo=nil
end
end
–if it was assigned to nobody, or they passed to everybody..
if passEventTo=="" then
for _,routine in pairs(activeRoutines) do
–if they haven't handled it already via assignments above..
if not alreadyHandledBy[routine] and not routine.dead then
local match=matchFilters(params,routine)
–if it matched, or this routine has never run…
if match then
sendEventTo(routine,params)
end
end
end
end
–clean up any dead coroutines
local dead={}
local function listChildren(routine,list)
for i=1,#routine.children do
if not routine.children.dead then
list[routine.children.name]=routine.children
listChildren(routine.children,list)
end
end
end
for name,routine in pairs(activeRoutines) do
if routine.dead then
dead[name]=routine
listChildren(routine,dead)
end
end
for name,routine in pairs(dead) do
os.queueEvent("coroutine_end",routine.name)
activeRoutines[name]=nil
numActiveCoroutines=numActiveCoroutines-1
local parent=routine.parent
if not parent.dead then
–find and remove from children
for i=1,#parent.children do
if parent.children==routine then
table.remove(parent.children,i)
break
end
end
end
end
–release all events assigned to dead coroutines
local remove={}
for k,v in pairs(eventAssignments) do
if dead[eventAssignments[k].co.name] then
table.insert(remove,k)
end
end
for i=1,#remove do
eventAssignments[remove]=nil
end
end
–Should I send every remaining process a terminate event, regardless
–of what they were waiting on, so they can do cleanup? Could cause
–errors in some cases…
–[[
for k,v in activeRoutines do
coroutine.resume(v.co,"terminate")
end
–]]
activeRoutines={}
eventAssignments = { }
passEventTo=nil
entryRoutine=nil
rootRoutine=nil
isRunning=false
–remove hooks from term.redirect and .restore
term.redirect=termNative.redirect
term.restore=termNative.restore
end
function launch(sh)
if not active then
active=true
sh=sh or "rom/programs/shell"
term.clear()
term.setCursorPos(1,1)
run(
function()
spawnBackground("errLogger",logCoroutineErrors)
os.run({},sh)
end
)
os.shutdown()
end
end
function drawWindows()
term.clear()
for name, v in pairs(windowsdata) do
x = windowsdata[name]["x"]-1
y = windowsdata[name]["y"]-1
w = windowsdata[name]["w"]+1
h = windowsdata[name]["h"]+1
wx = w+x
running = windowsdata[name]["running"]
dl = paintutils.drawLine
dp = paintutils.drawPixel
dl(x, y, wx, y, colors.blue)
dl(x, h+y, wx, h+y, colors.blue)
dl(x, y, x, h+y, colors.blue)
dl(wx, y, wx, h+y)
dp(wx, y, colors.red)
dp(wx-1, y, colors.lightBlue)
y1 = y
x = nil
w = nil
h = nil
x, y = term.getCursorPos()
term.setCursorPos(wx-1, y1)
term.write("DX")
term.setCursorPos(x, y)
y = nil
wx = nil
y1 = nil
windows[name].makeActive(windowsdata[name]["x"], windowsdata[name]["y"])
end
end
function drag(name, x, y)
windowsdata[name]["x"] = x
windowsdata[name]["y"] = y
drawWindows()
end
function close(name)
goroutine.kill("windows - "..name)
windows[name] = nil
windowsdata[name] = nil
drawWindows()
end
function handleEvents()
while true do
evt, p1, p2, p3 = os.pullEventRaw()
for k, v in pairs(windowsdata) do
if evt=="mouse_click" then
if p1==v["w"] and p2==v["y"] then
close(k)
elseif p1==v["w"]-1 and p2==v["y"] then
while true do
evt2, a1, a2 = os.pullEventRaw()
if evt2=="mouse_click" then break end
if evt2=="mouse_drag" then drag(k, a1, a2) end
end
end
end
end
end
end
function runactual(name)
shell.run(windowsdata[name]["program"])
drawWindows()
end
function run(name)
for k, v in pairs(windowsdata) do
if k==name then
v["running"] = true
spawnWithRedirect("windows - "..name,run,windows[k],name)
end
end
end
windows = {}
windowsdata = {}
function createWindow(name, w, h, x, y, prog)
windows[name] = createRedirectBuffer(w,h, nil, nil, true)
windowsdata[name]["x"] = x
windowsdata[name]["y"] = y
windowsdata[name]["w"] = w
windowsdata[name]["h"] = h
windowsdata[name]["program"] = prog
windowsdata[name]["running"] = false
end
program:
os.loadAPI("api")
api.createWindow("shellwindow", 10, 10, 10, 10, "rom/programs/shell")
api.run("shellwindow")
api.drawWindows()
creadits to gopher for gorutine and redirect