This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Exerro's profile picture

awsumben13's APIs-multitasking, multiscreening, logging, variable saving, shapes and more

Started by Exerro, 05 March 2013 - 11:12 PM
Exerro #1
Posted 06 March 2013 - 12:12 AM
Hi, i recently decided to make an OS but wanted to make it amazing with multitasking and amazing graphics so i stole some APIs from BlueSky, improved them and made them compatible, then added loads more to form one big API with everything in it.

What is has:
process api* - create processes and run them ( uses coroutines )
window api* - create windows and write to them, you can have multiple windows and do everything you normally could to them
file - read and save files into/from tables or strings, read and write specific lines ( lots more coming soon )
log api - save program logs, ( add to them in your code and see whats going on if it errors )
variable api - add, remove, save, and load variables to files which can be edited normally
shape api - create and edit shapes, add rectangles and circles, draw them or add them to windows ( window api )
menu api - create lists of data, scroll through it and get individual bits in it, a simple filebrowser example ( 68 lines ) using the apis:
Spoiler

local files = menu.create( fs.list( "" ), 31, 8 )
local screen = window:create( 1, 5, 31, 9, colours.lightGrey, colours.black, " ", "filebrowser" )
local path = ""
while true do
files:set( fs.list( path ) )
screen:setCursorPos( 1, 2 )
screen:clear( )
local m = files:getSelection( )
for i = 1,#m do
  screen:print( m[i] )
end
screen:draw( )
ev = { os.pullEvent( ) }
if ev[1] == "mouse_click" then
  if fs.isDir( tostring( files:getContents( ev[4]-5 ) ) ) then
   path = path.."/"..files:getContents( ev[4]-5 )
  elseif fs.exists( tostring( files:getContents( ev[4]-5 ) ) ) then
   shell.run( path.."/"..files:getContents( ev[4]-5 ) )
  end
elseif ev[1] == "key" and ev[2] == keys.backspace then
  local t = split( path, "/" )
  table.remove( t, #t )
  path = join( t, "/" )
elseif ev[1] == "mouse_scroll" then
  files:scroll( ev[2] )
end
end
misc functions - split( str, pattern ), join( tArgs, pattern ), splitChars( str )

You can literally use these API's in everything:
OS - use processes and screens to manage multitasking and graphics
Door lock-use variable to save passwords and save user inputted data
API - use these to extend an API you're using
Text Editor - use the menu api to hold the lines and print them

the possibilities are endless, it's all down to your imagination

code:
Spoiler

--Help:
--[[
how to use:
process:
  create a process-variable = process:create( function to be run, function to be run if/when it errors )
  to do things with the screen-variable:[any term functions]( args )
  //please note that setCursorPos will set the cursor pos relevant to the screens position
  to add a screen-variable:addWindow( name, x, y, width, height, default background colour, default text colour, default text, header ( can leave blank ) )
  to change screen-variable:setScreen( name ( the name given to the window when created ) )
  to run the process-variable:run( event )
  //this will return different things depending on how it ran and what the function returned
window:
  same as process but only the graphical parts of it ( anything that says screen or window )
shape:
  create a blank shape-variable = shape.create( "any name", width, height )
  add things to the shape-variable:addRectangle( x, y, width, height, fill?, background colour, text colour, text )/pixel( x, y, bc, tc, text )/circle( )
  to load for drawing/adding to a screen-variable:load( )
  to draw-variable:draw( )
log:
  to add to the log-log.add( type, ...data )
  to save the log-log.save( path )
variable:
  to add a variable-variable.add( str name, data )
  to save the variables-variable.save( path )
  to load them again-variable.load( path )
  to remove one-variable.remove( str name )
file:
  to save data-file.save( path, tContents or sContents )
  to read the file-file.read( path, into a table? )
  to change one line-file.writeLine( path, line, data )
  to read one line-file.readLine( path, line )
menu:
  to make a menu-variable = menu.create( data, max width, max height )
  to get all the contents-variable:getSelection( )
  more help coming soon
  //use this if you want a list of items in table format
other:
  split-splits a string with a specified pattern
  join-joins a string with a specified pattern
  splitChars-splits the string into each char
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

still to come:
master process = overlays to the screen
pause processes
have multiple screens drawn at the same time from each process ( currently limited to one screen at a time )
term overwriting for compatibility with functions that use term
a demo function
more shapes
advanced variable loading and protection
file protection & encryption
file classpath loading ( load files into classes )
]]
shape = {
create = function( name, x, y )
  local t = { }
  t.name = name
  t.data = { }
  for i = 1,y do
   t.data[i] = { }
   for k = 1,x do
	t.data[i][k] = { "", colours.black, colours.white }
   end
  end
  return setmetatable( t, { __index = shape } )
end;
load = function( self )
  local t = { }
  for i = 1,#self.data do
   for k = 1,#self.data[i] do
	if self.data[i][k][1] ~= "" then
	 table.insert( t, { x = k, y = i, text = self.data[i][k][1], bc = self.data[i][k][2], tc = self.data[i][k][3] } )
	end
   end
  end
  return t
end;
draw = function( self, x, y )
  for i = y, ( y+#self.data )-1 do
   for k = x, ( x+self.data[i] )-1 do
	term.native.setCursorPos( k, i )
	term.native.setBackgroundColour( self.data[i][k][2] )
	term.native.setTextColour( self.data[i][k][3] )
	term.native.write( self.data[i][k][1] )
   end
  end
end;
addRectangle = function( self, x, y, x2, y2, fill, bc, tc, text )
  local bc = bc or colours.black
  local tc = tc or colours.white
  local text = text or " "
  if not tc then tc = colours.white end
  if x > x2 then x3 = x2 x2 = x x = x3 end
  if y > y2 then y3 = y2 y2 = y y = y3 end
  local t = { }
  for i = y, y2 do
   for k = x, x2 do
	table.insert( t, { x = k, y = i, text = text, bc = bc, tc = tc } )
   end
  end
  if not fill then
   local i = 1
   while i < #t do
	if t[i].x > x and t[i].x < x2 and t[i].y > y and t[i].y < y2 then
	 table.remove( t, i )
	 i = i-1
	end
	i = i+1
   end
  end
  for i = 1,#t do
   if self.data[t[i].y][t[i].x] then
	self.data[t[i].y][t[i].x][3] = t[i].tc
	self.data[t[i].y][t[i].x][2] = t[i].bc
	self.data[t[i].y][t[i].x][1] = t[i].text
   end
  end
end;
addPixel = function( self, x, y, bc, tc, t )
  self.data[y][x] = { t, bc, tc }
end;
addCircle = function( self, x, y, r, bc, tc, text, fill )
  local t = { }
  for tx = x-r,x+r do
   for ty = y-r,y+r do
	if (math.sqrt((x-tx)^2+(y-ty)^2) >= r and math.sqrt((x-tx)^2+(y-ty)^2) <= r+(r/math.sqrt((x-tx)^2+(y-ty)^2))) then
	 table.insert( t, { x = tx, y = ty, text = text, bc = bc, tc = tc } )
	elseif (math.sqrt((x-tx)^2+(y-ty)^2) <= r+(r/math.sqrt((x-tx)^2+(y-ty)^2))) and fill then
	 table.insert( t, { x = tx, y = ty, text = text, bc = bc, tc = tc } )
	end
   end
  end
  for i = 1,#t do
   if self.data[t[i].y][t[i].x] then
	self.data[t[i].y][t[i].x][3] = t[i].tc
	self.data[t[i].y][t[i].x][2] = t[i].bc
	self.data[t[i].y][t[i].x][1] = t[i].text
   end
  end
end;
}
window = {
create = function( link, x, y, w, h, bc, tc, dt, header )
  if type( link ) ~= "table" then self = window end
  local t = { }
  t.xPos = 1
  t.yPos = 1
  t.x = x
  t.y = y
  t.w = w
  t.h = h
  t.dt = dt
  t.bc = bc
  t.tc = tc
  t.header = { text = header, bc = bc, tc = tc }
  t.screen = { }
  for i = 1,h do
   t.screen[i] = { }
   for k = 1,w do
	t.screen[i][k] = { t = dt, bc = bc, tc = tc }
   end
  end
  return setmetatable( t, { __index = link } )
end;
update = function( self )
  if self.xPos > self.w then
   self.xPos = self.w
  end
  if self.xPos < 1 then
   self.xPos = 1
  end
  if self.yPos > self.h then
   self.yPos = self.h
  end
  if self.yPos < 1 then
   self.yPos = 1
  end
end;
move = function( self, x, y )
  if x + self.x >= 1 and x + self.x + ( self.w - 1 ) <= 51 then
   self.x = self.x + x
  end
  if y + self.y >= 1 and y + self.y + ( self.h - 1 ) <= 19 then
   self.y = self.y + y
  end
  self:update( )
end;
moveOutOfScreen = function( self, x, y )
  self.x = x
  self.y = y
  self:update( )
end;
draw = function( self )
  local s = self.screen
  for i = 1,#s do
   if i > self.h then break end
   for k = 1,#s[i] do
	if k > self.w then break end
	term.native.setCursorPos( k + ( self.x - 1 ), i + ( self.y - 1 ) )
	if term.isColour( ) then
	 term.native.setBackgroundColour( s[i][k].bc )
	 term.native.setTextColour( s[i][k].tc )
	end
	term.native.write( string.sub( tostring( s[i][k].t ), 1, 1 ) )
   end
  end
  if self.header.text then
   self.header.text = string.sub( self.header.text, 1, self.w )
   term.native.setCursorPos( ( ( self.w/2 ) - ( self.header.text:len( )/2 ) ) + self.x, self.y )
   if term.isColour( ) then
	term.native.setBackgroundColour( self.header.bc )
	term.native.setTextColour( self.header.tc )
   end
   term.native.write( self.header.text )
  end
  self:update( )
end;
write = function( self, txt )
  local t = { }
  for i = 1,txt:len( ) do
   table.insert( t, txt:sub( i, i ) )
  end
  for i = 1,#t do
   if self.screen[self.yPos][self.xPos] then
	self.screen[self.yPos][self.xPos] = { t = t[i], bc = self.bc, tc = self.tc }
	self.xPos = self.xPos+1
	if self.xPos > self.w then
	 self.xPos = 1
	 self.yPos = self.yPos + 1
	end
   end
  end
  self:update( )
end;
print = function( self, text )
  self:write( text )
  self.yPos = self.yPos+1
  self.xPos = 1
end;
pixel = function( self, x, y, text, bc, tc )
  if self.screen[y][x] then
   self.screen[y][x] = { bc = bc, tc = tc, t = text }
  end
end;
addShape = function( self, t )
  y = self.yPos
  x = self.xPos
  for i = 1,#t do
   self:pixel( x+t[i].x-1, y+t[i].y-1, t[i].text, t[i].bc, t[i].tc )
  end
end;
setCursorPos = function( self, x, y )
  self.xPos = x
  self.yPos = y
  self:update( )
end;
setBackgroundColour = function( self, col )
  self.bc = col
  self:update( )
end;
setTextColour = function( self, col )
  self.tc = col
  self:update( )
end;
clear = function( self )
  for i = 1,#self.screen do
   for k = 1,#self.screen[i] do
	self.screen[i][k] = { t = self.dt, bc = self.bc, tc = self.tc }
   end
  end
  self:update( )
end;
setHeader = function( self, text )
  self.header.text = text
  self:update( )
end;
setHeaderColour = function( self, col, colo )
  self.header = { text = self.header.text, bc = col, tc = colo }
  self:update( )
end;
}
process = {
current = false;
create = function( link, func, stopfunc )
  if type( link ) ~= "table" then link = process end
  local t = { }
  t.co = coroutine.create( func )
  t.stopco = coroutine.create( stopfunc )
  t.status = "created"
  t.windows = { window:create( 1, 1, 51, 19, colours.black, colours.white, " ", "[ default window ]" ) }
  t.window = t.windows[1]
  return setmetatable( t, { __index = link } )
end;
run = function( self, ... )
  if coroutine.status( self.co ) == "dead" then
   self = nil
   return "removed"
  end
  if self.status == "stopping" then
   process.current = self
   err = { pcall( coroutine.resume, self.stopco, unpack( arg ) ) }
  elseif self.status == "running" or self.status == "created" then
   process.current = self
   err = { pcall( coroutine.resume, self.co, unpack( arg ) ) }
  end
  process.current = false
  if not err[1] then
   if self.status == "stopping" then
	self = nil
	err[1] = "removed"
   else
	self.status = "stopping"
	err[1] = "stopping"
   end
  end
  return( unpack( err ) )
end;
move = function( self, x, y )
  self.window:move( x, y )
end;
addWindow = function( self, index, x, y, w, h, bc, tc, dt, header )
  self.windows[index] = window:create( x, y, w, h, bc, tc, dt, header )
end;
setWindow = function( self, index )
  if self.windows[index] then
   self.window = self.windows[index]
  end
end;
getWindows = function( self )
  local t = { }
  for k, v in pairs( self.windows ) do
   table.insert( t, k )
  end
  return t
end;
draw = function( self )
  self.window:draw( )
end;
write = function( self, text )
  self.window:write( text )
end;
print = function( self, text )
  self.window:print( text )
end;
setCursorPos = function( self, x, y )
  self.window:setCursorPos( x, y )
end;
setBackgroundColour = function( self, col )
  self.window:setBackgroundColour( col )
end;
setTextColour = function( self, col )
  self.window:setTextColour( col )
end;
clear = function( self )
  self.window:clear( )
end;
setHeader = function( self, text )
  self.window:setHeader( text )
end;
setHeaderColour = function( self, col, colo )
  self.window:setHeaderColour( col, colo )
end;
}
join = function( t, pat )
local str = ""
if not pat then pat = "" end
for i = 1,#t do
  str = str..pat..tostring( t[i] )
end
return str
end
splitChars = function( str )
t = { }
for i = 1,string.len( str ) do
  table.insert( t, string.sub( str, i, i ) )
end
return t
end
split = function(str,pat)
local t = {}
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
menu = {
create = function( tContents, w, h )
  local t = { }
  t.contents = tContents
  t.os = 0
  t.w = w
  t.h = h
  return setmetatable( t, { __index = menu } )
end;
add = function( self, data )
  table.insert( self.contents, data )
end;
remove = function( self, index )
  table.remove( self.contents, index )
end;
set = function( self, tContents )
  self.contents = tContents
end;
getSelection = function( self )
  local h = self.h < 20 and self.h or 19
  local t = { }
  for i = 1+self.os,h+self.os do
   if not self.contents[i] then
	table.insert( t, "" )
   else
	table.insert( t, self.contents[i] )
   end
  end
  for i = 1,#t do
   if t[i]:len( ) > self.w then
	t[i] = string.sub( t[i], 1, self.w-3 ).."..."
   end
  end
  return t
end;
getContents = function( self, index )
  if index > self.h then return nil end
  return self.contents[index + self.os]
end;
getIndex = function( self, index )
  return self.contents[index]
end;
scroll = function( self, am )
  self.os = self.os+am
  if self.os < 0 then
   self.os = 0
  end
  if self.os+self.h >= #self.contents and #self.contents > self.h then
   self.os = #self.contents - self.h
  elseif #self.contents <= self.h then
   self.os = 0
  end
end;
}
file = {
read = function( path, intoTable )
  local k = assert( fs.open( path, "r" ), "File not found" )
  m = k.readAll( )
  k.close( )
  if intoTable then
   m = split( m, "\n" )
  end
  return m
end;
save = function( file, data )
  if type( data ) == "table" then isTable = true end
  local f = fs.open( file, "w" )
  if not isTable then
   f.write( tostring( data ) )
  else
   for i = 1,#data do
	f.writeLine( tostring( data[i] ) )
   end
  end
  f.close( )
end;
writeLine = function( path, line, data )
  file = file.read( path, true )
  file[line] = data
  file.save( path, file )
end;
readLine = function( path, line )
  local t = file.read( path, true )
  return t[line]
end;
}
variable = {
vars = { };
add = function( name, data )
  table.insert( variable.vars, { name = name, type = type( data ), data = data } )
end;
save = function( file )
  local t = { }
  for i = 1,#variable.vars do
   if variable.vars[i].type == "table" then
	variable.vars[i].data = textutils.serialize( variable.vars[i].data )
   end
   table.insert( t, variable.vars[i].name..";"..variable.vars[i].type..";"..tostring( variable.vars[i].data ) )
  end
  k = fs.open( file, "w" )
  for i = 1,#t do
   k.writeLine( t[i] )
  end
  k.close( )
end;
remove = function( index )
  if type( index ) == "string" then
   for i = 1,#variable.vars do
	if variable.vars[i].name == index then
	 table.remove( variable.vars, i )
	 return true
	end
   end
  else
   table.remove( variable.vars, index )
  end
end;
load = function( file )
  local k = assert( fs.open( file, "r" ), "File not found" )
  m = k.readAll( )
  k.close( )
  k = split( m, "\n" )
  for i = 1,#k do
   m = split( k[i], ";" )
   if m[2] == "table" then
	m[3] = textutils.unserialize( m[3] )
   elseif m[2] == "number" then
	m[3] = tonumber( m[3] )
   elseif m[2] == "boolean" then
	if m[3] == "false" then
	 m[3] = false
	else
	 m[3] = true
	end
   end
   variable.add( m[1], m[3] )
   _G[m[1]] = m[3]
  end
end;
}
log = {
data = {
  exlude = { };
};
save = function( path )
  local logging = { }
  for i = 1,#sky.log.data do
   local n = false
   for k = 1,#sky.log.exclude do
	if sky.log.data[i].type == sky.log.exclude[k] or reading then
	 n = true
	end
   end
   if not n then
	table.insert( logging, sky.log.data[i] )
   end
  end
  sky.log.data = logging
  sky.file.save( path, sky.log.data, true )
  return true
end;
add = function( type, ... )
  local str = ""
  for i = 1,#arg do
   str = str.." "..tostring( arg[i] )
  end
  table.insert( sky.log.data, { type = type, data = ":"..str } )
end;
load = function( path )
  local t = { }
  t = file.read( path, true )
  for i = 1,#t do
   t[i] = split( t[i], ":" )
  end
  return t
end;
}


pastebin: pastebin
screenies coming soon when I make some programs with the APIS

Thanks for reading :P/>
superaxander #2
Posted 06 March 2013 - 08:38 AM
Looks awesome
Exerro #3
Posted 06 March 2013 - 08:41 AM
thx, i'm currently making a game API to go with it, got rectangle collision and entities working but per-pixel collision is proving harder than i thought