mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-12-15 18:45:44 +00:00
Compare commits
14 commits
v.1.1.1-be
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2445175248 | |||
| c19fb78b1f | |||
| 5d0b6e864a | |||
| b2c3158d72 | |||
| 1ba7185939 | |||
| e0afdf177c | |||
| 993b7f43f3 | |||
| 74c9e25ad6 | |||
| f5d6bbfaae | |||
| 2bf4b17577 | |||
| 990b30b7bb | |||
| 3326d0e73d | |||
| f7d4b728f5 | |||
| bf24c6e989 |
47 changed files with 1786 additions and 161 deletions
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
7
Capy64/Assets/Lua/CapyOS/home/.shrc
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
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.0.3"
|
local version = "0.1.1"
|
||||||
local systemDirectory = "/sys"
|
local systemDirectory = "/sys"
|
||||||
|
|
||||||
print("Starting CapyOS")
|
print("Starting CapyOS")
|
||||||
|
|
|
||||||
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/bin/alias.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
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, " ")
|
||||||
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/bg.lua
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
local scheduler = require("scheduler")
|
||||||
|
scheduler.spawn(function()
|
||||||
|
shell.run(arg.string)
|
||||||
|
end)
|
||||||
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
9
Capy64/Assets/Lua/CapyOS/sys/bin/cat.lua
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
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()
|
||||||
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
4
Capy64/Assets/Lua/CapyOS/sys/bin/echo.lua
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
local argparser = require("argparser")
|
||||||
|
|
||||||
|
local args, options = argparser.parse(...)
|
||||||
|
print(table.concat(args, " "))
|
||||||
|
|
@ -1 +1 @@
|
||||||
shell.exit()
|
shell.exit()
|
||||||
40
Capy64/Assets/Lua/CapyOS/sys/bin/fun/donuts.lua
Normal file
40
Capy64/Assets/Lua/CapyOS/sys/bin/fun/donuts.lua
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
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,15 +38,19 @@ local function iter(cr, ci)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function draw()
|
local function draw()
|
||||||
local buffer <close> = gpu.newBuffer()
|
local size = w * h
|
||||||
|
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)
|
||||||
buffer[y * w + x] = colorUnit * (iterations - i)
|
canvas[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
|
||||||
|
|
||||||
|
|
|
||||||
209
Capy64/Assets/Lua/CapyOS/sys/bin/fun/paint.lua
Normal file
209
Capy64/Assets/Lua/CapyOS/sys/bin/fun/paint.lua
Normal file
|
|
@ -0,0 +1,209 @@
|
||||||
|
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,7 +11,13 @@ 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("Hello, World!", 0.05)
|
slowPrint(text, 0.05)
|
||||||
|
|
|
||||||
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
11
Capy64/Assets/Lua/CapyOS/sys/bin/help.lua
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
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))
|
||||||
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
78
Capy64/Assets/Lua/CapyOS/sys/bin/less.lua
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
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,25 +1,88 @@
|
||||||
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 dir = shell.getDir()
|
local argparser = require("argparser")
|
||||||
|
|
||||||
if args[1] then
|
local theme = {
|
||||||
dir = shell.resolve(args[1])
|
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
|
end
|
||||||
|
|
||||||
if not fs.isDir(dir) then
|
local args, options = argparser.parse(...)
|
||||||
error("No such directory: " .. dir, 0)
|
|
||||||
|
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
|
||||||
|
path = shell.resolve(args[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not fs.isDir(path) then
|
||||||
|
error("No such directory: " .. path, 0)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local files = fs.list(dir)
|
local entries = fs.list(path)
|
||||||
for k, v in ipairs(files) do
|
|
||||||
if fs.isDir(fs.combine(dir, v)) then
|
if options.l then
|
||||||
term.setForeground(colors.lightBlue)
|
print(string.format("total %d", #entries))
|
||||||
print(v .. "/")
|
end
|
||||||
else
|
local printed = 0
|
||||||
term.setForeground(colors.white)
|
for i, entry in ipairs(entries) do
|
||||||
print(v)
|
if entry:sub(1, 1) ~= "." or options.a then
|
||||||
|
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,49 +1,16 @@
|
||||||
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 colours = colors
|
local argparser = require("argparser")
|
||||||
|
local tableutils = require("tableutils")
|
||||||
|
|
||||||
local tArgs = { ... }
|
local args, options = argparser.parse(...)
|
||||||
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(s, "=lua", "t", tEnv)
|
local func, e = load(str, chunkname, "t", env)
|
||||||
local func2 = load("return " .. s, "=lua", "t", tEnv)
|
local func2 = load("return " .. str, chunkname, "t", env)
|
||||||
if not func then
|
if not func then
|
||||||
if func2 then
|
if func2 then
|
||||||
func = func2
|
func = func2
|
||||||
|
|
@ -62,14 +29,70 @@ while bRunning do
|
||||||
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(tostring(value))
|
print(tableutils.pretty(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
|
||||||
|
|
|
||||||
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
20
Capy64/Assets/Lua/CapyOS/sys/bin/motd.lua
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
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])
|
||||||
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
16
Capy64/Assets/Lua/CapyOS/sys/bin/mv.lua
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
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)
|
||||||
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
10
Capy64/Assets/Lua/CapyOS/sys/bin/programs.lua
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
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, " "))
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
local timer = require("timer")
|
|
||||||
local machine = require("machine")
|
|
||||||
|
|
||||||
print("Goodbye!")
|
|
||||||
|
|
||||||
timer.sleep(1)
|
|
||||||
|
|
||||||
machine.reboot()
|
|
||||||
|
|
@ -1,10 +1,16 @@
|
||||||
local fs = require("fs")
|
local fs = require("fs")
|
||||||
|
local argparser = require("argparser")
|
||||||
|
|
||||||
local args = { ... }
|
local args, options = argparser.parse(...)
|
||||||
if #args == 0 then
|
|
||||||
print("Usage: rm <file>")
|
if not args[1] or options.h or options.help then
|
||||||
|
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,40 +2,35 @@ 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 = "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||||
shell.homePath = "/home"
|
shell.homePath = parentShell and parentShell.home or "/home"
|
||||||
|
shell.aliases = parentShell and parentShell.aliases or {}
|
||||||
|
|
||||||
local currentDir = shell.homePath
|
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
||||||
|
|
||||||
local function buildEnvironment(path, args)
|
local function buildEnvironment(command, filepath, args, argf)
|
||||||
local arg = { table.unpack(args, 2) }
|
local arg = { table.unpack(args, 2) }
|
||||||
arg[0] = path
|
arg[0] = command
|
||||||
|
arg.string = argf
|
||||||
|
|
||||||
|
local envPackage = createPackageEnvironment(filepath)
|
||||||
|
envPackage.loaded.scheduler = scheduler
|
||||||
|
|
||||||
return setmetatable({
|
return setmetatable({
|
||||||
shell = shell,
|
shell = shell,
|
||||||
arg = arg
|
arg = arg,
|
||||||
}, { __index = _G })
|
scheduler = scheduler,
|
||||||
end
|
}, { __index = envPackage.loaded._G })
|
||||||
|
|
||||||
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()
|
||||||
|
|
@ -72,16 +67,24 @@ function shell.resolveProgram(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
function shell.run(...)
|
function shell.run(...)
|
||||||
local args = tokenise(...)
|
local args = argparser.tokenize(...)
|
||||||
|
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
|
||||||
io.stderr.print("Command not found: " .. command)
|
if shell.aliases[command] then
|
||||||
return false
|
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
||||||
|
else
|
||||||
|
io.stderr.print("Command not found: " .. command)
|
||||||
|
return false
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local env = buildEnvironment(command, args)
|
local env = buildEnvironment(command, path, args, argf)
|
||||||
|
|
||||||
local func, err = loadfile(path, "t", env)
|
local func, err = loadfile(path, "t", env)
|
||||||
|
|
||||||
|
|
@ -90,7 +93,21 @@ function shell.run(...)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local ok, err = pcall(func, table.unpack(args, 2))
|
local ok, err
|
||||||
|
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
|
||||||
|
|
@ -107,6 +124,21 @@ 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,8 +1,33 @@
|
||||||
local timer = require("timer")
|
|
||||||
local machine = require("machine")
|
local machine = require("machine")
|
||||||
|
local scheduler = require("scheduler")
|
||||||
|
local argparser = require("argparser")
|
||||||
|
|
||||||
print("Goodbye!")
|
local args, options = argparser.parse(...)
|
||||||
|
|
||||||
timer.sleep(1)
|
if options.h or options.help then
|
||||||
|
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
|
||||||
|
|
||||||
machine.shutdown()
|
local time = 0
|
||||||
|
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,6 +19,8 @@ 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,
|
||||||
})
|
})
|
||||||
|
|
@ -27,8 +29,8 @@ if not response then
|
||||||
end
|
end
|
||||||
|
|
||||||
local file <close> = fs.open(outputPath, "wb")
|
local file <close> = fs.open(outputPath, "wb")
|
||||||
file:write(response:readAll())
|
file:write(response.content:read("a"))
|
||||||
file:close()
|
file:close()
|
||||||
response:close()
|
response.content:close()
|
||||||
|
|
||||||
print("File written to " .. outputPath)
|
print("Downloaded to " .. outputPath)
|
||||||
|
|
|
||||||
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
28
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_fs.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
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
|
||||||
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
47
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/50_os_manager.lua
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
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()
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
require("machine").shutdown()
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
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()
|
|
||||||
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
91
Capy64/Assets/Lua/CapyOS/sys/lib/argparser.lua
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
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,7 +6,347 @@ local machine = require("machine")
|
||||||
|
|
||||||
local io = {}
|
local io = {}
|
||||||
|
|
||||||
function io.write(text)
|
function io.write(sText)
|
||||||
|
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
|
||||||
|
|
@ -28,7 +368,7 @@ function io.write(text)
|
||||||
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()
|
||||||
|
|
@ -83,7 +423,7 @@ function io.read(replace, history, complete, default)
|
||||||
|
|
||||||
local function clearCompletion()
|
local function clearCompletion()
|
||||||
if completions[comp_id] then
|
if completions[comp_id] then
|
||||||
write((" "):rep(#completions[comp_id]))
|
io.write((" "):rep(#completions[comp_id]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -142,7 +482,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
|
||||||
|
|
@ -151,7 +491,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
|
||||||
|
|
@ -161,7 +501,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
|
||||||
|
|
@ -243,7 +583,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
|
||||||
|
|
@ -255,6 +595,7 @@ function io.read(replace, history, complete, default)
|
||||||
|
|
||||||
return buffer
|
return buffer
|
||||||
end
|
end
|
||||||
|
]]
|
||||||
|
|
||||||
io.stderr = {}
|
io.stderr = {}
|
||||||
|
|
||||||
|
|
|
||||||
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/lib/lib.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
local x = math.random()
|
||||||
|
|
||||||
|
return x
|
||||||
182
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
182
Capy64/Assets/Lua/CapyOS/sys/lib/scheduler.lua
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
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
|
||||||
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
112
Capy64/Assets/Lua/CapyOS/sys/lib/shell/package.lua
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
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
|
||||||
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
87
Capy64/Assets/Lua/CapyOS/sys/lib/tableutils.lua
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
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
|
||||||
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
2
Capy64/Assets/Lua/CapyOS/sys/share/help/index
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
Welcome to CapyOS!
|
||||||
|
Run "programs" to get a list of available programs.
|
||||||
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
12
Capy64/Assets/Lua/CapyOS/sys/share/help/license
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
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
|
||||||
3
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/share/motd.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
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,6 +24,9 @@ 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)
|
||||||
|
|
@ -97,11 +100,60 @@ 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)
|
||||||
|
|
@ -116,6 +168,7 @@ local function setupScreen()
|
||||||
"Install default OS",
|
"Install default OS",
|
||||||
installDefaultOS,
|
installDefaultOS,
|
||||||
},
|
},
|
||||||
|
{},
|
||||||
{
|
{
|
||||||
"Exit setup",
|
"Exit setup",
|
||||||
exit,
|
exit,
|
||||||
|
|
@ -127,26 +180,40 @@ local function setupScreen()
|
||||||
}
|
}
|
||||||
|
|
||||||
local selection = 1
|
local selection = 1
|
||||||
local function redraw()
|
local function redraw(noDrawSel)
|
||||||
term.setForeground(fg)
|
local w, h = term.getSize()
|
||||||
term.setBackground(bg)
|
term.setForeground(setupfg)
|
||||||
|
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 selection == k then
|
if #v > 0 then
|
||||||
writeCenter("[ " .. v[1] .. " ]")
|
if selection == k and not noDrawSel then
|
||||||
else
|
writeCenter("[ " .. v[1] .. " ]")
|
||||||
writeCenter(v[1])
|
else
|
||||||
|
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
|
||||||
|
|
@ -154,9 +221,16 @@ 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()
|
||||||
|
|
@ -177,7 +251,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("Powered by Capybaras")
|
writeCenter("(c) 2023 AlexDevs")
|
||||||
|
|
||||||
term.setPos(1, h - 1)
|
term.setPos(1, h - 1)
|
||||||
writeCenter("Press F2 to open setup")
|
writeCenter("Press F2 to open setup")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"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.0-beta";
|
public const string Version = "1.1.2-beta";
|
||||||
|
|
||||||
public static class DefaultParameters
|
public static class DefaultParameters
|
||||||
{
|
{
|
||||||
|
|
@ -112,7 +112,6 @@ 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;
|
||||||
|
|
@ -122,7 +121,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();
|
||||||
|
|
@ -131,11 +130,6 @@ 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)
|
||||||
|
|
@ -265,7 +259,9 @@ public class Capy64 : Game
|
||||||
Audio = new Audio();
|
Audio = new Audio();
|
||||||
|
|
||||||
NativePlugins = GetNativePlugins();
|
NativePlugins = GetNativePlugins();
|
||||||
Plugins = PluginLoader.LoadAllPlugins("plugins", _serviceProvider);
|
var safeMode = Configuration.GetValue("SafeMode", false);
|
||||||
|
if (!safeMode)
|
||||||
|
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
|
||||||
|
|
||||||
EventEmitter.RaiseInit();
|
EventEmitter.RaiseInit();
|
||||||
|
|
||||||
|
|
@ -284,7 +280,6 @@ 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>net7.0</TargetFramework>
|
<TargetFramework>net8.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.1.3.18" />
|
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.2.8" />
|
<PackageReference Include="FontStashSharp.MonoGame" Version="1.3.6" />
|
||||||
<PackageReference Include="KeraLua" Version="1.3.3" />
|
<PackageReference Include="KeraLua" Version="1.4.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.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, IServiceProvider provider)
|
public static List<IComponent> LoadAllPlugins(string pluginsPath)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists(pluginsPath))
|
if (!Directory.Exists(pluginsPath))
|
||||||
Directory.CreateDirectory(pluginsPath);
|
Directory.CreateDirectory(pluginsPath);
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,9 @@ 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,6 +136,7 @@ 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);
|
||||||
|
|
||||||
|
|
@ -162,11 +163,31 @@ 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>net7.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||||
|
|
|
||||||
BIN
Resources/256.png
Normal file
BIN
Resources/256.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.8 KiB |
11
Resources/Capy64.desktop
Normal file
11
Resources/Capy64.desktop
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
|
||||||
|
Name=Capy64
|
||||||
|
Comment=Lua Fantasy Computer
|
||||||
|
Categories=Game;Emulator;
|
||||||
|
|
||||||
|
Icon=me.alexdevs.Capy64
|
||||||
|
Exec=Capy64
|
||||||
|
Terminal=false
|
||||||
37
Resources/appdata.xml
Normal file
37
Resources/appdata.xml
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?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