mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-12-16 11:05:45 +00:00
Compare commits
No commits in common. "main" and "v1.1.1-beta" have entirely different histories.
main
...
v1.1.1-bet
47 changed files with 160 additions and 1785 deletions
|
|
@ -1,7 +0,0 @@
|
||||||
alias ll "ls -al"
|
|
||||||
alias la "ls -a"
|
|
||||||
alias rmdir "rm -r"
|
|
||||||
alias reboot "shutdown -r"
|
|
||||||
|
|
||||||
# Comment or remove the line below to disable the MOTD
|
|
||||||
motd
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
local version = "0.1.1"
|
local version = "0.0.3"
|
||||||
local systemDirectory = "/sys"
|
local systemDirectory = "/sys"
|
||||||
|
|
||||||
print("Starting CapyOS")
|
print("Starting CapyOS")
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
|
|
||||||
if options.l or options.list then
|
|
||||||
for alias, value in pairs(shell.aliases) do
|
|
||||||
print(string.format("%s = \"%s\"", alias, value))
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local alias = args[1]
|
|
||||||
|
|
||||||
if not alias or options.h or options.help then
|
|
||||||
print("Usage: alias [option...] <alias> [command]")
|
|
||||||
print("Options:")
|
|
||||||
print(" -l --list: List aliases")
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local command = table.pack(select(2, ...))
|
|
||||||
if #command == 0 then
|
|
||||||
shell.aliases[alias] = nil
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
shell.aliases[alias] = table.concat(command, " ")
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
local scheduler = require("scheduler")
|
|
||||||
scheduler.spawn(function()
|
|
||||||
shell.run(arg.string)
|
|
||||||
end)
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
|
|
||||||
local args = {...}
|
|
||||||
|
|
||||||
local path = shell.resolve(args[1])
|
|
||||||
|
|
||||||
local f<close> = fs.open(path, "r")
|
|
||||||
print(f:read("a"))
|
|
||||||
f:close()
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
print(table.concat(args, " "))
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
shell.exit()
|
shell.exit()
|
||||||
|
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
local gpu = require("gpu")
|
|
||||||
local event = require("event")
|
|
||||||
local donuts = {}
|
|
||||||
local limit = 100
|
|
||||||
|
|
||||||
local w, h = gpu.getSize()
|
|
||||||
local function insert()
|
|
||||||
local donut = {
|
|
||||||
x = math.random(-20, w + 20),
|
|
||||||
y = math.random(-20, h + 20),
|
|
||||||
d = math.random() * math.pi*2,
|
|
||||||
dir = math.random(0, 1),
|
|
||||||
c = math.random(0xffffff),
|
|
||||||
life = math.random(100, 1000),
|
|
||||||
}
|
|
||||||
table.insert(donuts, donut)
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
if #donuts < limit then
|
|
||||||
insert()
|
|
||||||
end
|
|
||||||
gpu.clear(0)
|
|
||||||
for k, donut in ipairs(donuts) do
|
|
||||||
if donut.life <= 0 then
|
|
||||||
table.remove(donuts, k)
|
|
||||||
end
|
|
||||||
local doReverse = math.random(0, 1000) > 950
|
|
||||||
donut.x = donut.x + math.cos(donut.d) * 4
|
|
||||||
donut.y = donut.y + math.sin(donut.d) * 4
|
|
||||||
donut.d = donut.d + (donut.dir == 1 and 0.05 or -0.05)
|
|
||||||
gpu.drawCircle(donut.x, donut.y, 20, donut.c, 10)
|
|
||||||
if doReverse then
|
|
||||||
donut.dir = donut.dir == 1 and 0 or 1
|
|
||||||
end
|
|
||||||
donut.life = donut.life - 1
|
|
||||||
end
|
|
||||||
event.push("donuts")
|
|
||||||
event.pull("donuts")
|
|
||||||
end
|
|
||||||
|
|
@ -38,19 +38,15 @@ local function iter(cr, ci)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function draw()
|
local function draw()
|
||||||
local size = w * h
|
local buffer <close> = gpu.newBuffer()
|
||||||
local canvas = { string.unpack(("B"):rep(size), ("\0"):rep(size)) }
|
|
||||||
canvas[#canvas] = nil
|
|
||||||
|
|
||||||
|
|
||||||
for y = 0, h - 1 do
|
for y = 0, h - 1 do
|
||||||
for x = 0, w - 1 do
|
for x = 0, w - 1 do
|
||||||
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
|
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
|
||||||
canvas[y * w + x] = colorUnit * (iterations - i)
|
buffer[y * w + x] = colorUnit * (iterations - i)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local buffer <close> = gpu.bufferFrom(canvas, w, h)
|
|
||||||
gpu.setBuffer(buffer)
|
gpu.setBuffer(buffer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,209 +0,0 @@
|
||||||
local event = require("event")
|
|
||||||
local gpu = require("gpu")
|
|
||||||
local colors = require("colors")
|
|
||||||
local term = require("term")
|
|
||||||
local timer = require("timer")
|
|
||||||
|
|
||||||
local w, h = gpu.getSize()
|
|
||||||
local tw, th = term.getSize()
|
|
||||||
|
|
||||||
local selectedColor = 1
|
|
||||||
local thickness = 4
|
|
||||||
|
|
||||||
local canvasW, canvasH = term.toRealPos(tw - 1, th + 1)
|
|
||||||
canvasW = canvasW - 3
|
|
||||||
local size = canvasW * canvasH
|
|
||||||
local canvas = {string.unpack(("B"):rep(size), ("\0"):rep(size))}
|
|
||||||
canvas[#canvas] = nil
|
|
||||||
|
|
||||||
local function drawCircle(buffer, x, y, radius, color)
|
|
||||||
radius = math.max(0, radius)
|
|
||||||
if radius == 0 then
|
|
||||||
buffer[x + buffer.width * y] = color
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local width = buffer.width
|
|
||||||
local height = buffer.height
|
|
||||||
|
|
||||||
local index = function(x, y)
|
|
||||||
return y * width + x
|
|
||||||
end
|
|
||||||
|
|
||||||
local isValid = function(x, y)
|
|
||||||
return x >= 0 and x < width and y >= 0 and y < height
|
|
||||||
end
|
|
||||||
|
|
||||||
local setPixel = function(x, y, color)
|
|
||||||
if isValid(x, y) then
|
|
||||||
buffer[index(x, y)] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawFilledCirclePoints = function(cx, cy, x, y)
|
|
||||||
for dx = -x, x do
|
|
||||||
for dy = -y, y do
|
|
||||||
setPixel(cx + dx, cy + dy, color)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawCircleBresenham = function(cx, cy, radius)
|
|
||||||
local x = 0
|
|
||||||
local y = radius
|
|
||||||
local d = 3 - 2 * radius
|
|
||||||
drawFilledCirclePoints(cx, cy, x, y)
|
|
||||||
while y >= x do
|
|
||||||
x = x + 1
|
|
||||||
if d > 0 then
|
|
||||||
y = y - 1
|
|
||||||
d = d + 4 * (x - y) + 10
|
|
||||||
else
|
|
||||||
d = d + 4 * x + 6
|
|
||||||
end
|
|
||||||
drawFilledCirclePoints(cx, cy, x, y)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
drawCircleBresenham(x, y, radius)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawLine(buffer, x0, y0, x1, y1, color, thickness)
|
|
||||||
local width = canvasW
|
|
||||||
local height = canvasH
|
|
||||||
|
|
||||||
local index = function(x, y)
|
|
||||||
return y * width + x
|
|
||||||
end
|
|
||||||
|
|
||||||
local isValid = function(x, y)
|
|
||||||
return x >= 0 and x < width and y >= 0 and y < height
|
|
||||||
end
|
|
||||||
|
|
||||||
local setPixel = function(x, y)
|
|
||||||
if isValid(x, y) then
|
|
||||||
buffer[index(x, y)] = color
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawLineBresenham = function()
|
|
||||||
local i = 0
|
|
||||||
local dx = math.abs(x1 - x0)
|
|
||||||
local dy = math.abs(y1 - y0)
|
|
||||||
local sx = x0 < x1 and 1 or -1
|
|
||||||
local sy = y0 < y1 and 1 or -1
|
|
||||||
local err = dx - dy
|
|
||||||
|
|
||||||
local majorAxis = dx > dy
|
|
||||||
|
|
||||||
while x0 ~= x1 or y0 ~= y1 do
|
|
||||||
for i = 0, thickness - 1 do
|
|
||||||
if majorAxis then
|
|
||||||
setPixel(x0, y0 + i)
|
|
||||||
else
|
|
||||||
setPixel(x0 + i, y0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local err2 = 2 * err
|
|
||||||
if err2 > -dy then
|
|
||||||
err = err - dy
|
|
||||||
x0 = x0 + sx
|
|
||||||
end
|
|
||||||
if err2 < dx then
|
|
||||||
err = err + dx
|
|
||||||
y0 = y0 + sy
|
|
||||||
end
|
|
||||||
|
|
||||||
if i % 1024 == 0 then
|
|
||||||
--event.push("paint")
|
|
||||||
--event.pull("paint")
|
|
||||||
--timer.sleep(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
drawLineBresenham()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function drawUI()
|
|
||||||
term.setBackground(0)
|
|
||||||
term.clear()
|
|
||||||
for y = 1, 16 do
|
|
||||||
term.setPos(tw - 1, y)
|
|
||||||
term.setBackground(0)
|
|
||||||
term.setForeground(colors[y])
|
|
||||||
if selectedColor == y then
|
|
||||||
term.setBackground(colors[y])
|
|
||||||
term.write(" ")
|
|
||||||
else
|
|
||||||
term.write("##")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
term.setPos(tw - 1, 17)
|
|
||||||
|
|
||||||
if selectedColor == 0 then
|
|
||||||
term.setBackground(colors.white)
|
|
||||||
term.setForeground(0)
|
|
||||||
else
|
|
||||||
term.setBackground(0)
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
end
|
|
||||||
term.write("XX")
|
|
||||||
|
|
||||||
term.setPos(tw - 1, 18)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.write(thickness)
|
|
||||||
|
|
||||||
gpu.drawLine(canvasW + 1, 0, canvasW, canvasH, colors.gray, 2)
|
|
||||||
|
|
||||||
local b<close> = gpu.bufferFrom(canvas, canvasW, canvasH)
|
|
||||||
gpu.drawBuffer(b, 0, 0, {
|
|
||||||
source = {
|
|
||||||
0, 0, canvasW, canvasH
|
|
||||||
}
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function contains(arr, val)
|
|
||||||
for i, v in ipairs(arr) do
|
|
||||||
if v == val then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldX, oldY
|
|
||||||
while true do
|
|
||||||
drawUI()
|
|
||||||
|
|
||||||
local ev, b, x, y = event.pull("mouse_down", "mouse_up", "mouse_move", "mouse_scroll")
|
|
||||||
local tx, ty = term.fromRealPos(x, y)
|
|
||||||
if ev == "mouse_up" then
|
|
||||||
if x >= canvasW then
|
|
||||||
if ty <= 16 then
|
|
||||||
selectedColor = ty
|
|
||||||
elseif ty == 17 then
|
|
||||||
selectedColor = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
oldX, oldY = nil, nil
|
|
||||||
elseif ev == "mouse_down" or (ev == "mouse_move" and contains(b, 1)) then
|
|
||||||
if x < canvasW and y < canvasH then
|
|
||||||
--canvas[x + y * canvasW] = colors[selectedColor] or 0
|
|
||||||
--drawCircle(canvas, x, y, thickness - 2, colors[selectedColor])
|
|
||||||
|
|
||||||
drawLine(canvas, x, y, oldX or x, oldY or y, colors[selectedColor] or 0, thickness)
|
|
||||||
--gpu.drawLine(x, y, oldX or x, oldY or y, colors[selectedColor] or 0)
|
|
||||||
--canvas = gpu.getBuffer()
|
|
||||||
|
|
||||||
oldX, oldY = x, y
|
|
||||||
end
|
|
||||||
elseif ev == "mouse_scroll" then
|
|
||||||
local x, y, b = b, x, y
|
|
||||||
local tx, ty = term.fromRealPos(x, y)
|
|
||||||
if x >= canvasW and ty == 18 then
|
|
||||||
thickness = math.min(99, math.max(0, thickness - b))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -11,13 +11,7 @@ local function slowPrint(text, delay)
|
||||||
print()
|
print()
|
||||||
end
|
end
|
||||||
|
|
||||||
local args = {...}
|
|
||||||
local text = "Hello, World!"
|
|
||||||
if #args > 0 then
|
|
||||||
text = table.concat(args, " ")
|
|
||||||
end
|
|
||||||
|
|
||||||
local color = colors[math.random(1, #colors)]
|
local color = colors[math.random(1, #colors)]
|
||||||
|
|
||||||
term.setForeground(color)
|
term.setForeground(color)
|
||||||
slowPrint(text, 0.05)
|
slowPrint("Hello, World!", 0.05)
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local helpPath = "/sys/share/help"
|
|
||||||
|
|
||||||
local topicName = arg[1] or "index"
|
|
||||||
|
|
||||||
if not fs.exists(fs.combine(helpPath, topicName)) then
|
|
||||||
print(string.format("Topic \"%s\" not found.", topicName))
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
shell.run("/sys/bin/less.lua", fs.combine(helpPath, topicName))
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
local term = require("term")
|
|
||||||
local keys = require("keys")
|
|
||||||
local event = require("event")
|
|
||||||
local fs = require("fs")
|
|
||||||
local timer = require("timer")
|
|
||||||
local colors = require("colors")
|
|
||||||
|
|
||||||
local filename = shell.resolve(arg[1])
|
|
||||||
|
|
||||||
local f<close> = fs.open(filename, "r")
|
|
||||||
local lines = {}
|
|
||||||
local lineMax = 0
|
|
||||||
for line in f:lines() do
|
|
||||||
table.insert(lines, line)
|
|
||||||
lineMax = math.max(lineMax, #line)
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
|
|
||||||
local width, height = term.getSize()
|
|
||||||
height = height - 1
|
|
||||||
local posx, posy = 0, 0
|
|
||||||
|
|
||||||
local function redraw()
|
|
||||||
term.clear()
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
for i = 1, height do
|
|
||||||
if i + posy > #lines then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
term.setPos(-posx + 1, i)
|
|
||||||
term.write(lines[i + posy])
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
term.setPos(1, height + 1)
|
|
||||||
term.write("Use arrow keys to move or press Q to exit.")
|
|
||||||
end
|
|
||||||
|
|
||||||
while true do
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
local _, key = event.pull("key_down")
|
|
||||||
|
|
||||||
if key == keys.enter or key == keys.down then
|
|
||||||
posy = posy + 1
|
|
||||||
elseif key == keys.up then
|
|
||||||
posy = posy - 1
|
|
||||||
elseif key == keys.right then
|
|
||||||
posx = posx + 1
|
|
||||||
elseif key == keys.left then
|
|
||||||
posx = posx - 1
|
|
||||||
elseif key == keys.q or key == keys.escape then
|
|
||||||
-- Clear event queue
|
|
||||||
timer.sleep(0)
|
|
||||||
term.clear()
|
|
||||||
term.setPos(1, 1)
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if posy > #lines - height then
|
|
||||||
posy = #lines - height
|
|
||||||
end
|
|
||||||
|
|
||||||
if posy < 0 then
|
|
||||||
posy = 0
|
|
||||||
end
|
|
||||||
|
|
||||||
if posx + width > lineMax + 1 then
|
|
||||||
posx = lineMax - width + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if posx < 0 then
|
|
||||||
posx = 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
@ -1,88 +1,25 @@
|
||||||
|
local args = { ... }
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
local term = require("term")
|
local term = require("term")
|
||||||
local colors = require("colors")
|
local colors = require("colors")
|
||||||
local argparser = require("argparser")
|
local dir = shell.getDir()
|
||||||
|
|
||||||
local theme = {
|
|
||||||
directory = colors.lightBlue,
|
|
||||||
file = colors.white,
|
|
||||||
lua = colors.yellow,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function humanizeBytes(n)
|
|
||||||
local prefixes = {
|
|
||||||
[0] = "",
|
|
||||||
"k",
|
|
||||||
"M",
|
|
||||||
"G",
|
|
||||||
"T",
|
|
||||||
}
|
|
||||||
local block = 1024
|
|
||||||
local prefixIndex = 0
|
|
||||||
|
|
||||||
while n >= block do
|
|
||||||
n = n / 1024
|
|
||||||
prefixIndex = prefixIndex + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return string.format("%.0f%s", n, prefixes[prefixIndex])
|
|
||||||
end
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if options.h or options.help then
|
|
||||||
print("Usage: ls [option...] [path]")
|
|
||||||
print("List files (current directory by default)")
|
|
||||||
print("Options:")
|
|
||||||
print(" -a: Include hidden files")
|
|
||||||
print(" -l: Use long listing format")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local path = shell.getDir()
|
|
||||||
|
|
||||||
if args[1] then
|
if args[1] then
|
||||||
path = shell.resolve(args[1])
|
dir = shell.resolve(args[1])
|
||||||
end
|
end
|
||||||
|
|
||||||
if not fs.isDir(path) then
|
if not fs.isDir(dir) then
|
||||||
error("No such directory: " .. path, 0)
|
error("No such directory: " .. dir, 0)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local entries = fs.list(path)
|
local files = fs.list(dir)
|
||||||
|
for k, v in ipairs(files) do
|
||||||
if options.l then
|
if fs.isDir(fs.combine(dir, v)) then
|
||||||
print(string.format("total %d", #entries))
|
term.setForeground(colors.lightBlue)
|
||||||
end
|
print(v .. "/")
|
||||||
local printed = 0
|
else
|
||||||
for i, entry in ipairs(entries) do
|
term.setForeground(colors.white)
|
||||||
if entry:sub(1, 1) ~= "." or options.a then
|
print(v)
|
||||||
printed = printed + 1
|
|
||||||
local attributes = fs.attributes(fs.combine(path, entry))
|
|
||||||
local size = humanizeBytes(attributes.size)
|
|
||||||
local date = os.date("%x %H:%m", attributes.modified // 1000)
|
|
||||||
|
|
||||||
local entryType
|
|
||||||
if attributes.isDirectory then
|
|
||||||
entryType = "directory"
|
|
||||||
else
|
|
||||||
entryType = "file"
|
|
||||||
if string.match(entry, "%.lua$") then
|
|
||||||
entryType = "lua"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if options.l then
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.write(string.format("%s %5s %s ", attributes.isDirectory and "d" or "-", size, date))
|
|
||||||
end
|
|
||||||
term.setForeground(theme[entryType])
|
|
||||||
io.write(entry)
|
|
||||||
|
|
||||||
io.write(options.l and "\n" or "\t")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not options.l and printed > 0 then
|
|
||||||
print()
|
|
||||||
end
|
|
||||||
|
|
@ -1,16 +1,49 @@
|
||||||
local term = require("term")
|
local term = require("term")
|
||||||
local io = require("io")
|
local io = require("io")
|
||||||
local colors = require("colors")
|
local colors = require("colors")
|
||||||
local argparser = require("argparser")
|
local colours = colors
|
||||||
local tableutils = require("tableutils")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
local tArgs = { ... }
|
||||||
|
if #tArgs > 0 then
|
||||||
|
print("This is an interactive Lua prompt.")
|
||||||
|
print("To run a lua program, just type its name.")
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
--local pretty = require "cc.pretty"
|
||||||
|
|
||||||
|
local bRunning = true
|
||||||
|
local tCommandHistory = {}
|
||||||
|
local tEnv = {
|
||||||
|
["exit"] = setmetatable({}, {
|
||||||
|
__tostring = function() return "Call exit() to exit." end,
|
||||||
|
__call = function() bRunning = false end,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
setmetatable(tEnv, { __index = _ENV })
|
||||||
|
|
||||||
|
for k, v in pairs(package.loaded) do
|
||||||
|
tEnv[k] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
term.setForeground(colours.yellow)
|
||||||
|
print(_VERSION .. " interactive prompt")
|
||||||
|
print("Call exit() to exit.")
|
||||||
|
term.setForeground(colours.white)
|
||||||
|
|
||||||
|
while bRunning do
|
||||||
|
term.setForeground(colours.yellow)
|
||||||
|
io.write("> ")
|
||||||
|
term.setForeground(colours.white)
|
||||||
|
|
||||||
|
local s = io.read(nil, tCommandHistory)
|
||||||
|
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
||||||
|
table.insert(tCommandHistory, s)
|
||||||
|
end
|
||||||
|
|
||||||
local function evaluate(str, env, chunkname)
|
|
||||||
chunkname = chunkname or "=lua"
|
|
||||||
local nForcePrint = 0
|
local nForcePrint = 0
|
||||||
local func, e = load(str, chunkname, "t", env)
|
local func, e = load(s, "=lua", "t", tEnv)
|
||||||
local func2 = load("return " .. str, chunkname, "t", env)
|
local func2 = load("return " .. s, "=lua", "t", tEnv)
|
||||||
if not func then
|
if not func then
|
||||||
if func2 then
|
if func2 then
|
||||||
func = func2
|
func = func2
|
||||||
|
|
@ -29,70 +62,14 @@ local function evaluate(str, env, chunkname)
|
||||||
local n = 1
|
local n = 1
|
||||||
while n < tResults.n or n <= nForcePrint do
|
while n < tResults.n or n <= nForcePrint do
|
||||||
local value = tResults[n + 1]
|
local value = tResults[n + 1]
|
||||||
print(tableutils.pretty(value))
|
print(tostring(value))
|
||||||
n = n + 1
|
n = n + 1
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
io.stderr.print(tResults[2])
|
io.stderr.print(tResults[2])
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
io.stderr.print(e)
|
io.stderr.print(e)
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createEnvironment()
|
|
||||||
return setmetatable({}, { __index = _ENV })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function loadPackages(env)
|
|
||||||
for k, v in pairs(package.loaded) do
|
|
||||||
env[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if options.e then
|
|
||||||
local env = createEnvironment()
|
|
||||||
loadPackages(env)
|
|
||||||
return evaluate(table.concat(args, " "), env)
|
|
||||||
end
|
|
||||||
|
|
||||||
if #args > 0 then
|
|
||||||
print("This is an interactive Lua prompt.")
|
|
||||||
print("To run a lua program, just type its name.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
--local pretty = require "cc.pretty"
|
|
||||||
|
|
||||||
local bRunning = true
|
|
||||||
local tCommandHistory = {}
|
|
||||||
|
|
||||||
|
|
||||||
local tEnv = createEnvironment()
|
|
||||||
tEnv.exit = setmetatable({}, {
|
|
||||||
__tostring = function() return "Call exit() to exit." end,
|
|
||||||
__call = function() bRunning = false end,
|
|
||||||
})
|
|
||||||
loadPackages(tEnv)
|
|
||||||
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
print(_VERSION .. " interactive prompt")
|
|
||||||
print("Call exit() to exit.")
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
|
|
||||||
while bRunning do
|
|
||||||
term.setForeground(colors.yellow)
|
|
||||||
io.write("> ")
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
|
|
||||||
local s = io.read(nil, tCommandHistory)
|
|
||||||
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
|
|
||||||
table.insert(tCommandHistory, s)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
evaluate(s, tEnv)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
|
|
||||||
local date = os.date("*t")
|
|
||||||
|
|
||||||
if date.month == 4 and date.day == 28 then
|
|
||||||
print("Ed Balls")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local motdList = {}
|
|
||||||
|
|
||||||
local f<close> = fs.open("/sys/share/motd.txt", "r")
|
|
||||||
for line in f:lines() do
|
|
||||||
table.insert(motdList, line)
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
|
|
||||||
local motdIndex = math.random(1, #motdList)
|
|
||||||
|
|
||||||
print(motdList[motdIndex])
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
|
||||||
|
|
||||||
if not args[1] or not args[2] or options.h or options.help then
|
|
||||||
print("Usage: mv [option...] <source> <target>")
|
|
||||||
print("Options:")
|
|
||||||
print(" -h --help: Display help")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local source = shell.resolve(args[1])
|
|
||||||
local destination = shell.resolve(args[2])
|
|
||||||
|
|
||||||
fs.move(source, destination)
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local programs = fs.list("/sys/bin", function(name, attr)
|
|
||||||
return not attr.isDirectory
|
|
||||||
end)
|
|
||||||
|
|
||||||
for i, v in ipairs(programs) do
|
|
||||||
programs[i] = string.gsub(v, "%.lua$", "")
|
|
||||||
end
|
|
||||||
|
|
||||||
print(table.concat(programs, " "))
|
|
||||||
8
Capy64/Assets/Lua/CapyOS/sys/bin/reboot.lua
Normal file
8
Capy64/Assets/Lua/CapyOS/sys/bin/reboot.lua
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
local timer = require("timer")
|
||||||
|
local machine = require("machine")
|
||||||
|
|
||||||
|
print("Goodbye!")
|
||||||
|
|
||||||
|
timer.sleep(1)
|
||||||
|
|
||||||
|
machine.reboot()
|
||||||
|
|
@ -1,16 +1,10 @@
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
local args = { ... }
|
||||||
|
if #args == 0 then
|
||||||
if not args[1] or options.h or options.help then
|
print("Usage: rm <file>")
|
||||||
print("Usage: rm [option...] <path>")
|
|
||||||
print("Options:")
|
|
||||||
print(" -r --recursive: Delete non-empty directories")
|
|
||||||
print(" -h --help: Display help")
|
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local file = shell.resolve(args[1])
|
local file = shell.resolve(args[1])
|
||||||
|
fs.delete(file, true)
|
||||||
fs.delete(file, options.recursive or options.r)
|
|
||||||
|
|
|
||||||
|
|
@ -2,35 +2,40 @@ local term = require("term")
|
||||||
local colors = require("colors")
|
local colors = require("colors")
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
local machine = require("machine")
|
local machine = require("machine")
|
||||||
local argparser = require("argparser")
|
|
||||||
local scheduler = require("scheduler")
|
|
||||||
local createPackageEnvironment = require("shell.package")
|
|
||||||
|
|
||||||
local useScheduler = false
|
|
||||||
local exit = false
|
local exit = false
|
||||||
local parentShell = shell
|
|
||||||
local isStartupShell = parentShell == nil
|
|
||||||
local shell = {}
|
local shell = {}
|
||||||
|
|
||||||
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
shell.path = "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||||
shell.homePath = parentShell and parentShell.home or "/home"
|
shell.homePath = "/home"
|
||||||
shell.aliases = parentShell and parentShell.aliases or {}
|
|
||||||
|
|
||||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
local currentDir = shell.homePath
|
||||||
|
|
||||||
local function buildEnvironment(command, filepath, args, argf)
|
local function buildEnvironment(path, args)
|
||||||
local arg = { table.unpack(args, 2) }
|
local arg = { table.unpack(args, 2) }
|
||||||
arg[0] = command
|
arg[0] = path
|
||||||
arg.string = argf
|
|
||||||
|
|
||||||
local envPackage = createPackageEnvironment(filepath)
|
|
||||||
envPackage.loaded.scheduler = scheduler
|
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
shell = shell,
|
shell = shell,
|
||||||
arg = arg,
|
arg = arg
|
||||||
scheduler = scheduler,
|
}, { __index = _G })
|
||||||
}, { __index = envPackage.loaded._G })
|
end
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
function shell.getDir()
|
function shell.getDir()
|
||||||
|
|
@ -67,24 +72,16 @@ function shell.resolveProgram(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.run(...)
|
function shell.run(...)
|
||||||
local args = argparser.tokenize(...)
|
local args = tokenise(...)
|
||||||
local argf = table.concat({...}, " ")
|
|
||||||
local command = args[1]
|
local command = args[1]
|
||||||
|
|
||||||
argf = argf:sub(#command + 2)
|
|
||||||
|
|
||||||
local path = shell.resolveProgram(command)
|
local path = shell.resolveProgram(command)
|
||||||
|
|
||||||
if not path then
|
if not path then
|
||||||
if shell.aliases[command] then
|
io.stderr.print("Command not found: " .. command)
|
||||||
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
return false
|
||||||
else
|
|
||||||
io.stderr.print("Command not found: " .. command)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local env = buildEnvironment(command, path, args, argf)
|
local env = buildEnvironment(command, args)
|
||||||
|
|
||||||
local func, err = loadfile(path, "t", env)
|
local func, err = loadfile(path, "t", env)
|
||||||
|
|
||||||
|
|
@ -93,21 +90,7 @@ function shell.run(...)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err
|
local ok, err = pcall(func, table.unpack(args, 2))
|
||||||
local function run()
|
|
||||||
ok, err = pcall(func, table.unpack(args, 2))
|
|
||||||
end
|
|
||||||
|
|
||||||
if useScheduler then
|
|
||||||
local programTask, yielded = scheduler.spawn(run)
|
|
||||||
|
|
||||||
if yielded then
|
|
||||||
coroutine.yield("scheduler_task_end")
|
|
||||||
end
|
|
||||||
else
|
|
||||||
run()
|
|
||||||
end
|
|
||||||
|
|
||||||
if not ok then
|
if not ok then
|
||||||
io.stderr.print(err)
|
io.stderr.print(err)
|
||||||
return false
|
return false
|
||||||
|
|
@ -124,21 +107,6 @@ if not fs.exists(shell.homePath) then
|
||||||
fs.makeDir(shell.homePath)
|
fs.makeDir(shell.homePath)
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setForeground(colors.white)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
|
|
||||||
if isStartupShell then
|
|
||||||
if fs.exists(fs.combine(shell.homePath, ".shrc")) then
|
|
||||||
local f <close> = fs.open(fs.combine(shell.homePath, ".shrc"), "r")
|
|
||||||
for line in f:lines() do
|
|
||||||
if line:match("%S") and not line:match("^%s-#") then
|
|
||||||
shell.run(line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
f:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local history = {}
|
local history = {}
|
||||||
local lastExecSuccess = true
|
local lastExecSuccess = true
|
||||||
while not exit do
|
while not exit do
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,8 @@
|
||||||
|
local timer = require("timer")
|
||||||
local machine = require("machine")
|
local machine = require("machine")
|
||||||
local scheduler = require("scheduler")
|
|
||||||
local argparser = require("argparser")
|
|
||||||
|
|
||||||
local args, options = argparser.parse(...)
|
print("Goodbye!")
|
||||||
|
|
||||||
if options.h or options.help then
|
timer.sleep(1)
|
||||||
print("Usage: shutdown [option...]")
|
|
||||||
print("Shutdown or restart Capy64.")
|
|
||||||
print("Options:")
|
|
||||||
print(" -s --shutdown: Shutdown and exit Capy64. (default)")
|
|
||||||
print(" -r --reboot: Restart Capy64.")
|
|
||||||
print(" -t --time: Time to wait in seconds. (\"now\" is 0 seconds, default)")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local time = 0
|
machine.shutdown()
|
||||||
if options.t or options.time then
|
|
||||||
time = options.t or options.time
|
|
||||||
end
|
|
||||||
if time == "now" then
|
|
||||||
time = 0
|
|
||||||
else
|
|
||||||
time = tonumber(time)
|
|
||||||
if not time then
|
|
||||||
error("Invalid time option: " .. (options.t or options.time), 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
scheduler.ipc(1, "power", {
|
|
||||||
reboot = options.r or options.reboot,
|
|
||||||
time = time,
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,6 @@ if not http.checkURL(args[1]) then
|
||||||
error("Invalid URL", 0)
|
error("Invalid URL", 0)
|
||||||
end
|
end
|
||||||
|
|
||||||
print("Connecting...")
|
|
||||||
|
|
||||||
local response, err = http.get(args[1], nil, {
|
local response, err = http.get(args[1], nil, {
|
||||||
binary = true,
|
binary = true,
|
||||||
})
|
})
|
||||||
|
|
@ -29,8 +27,8 @@ if not response then
|
||||||
end
|
end
|
||||||
|
|
||||||
local file <close> = fs.open(outputPath, "wb")
|
local file <close> = fs.open(outputPath, "wb")
|
||||||
file:write(response.content:read("a"))
|
file:write(response:readAll())
|
||||||
file:close()
|
file:close()
|
||||||
response.content:close()
|
response:close()
|
||||||
|
|
||||||
print("Downloaded to " .. outputPath)
|
print("File written to " .. outputPath)
|
||||||
|
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
local fs = require("fs")
|
|
||||||
local expect = require("expect").expect
|
|
||||||
|
|
||||||
local fsList = fs.list
|
|
||||||
|
|
||||||
function fs.list(path, filter)
|
|
||||||
expect(1, path, "string")
|
|
||||||
expect(2, filter, "nil", "function")
|
|
||||||
|
|
||||||
if not fs.isDir(path) then
|
|
||||||
error("directory not found", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local list = fsList(path)
|
|
||||||
if not filter then
|
|
||||||
return list
|
|
||||||
end
|
|
||||||
|
|
||||||
local filteredList = {}
|
|
||||||
for i = 1, #list do
|
|
||||||
local attributes = fs.attributes(fs.combine(path, list[i]))
|
|
||||||
if filter(list[i], attributes) then
|
|
||||||
table.insert(filteredList, list[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return filteredList
|
|
||||||
end
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
local machine = require("machine")
|
|
||||||
local scheduler = require("scheduler")
|
|
||||||
|
|
||||||
local term = require("term")
|
|
||||||
local colors = require("colors")
|
|
||||||
term.setForeground(0x59c9ff)
|
|
||||||
term.setBackground(colors.black)
|
|
||||||
term.clear()
|
|
||||||
term.setPos(1, 1)
|
|
||||||
|
|
||||||
term.write(os.version())
|
|
||||||
term.setPos(1, 2)
|
|
||||||
|
|
||||||
local function spawnShell()
|
|
||||||
return scheduler.spawn(loadfile("/sys/bin/shell.lua"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function main()
|
|
||||||
local shellTask = spawnShell()
|
|
||||||
while true do
|
|
||||||
local ev = {coroutine.yield()}
|
|
||||||
if ev[1] == "ipc_message" then
|
|
||||||
local sender = ev[2]
|
|
||||||
local call = ev[3]
|
|
||||||
if call == "power" then
|
|
||||||
local options = ev[4]
|
|
||||||
--todo: handle time and cancels
|
|
||||||
if options.reboot then
|
|
||||||
machine.reboot()
|
|
||||||
else
|
|
||||||
machine.shutdown()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
elseif ev[1] == "scheduler_task_end" then
|
|
||||||
if ev[2].pid == shellTask.pid then
|
|
||||||
if not ev[3] then
|
|
||||||
io.stderr.print(ev[4])
|
|
||||||
end
|
|
||||||
shellTask = spawnShell()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
scheduler.spawn(main)
|
|
||||||
|
|
||||||
scheduler.init()
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
require("machine").shutdown()
|
|
||||||
15
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/99_shell.lua
Normal file
15
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/99_shell.lua
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
local term = require("term")
|
||||||
|
local colors = require("colors")
|
||||||
|
local machine = require("machine")
|
||||||
|
|
||||||
|
term.setForeground(0x59c9ff)
|
||||||
|
term.setBackground(colors.black)
|
||||||
|
term.clear()
|
||||||
|
term.setPos(1, 1)
|
||||||
|
|
||||||
|
term.write(os.version())
|
||||||
|
term.setPos(1, 2)
|
||||||
|
|
||||||
|
dofile("/sys/bin/shell.lua")
|
||||||
|
|
||||||
|
machine.shutdown()
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
local argparser = {}
|
|
||||||
|
|
||||||
function argparser.tokenize(...)
|
|
||||||
local input = table.concat(table.pack(...), " ")
|
|
||||||
local tokens = {}
|
|
||||||
|
|
||||||
-- surely there must be a better way
|
|
||||||
local quoted = false
|
|
||||||
local escaped = false
|
|
||||||
local current = ""
|
|
||||||
for i = 1, #input do
|
|
||||||
local char = input:sub(i, i)
|
|
||||||
if escaped then
|
|
||||||
escaped = false
|
|
||||||
current = current .. char
|
|
||||||
else
|
|
||||||
if char == "\\" then
|
|
||||||
escaped = true
|
|
||||||
elseif char == "\"" then
|
|
||||||
if quoted then
|
|
||||||
-- close quote
|
|
||||||
table.insert(tokens, current)
|
|
||||||
current = ""
|
|
||||||
end
|
|
||||||
quoted = not quoted
|
|
||||||
elseif char == " " and not quoted then
|
|
||||||
if #current > 0 then
|
|
||||||
table.insert(tokens, current)
|
|
||||||
end
|
|
||||||
current = ""
|
|
||||||
else
|
|
||||||
current = current .. char
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if current ~= "" then
|
|
||||||
table.insert(tokens, current)
|
|
||||||
end
|
|
||||||
|
|
||||||
return tokens
|
|
||||||
end
|
|
||||||
|
|
||||||
function argparser.parse(...)
|
|
||||||
local tokens = { ... }
|
|
||||||
local args = {}
|
|
||||||
local options = {}
|
|
||||||
local ignoreOptions = false
|
|
||||||
|
|
||||||
for i = 1, #tokens do
|
|
||||||
local token = tokens[i]
|
|
||||||
if not ignoreOptions then
|
|
||||||
if token == "--" then
|
|
||||||
ignoreOptions = true
|
|
||||||
elseif token:sub(1, 2) == "--" then
|
|
||||||
local opt, value = token:match("%-%-(.+)=(.+)")
|
|
||||||
if not opt then
|
|
||||||
opt = token:sub(3)
|
|
||||||
if opt:sub(-1) == "=" then
|
|
||||||
-- next token is value
|
|
||||||
value = tokens[i + 1]
|
|
||||||
opt = opt:sub(1, -2)
|
|
||||||
options[opt] = value
|
|
||||||
i = i + 1
|
|
||||||
else
|
|
||||||
options[opt] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
options[opt] = value
|
|
||||||
end
|
|
||||||
elseif token:sub(1, 1) == "-" then
|
|
||||||
local opts = token:sub(2)
|
|
||||||
for j = 1, #opts do
|
|
||||||
options[opts:sub(j, j)] = true
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if #token > 0 then
|
|
||||||
table.insert(args, token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if #token > 0 then
|
|
||||||
table.insert(args, token)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return args, options
|
|
||||||
end
|
|
||||||
|
|
||||||
return argparser
|
|
||||||
|
|
@ -6,347 +6,7 @@ local machine = require("machine")
|
||||||
|
|
||||||
local io = {}
|
local io = {}
|
||||||
|
|
||||||
function io.write(sText)
|
function io.write(text)
|
||||||
sText = tostring(sText)
|
|
||||||
|
|
||||||
local w, h = term.getSize()
|
|
||||||
local x, y = term.getPos()
|
|
||||||
|
|
||||||
local nLinesPrinted = 0
|
|
||||||
local function newLine()
|
|
||||||
if y + 1 <= h then
|
|
||||||
term.setPos(1, y + 1)
|
|
||||||
else
|
|
||||||
term.setPos(1, h)
|
|
||||||
term.scroll(1)
|
|
||||||
end
|
|
||||||
x, y = term.getPos()
|
|
||||||
nLinesPrinted = nLinesPrinted + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Print the line with proper word wrapping
|
|
||||||
sText = tostring(sText)
|
|
||||||
while #sText > 0 do
|
|
||||||
local whitespace = string.match(sText, "^[ \t]+")
|
|
||||||
if whitespace then
|
|
||||||
-- Print whitespace
|
|
||||||
term.write(whitespace)
|
|
||||||
x, y = term.getPos()
|
|
||||||
sText = string.sub(sText, #whitespace + 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local newline = string.match(sText, "^\n")
|
|
||||||
if newline then
|
|
||||||
-- Print newlines
|
|
||||||
newLine()
|
|
||||||
sText = string.sub(sText, 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = string.match(sText, "^[^ \t\n]+")
|
|
||||||
if text then
|
|
||||||
sText = string.sub(sText, #text + 1)
|
|
||||||
if #text > w then
|
|
||||||
-- Print a multiline word
|
|
||||||
while #text > 0 do
|
|
||||||
if x > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
text = string.sub(text, w - x + 2)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Print a word normally
|
|
||||||
if x + #text - 1 > w then
|
|
||||||
newLine()
|
|
||||||
end
|
|
||||||
term.write(text)
|
|
||||||
x, y = term.getPos()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nLinesPrinted
|
|
||||||
end
|
|
||||||
|
|
||||||
function io.read(_sReplaceChar, _tHistory, _fnComplete, _sDefault)
|
|
||||||
expect(1, _sReplaceChar, "string", "nil")
|
|
||||||
expect(2, _tHistory, "table", "nil")
|
|
||||||
expect(3, _fnComplete, "function", "nil")
|
|
||||||
expect(4, _sDefault, "string", "nil")
|
|
||||||
|
|
||||||
term.setBlink(true)
|
|
||||||
|
|
||||||
local sLine
|
|
||||||
if type(_sDefault) == "string" then
|
|
||||||
sLine = _sDefault
|
|
||||||
else
|
|
||||||
sLine = ""
|
|
||||||
end
|
|
||||||
local nHistoryPos
|
|
||||||
local nPos, nScroll = #sLine, 0
|
|
||||||
if _sReplaceChar then
|
|
||||||
_sReplaceChar = string.sub(_sReplaceChar, 1, 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local tCompletions
|
|
||||||
local nCompletion
|
|
||||||
local function recomplete()
|
|
||||||
if _fnComplete and nPos == #sLine then
|
|
||||||
tCompletions = _fnComplete(sLine)
|
|
||||||
if tCompletions and #tCompletions > 0 then
|
|
||||||
nCompletion = 1
|
|
||||||
else
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
else
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function uncomplete()
|
|
||||||
tCompletions = nil
|
|
||||||
nCompletion = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local w = term.getSize()
|
|
||||||
local sx = term.getPos()
|
|
||||||
|
|
||||||
local function redraw(_bClear)
|
|
||||||
local cursor_pos = nPos - nScroll
|
|
||||||
if sx + cursor_pos >= w then
|
|
||||||
-- We've moved beyond the RHS, ensure we're on the edge.
|
|
||||||
nScroll = sx + nPos - w
|
|
||||||
elseif cursor_pos < 0 then
|
|
||||||
-- We've moved beyond the LHS, ensure we're on the edge.
|
|
||||||
nScroll = nPos
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
term.setPos(sx, cy)
|
|
||||||
local sReplace = _bClear and " " or _sReplaceChar
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, math.max(#sLine - nScroll, 0)))
|
|
||||||
else
|
|
||||||
term.write(string.sub(sLine, nScroll + 1))
|
|
||||||
end
|
|
||||||
|
|
||||||
if nCompletion then
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
local oldText, oldBg
|
|
||||||
if not _bClear then
|
|
||||||
oldText = term.getTextColor()
|
|
||||||
oldBg = term.getBackgroundColor()
|
|
||||||
term.setTextColor(colors.white)
|
|
||||||
term.setBackgroundColor(colors.gray)
|
|
||||||
end
|
|
||||||
if sReplace then
|
|
||||||
term.write(string.rep(sReplace, #sCompletion))
|
|
||||||
else
|
|
||||||
term.write(sCompletion)
|
|
||||||
end
|
|
||||||
if not _bClear then
|
|
||||||
term.setTextColor(oldText)
|
|
||||||
term.setBackgroundColor(oldBg)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
term.setPos(sx + nPos - nScroll, cy)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function clear()
|
|
||||||
redraw(true)
|
|
||||||
end
|
|
||||||
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
local function acceptCompletion()
|
|
||||||
if nCompletion then
|
|
||||||
-- Clear
|
|
||||||
clear()
|
|
||||||
|
|
||||||
-- Find the common prefix of all the other suggestions which start with the same letter as the current one
|
|
||||||
local sCompletion = tCompletions[nCompletion]
|
|
||||||
sLine = sLine .. sCompletion
|
|
||||||
nPos = #sLine
|
|
||||||
|
|
||||||
-- Redraw
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
while true do
|
|
||||||
local sEvent, param, param1, param2 = event.pull()
|
|
||||||
if sEvent == "char" then
|
|
||||||
-- Typed key
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif sEvent == "paste" then
|
|
||||||
-- Pasted text
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. param .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos + #param
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif sEvent == "key_down" then
|
|
||||||
if param == keys.enter or param == keys.numPadEnter then
|
|
||||||
-- Enter/Numpad Enter
|
|
||||||
if nCompletion then
|
|
||||||
clear()
|
|
||||||
uncomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
break
|
|
||||||
|
|
||||||
elseif param == keys.left then
|
|
||||||
-- Left
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
nPos = nPos - 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.right then
|
|
||||||
-- Right
|
|
||||||
if nPos < #sLine then
|
|
||||||
-- Move right
|
|
||||||
clear()
|
|
||||||
nPos = nPos + 1
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
else
|
|
||||||
-- Accept autocomplete
|
|
||||||
acceptCompletion()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.up or param == keys.down then
|
|
||||||
-- Up or down
|
|
||||||
if nCompletion then
|
|
||||||
-- Cycle completions
|
|
||||||
clear()
|
|
||||||
if param == keys.up then
|
|
||||||
nCompletion = nCompletion - 1
|
|
||||||
if nCompletion < 1 then
|
|
||||||
nCompletion = #tCompletions
|
|
||||||
end
|
|
||||||
elseif param == keys.down then
|
|
||||||
nCompletion = nCompletion + 1
|
|
||||||
if nCompletion > #tCompletions then
|
|
||||||
nCompletion = 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
elseif _tHistory then
|
|
||||||
-- Cycle history
|
|
||||||
clear()
|
|
||||||
if param == keys.up then
|
|
||||||
-- Up
|
|
||||||
if nHistoryPos == nil then
|
|
||||||
if #_tHistory > 0 then
|
|
||||||
nHistoryPos = #_tHistory
|
|
||||||
end
|
|
||||||
elseif nHistoryPos > 1 then
|
|
||||||
nHistoryPos = nHistoryPos - 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- Down
|
|
||||||
if nHistoryPos == #_tHistory then
|
|
||||||
nHistoryPos = nil
|
|
||||||
elseif nHistoryPos ~= nil then
|
|
||||||
nHistoryPos = nHistoryPos + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if nHistoryPos then
|
|
||||||
sLine = _tHistory[nHistoryPos]
|
|
||||||
nPos, nScroll = #sLine, 0
|
|
||||||
else
|
|
||||||
sLine = ""
|
|
||||||
nPos, nScroll = 0, 0
|
|
||||||
end
|
|
||||||
uncomplete()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.back then
|
|
||||||
-- Backspace
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos - 1) .. string.sub(sLine, nPos + 1)
|
|
||||||
nPos = nPos - 1
|
|
||||||
if nScroll > 0 then nScroll = nScroll - 1 end
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.home then
|
|
||||||
-- Home
|
|
||||||
if nPos > 0 then
|
|
||||||
clear()
|
|
||||||
nPos = 0
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.delete then
|
|
||||||
-- Delete
|
|
||||||
if nPos < #sLine then
|
|
||||||
clear()
|
|
||||||
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys["end"] then
|
|
||||||
-- End
|
|
||||||
if nPos < #sLine then
|
|
||||||
clear()
|
|
||||||
nPos = #sLine
|
|
||||||
recomplete()
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif param == keys.tab then
|
|
||||||
-- Tab (accept autocomplete)
|
|
||||||
acceptCompletion()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif sEvent == "mouse_down" or sEvent == "mouse_drag" and param == 1 then
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
if param1 >= sx and param1 <= w and param2 == cy then
|
|
||||||
-- Ensure we don't scroll beyond the current line
|
|
||||||
nPos = math.min(math.max(nScroll + param1 - sx, 0), #sLine)
|
|
||||||
redraw()
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif sEvent == "term_resize" then
|
|
||||||
-- Terminal resized
|
|
||||||
w = term.getSize()
|
|
||||||
redraw()
|
|
||||||
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, cy = term.getPos()
|
|
||||||
term.setBlink(false)
|
|
||||||
term.setPos(w + 1, cy)
|
|
||||||
print()
|
|
||||||
|
|
||||||
return sLine
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
--[[function io.write(text)
|
|
||||||
text = tostring(text)
|
text = tostring(text)
|
||||||
|
|
||||||
local lines = 0
|
local lines = 0
|
||||||
|
|
@ -368,7 +28,7 @@ end
|
||||||
local chunk = text:sub(1, nl)
|
local chunk = text:sub(1, nl)
|
||||||
text = text:sub(#chunk + 1)
|
text = text:sub(#chunk + 1)
|
||||||
|
|
||||||
local has_nl = chunk:sub(-1) == "\n"
|
local has_nl = chunk:sub( -1) == "\n"
|
||||||
if has_nl then chunk = chunk:sub(1, -2) end
|
if has_nl then chunk = chunk:sub(1, -2) end
|
||||||
|
|
||||||
local cx, cy = term.getPos()
|
local cx, cy = term.getPos()
|
||||||
|
|
@ -423,7 +83,7 @@ function io.read(replace, history, complete, default)
|
||||||
|
|
||||||
local function clearCompletion()
|
local function clearCompletion()
|
||||||
if completions[comp_id] then
|
if completions[comp_id] then
|
||||||
io.write((" "):rep(#completions[comp_id]))
|
write((" "):rep(#completions[comp_id]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -482,7 +142,7 @@ function io.read(replace, history, complete, default)
|
||||||
elseif cursor_pos == #buffer then
|
elseif cursor_pos == #buffer then
|
||||||
buffer = par1 .. buffer
|
buffer = par1 .. buffer
|
||||||
else
|
else
|
||||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub( -cursor_pos)
|
||||||
end
|
end
|
||||||
elseif evt == "key_down" then
|
elseif evt == "key_down" then
|
||||||
if par1 == keys.back and #buffer > 0 then
|
if par1 == keys.back and #buffer > 0 then
|
||||||
|
|
@ -491,7 +151,7 @@ function io.read(replace, history, complete, default)
|
||||||
buffer = buffer:sub(1, -2)
|
buffer = buffer:sub(1, -2)
|
||||||
clearCompletion()
|
clearCompletion()
|
||||||
elseif cursor_pos < #buffer then
|
elseif cursor_pos < #buffer then
|
||||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub( -cursor_pos)
|
||||||
end
|
end
|
||||||
elseif par1 == keys.delete and cursor_pos > 0 then
|
elseif par1 == keys.delete and cursor_pos > 0 then
|
||||||
dirty = true
|
dirty = true
|
||||||
|
|
@ -501,7 +161,7 @@ function io.read(replace, history, complete, default)
|
||||||
elseif cursor_pos == 1 then
|
elseif cursor_pos == 1 then
|
||||||
buffer = buffer:sub(1, -2)
|
buffer = buffer:sub(1, -2)
|
||||||
else
|
else
|
||||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub( -cursor_pos + 1)
|
||||||
end
|
end
|
||||||
cursor_pos = cursor_pos - 1
|
cursor_pos = cursor_pos - 1
|
||||||
elseif par1 == keys.up then
|
elseif par1 == keys.up then
|
||||||
|
|
@ -583,7 +243,7 @@ function io.read(replace, history, complete, default)
|
||||||
buffer = text .. buffer
|
buffer = text .. buffer
|
||||||
else
|
else
|
||||||
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
||||||
buffer:sub(-cursor_pos + (#text - 1))
|
buffer:sub( -cursor_pos + (#text - 1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -595,7 +255,6 @@ function io.read(replace, history, complete, default)
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
end
|
end
|
||||||
]]
|
|
||||||
|
|
||||||
io.stderr = {}
|
io.stderr = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
local x = math.random()
|
|
||||||
|
|
||||||
return x
|
|
||||||
|
|
@ -1,182 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local tableutils = require("tableutils")
|
|
||||||
local event = require("event")
|
|
||||||
|
|
||||||
local scheduler = {}
|
|
||||||
|
|
||||||
local function contains(array, value)
|
|
||||||
for k, v in pairs(array) do
|
|
||||||
if v == value then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local tasks = {}
|
|
||||||
local processes = 0
|
|
||||||
|
|
||||||
local Task = {}
|
|
||||||
local TaskMeta = {
|
|
||||||
__index = Task,
|
|
||||||
__name = "OS_TASK",
|
|
||||||
__tostring = function(self)
|
|
||||||
return string.format("OS_TASK[%s]: %d", self.source or "", self.pid or 0)
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
local function newTask()
|
|
||||||
local task = {}
|
|
||||||
return setmetatable(task, TaskMeta)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Task:queue(eventName, ...)
|
|
||||||
expect(1, eventName, "string")
|
|
||||||
event.push("scheduler", self.pid, eventName, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function findParent()
|
|
||||||
local i = 3
|
|
||||||
|
|
||||||
while true do
|
|
||||||
local info = debug.getinfo(i)
|
|
||||||
if not info then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
if task.uuid == tostring(info.func) then
|
|
||||||
return task
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function cascadeKill(pid, err)
|
|
||||||
local task = tasks[pid]
|
|
||||||
if not task then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
for i, cpid in ipairs(task.children) do
|
|
||||||
cascadeKill(cpid, err)
|
|
||||||
end
|
|
||||||
if task.parent then
|
|
||||||
local parent = tasks[task.parent]
|
|
||||||
if parent then
|
|
||||||
local index = tableutils.find(parent.children, task.pid)
|
|
||||||
table.remove(parent.children, index)
|
|
||||||
parent:queue("scheduler_task_end", task, err == nil, err)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if err then
|
|
||||||
error(err, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if task then
|
|
||||||
task.killed = true
|
|
||||||
coroutine.close(task.thread)
|
|
||||||
tasks[pid] = nil
|
|
||||||
processes = processes - 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function resumeTask(task, yieldPars)
|
|
||||||
local pars = table.pack(coroutine.resume(task.thread, table.unpack(yieldPars)))
|
|
||||||
if pars[1] then
|
|
||||||
task.filters = table.pack(table.unpack(pars, 2))
|
|
||||||
return coroutine.status(task.thread) ~= "dead"
|
|
||||||
else
|
|
||||||
cascadeKill(task.pid, pars[2])
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.spawn(func, options)
|
|
||||||
expect(1, func, "function")
|
|
||||||
expect(2, options, "nil", "table")
|
|
||||||
|
|
||||||
options = options or {}
|
|
||||||
options.args = options.args or {}
|
|
||||||
|
|
||||||
local source = debug.getinfo(2)
|
|
||||||
|
|
||||||
local task = newTask()
|
|
||||||
local pid = #tasks + 1
|
|
||||||
task.pid = pid
|
|
||||||
task.options = options
|
|
||||||
task.source = source.source
|
|
||||||
task.uuid = tostring(func)
|
|
||||||
task.thread = coroutine.create(func)
|
|
||||||
local parent = findParent()
|
|
||||||
if parent then
|
|
||||||
task.parent = parent.pid
|
|
||||||
table.insert(parent.children, pid)
|
|
||||||
end
|
|
||||||
task.filters = {}
|
|
||||||
task.children = {}
|
|
||||||
task.eventQueue = {}
|
|
||||||
task.skip = true
|
|
||||||
|
|
||||||
tasks[pid] = task
|
|
||||||
|
|
||||||
processes = processes + 1
|
|
||||||
|
|
||||||
return task, resumeTask(task, task.options.args)
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.kill(pid)
|
|
||||||
expect(1, pid, "number")
|
|
||||||
cascadeKill(pid)
|
|
||||||
end
|
|
||||||
|
|
||||||
function scheduler.ipc(pid, ...)
|
|
||||||
expect(1, pid, "number")
|
|
||||||
if not tasks[pid] then
|
|
||||||
error("process by pid " .. pid .. " does not exist.", 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local sender = findParent()
|
|
||||||
tasks[pid]:queue("ipc_message", sender, ...)
|
|
||||||
end
|
|
||||||
|
|
||||||
local running = false
|
|
||||||
function scheduler.init()
|
|
||||||
if running then
|
|
||||||
error("scheduler already running", 2)
|
|
||||||
end
|
|
||||||
running = true
|
|
||||||
|
|
||||||
local ev = { n = 0 }
|
|
||||||
while processes > 0 do
|
|
||||||
for pid, task in pairs(tasks) do
|
|
||||||
local yieldPars = ev
|
|
||||||
if ev[1] == "scheduler" and ev[2] == pid then
|
|
||||||
yieldPars = table.pack(table.unpack(ev, 3))
|
|
||||||
end
|
|
||||||
if yieldPars[1] ~= "scheduler" and not task.filters or #task.filters == 0 or contains(task.filters, yieldPars[1]) or yieldPars[1] == "interrupt" then
|
|
||||||
if task.skip then
|
|
||||||
task.skip = false
|
|
||||||
else
|
|
||||||
resumeTask(task, yieldPars)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if coroutine.status(task.thread) == "dead" then
|
|
||||||
cascadeKill(pid)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if processes <= 0 then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
ev = table.pack(coroutine.yield())
|
|
||||||
end
|
|
||||||
|
|
||||||
running = false
|
|
||||||
end
|
|
||||||
|
|
||||||
return scheduler
|
|
||||||
|
|
@ -1,112 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local fs = require("fs")
|
|
||||||
local nativePackage = package
|
|
||||||
|
|
||||||
local function copyTable(source, target)
|
|
||||||
target = target or {}
|
|
||||||
|
|
||||||
for k, v in pairs(source) do
|
|
||||||
target[k] = v
|
|
||||||
end
|
|
||||||
|
|
||||||
return target
|
|
||||||
end
|
|
||||||
|
|
||||||
local hostPackages = copyTable(nativePackage._host)
|
|
||||||
|
|
||||||
local function createPreloadSearcher(envPackage)
|
|
||||||
return function(name)
|
|
||||||
if not envPackage.preload[name] then
|
|
||||||
return string.format("no field package.preload['%s']", name)
|
|
||||||
end
|
|
||||||
return envPackage.preload[name], ":preload:"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createLoaderSearcher(envPackage)
|
|
||||||
return function(name)
|
|
||||||
local path, err = envPackage.searchpath(name, envPackage.path)
|
|
||||||
|
|
||||||
if not path then
|
|
||||||
return err
|
|
||||||
end
|
|
||||||
|
|
||||||
local func, err = loadfile(path)
|
|
||||||
if not func then
|
|
||||||
return string.format("error loading module '%s' from file '%s':\t%s", name, path, err)
|
|
||||||
end
|
|
||||||
|
|
||||||
return func, path
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createEnvironment(filePath)
|
|
||||||
local envPackage = {
|
|
||||||
cpath = nativePackage.cpath,
|
|
||||||
searchpath = nativePackage.searchpath,
|
|
||||||
config = nativePackage.config,
|
|
||||||
searchers = {},
|
|
||||||
loaded = {},
|
|
||||||
preload = {},
|
|
||||||
}
|
|
||||||
|
|
||||||
local dirName = fs.getDir(filePath)
|
|
||||||
--envPackage.path = string.format("%s/?.lua;%s/?/init.lua;", dirName, dirName) .. nativePackage.path
|
|
||||||
envPackage.path = nativePackage.path
|
|
||||||
|
|
||||||
envPackage.searchers[1] = createPreloadSearcher(envPackage)
|
|
||||||
envPackage.searchers[2] = createLoaderSearcher(envPackage)
|
|
||||||
|
|
||||||
local function envRequire(modname)
|
|
||||||
expect(1, modname, "string", "number")
|
|
||||||
modname = tostring(modname)
|
|
||||||
|
|
||||||
if envPackage.loaded[modname] then
|
|
||||||
return envPackage.loaded[modname]
|
|
||||||
end
|
|
||||||
|
|
||||||
local errorOutput = ""
|
|
||||||
local libFunction, libPath
|
|
||||||
for i = 1, #envPackage.searchers do
|
|
||||||
local par, path = envPackage.searchers[i](modname)
|
|
||||||
if type(par) == "function" then
|
|
||||||
libFunction, libPath = par, path
|
|
||||||
break
|
|
||||||
else
|
|
||||||
errorOutput = errorOutput .. "\n\t" .. par
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not libFunction then
|
|
||||||
error(string.format("module '%s' not found:%s", modname, errorOutput), 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, par = pcall(libFunction)
|
|
||||||
if not ok then
|
|
||||||
error(par, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
if par == nil then
|
|
||||||
envPackage.loaded[modname] = true
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
envPackage.loaded[modname] = par
|
|
||||||
|
|
||||||
return par, libPath
|
|
||||||
end
|
|
||||||
|
|
||||||
copyTable(hostPackages, envPackage.loaded)
|
|
||||||
envPackage.loaded.package = envPackage
|
|
||||||
|
|
||||||
local env_G = copyTable(envPackage.loaded._G or _G)
|
|
||||||
envPackage.loaded._G = env_G
|
|
||||||
env_G._G = env_G
|
|
||||||
|
|
||||||
envPackage.loaded._G.package = envPackage
|
|
||||||
envPackage.loaded._G.require = envRequire
|
|
||||||
|
|
||||||
return envPackage, envRequire
|
|
||||||
end
|
|
||||||
|
|
||||||
return createEnvironment
|
|
||||||
|
|
@ -1,87 +0,0 @@
|
||||||
local expect = require("expect").expect
|
|
||||||
local tableutils = {}
|
|
||||||
|
|
||||||
local function serialize(data, circular)
|
|
||||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
|
||||||
if type(data) == "table" then
|
|
||||||
if not circular then
|
|
||||||
circular = {}
|
|
||||||
end
|
|
||||||
local output = "{"
|
|
||||||
for k, v in pairs(data) do
|
|
||||||
if type(v) == "table" then
|
|
||||||
local name = tostring(v)
|
|
||||||
if circular[name] then
|
|
||||||
error("circular reference in table", 2)
|
|
||||||
end
|
|
||||||
circular[name] = true
|
|
||||||
end
|
|
||||||
output = output .. string.format("[%q] = %s,", k, serialize(v, circular))
|
|
||||||
end
|
|
||||||
output = output .. "}"
|
|
||||||
return output
|
|
||||||
else
|
|
||||||
return string.format("%q", data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.serialize(data)
|
|
||||||
expect(1, data, "string", "table", "number", "boolean", "nil")
|
|
||||||
return serialize(data)
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.deserialize(data)
|
|
||||||
local func, err = load("return " .. data, "=tableutils", "t", {})
|
|
||||||
if not func then
|
|
||||||
error(err, 2)
|
|
||||||
end
|
|
||||||
return func()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function prettyvalue(value)
|
|
||||||
if type(value) == "table" or type(value) == "function" or type(value) == "thread" or type(value) == "userdata" or type(value) == "number" then
|
|
||||||
return tostring(value)
|
|
||||||
else
|
|
||||||
return string.format("%q", value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.pretty(data)
|
|
||||||
if type(data) == "table" then
|
|
||||||
local output = "{"
|
|
||||||
|
|
||||||
local index = 0
|
|
||||||
for k, v in pairs(data) do
|
|
||||||
local value = prettyvalue(v)
|
|
||||||
|
|
||||||
if type(k) == "number" and k - 1 == index then
|
|
||||||
index = index + 1
|
|
||||||
output = output .. string.format("\n %s,", value)
|
|
||||||
elseif type(k) == "string" and k:match("^[%a_][%w_]*$") then
|
|
||||||
output = output .. string.format("\n %s = %s,", k, value)
|
|
||||||
else
|
|
||||||
output = output .. string.format("\n [%s] = %s,", prettyvalue(k), value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if output == "{" then
|
|
||||||
return "{}"
|
|
||||||
end
|
|
||||||
output = output .. "\n}"
|
|
||||||
|
|
||||||
return output
|
|
||||||
else
|
|
||||||
return prettyvalue(data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function tableutils.find(tbl, element)
|
|
||||||
for i = 1, #tbl do
|
|
||||||
if tbl[i] == element then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return tableutils
|
|
||||||
|
|
@ -1,2 +0,0 @@
|
||||||
Welcome to CapyOS!
|
|
||||||
Run "programs" to get a list of available programs.
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
CapyOS and Capy64 are licensed under the Apache 2.0 Public License.
|
|
||||||
https://capy64.alexdevs.me/
|
|
||||||
https://github.com/Ale32bit/Capy64
|
|
||||||
|
|
||||||
Some CapyOS components include code from the CC Recrafted project.
|
|
||||||
https://github.com/Ocawesome101/recrafted
|
|
||||||
|
|
||||||
Some CapyOS components may include code from CC: Tweaked.
|
|
||||||
https://github.com/CC-Tweaked/CC-Tweaked
|
|
||||||
|
|
||||||
json.lua by rxi is licensed under the MIT License.
|
|
||||||
https://github.com/rxi/json.lua
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
You can get started with Lua by following this manual: https://www.lua.org/manual/
|
|
||||||
Learn more about Capy64 by following the documentation: https://capy64.alexdevs.me/
|
|
||||||
Found a bug or would like to suggest a feature? https://github.com/Ale32bit/Capy64/issues
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
||||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
||||||
--
|
--
|
||||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||||
|
|
@ -24,9 +24,6 @@ local event = require("event")
|
||||||
local bootSleep = 2
|
local bootSleep = 2
|
||||||
local bg = 0x0
|
local bg = 0x0
|
||||||
local fg = 0xffffff
|
local fg = 0xffffff
|
||||||
local setupbg = 0x0608a6
|
|
||||||
local setupfg = 0xffffff
|
|
||||||
local accent = 0xffea00
|
|
||||||
|
|
||||||
term.setForeground(fg)
|
term.setForeground(fg)
|
||||||
term.setBackground(bg)
|
term.setBackground(bg)
|
||||||
|
|
@ -100,60 +97,11 @@ local function promptKey()
|
||||||
event.pull("key_down")
|
event.pull("key_down")
|
||||||
end
|
end
|
||||||
|
|
||||||
local function alert(...)
|
|
||||||
local args = {...}
|
|
||||||
table.insert(args, "[ OK ]")
|
|
||||||
local lines = {}
|
|
||||||
local width = 0
|
|
||||||
local padding = 1
|
|
||||||
for k, v in ipairs(args) do
|
|
||||||
lines[k] = tostring(v)
|
|
||||||
width = math.max(width, #tostring(v))
|
|
||||||
end
|
|
||||||
|
|
||||||
lines[#lines] = nil
|
|
||||||
|
|
||||||
local okPad = string.rep(" ", (width - #args[#args]) // 2)
|
|
||||||
local okLine = string.format("%s%s", okPad, args[#args])
|
|
||||||
|
|
||||||
local w, h = term.getSize()
|
|
||||||
local cx, cy = w//2, h//2
|
|
||||||
local dx, dy = cx - (width + padding * 2) // 2, cy - #lines//2 - 1
|
|
||||||
|
|
||||||
local pad = string.rep(" ", padding)
|
|
||||||
local emptyLine = string.format("\u{258C}%s%s%s\u{2590}", pad, string.rep(" ", width), pad)
|
|
||||||
|
|
||||||
term.setPos(dx, dy)
|
|
||||||
print("\u{259B}" .. string.rep("\u{2580}", width + padding * 2) .. "\u{259C}")
|
|
||||||
for k, v in ipairs(lines) do
|
|
||||||
term.setPos(dx, dy + k)
|
|
||||||
local space = string.rep(" ", width - #v)
|
|
||||||
print(string.format("\u{258C}%s%s%s%s\u{2590}", pad, v, space, pad))
|
|
||||||
end
|
|
||||||
term.setPos(dx, dy + #lines + 1)
|
|
||||||
print(emptyLine)
|
|
||||||
term.setPos(dx, dy + #lines + 2)
|
|
||||||
local space = string.rep(" ", width - #okLine)
|
|
||||||
term.write(string.format("\u{258C}%s", pad))
|
|
||||||
term.setForeground(accent)
|
|
||||||
term.write(okLine)
|
|
||||||
term.setForeground(setupfg)
|
|
||||||
print(string.format("%s%s\u{2590}", space, pad))
|
|
||||||
term.setPos(dx, dy + #lines + 3)
|
|
||||||
print("\u{2599}" .. string.rep("\u{2584}", width + padding * 2) .. "\u{259F}")
|
|
||||||
|
|
||||||
local _, key, keyname
|
|
||||||
repeat
|
|
||||||
_, key, keyname = event.pull("key_down")
|
|
||||||
until keyname == "enter" or keyname == "escape"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function installDefaultOS()
|
local function installDefaultOS()
|
||||||
if fs.exists("/sys") then
|
if fs.exists("/sys") then
|
||||||
fs.delete("/sys", true)
|
fs.delete("/sys", true)
|
||||||
end
|
end
|
||||||
installOS()
|
installOS()
|
||||||
alert("Default OS installed!")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setBlink(false)
|
term.setBlink(false)
|
||||||
|
|
@ -168,7 +116,6 @@ local function setupScreen()
|
||||||
"Install default OS",
|
"Install default OS",
|
||||||
installDefaultOS,
|
installDefaultOS,
|
||||||
},
|
},
|
||||||
{},
|
|
||||||
{
|
{
|
||||||
"Exit setup",
|
"Exit setup",
|
||||||
exit,
|
exit,
|
||||||
|
|
@ -180,40 +127,26 @@ local function setupScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
local selection = 1
|
local selection = 1
|
||||||
local function redraw(noDrawSel)
|
local function redraw()
|
||||||
local w, h = term.getSize()
|
term.setForeground(fg)
|
||||||
term.setForeground(setupfg)
|
term.setBackground(bg)
|
||||||
term.setBackground(setupbg)
|
|
||||||
term.clear()
|
term.clear()
|
||||||
term.setPos(1,2)
|
term.setPos(1,2)
|
||||||
writeCenter("Capy64 Setup")
|
writeCenter("Capy64 Setup")
|
||||||
|
|
||||||
term.setPos(1,3)
|
term.setPos(1,3)
|
||||||
|
|
||||||
term.setForeground(accent)
|
|
||||||
|
|
||||||
for k, v in ipairs(options) do
|
for k, v in ipairs(options) do
|
||||||
local _, y = term.getPos()
|
local _, y = term.getPos()
|
||||||
term.setPos(1, y + 1)
|
term.setPos(1, y + 1)
|
||||||
term.clearLine()
|
term.clearLine()
|
||||||
|
|
||||||
if #v > 0 then
|
if selection == k then
|
||||||
if selection == k and not noDrawSel then
|
writeCenter("[ " .. v[1] .. " ]")
|
||||||
writeCenter("[ " .. v[1] .. " ]")
|
else
|
||||||
else
|
writeCenter(v[1])
|
||||||
writeCenter(v[1])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
term.setForeground(setupfg)
|
|
||||||
|
|
||||||
term.setPos(1, h - 2)
|
|
||||||
term.write("\u{2190}\u{2191}\u{2192}\u{2193}: Move")
|
|
||||||
term.setPos(1, h - 1)
|
|
||||||
term.write("Escape: Exit")
|
|
||||||
term.setPos(1, h)
|
|
||||||
term.write("Enter: Select")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
|
|
@ -221,16 +154,9 @@ local function setupScreen()
|
||||||
local ev = { coroutine.yield("key_down") }
|
local ev = { coroutine.yield("key_down") }
|
||||||
if ev[3] == "up" then
|
if ev[3] == "up" then
|
||||||
selection = selection - 1
|
selection = selection - 1
|
||||||
if options[selection] and #options[selection] == 0 then
|
|
||||||
selection = selection - 1
|
|
||||||
end
|
|
||||||
elseif ev[3] == "down" then
|
elseif ev[3] == "down" then
|
||||||
selection = selection + 1
|
selection = selection + 1
|
||||||
if options[selection] and #options[selection] == 0 then
|
|
||||||
selection = selection + 1
|
|
||||||
end
|
|
||||||
elseif ev[3] == "enter" then
|
elseif ev[3] == "enter" then
|
||||||
redraw(true)
|
|
||||||
options[selection][2]()
|
options[selection][2]()
|
||||||
elseif ev[3] == "escape" then
|
elseif ev[3] == "escape" then
|
||||||
exit()
|
exit()
|
||||||
|
|
@ -251,7 +177,7 @@ local function bootScreen()
|
||||||
term.setPos(1,2)
|
term.setPos(1,2)
|
||||||
writeCenter("Capy64")
|
writeCenter("Capy64")
|
||||||
term.setPos(1,4)
|
term.setPos(1,4)
|
||||||
writeCenter("(c) 2023 AlexDevs")
|
writeCenter("Powered by Capybaras")
|
||||||
|
|
||||||
term.setPos(1, h - 1)
|
term.setPos(1, h - 1)
|
||||||
writeCenter("Press F2 to open setup")
|
writeCenter("Press F2 to open setup")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
{
|
{
|
||||||
"EngineMode": 0,
|
"EngineMode": 0,
|
||||||
"SafeMode": false,
|
|
||||||
"Window": {
|
"Window": {
|
||||||
"Scale": 2
|
"Scale": 2
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public enum EngineMode
|
||||||
|
|
||||||
public class Capy64 : Game
|
public class Capy64 : Game
|
||||||
{
|
{
|
||||||
public const string Version = "1.1.2-beta";
|
public const string Version = "1.1.0-beta";
|
||||||
|
|
||||||
public static class DefaultParameters
|
public static class DefaultParameters
|
||||||
{
|
{
|
||||||
|
|
@ -112,6 +112,7 @@ public class Capy64 : Game
|
||||||
private readonly InputManager _inputManager;
|
private readonly InputManager _inputManager;
|
||||||
private RenderTarget2D renderTarget;
|
private RenderTarget2D renderTarget;
|
||||||
private readonly GraphicsDeviceManager _graphics;
|
private readonly GraphicsDeviceManager _graphics;
|
||||||
|
private IServiceProvider _serviceProvider;
|
||||||
private ulong _totalTicks = 0;
|
private ulong _totalTicks = 0;
|
||||||
private int tickrate = 0;
|
private int tickrate = 0;
|
||||||
private int everyTick => 60 / tickrate;
|
private int everyTick => 60 / tickrate;
|
||||||
|
|
@ -121,7 +122,7 @@ public class Capy64 : Game
|
||||||
Instance = this;
|
Instance = this;
|
||||||
|
|
||||||
_graphics = new GraphicsDeviceManager(this);
|
_graphics = new GraphicsDeviceManager(this);
|
||||||
//Content.RootDirectory = "Content";
|
Content.RootDirectory = "Content";
|
||||||
IsMouseVisible = true;
|
IsMouseVisible = true;
|
||||||
|
|
||||||
EventEmitter = new();
|
EventEmitter = new();
|
||||||
|
|
@ -130,6 +131,11 @@ public class Capy64 : Game
|
||||||
Drawing = new();
|
Drawing = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
_serviceProvider = serviceProvider;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetEngineMode(EngineMode mode)
|
public void SetEngineMode(EngineMode mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
|
|
@ -259,9 +265,7 @@ public class Capy64 : Game
|
||||||
Audio = new Audio();
|
Audio = new Audio();
|
||||||
|
|
||||||
NativePlugins = GetNativePlugins();
|
NativePlugins = GetNativePlugins();
|
||||||
var safeMode = Configuration.GetValue("SafeMode", false);
|
Plugins = PluginLoader.LoadAllPlugins("plugins", _serviceProvider);
|
||||||
if (!safeMode)
|
|
||||||
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
|
|
||||||
|
|
||||||
EventEmitter.RaiseInit();
|
EventEmitter.RaiseInit();
|
||||||
|
|
||||||
|
|
@ -280,6 +284,7 @@ public class Capy64 : Game
|
||||||
foreach (var type in types)
|
foreach (var type in types)
|
||||||
{
|
{
|
||||||
var instance = (IComponent)Activator.CreateInstance(type, this);
|
var instance = (IComponent)Activator.CreateInstance(type, this);
|
||||||
|
//var instance = (IComponent)ActivatorUtilities.CreateInstance(_serviceProvider, type)!;
|
||||||
plugins.Add(instance);
|
plugins.Add(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>WinExe</OutputType>
|
<OutputType>WinExe</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<RollForward>Major</RollForward>
|
<RollForward>Major</RollForward>
|
||||||
<PublishReadyToRun>false</PublishReadyToRun>
|
<PublishReadyToRun>false</PublishReadyToRun>
|
||||||
<TieredCompilation>false</TieredCompilation>
|
<TieredCompilation>false</TieredCompilation>
|
||||||
|
|
@ -37,13 +37,13 @@
|
||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.3.6" />
|
<PackageReference Include="FontStashSharp.MonoGame" Version="1.2.8" />
|
||||||
<PackageReference Include="KeraLua" Version="1.4.1" />
|
<PackageReference Include="KeraLua" Version="1.3.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
||||||
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
||||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ internal class PluginLoader
|
||||||
return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
|
return loadContext.LoadFromAssemblyName(new AssemblyName(Path.GetFileNameWithoutExtension(path)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<IComponent> LoadAllPlugins(string pluginsPath)
|
public static List<IComponent> LoadAllPlugins(string pluginsPath, IServiceProvider provider)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(pluginsPath))
|
if (!Directory.Exists(pluginsPath))
|
||||||
Directory.CreateDirectory(pluginsPath);
|
Directory.CreateDirectory(pluginsPath);
|
||||||
|
|
|
||||||
|
|
@ -134,9 +134,6 @@ public class FileSystemLib : IComponent
|
||||||
// Get drive root (C:\ for Windows, / for *nix)
|
// Get drive root (C:\ for Windows, / for *nix)
|
||||||
var rootPath = Path.GetFullPath(Path.GetPathRoot("/") ?? "/");
|
var rootPath = Path.GetFullPath(Path.GetPathRoot("/") ?? "/");
|
||||||
|
|
||||||
var invalidPathChars = Path.GetInvalidPathChars();
|
|
||||||
path = invalidPathChars.Aggregate(path, (current, invalidChar) => current.Replace(invalidChar, ' '));
|
|
||||||
|
|
||||||
// Join path to rootPath and resolves to absolute path
|
// Join path to rootPath and resolves to absolute path
|
||||||
// Relative paths are resolved here (es. ../ and ./)
|
// Relative paths are resolved here (es. ../ and ./)
|
||||||
var absolutePath = Path.GetFullPath(path, rootPath);
|
var absolutePath = Path.GetFullPath(path, rootPath);
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,6 @@ internal class RuntimeManager : IComponent
|
||||||
luaState = new LuaState();
|
luaState = new LuaState();
|
||||||
_game.LuaRuntime = luaState;
|
_game.LuaRuntime = luaState;
|
||||||
luaState.Init();
|
luaState.Init();
|
||||||
CopyHostLibraries();
|
|
||||||
|
|
||||||
emitter = new(_game.EventEmitter, luaState);
|
emitter = new(_game.EventEmitter, luaState);
|
||||||
|
|
||||||
|
|
@ -163,31 +162,11 @@ internal class RuntimeManager : IComponent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyHostLibraries()
|
|
||||||
{
|
|
||||||
// table that will contain host libraries
|
|
||||||
luaState.Thread.NewTable();
|
|
||||||
|
|
||||||
luaState.Thread.GetGlobal("package");
|
|
||||||
luaState.Thread.GetField(-1, "loaded");
|
|
||||||
|
|
||||||
luaState.Thread.PushNil();
|
|
||||||
while (luaState.Thread.Next(-2))
|
|
||||||
{
|
|
||||||
var libname = luaState.Thread.ToString(-2);
|
|
||||||
luaState.Thread.SetField(1, libname);
|
|
||||||
}
|
|
||||||
|
|
||||||
luaState.Thread.Rotate(1, -1);
|
|
||||||
luaState.Thread.SetField(1, "_host");
|
|
||||||
luaState.Thread.SetTop(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadFirmware()
|
private void LoadFirmware()
|
||||||
{
|
{
|
||||||
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
||||||
var errored = luaState.Thread.DoString(firmwareContent);
|
var errored = luaState.Thread.DoString(firmwareContent);
|
||||||
if (errored)
|
if(errored)
|
||||||
{
|
{
|
||||||
throw new LuaException(luaState.Thread.ToString(-1));
|
throw new LuaException(luaState.Thread.ToString(-1));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.8 KiB |
|
|
@ -1,11 +0,0 @@
|
||||||
[Desktop Entry]
|
|
||||||
Version=1.0
|
|
||||||
Type=Application
|
|
||||||
|
|
||||||
Name=Capy64
|
|
||||||
Comment=Lua Fantasy Computer
|
|
||||||
Categories=Game;Emulator;
|
|
||||||
|
|
||||||
Icon=me.alexdevs.Capy64
|
|
||||||
Exec=Capy64
|
|
||||||
Terminal=false
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<component type="desktop-application">
|
|
||||||
<id>me.alexdevs.Capy64</id>
|
|
||||||
<launchable type="desktop-id">me.alexdevs.Capy64.desktop</launchable>
|
|
||||||
|
|
||||||
<name>Capy64</name>
|
|
||||||
<summary>Lua Fantasy Computer</summary>
|
|
||||||
<developer_name>AlexDevs</developer_name>
|
|
||||||
<url>https://capy64.alexdevs.me/</url>
|
|
||||||
|
|
||||||
<metadata_license>MIT</metadata_license>
|
|
||||||
<project_license>Apache-2.0</project_license>
|
|
||||||
|
|
||||||
<content_rating type="oars-1.1" />
|
|
||||||
|
|
||||||
<supports>
|
|
||||||
<control>pointing</control>
|
|
||||||
<control>keyboard</control>
|
|
||||||
<control>gamepad</control>
|
|
||||||
</supports>
|
|
||||||
|
|
||||||
<description>
|
|
||||||
<p>
|
|
||||||
Capy64 is a fantasy console that runs sandbox Lua 5.4.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Create anything, from Hello Worlds to games!
|
|
||||||
</p>
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<releases>
|
|
||||||
<release version="1.1.2" date="2023-07-24" type="beta">
|
|
||||||
<url>https://github.com/Ale32bit/Capy64/releases/tag/v1.1.2-beta</url>
|
|
||||||
</release>
|
|
||||||
</releases>
|
|
||||||
|
|
||||||
</component>
|
|
||||||
Loading…
Add table
Reference in a new issue