mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-12-14 18:15:44 +00:00
Compare commits
44 commits
v0.0.10-al
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 2445175248 | |||
| c19fb78b1f | |||
| 5d0b6e864a | |||
| b2c3158d72 | |||
| 1ba7185939 | |||
| e0afdf177c | |||
| 993b7f43f3 | |||
| 74c9e25ad6 | |||
| f5d6bbfaae | |||
| 2bf4b17577 | |||
| 990b30b7bb | |||
| 3326d0e73d | |||
| f7d4b728f5 | |||
| bf24c6e989 | |||
|
|
2b8eb54a8b | ||
|
|
b85adb960b | ||
| d8d6e76729 | |||
| 3fa0a73ac6 | |||
| fe9446e2e7 | |||
| 2f7c4fb032 | |||
| 55c4f70533 | |||
| 4880fea8e3 | |||
|
|
59bbf10e78 | ||
|
|
b44f943456 | ||
|
|
efe510d0da | ||
| 589042ca38 | |||
|
|
86c433e966 | ||
|
|
ad8a0dcb84 | ||
|
|
74b799a0b4 | ||
| e152203afd | |||
| 97161f0c85 | |||
| 7c799426a9 | |||
|
|
fa39689e3a | ||
|
|
b09ab4bdff | ||
|
|
756b8b35dd | ||
| e520383da3 | |||
| f5887c63c0 | |||
| 544025290b | |||
| 4b21daa095 | |||
| 1d28f07a7e | |||
| e07f79c31a | |||
| e926bd6d6b | |||
| f124520a2f | |||
| 451abe6b86 |
100 changed files with 6142 additions and 789 deletions
4
.github/workflows/publish.yml
vendored
4
.github/workflows/publish.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Windows x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os win -p:PublishSingleFile=true -o capy64-windows-x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os win -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-windows-x64 --self-contained
|
||||
|
||||
- name: Upload Windows x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
|
|
@ -64,7 +64,7 @@ jobs:
|
|||
run: dotnet test --no-build --verbosity normal
|
||||
|
||||
- name: Publish for Linux x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release --no-self-contained -a x64 --os linux -p:PublishSingleFile=true -o capy64-linux-x64
|
||||
run: dotnet publish Capy64/Capy64.csproj -c Release -a x64 --os linux -p:PublishReadyToRun=false -p:PublishSingleFile=true -p:TieredCompilation=false -o capy64-linux-x64 --self-contained
|
||||
|
||||
- name: Upload Linux x64 artifact
|
||||
uses: actions/upload-artifact@v3.1.2
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
{
|
||||
"version": 1,
|
||||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-mgcb": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-linux": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-linux"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-windows": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-windows"
|
||||
]
|
||||
},
|
||||
"dotnet-mgcb-editor-mac": {
|
||||
"version": "3.8.1.303",
|
||||
"commands": [
|
||||
"mgcb-editor-mac"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -14,13 +14,10 @@
|
|||
// limitations under the License.
|
||||
|
||||
using KeraLua;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Capy64.API;
|
||||
|
||||
public interface IComponent
|
||||
{
|
||||
void ConfigureServices(IServiceCollection services) { }
|
||||
void LuaInit(Lua L) { }
|
||||
|
||||
}
|
||||
|
|
|
|||
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
|
||||
46
Capy64/Assets/Lua/CapyOS/init.lua
Normal file
46
Capy64/Assets/Lua/CapyOS/init.lua
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
local version = "0.1.1"
|
||||
local systemDirectory = "/sys"
|
||||
|
||||
print("Starting CapyOS")
|
||||
|
||||
local term = require("term")
|
||||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
|
||||
local nPrint = print
|
||||
local function showError(err)
|
||||
nPrint(err)
|
||||
local x, y = term.getPos()
|
||||
term.setForeground(0xff0000)
|
||||
term.setPos(1, y)
|
||||
term.write(err)
|
||||
|
||||
term.setPos(1, y + 1)
|
||||
term.setForeground(0xffffff)
|
||||
term.write("Press any key to continue")
|
||||
|
||||
coroutine.yield("key_down")
|
||||
end
|
||||
|
||||
function os.version()
|
||||
return "CapyOS " .. version
|
||||
end
|
||||
|
||||
term.setPos(1, 1)
|
||||
term.write(machine.version())
|
||||
term.setPos(1, 3)
|
||||
|
||||
local files = fs.list(fs.combine(systemDirectory, "boot/autorun"))
|
||||
for i = 1, #files do
|
||||
local func, err = loadfile(fs.combine(systemDirectory, "boot/autorun", files[i]))
|
||||
if not func then
|
||||
showError(err)
|
||||
break
|
||||
end
|
||||
|
||||
local ok, err = pcall(func)
|
||||
if not ok then
|
||||
showError(debug.traceback(err))
|
||||
break
|
||||
end
|
||||
end
|
||||
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()
|
||||
17
Capy64/Assets/Lua/CapyOS/sys/bin/cd.lua
Normal file
17
Capy64/Assets/Lua/CapyOS/sys/bin/cd.lua
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
local args = {...}
|
||||
local fs = require("fs")
|
||||
|
||||
local dir = args[1]
|
||||
|
||||
if not dir then
|
||||
dir = shell.homePath
|
||||
end
|
||||
|
||||
dir = shell.resolve(dir)
|
||||
|
||||
if not fs.isDir(dir) then
|
||||
error("No such directory: " .. dir, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
shell.setDir(dir)
|
||||
5
Capy64/Assets/Lua/CapyOS/sys/bin/clear.lua
Normal file
5
Capy64/Assets/Lua/CapyOS/sys/bin/clear.lua
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
term.setBackground(colors.black)
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
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
Capy64/Assets/Lua/CapyOS/sys/bin/exit.lua
Normal file
1
Capy64/Assets/Lua/CapyOS/sys/bin/exit.lua
Normal file
|
|
@ -0,0 +1 @@
|
|||
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
|
||||
98
Capy64/Assets/Lua/CapyOS/sys/bin/fun/mandelbrot.lua
Normal file
98
Capy64/Assets/Lua/CapyOS/sys/bin/fun/mandelbrot.lua
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
-- Mandelbrot in Capy64
|
||||
|
||||
local gpu = require("gpu")
|
||||
local timer = require("timer")
|
||||
local event = require("event")
|
||||
local term = require("term")
|
||||
|
||||
-- lower = closer = slower
|
||||
local scale = 4
|
||||
|
||||
-- higher = more detailed = slower
|
||||
local iterations = 100
|
||||
|
||||
local pscale = scale
|
||||
local w, h = gpu.getSize()
|
||||
local px = pscale / w
|
||||
local colorUnit = math.floor(0xffffff / iterations)
|
||||
-- todo: make it interactive
|
||||
local dx, dy = 0, 0
|
||||
local cx, cy = math.floor(w / 2), math.floor(h / 2)
|
||||
|
||||
-- z = z^2 + c
|
||||
local function mandelbrot(zr, zi, cr, ci)
|
||||
return zr ^ 2 - zi ^ 2 + cr,
|
||||
2 * zr * zi + ci
|
||||
end
|
||||
|
||||
local function iter(cr, ci)
|
||||
local zr, zi = 0, 0
|
||||
for i = 1, iterations do
|
||||
zr, zi = mandelbrot(zr, zi, cr, ci)
|
||||
if math.abs(zr) >= (pscale >= 2 and pscale or 2) or math.abs(zi) >= (pscale >= 2 and pscale or 2) then
|
||||
return false, colorUnit, i
|
||||
end
|
||||
end
|
||||
|
||||
return true, 0, iterations
|
||||
end
|
||||
|
||||
local function draw()
|
||||
local size = w * h
|
||||
local canvas = { string.unpack(("B"):rep(size), ("\0"):rep(size)) }
|
||||
canvas[#canvas] = nil
|
||||
|
||||
|
||||
for y = 0, h - 1 do
|
||||
for x = 0, w - 1 do
|
||||
local _, _, i = iter((x - cx + dx * pscale) * px, (y - cy + dy * pscale) * px)
|
||||
canvas[y * w + x] = colorUnit * (iterations - i)
|
||||
end
|
||||
end
|
||||
|
||||
local buffer <close> = gpu.bufferFrom(canvas, w, h)
|
||||
gpu.setBuffer(buffer)
|
||||
end
|
||||
|
||||
-- no idea why it's needed
|
||||
timer.sleep(0)
|
||||
|
||||
draw()
|
||||
|
||||
local tw, th = term.getSize()
|
||||
|
||||
while true do
|
||||
term.setPos(1, th)
|
||||
term.setBackground(0)
|
||||
term.setForeground(0xffffff)
|
||||
term.write("X: " .. dx .. "; Y: " .. dy .. "; S: " .. pscale .. "; " .. px .. "!")
|
||||
local ev = { event.pull("key_down") }
|
||||
if ev[1] == "key_down" then
|
||||
local key = ev[3]
|
||||
if key == "up" then
|
||||
dy = dy - 10 / pscale
|
||||
elseif key == "down" then
|
||||
dy = dy + 10 / pscale
|
||||
elseif key == "right" then
|
||||
dx = dx + 10 / pscale
|
||||
elseif key == "left" then
|
||||
dx = dx - 10 / pscale
|
||||
elseif key == "enter" then
|
||||
draw()
|
||||
elseif key == "page_down" then
|
||||
pscale = pscale * 1.25
|
||||
dx = dx * pscale
|
||||
dy = dy * pscale
|
||||
elseif key == "page_up" then
|
||||
pscale = pscale / 1.25
|
||||
dx = dx / pscale
|
||||
dy = dy / pscale
|
||||
elseif key == "r" then
|
||||
pscale = scale
|
||||
dx = 0
|
||||
dy = 0
|
||||
end
|
||||
end
|
||||
|
||||
px = pscale / w
|
||||
end
|
||||
73
Capy64/Assets/Lua/CapyOS/sys/bin/fun/melt.lua
Normal file
73
Capy64/Assets/Lua/CapyOS/sys/bin/fun/melt.lua
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
local gpu = require("gpu")
|
||||
local timer = require("timer")
|
||||
local event = require("event")
|
||||
local colors = require("colors")
|
||||
local parallel = require("parallel")
|
||||
|
||||
local melts = 2 ^ 12
|
||||
|
||||
local function contains(arr, val)
|
||||
for k, v in ipairs(arr) do
|
||||
if v == val then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function melt()
|
||||
local w, h = gpu.getSize()
|
||||
local x, y = 0, 0
|
||||
|
||||
while true do
|
||||
local buffer <close> = gpu.getBuffer()
|
||||
for i = 1, melts do
|
||||
local nx = math.random(x, w)
|
||||
local ny = math.random(y, h)
|
||||
|
||||
local c = buffer[ny * w + nx]
|
||||
buffer[(ny + 1) * w + nx] = c
|
||||
end
|
||||
gpu.setBuffer(buffer)
|
||||
|
||||
timer.delay(0):await()
|
||||
end
|
||||
end
|
||||
|
||||
local function draw()
|
||||
local ox, oy
|
||||
while true do
|
||||
local ev, b, x, y = event.pull("mouse_move", "mouse_down")
|
||||
|
||||
if ev == "mouse_down" then
|
||||
if b == 1 then
|
||||
ox = x
|
||||
oy = y
|
||||
end
|
||||
elseif ev == "mouse_move" then
|
||||
if contains(b, 1) then
|
||||
gpu.plot(x, y, colors.red)
|
||||
gpu.drawLine(x, y, ox, oy, colors.red)
|
||||
ox, oy = x, y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function random()
|
||||
local w, h = gpu.getSize()
|
||||
while true do
|
||||
for i = 1, 24 do
|
||||
gpu.drawString(
|
||||
math.random(-7, w),
|
||||
math.random(-13, h),
|
||||
math.random(0, 0xffffff),
|
||||
string.char(math.random(32, 127))
|
||||
)
|
||||
end
|
||||
|
||||
timer.delay(0.1):await()
|
||||
end
|
||||
end
|
||||
|
||||
parallel.waitForAny(draw, melt, random)
|
||||
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
|
||||
23
Capy64/Assets/Lua/CapyOS/sys/bin/hello.lua
Normal file
23
Capy64/Assets/Lua/CapyOS/sys/bin/hello.lua
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
local timer = require("timer")
|
||||
local colors = require("colors")
|
||||
local term = require("term")
|
||||
|
||||
local function slowPrint(text, delay)
|
||||
for i = 1, #text do
|
||||
local ch = text:sub(i, i)
|
||||
io.write(ch)
|
||||
timer.sleep(delay)
|
||||
end
|
||||
print()
|
||||
end
|
||||
|
||||
local args = {...}
|
||||
local text = "Hello, World!"
|
||||
if #args > 0 then
|
||||
text = table.concat(args, " ")
|
||||
end
|
||||
|
||||
local color = colors[math.random(1, #colors)]
|
||||
|
||||
term.setForeground(color)
|
||||
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
|
||||
88
Capy64/Assets/Lua/CapyOS/sys/bin/ls.lua
Normal file
88
Capy64/Assets/Lua/CapyOS/sys/bin/ls.lua
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
local fs = require("fs")
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local argparser = require("argparser")
|
||||
|
||||
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
|
||||
path = shell.resolve(args[1])
|
||||
end
|
||||
|
||||
if not fs.isDir(path) then
|
||||
error("No such directory: " .. path, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local entries = fs.list(path)
|
||||
|
||||
if options.l then
|
||||
print(string.format("total %d", #entries))
|
||||
end
|
||||
local printed = 0
|
||||
for i, entry in ipairs(entries) do
|
||||
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
|
||||
|
||||
if not options.l and printed > 0 then
|
||||
print()
|
||||
end
|
||||
98
Capy64/Assets/Lua/CapyOS/sys/bin/lua.lua
Normal file
98
Capy64/Assets/Lua/CapyOS/sys/bin/lua.lua
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
local term = require("term")
|
||||
local io = require("io")
|
||||
local colors = require("colors")
|
||||
local argparser = require("argparser")
|
||||
local tableutils = require("tableutils")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
local function evaluate(str, env, chunkname)
|
||||
chunkname = chunkname or "=lua"
|
||||
local nForcePrint = 0
|
||||
local func, e = load(str, chunkname, "t", env)
|
||||
local func2 = load("return " .. str, chunkname, "t", env)
|
||||
if not func then
|
||||
if func2 then
|
||||
func = func2
|
||||
e = nil
|
||||
nForcePrint = 1
|
||||
end
|
||||
else
|
||||
if func2 then
|
||||
func = func2
|
||||
end
|
||||
end
|
||||
|
||||
if func then
|
||||
local tResults = table.pack(pcall(func))
|
||||
if tResults[1] then
|
||||
local n = 1
|
||||
while n < tResults.n or n <= nForcePrint do
|
||||
local value = tResults[n + 1]
|
||||
print(tableutils.pretty(value))
|
||||
n = n + 1
|
||||
end
|
||||
else
|
||||
io.stderr.print(tResults[2])
|
||||
return false
|
||||
end
|
||||
else
|
||||
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
|
||||
|
||||
evaluate(s, tEnv)
|
||||
|
||||
end
|
||||
14
Capy64/Assets/Lua/CapyOS/sys/bin/mkdir.lua
Normal file
14
Capy64/Assets/Lua/CapyOS/sys/bin/mkdir.lua
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
local fs = require("fs")
|
||||
local args = { ... }
|
||||
|
||||
if #args == 0 then
|
||||
print("Usage: mkdir <directory>")
|
||||
return
|
||||
end
|
||||
|
||||
local dir = shell.resolve(args[1])
|
||||
if fs.exists(dir) then
|
||||
error("Path already exists", 0)
|
||||
end
|
||||
|
||||
fs.makeDir(dir)
|
||||
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
Capy64/Assets/Lua/CapyOS/sys/bin/pwd.lua
Normal file
1
Capy64/Assets/Lua/CapyOS/sys/bin/pwd.lua
Normal file
|
|
@ -0,0 +1 @@
|
|||
print(shell.getDir())
|
||||
16
Capy64/Assets/Lua/CapyOS/sys/bin/rm.lua
Normal file
16
Capy64/Assets/Lua/CapyOS/sys/bin/rm.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 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
|
||||
end
|
||||
|
||||
local file = shell.resolve(args[1])
|
||||
|
||||
fs.delete(file, options.recursive or options.r)
|
||||
175
Capy64/Assets/Lua/CapyOS/sys/bin/shell.lua
Normal file
175
Capy64/Assets/Lua/CapyOS/sys/bin/shell.lua
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
local argparser = require("argparser")
|
||||
local scheduler = require("scheduler")
|
||||
local createPackageEnvironment = require("shell.package")
|
||||
|
||||
local useScheduler = false
|
||||
local exit = false
|
||||
local parentShell = shell
|
||||
local isStartupShell = parentShell == nil
|
||||
local shell = {}
|
||||
|
||||
shell.path = parentShell and parentShell.path or "./?;./?.lua;/bin/?.lua;/sys/bin/?.lua"
|
||||
shell.homePath = parentShell and parentShell.home or "/home"
|
||||
shell.aliases = parentShell and parentShell.aliases or {}
|
||||
|
||||
local currentDir = parentShell and parentShell.getDir() or shell.homePath
|
||||
|
||||
local function buildEnvironment(command, filepath, args, argf)
|
||||
local arg = { table.unpack(args, 2) }
|
||||
arg[0] = command
|
||||
arg.string = argf
|
||||
|
||||
local envPackage = createPackageEnvironment(filepath)
|
||||
envPackage.loaded.scheduler = scheduler
|
||||
|
||||
return setmetatable({
|
||||
shell = shell,
|
||||
arg = arg,
|
||||
scheduler = scheduler,
|
||||
}, { __index = envPackage.loaded._G })
|
||||
end
|
||||
|
||||
function shell.getDir()
|
||||
return currentDir
|
||||
end
|
||||
|
||||
function shell.setDir(path)
|
||||
currentDir = path
|
||||
end
|
||||
|
||||
function shell.resolve(path)
|
||||
if path:sub(1, 1) == "/" then
|
||||
return fs.combine("", path)
|
||||
end
|
||||
|
||||
if path:sub(1, 1) == "~" then
|
||||
return fs.combine(shell.homePath, path)
|
||||
end
|
||||
|
||||
return fs.combine(currentDir, path)
|
||||
end
|
||||
|
||||
function shell.resolveProgram(path)
|
||||
if path:sub(1, 1) == "/" then
|
||||
return shell.resolve(path)
|
||||
end
|
||||
|
||||
for seg in shell.path:gmatch("[^;]+") do
|
||||
local resolved = shell.resolve(seg:gsub("%?", path))
|
||||
if fs.exists(resolved) and not fs.isDir(resolved) then
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shell.run(...)
|
||||
local args = argparser.tokenize(...)
|
||||
local argf = table.concat({...}, " ")
|
||||
local command = args[1]
|
||||
|
||||
argf = argf:sub(#command + 2)
|
||||
|
||||
local path = shell.resolveProgram(command)
|
||||
|
||||
if not path then
|
||||
if shell.aliases[command] then
|
||||
return shell.run(shell.aliases[command], select(2, table.unpack(args)))
|
||||
else
|
||||
io.stderr.print("Command not found: " .. command)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local env = buildEnvironment(command, path, args, argf)
|
||||
|
||||
local func, err = loadfile(path, "t", env)
|
||||
|
||||
if not func then
|
||||
io.stderr.print(err)
|
||||
return false
|
||||
end
|
||||
|
||||
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
|
||||
io.stderr.print(err)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function shell.exit()
|
||||
exit = true
|
||||
end
|
||||
|
||||
if not fs.exists(shell.homePath) then
|
||||
fs.makeDir(shell.homePath)
|
||||
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 lastExecSuccess = true
|
||||
while not exit do
|
||||
machine.setRPC(os.version(), "On shell")
|
||||
|
||||
term.setBackground(colors.black)
|
||||
term.setForeground(colors.white)
|
||||
io.write(":")
|
||||
term.setForeground(colors.lightBlue)
|
||||
if currentDir == shell.homePath then
|
||||
io.write("~")
|
||||
else
|
||||
io.write(currentDir)
|
||||
end
|
||||
|
||||
if lastExecSuccess then
|
||||
term.setForeground(colors.yellow)
|
||||
else
|
||||
term.setForeground(colors.red)
|
||||
end
|
||||
io.write("$ ")
|
||||
|
||||
term.setForeground(colors.white)
|
||||
local line = io.read(nil, history)
|
||||
|
||||
if line:match("%S") and history[#history] ~= line then
|
||||
table.insert(history, line)
|
||||
end
|
||||
|
||||
if line:match("%S") then
|
||||
machine.setRPC(os.version(), "Running: " .. line)
|
||||
lastExecSuccess = shell.run(line)
|
||||
end
|
||||
end
|
||||
33
Capy64/Assets/Lua/CapyOS/sys/bin/shutdown.lua
Normal file
33
Capy64/Assets/Lua/CapyOS/sys/bin/shutdown.lua
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
local machine = require("machine")
|
||||
local scheduler = require("scheduler")
|
||||
local argparser = require("argparser")
|
||||
|
||||
local args, options = argparser.parse(...)
|
||||
|
||||
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
|
||||
|
||||
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,
|
||||
})
|
||||
2
Capy64/Assets/Lua/CapyOS/sys/bin/version.lua
Normal file
2
Capy64/Assets/Lua/CapyOS/sys/bin/version.lua
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
local machine = require("machine")
|
||||
print(string.format("%s @ %s - %s", os.version(), machine.version(), _VERSION))
|
||||
36
Capy64/Assets/Lua/CapyOS/sys/bin/wget.lua
Normal file
36
Capy64/Assets/Lua/CapyOS/sys/bin/wget.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
local http = require("http")
|
||||
local fs = require("fs")
|
||||
|
||||
local args = { ... }
|
||||
|
||||
if not http then
|
||||
error("HTTP is not enabled", 0)
|
||||
end
|
||||
|
||||
if #args == 0 then
|
||||
print("Usage: wget <url> [outputPath]")
|
||||
return false
|
||||
end
|
||||
|
||||
local outputName = args[2] or fs.getName(args[1])
|
||||
local outputPath = shell.resolve(outputName)
|
||||
|
||||
if not http.checkURL(args[1]) then
|
||||
error("Invalid URL", 0)
|
||||
end
|
||||
|
||||
print("Connecting...")
|
||||
|
||||
local response, err = http.get(args[1], nil, {
|
||||
binary = true,
|
||||
})
|
||||
if not response then
|
||||
error(err, 0)
|
||||
end
|
||||
|
||||
local file <close> = fs.open(outputPath, "wb")
|
||||
file:write(response.content:read("a"))
|
||||
file:close()
|
||||
response.content:close()
|
||||
|
||||
print("Downloaded to " .. outputPath)
|
||||
3
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/00_package.lua
Normal file
3
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/00_package.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
package.path = "/lib/?.lua;/lib/?/init.lua;/sys/lib/?.lua;/sys/lib/?/init.lua;" .. package.path
|
||||
|
||||
_G.io = require("io")
|
||||
15
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/01_stdio.lua
Normal file
15
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/01_stdio.lua
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
function _G.print(...)
|
||||
local args = { ... }
|
||||
local size = #args
|
||||
local lines = 0
|
||||
for n, v in ipairs(args) do
|
||||
local s = tostring(v)
|
||||
if n < size then
|
||||
s = s .. "\t"
|
||||
end
|
||||
lines = lines + io.write(s)
|
||||
end
|
||||
lines = lines + io.write("\n")
|
||||
|
||||
return lines
|
||||
end
|
||||
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
|
||||
77
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_http.lua
Normal file
77
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_http.lua
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
local http = require("http")
|
||||
local event = require("event")
|
||||
local expect = require("expect").expect
|
||||
|
||||
|
||||
function http.request(url, body, headers, options)
|
||||
expect(1, url, "string")
|
||||
expect(2, body, "string", "nil")
|
||||
expect(3, headers, "table", "nil")
|
||||
expect(4, options, "table", "nil")
|
||||
|
||||
if not http.checkURL(url) then
|
||||
return nil, "Invalid URL"
|
||||
end
|
||||
|
||||
local task<close> = http.requestAsync(url, body, headers, options)
|
||||
return task:await()
|
||||
end
|
||||
|
||||
function http.get(url, headers, options)
|
||||
expect(1, url, "string")
|
||||
expect(2, headers, "table", "nil")
|
||||
expect(3, options, "table", "nil")
|
||||
|
||||
return http.request(url, nil, headers, options)
|
||||
end
|
||||
|
||||
function http.post(url, body, headers, options)
|
||||
expect(1, url, "string")
|
||||
expect(2, body, "string", "nil")
|
||||
expect(3, headers, "table", "nil")
|
||||
expect(4, options, "table", "nil")
|
||||
|
||||
return http.request(url, body, headers, options)
|
||||
end
|
||||
|
||||
local WebSocketHandle
|
||||
local function buildWebsocketHandle(handle)
|
||||
if not handle then
|
||||
return nil
|
||||
end
|
||||
if not WebSocketHandle then
|
||||
WebSocketHandle = getmetatable(handle) or { __index = {} }
|
||||
function WebSocketHandle.__index:close()
|
||||
self:closeAsync()
|
||||
local _, id
|
||||
repeat
|
||||
_, id = event.pull("websocket_close")
|
||||
until id == self:getRequestID()
|
||||
end
|
||||
|
||||
function WebSocketHandle.__index:receive()
|
||||
local _, id, par
|
||||
repeat
|
||||
_, id, par = event.pull("websocket_message")
|
||||
until id == self:getRequestID()
|
||||
|
||||
return par
|
||||
end
|
||||
end
|
||||
|
||||
return handle
|
||||
end
|
||||
|
||||
function http.websocket(url, headers)
|
||||
expect(1, url, "string")
|
||||
expect(2, headers, "table", "nil")
|
||||
|
||||
if not http.checkURL(url) then
|
||||
return nil, "Invalid URL"
|
||||
end
|
||||
|
||||
local task<close> = http.websocketAsync(url, headers)
|
||||
local client, err = task:await()
|
||||
|
||||
return buildWebsocketHandle(client), err
|
||||
end
|
||||
13
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_timer.lua
Normal file
13
Capy64/Assets/Lua/CapyOS/sys/boot/autorun/02_timer.lua
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
local timer = require("timer")
|
||||
local event = require("event")
|
||||
local expect = require("expect").expect
|
||||
local range = require("expect").range
|
||||
|
||||
function timer.sleep(n)
|
||||
expect(1, n, "number")
|
||||
|
||||
local timerId = timer.start(n)
|
||||
repeat
|
||||
local _, par = event.pull("timer")
|
||||
until par == timerId
|
||||
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()
|
||||
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
|
||||
94
Capy64/Assets/Lua/CapyOS/sys/lib/colors.lua
Normal file
94
Capy64/Assets/Lua/CapyOS/sys/lib/colors.lua
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
local expect = require("expect")
|
||||
|
||||
local palette = {
|
||||
{
|
||||
"white",
|
||||
0xf0f0f0
|
||||
},
|
||||
{
|
||||
"orange",
|
||||
0xf2b233
|
||||
},
|
||||
{
|
||||
"magenta",
|
||||
0xe57fd8
|
||||
},
|
||||
{
|
||||
"lightBlue",
|
||||
0x99b2f2
|
||||
},
|
||||
{
|
||||
"yellow",
|
||||
0xdede6c
|
||||
},
|
||||
{
|
||||
"lime",
|
||||
0x7fcc19
|
||||
},
|
||||
{
|
||||
"pink",
|
||||
0xf2b2cc
|
||||
},
|
||||
{
|
||||
"gray",
|
||||
0x4c4c4c
|
||||
},
|
||||
{
|
||||
"lightGray",
|
||||
0x999999
|
||||
},
|
||||
{
|
||||
"cyan",
|
||||
0x4c99b2
|
||||
},
|
||||
{
|
||||
"purple",
|
||||
0xb266e5
|
||||
},
|
||||
{
|
||||
"blue",
|
||||
0x3366cc
|
||||
},
|
||||
{
|
||||
"brown",
|
||||
0x7f664c
|
||||
},
|
||||
{
|
||||
"green",
|
||||
0x57a64e
|
||||
},
|
||||
{
|
||||
"red",
|
||||
0xcc4c4c
|
||||
},
|
||||
{
|
||||
"black",
|
||||
0x111111
|
||||
}
|
||||
}
|
||||
|
||||
local colors = {}
|
||||
for k, v in ipairs(palette) do
|
||||
colors[v[1]] = v[2]
|
||||
colors[k] = v[2]
|
||||
end
|
||||
|
||||
function colors.packRGB(r, g, b)
|
||||
expect(1, r, "number")
|
||||
expect(2, g, "number")
|
||||
expect(3, b, "number")
|
||||
|
||||
return (r << 16) +
|
||||
(g << 8) +
|
||||
b
|
||||
end
|
||||
|
||||
function colors.unpackRGB(rgb)
|
||||
expect(1, rgb, "number")
|
||||
|
||||
return (rgb >> 16) & 0xff,
|
||||
(rgb >> 8) & 0xff,
|
||||
rgb & 0xff
|
||||
end
|
||||
|
||||
return colors;
|
||||
52
Capy64/Assets/Lua/CapyOS/sys/lib/expect.lua
Normal file
52
Capy64/Assets/Lua/CapyOS/sys/lib/expect.lua
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
-- Credits: https://github.com/Ocawesome101/recrafted
|
||||
|
||||
-- cc.expect
|
||||
|
||||
local _expect = {}
|
||||
|
||||
local function checkType(index, valueType, value, ...)
|
||||
local expected = table.pack(...)
|
||||
local isType = false
|
||||
|
||||
for i = 1, expected.n, 1 do
|
||||
if type(value) == expected[i] then
|
||||
isType = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if not isType then
|
||||
error(string.format("bad %s %s (%s expected, got %s)", valueType,
|
||||
index, table.concat(expected, " or "), type(value)), 3)
|
||||
end
|
||||
|
||||
return value
|
||||
end
|
||||
|
||||
function _expect.expect(index, value, ...)
|
||||
return checkType(("#%d"):format(index), "argument", value, ...)
|
||||
end
|
||||
|
||||
function _expect.field(tbl, index, ...)
|
||||
_expect.expect(1, tbl, "table")
|
||||
_expect.expect(2, index, "string")
|
||||
return checkType(("%q"):format(index), "field", tbl[index], ...)
|
||||
end
|
||||
|
||||
function _expect.range(num, min, max)
|
||||
_expect.expect(1, num, "number")
|
||||
_expect.expect(2, min, "number", "nil")
|
||||
_expect.expect(3, max, "number", "nil")
|
||||
min = min or -math.huge
|
||||
max = max or math.huge
|
||||
if num < min or num > max then
|
||||
error(("number outside of range (expected %d to be within %d and %d")
|
||||
:format(num, min, max), 2)
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(_expect, { __call = function(_, ...)
|
||||
return _expect.expect(...)
|
||||
end })
|
||||
|
||||
return _expect
|
||||
616
Capy64/Assets/Lua/CapyOS/sys/lib/io.lua
Normal file
616
Capy64/Assets/Lua/CapyOS/sys/lib/io.lua
Normal file
|
|
@ -0,0 +1,616 @@
|
|||
local expect = require("expect").expect
|
||||
local event = require("event")
|
||||
local term = require("term")
|
||||
local keys = require("keys")
|
||||
local machine = require("machine")
|
||||
|
||||
local io = {}
|
||||
|
||||
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)
|
||||
|
||||
local lines = 0
|
||||
local w, h = term.getSize()
|
||||
|
||||
local function inc_cy(cy)
|
||||
lines = lines + 1
|
||||
|
||||
if cy > h - 1 then
|
||||
term.scroll(1)
|
||||
return cy
|
||||
else
|
||||
return cy + 1
|
||||
end
|
||||
end
|
||||
|
||||
while #text > 0 do
|
||||
local nl = text:find("\n") or #text
|
||||
local chunk = text:sub(1, nl)
|
||||
text = text:sub(#chunk + 1)
|
||||
|
||||
local has_nl = chunk:sub(-1) == "\n"
|
||||
if has_nl then chunk = chunk:sub(1, -2) end
|
||||
|
||||
local cx, cy = term.getPos()
|
||||
while #chunk > 0 do
|
||||
if cx > w then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
local to_write = chunk:sub(1, w - cx + 1)
|
||||
term.write(to_write)
|
||||
|
||||
chunk = chunk:sub(#to_write + 1)
|
||||
cx, cy = term.getPos()
|
||||
end
|
||||
|
||||
if has_nl then
|
||||
term.setPos(1, inc_cy(cy))
|
||||
end
|
||||
end
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
local empty = {}
|
||||
function io.read(replace, history, complete, default)
|
||||
expect(1, replace, "string", "nil")
|
||||
expect(2, history, "table", "nil")
|
||||
expect(3, complete, "function", "nil")
|
||||
expect(4, default, "string", "nil")
|
||||
|
||||
if replace then replace = replace:sub(1, 1) end
|
||||
local hist = history or {}
|
||||
history = {}
|
||||
for i = 1, #hist, 1 do
|
||||
history[i] = hist[i]
|
||||
end
|
||||
|
||||
local buffer = default or ""
|
||||
local prev_buf = buffer
|
||||
history[#history + 1] = buffer
|
||||
|
||||
local hist_pos = #history
|
||||
local cursor_pos = 0
|
||||
|
||||
local stx, sty = term.getPos()
|
||||
local w, h = term.getSize()
|
||||
|
||||
local dirty = false
|
||||
local completions = {}
|
||||
local comp_id = 0
|
||||
|
||||
local function clearCompletion()
|
||||
if completions[comp_id] then
|
||||
io.write((" "):rep(#completions[comp_id]))
|
||||
end
|
||||
end
|
||||
|
||||
local function full_redraw(force)
|
||||
if force or dirty then
|
||||
if complete and buffer ~= prev_buf then
|
||||
completions = complete(buffer) or empty
|
||||
comp_id = math.min(1, #completions)
|
||||
end
|
||||
prev_buf = buffer
|
||||
|
||||
term.setPos(stx, sty)
|
||||
local text = buffer
|
||||
if replace then text = replace:rep(#text) end
|
||||
local ln = io.write(text)
|
||||
|
||||
if completions[comp_id] then
|
||||
local oldfg = term.getForeground()
|
||||
local oldbg = term.getBackground()
|
||||
term.setForeground(colors.white)
|
||||
term.setBackground(colors.gray)
|
||||
ln = ln + write(completions[comp_id])
|
||||
term.setForeground(oldfg)
|
||||
term.setBackground(oldbg)
|
||||
else
|
||||
ln = ln + io.write(" ")
|
||||
end
|
||||
|
||||
if sty + ln > h then
|
||||
sty = sty - (sty + ln - h)
|
||||
end
|
||||
end
|
||||
|
||||
-- set cursor to the appropriate spot
|
||||
local cx, cy = stx, sty
|
||||
cx = cx + #buffer - cursor_pos -- + #(completions[comp_id] or "")
|
||||
while cx > w do
|
||||
cx = cx - w
|
||||
cy = cy + 1
|
||||
end
|
||||
term.setPos(cx, cy)
|
||||
end
|
||||
|
||||
term.setBlink(true)
|
||||
|
||||
while true do
|
||||
full_redraw()
|
||||
-- get input
|
||||
local evt, par1, par2, mods = event.pull()
|
||||
|
||||
if evt == "char" then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. par1
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = par1 .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. par1 .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif evt == "key_down" then
|
||||
if par1 == keys.back and #buffer > 0 then
|
||||
dirty = true
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
clearCompletion()
|
||||
elseif cursor_pos < #buffer then
|
||||
buffer = buffer:sub(0, -cursor_pos - 2) .. buffer:sub(-cursor_pos)
|
||||
end
|
||||
elseif par1 == keys.delete and cursor_pos > 0 then
|
||||
dirty = true
|
||||
|
||||
if cursor_pos == #buffer then
|
||||
buffer = buffer:sub(2)
|
||||
elseif cursor_pos == 1 then
|
||||
buffer = buffer:sub(1, -2)
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. buffer:sub(-cursor_pos + 1)
|
||||
end
|
||||
cursor_pos = cursor_pos - 1
|
||||
elseif par1 == keys.up then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id > 1 then
|
||||
comp_id = comp_id - 1
|
||||
else
|
||||
comp_id = #completions
|
||||
end
|
||||
elseif hist_pos > 1 then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos - 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
elseif par1 == keys.down then
|
||||
if #completions > 1 then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
if comp_id < #completions then
|
||||
comp_id = comp_id + 1
|
||||
else
|
||||
comp_id = 1
|
||||
end
|
||||
elseif hist_pos < #history then
|
||||
cursor_pos = 0
|
||||
|
||||
history[hist_pos] = buffer
|
||||
hist_pos = hist_pos + 1
|
||||
|
||||
buffer = (" "):rep(#buffer)
|
||||
full_redraw(true)
|
||||
|
||||
buffer = history[hist_pos]
|
||||
dirty = true
|
||||
end
|
||||
elseif par1 == keys.left then
|
||||
if cursor_pos < #buffer then
|
||||
clearCompletion()
|
||||
cursor_pos = cursor_pos + 1
|
||||
end
|
||||
elseif par1 == keys.right then
|
||||
if cursor_pos > 0 then
|
||||
cursor_pos = cursor_pos - 1
|
||||
elseif comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
elseif par1 == keys.tab then
|
||||
if comp_id > 0 then
|
||||
dirty = true
|
||||
buffer = buffer .. completions[comp_id]
|
||||
end
|
||||
elseif par1 == keys.home then
|
||||
cursor_pos = #buffer
|
||||
elseif par1 == keys["end"] then
|
||||
cursor_pos = 0
|
||||
elseif par1 == keys.enter then
|
||||
clearCompletion()
|
||||
print()
|
||||
break
|
||||
elseif mods & keys.mods.ctrl ~= 0 then
|
||||
if par1 == keys.v then
|
||||
dirty = true
|
||||
clearCompletion()
|
||||
local text = machine.getClipboard()
|
||||
if text then
|
||||
if cursor_pos == 0 then
|
||||
buffer = buffer .. text
|
||||
elseif cursor_pos == #buffer then
|
||||
buffer = text .. buffer
|
||||
else
|
||||
buffer = buffer:sub(0, -cursor_pos - 1) .. text ..
|
||||
buffer:sub(-cursor_pos + (#text - 1))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
term.setBlink(false)
|
||||
|
||||
return buffer
|
||||
end
|
||||
]]
|
||||
|
||||
io.stderr = {}
|
||||
|
||||
function io.stderr.write(text)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
io.write(text)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
function io.stderr.print(...)
|
||||
local fg = term.getForeground()
|
||||
term.setForeground(0xff0000)
|
||||
print(...)
|
||||
term.setForeground(fg)
|
||||
end
|
||||
|
||||
return io
|
||||
388
Capy64/Assets/Lua/CapyOS/sys/lib/json.lua
Normal file
388
Capy64/Assets/Lua/CapyOS/sys/lib/json.lua
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
--
|
||||
-- json.lua
|
||||
--
|
||||
-- Copyright (c) 2020 rxi
|
||||
--
|
||||
-- Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
-- this software and associated documentation files (the "Software"), to deal in
|
||||
-- the Software without restriction, including without limitation the rights to
|
||||
-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
-- of the Software, and to permit persons to whom the Software is furnished to do
|
||||
-- so, subject to the following conditions:
|
||||
--
|
||||
-- The above copyright notice and this permission notice shall be included in all
|
||||
-- copies or substantial portions of the Software.
|
||||
--
|
||||
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
-- SOFTWARE.
|
||||
--
|
||||
|
||||
local json = { _version = "0.1.2" }
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Encode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local encode
|
||||
|
||||
local escape_char_map = {
|
||||
[ "\\" ] = "\\",
|
||||
[ "\"" ] = "\"",
|
||||
[ "\b" ] = "b",
|
||||
[ "\f" ] = "f",
|
||||
[ "\n" ] = "n",
|
||||
[ "\r" ] = "r",
|
||||
[ "\t" ] = "t",
|
||||
}
|
||||
|
||||
local escape_char_map_inv = { [ "/" ] = "/" }
|
||||
for k, v in pairs(escape_char_map) do
|
||||
escape_char_map_inv[v] = k
|
||||
end
|
||||
|
||||
|
||||
local function escape_char(c)
|
||||
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
|
||||
end
|
||||
|
||||
|
||||
local function encode_nil(val)
|
||||
return "null"
|
||||
end
|
||||
|
||||
|
||||
local function encode_table(val, stack)
|
||||
local res = {}
|
||||
stack = stack or {}
|
||||
|
||||
-- Circular reference?
|
||||
if stack[val] then error("circular reference") end
|
||||
|
||||
stack[val] = true
|
||||
|
||||
if rawget(val, 1) ~= nil or next(val) == nil then
|
||||
-- Treat as array -- check keys are valid and it is not sparse
|
||||
local n = 0
|
||||
for k in pairs(val) do
|
||||
if type(k) ~= "number" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
n = n + 1
|
||||
end
|
||||
if n ~= #val then
|
||||
error("invalid table: sparse array")
|
||||
end
|
||||
-- Encode
|
||||
for i, v in ipairs(val) do
|
||||
table.insert(res, encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "[" .. table.concat(res, ",") .. "]"
|
||||
|
||||
else
|
||||
-- Treat as an object
|
||||
for k, v in pairs(val) do
|
||||
if type(k) ~= "string" then
|
||||
error("invalid table: mixed or invalid key types")
|
||||
end
|
||||
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
|
||||
end
|
||||
stack[val] = nil
|
||||
return "{" .. table.concat(res, ",") .. "}"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function encode_string(val)
|
||||
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
|
||||
end
|
||||
|
||||
|
||||
local function encode_number(val)
|
||||
-- Check for NaN, -inf and inf
|
||||
if val ~= val or val <= -math.huge or val >= math.huge then
|
||||
error("unexpected number value '" .. tostring(val) .. "'")
|
||||
end
|
||||
return string.format("%.14g", val)
|
||||
end
|
||||
|
||||
|
||||
local type_func_map = {
|
||||
[ "nil" ] = encode_nil,
|
||||
[ "table" ] = encode_table,
|
||||
[ "string" ] = encode_string,
|
||||
[ "number" ] = encode_number,
|
||||
[ "boolean" ] = tostring,
|
||||
}
|
||||
|
||||
|
||||
encode = function(val, stack)
|
||||
local t = type(val)
|
||||
local f = type_func_map[t]
|
||||
if f then
|
||||
return f(val, stack)
|
||||
end
|
||||
error("unexpected type '" .. t .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.encode(val)
|
||||
return ( encode(val) )
|
||||
end
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
-- Decode
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
local parse
|
||||
|
||||
local function create_set(...)
|
||||
local res = {}
|
||||
for i = 1, select("#", ...) do
|
||||
res[ select(i, ...) ] = true
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
local space_chars = create_set(" ", "\t", "\r", "\n")
|
||||
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
|
||||
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
|
||||
local literals = create_set("true", "false", "null")
|
||||
|
||||
local literal_map = {
|
||||
[ "true" ] = true,
|
||||
[ "false" ] = false,
|
||||
[ "null" ] = nil,
|
||||
}
|
||||
|
||||
|
||||
local function next_char(str, idx, set, negate)
|
||||
for i = idx, #str do
|
||||
if set[str:sub(i, i)] ~= negate then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return #str + 1
|
||||
end
|
||||
|
||||
|
||||
local function decode_error(str, idx, msg)
|
||||
local line_count = 1
|
||||
local col_count = 1
|
||||
for i = 1, idx - 1 do
|
||||
col_count = col_count + 1
|
||||
if str:sub(i, i) == "\n" then
|
||||
line_count = line_count + 1
|
||||
col_count = 1
|
||||
end
|
||||
end
|
||||
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
|
||||
end
|
||||
|
||||
|
||||
local function codepoint_to_utf8(n)
|
||||
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
|
||||
local f = math.floor
|
||||
if n <= 0x7f then
|
||||
return string.char(n)
|
||||
elseif n <= 0x7ff then
|
||||
return string.char(f(n / 64) + 192, n % 64 + 128)
|
||||
elseif n <= 0xffff then
|
||||
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
elseif n <= 0x10ffff then
|
||||
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
|
||||
f(n % 4096 / 64) + 128, n % 64 + 128)
|
||||
end
|
||||
error( string.format("invalid unicode codepoint '%x'", n) )
|
||||
end
|
||||
|
||||
|
||||
local function parse_unicode_escape(s)
|
||||
local n1 = tonumber( s:sub(1, 4), 16 )
|
||||
local n2 = tonumber( s:sub(7, 10), 16 )
|
||||
-- Surrogate pair?
|
||||
if n2 then
|
||||
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
|
||||
else
|
||||
return codepoint_to_utf8(n1)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function parse_string(str, i)
|
||||
local res = ""
|
||||
local j = i + 1
|
||||
local k = j
|
||||
|
||||
while j <= #str do
|
||||
local x = str:byte(j)
|
||||
|
||||
if x < 32 then
|
||||
decode_error(str, j, "control character in string")
|
||||
|
||||
elseif x == 92 then -- `\`: Escape
|
||||
res = res .. str:sub(k, j - 1)
|
||||
j = j + 1
|
||||
local c = str:sub(j, j)
|
||||
if c == "u" then
|
||||
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
|
||||
or str:match("^%x%x%x%x", j + 1)
|
||||
or decode_error(str, j - 1, "invalid unicode escape in string")
|
||||
res = res .. parse_unicode_escape(hex)
|
||||
j = j + #hex
|
||||
else
|
||||
if not escape_chars[c] then
|
||||
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
|
||||
end
|
||||
res = res .. escape_char_map_inv[c]
|
||||
end
|
||||
k = j + 1
|
||||
|
||||
elseif x == 34 then -- `"`: End of string
|
||||
res = res .. str:sub(k, j - 1)
|
||||
return res, j + 1
|
||||
end
|
||||
|
||||
j = j + 1
|
||||
end
|
||||
|
||||
decode_error(str, i, "expected closing quote for string")
|
||||
end
|
||||
|
||||
|
||||
local function parse_number(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local s = str:sub(i, x - 1)
|
||||
local n = tonumber(s)
|
||||
if not n then
|
||||
decode_error(str, i, "invalid number '" .. s .. "'")
|
||||
end
|
||||
return n, x
|
||||
end
|
||||
|
||||
|
||||
local function parse_literal(str, i)
|
||||
local x = next_char(str, i, delim_chars)
|
||||
local word = str:sub(i, x - 1)
|
||||
if not literals[word] then
|
||||
decode_error(str, i, "invalid literal '" .. word .. "'")
|
||||
end
|
||||
return literal_map[word], x
|
||||
end
|
||||
|
||||
|
||||
local function parse_array(str, i)
|
||||
local res = {}
|
||||
local n = 1
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local x
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of array?
|
||||
if str:sub(i, i) == "]" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read token
|
||||
x, i = parse(str, i)
|
||||
res[n] = x
|
||||
n = n + 1
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "]" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local function parse_object(str, i)
|
||||
local res = {}
|
||||
i = i + 1
|
||||
while 1 do
|
||||
local key, val
|
||||
i = next_char(str, i, space_chars, true)
|
||||
-- Empty / end of object?
|
||||
if str:sub(i, i) == "}" then
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
-- Read key
|
||||
if str:sub(i, i) ~= '"' then
|
||||
decode_error(str, i, "expected string for key")
|
||||
end
|
||||
key, i = parse(str, i)
|
||||
-- Read ':' delimiter
|
||||
i = next_char(str, i, space_chars, true)
|
||||
if str:sub(i, i) ~= ":" then
|
||||
decode_error(str, i, "expected ':' after key")
|
||||
end
|
||||
i = next_char(str, i + 1, space_chars, true)
|
||||
-- Read value
|
||||
val, i = parse(str, i)
|
||||
-- Set
|
||||
res[key] = val
|
||||
-- Next token
|
||||
i = next_char(str, i, space_chars, true)
|
||||
local chr = str:sub(i, i)
|
||||
i = i + 1
|
||||
if chr == "}" then break end
|
||||
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
|
||||
end
|
||||
return res, i
|
||||
end
|
||||
|
||||
|
||||
local char_func_map = {
|
||||
[ '"' ] = parse_string,
|
||||
[ "0" ] = parse_number,
|
||||
[ "1" ] = parse_number,
|
||||
[ "2" ] = parse_number,
|
||||
[ "3" ] = parse_number,
|
||||
[ "4" ] = parse_number,
|
||||
[ "5" ] = parse_number,
|
||||
[ "6" ] = parse_number,
|
||||
[ "7" ] = parse_number,
|
||||
[ "8" ] = parse_number,
|
||||
[ "9" ] = parse_number,
|
||||
[ "-" ] = parse_number,
|
||||
[ "t" ] = parse_literal,
|
||||
[ "f" ] = parse_literal,
|
||||
[ "n" ] = parse_literal,
|
||||
[ "[" ] = parse_array,
|
||||
[ "{" ] = parse_object,
|
||||
}
|
||||
|
||||
|
||||
parse = function(str, idx)
|
||||
local chr = str:sub(idx, idx)
|
||||
local f = char_func_map[chr]
|
||||
if f then
|
||||
return f(str, idx)
|
||||
end
|
||||
decode_error(str, idx, "unexpected character '" .. chr .. "'")
|
||||
end
|
||||
|
||||
|
||||
function json.decode(str)
|
||||
if type(str) ~= "string" then
|
||||
error("expected argument of type string, got " .. type(str))
|
||||
end
|
||||
local res, idx = parse(str, next_char(str, 1, space_chars, true))
|
||||
idx = next_char(str, idx, space_chars, true)
|
||||
if idx <= #str then
|
||||
decode_error(str, idx, "trailing garbage")
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
|
||||
return json
|
||||
179
Capy64/Assets/Lua/CapyOS/sys/lib/keys.lua
Normal file
179
Capy64/Assets/Lua/CapyOS/sys/lib/keys.lua
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
local keys = {
|
||||
none = 0,
|
||||
back = 8,
|
||||
tab = 9,
|
||||
enter = 13,
|
||||
pause = 19,
|
||||
caps_lock = 20,
|
||||
kana = 21,
|
||||
kanji = 25,
|
||||
escape = 27,
|
||||
ime_convert = 28,
|
||||
ime_no_convert = 29,
|
||||
space = 32,
|
||||
page_up = 33,
|
||||
page_down = 34,
|
||||
["end"] = 35,
|
||||
home = 36,
|
||||
left = 37,
|
||||
up = 38,
|
||||
right = 39,
|
||||
down = 40,
|
||||
select = 41,
|
||||
print = 42,
|
||||
execute = 43,
|
||||
print_screen = 44,
|
||||
insert = 45,
|
||||
delete = 46,
|
||||
help = 47,
|
||||
zero = 48,
|
||||
one = 49,
|
||||
two = 50,
|
||||
three = 51,
|
||||
four = 52,
|
||||
five = 53,
|
||||
six = 54,
|
||||
seven = 55,
|
||||
eight = 56,
|
||||
nine = 57,
|
||||
a = 65,
|
||||
b = 66,
|
||||
c = 67,
|
||||
d = 68,
|
||||
e = 69,
|
||||
f = 70,
|
||||
g = 71,
|
||||
h = 72,
|
||||
i = 73,
|
||||
j = 74,
|
||||
k = 75,
|
||||
l = 76,
|
||||
m = 77,
|
||||
n = 78,
|
||||
o = 79,
|
||||
p = 80,
|
||||
q = 81,
|
||||
r = 82,
|
||||
s = 83,
|
||||
t = 84,
|
||||
u = 85,
|
||||
v = 86,
|
||||
w = 87,
|
||||
x = 88,
|
||||
y = 89,
|
||||
z = 90,
|
||||
left_windows = 91,
|
||||
right_windows = 92,
|
||||
apps = 93,
|
||||
sleep = 95,
|
||||
num_pad0 = 96,
|
||||
num_pad1 = 97,
|
||||
num_pad2 = 98,
|
||||
num_pad3 = 99,
|
||||
num_pad4 = 100,
|
||||
num_pad5 = 101,
|
||||
num_pad6 = 102,
|
||||
num_pad7 = 103,
|
||||
num_pad8 = 104,
|
||||
num_pad9 = 105,
|
||||
multiply = 106,
|
||||
add = 107,
|
||||
separator = 108,
|
||||
subtract = 109,
|
||||
decimal = 110,
|
||||
divide = 111,
|
||||
f1 = 112,
|
||||
f2 = 113,
|
||||
f3 = 114,
|
||||
f4 = 115,
|
||||
f5 = 116,
|
||||
f6 = 117,
|
||||
f7 = 118,
|
||||
f8 = 119,
|
||||
f9 = 120,
|
||||
f10 = 121,
|
||||
f11 = 122,
|
||||
f12 = 123,
|
||||
f13 = 124,
|
||||
f14 = 125,
|
||||
f15 = 126,
|
||||
f16 = 127,
|
||||
f17 = 128,
|
||||
f18 = 129,
|
||||
f19 = 130,
|
||||
f20 = 131,
|
||||
f21 = 132,
|
||||
f22 = 133,
|
||||
f23 = 134,
|
||||
f24 = 135,
|
||||
num_lock = 144,
|
||||
scroll = 145,
|
||||
left_shift = 160,
|
||||
right_shift = 161,
|
||||
left_control = 162,
|
||||
right_control = 163,
|
||||
left_alt = 164,
|
||||
right_alt = 165,
|
||||
browser_back = 166,
|
||||
browser_forward = 167,
|
||||
browser_refresh = 168,
|
||||
browser_stop = 169,
|
||||
browser_search = 170,
|
||||
browser_favorites = 171,
|
||||
browser_home = 172,
|
||||
volume_mute = 173,
|
||||
volume_down = 174,
|
||||
volume_up = 175,
|
||||
media_next_track = 176,
|
||||
media_previous_track = 177,
|
||||
media_stop = 178,
|
||||
media_play_pause = 179,
|
||||
launch_mail = 180,
|
||||
select_media = 181,
|
||||
launch_application1 = 182,
|
||||
launch_application2 = 183,
|
||||
semicolon = 186,
|
||||
plus = 187,
|
||||
comma = 188,
|
||||
minus = 189,
|
||||
period = 190,
|
||||
question = 191,
|
||||
tilde = 192,
|
||||
chat_pad_green = 202,
|
||||
chat_pad_orange = 203,
|
||||
open_brackets = 219,
|
||||
pipe = 220,
|
||||
close_brackets = 221,
|
||||
quotes = 222,
|
||||
oem8 = 223,
|
||||
backslash = 226,
|
||||
process_key = 229,
|
||||
copy = 242,
|
||||
auto = 243,
|
||||
enl_w = 244,
|
||||
attn = 246,
|
||||
crsel = 247,
|
||||
exsel = 248,
|
||||
erase_eof = 249,
|
||||
play = 250,
|
||||
zoom = 251,
|
||||
pa1 = 253,
|
||||
clear = 254,
|
||||
}
|
||||
|
||||
keys.mods = {
|
||||
none = 0,
|
||||
left_shift = 1,
|
||||
right_shift = 2,
|
||||
left_alt = 4,
|
||||
right_alt = 8,
|
||||
left_control = 16,
|
||||
right_control = 32,
|
||||
}
|
||||
|
||||
keys.mods.shift = keys.mods.left_shift | keys.mods.right_shift
|
||||
keys.mods.alt = keys.mods.left_alt | keys.mods.right_alt
|
||||
keys.mods.control = keys.mods.left_control | keys.mods.right_control
|
||||
keys.mods.ctrl = keys.mods.control
|
||||
|
||||
return keys
|
||||
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
|
||||
61
Capy64/Assets/Lua/CapyOS/sys/lib/parallel.lua
Normal file
61
Capy64/Assets/Lua/CapyOS/sys/lib/parallel.lua
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
local expect = require("expect")
|
||||
|
||||
local parallel = {}
|
||||
|
||||
local function contains(array, value)
|
||||
for k, v in pairs(array) do
|
||||
if v == value then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function run(threads, exitOnAny)
|
||||
local alive = #threads
|
||||
local filters = {}
|
||||
local ev = {}
|
||||
while true do
|
||||
for i, thread in pairs(threads) do
|
||||
if not filters[i] or #filters[i] == 0 or contains(filters[i], ev[1]) or ev[1] == "interrupt" then
|
||||
local pars = table.pack(coroutine.resume(thread, table.unpack(ev)))
|
||||
if pars[1] then
|
||||
filters[i] = table.pack(table.unpack(pars, 2))
|
||||
else
|
||||
error(pars[2], 0)
|
||||
end
|
||||
end
|
||||
|
||||
if coroutine.status(thread) == "dead" then
|
||||
alive = alive - 1
|
||||
if exitOnAny or alive <= 0 then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ev = table.pack(coroutine.yield())
|
||||
end
|
||||
end
|
||||
|
||||
function parallel.waitForAll(...)
|
||||
local threads = {}
|
||||
for k, v in ipairs({ ... }) do
|
||||
expect(k, v, "function")
|
||||
table.insert(threads, coroutine.create(v))
|
||||
end
|
||||
|
||||
return run(threads, false)
|
||||
end
|
||||
|
||||
function parallel.waitForAny(...)
|
||||
local threads = {}
|
||||
for k, v in ipairs({ ... }) do
|
||||
expect(k, v, "function")
|
||||
table.insert(threads, coroutine.create(v))
|
||||
end
|
||||
|
||||
return run(threads, true)
|
||||
end
|
||||
|
||||
return parallel
|
||||
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
|
||||
1776
Capy64/Assets/Lua/CapyOS/sys/lib/utfstring.lua
Normal file
1776
Capy64/Assets/Lua/CapyOS/sys/lib/utfstring.lua
Normal file
File diff suppressed because it is too large
Load diff
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
|
||||
BIN
Capy64/Assets/Lua/CapyOS/sys/vendor.bmp
Normal file
BIN
Capy64/Assets/Lua/CapyOS/sys/vendor.bmp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
5
Capy64/Assets/Lua/README.txt
Normal file
5
Capy64/Assets/Lua/README.txt
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
If you are looking for your data directory look in:
|
||||
|
||||
Windows: %APPDATA%\Capy64\data
|
||||
Mac: $HOME/.local/share/Capy64/data
|
||||
Linux: $HOME/.local/share/Capy64/data
|
||||
|
|
@ -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
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||
|
|
@ -19,22 +19,22 @@ local gpu = require("gpu")
|
|||
local fs = require("fs")
|
||||
local machine = require("machine")
|
||||
local audio = require("audio")
|
||||
local http = require("http")
|
||||
local event = require("event")
|
||||
|
||||
|
||||
local INDEX_URL = "https://raw.github.com/Ale32bit/CapyOS/deploy/index.json"
|
||||
local JSON_URL = "https://raw.github.com/Ale32bit/CapyOS/main/lib/json.lua"
|
||||
|
||||
local bootSleep = 2000
|
||||
local bootSleep = 2
|
||||
local bg = 0x0
|
||||
local fg = 0xffffff
|
||||
local setupbg = 0x0608a6
|
||||
local setupfg = 0xffffff
|
||||
local accent = 0xffea00
|
||||
|
||||
term.setForeground(fg)
|
||||
term.setBackground(bg)
|
||||
term.clear()
|
||||
|
||||
term.setSize(53, 20)
|
||||
if term.isResizable() then
|
||||
term.setSize(53, 20)
|
||||
end
|
||||
|
||||
local w, h = term.getSize()
|
||||
|
||||
|
|
@ -56,13 +56,13 @@ local function writeCenter(text)
|
|||
end
|
||||
|
||||
local function drawVendorImage()
|
||||
if not fs.exists("/boot/vendor.bmp") then
|
||||
if not fs.exists("/sys/vendor.bmp") then
|
||||
return
|
||||
end
|
||||
|
||||
local w, h = gpu.getSize()
|
||||
local ok, err = pcall(function()
|
||||
local task<close> = gpu.loadImageAsync("/boot/vendor.bmp")
|
||||
local task<close> = gpu.loadImageAsync("/sys/vendor.bmp")
|
||||
local buffer<close> = task:await()
|
||||
|
||||
local x, y =
|
||||
|
|
@ -95,62 +95,65 @@ local function printError( text )
|
|||
term.setForeground(0xffffff)
|
||||
end
|
||||
|
||||
local function hget(url, headers)
|
||||
local response, err = http.requestAsync(url, nul, headers, {binary = true}):await()
|
||||
|
||||
if not response then
|
||||
return nil, err
|
||||
end
|
||||
|
||||
local content = response.content:read("a")
|
||||
response.content:close()
|
||||
return content, response
|
||||
end
|
||||
|
||||
local function promptKey()
|
||||
print("Press any key to continue")
|
||||
event.pull("key_down")
|
||||
end
|
||||
|
||||
local function installOS()
|
||||
term.clear()
|
||||
term.setPos(1, 1)
|
||||
|
||||
print("Installing CapyOS...")
|
||||
|
||||
local jsonLib, par = hget(JSON_URL)
|
||||
if not jsonLib then
|
||||
printError(par)
|
||||
promptKey()
|
||||
return
|
||||
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
|
||||
|
||||
local json = load(jsonLib)()
|
||||
local indexData, par = hget(INDEX_URL)
|
||||
if not indexData then
|
||||
printError(par)
|
||||
promptKey()
|
||||
return
|
||||
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
|
||||
local index = json.decode(indexData)
|
||||
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}")
|
||||
|
||||
for i, v in ipairs(index) do
|
||||
local dirname = fs.getDir(v.path)
|
||||
if not fs.exists(dirname) then
|
||||
fs.makeDir(dirname)
|
||||
end
|
||||
print("Downloading " .. v.path)
|
||||
local fileContent = hget(v.raw_url)
|
||||
local f = fs.open(v.path, "w")
|
||||
f:write(fileContent)
|
||||
f:close()
|
||||
print("Written to " .. v.path)
|
||||
local _, key, keyname
|
||||
repeat
|
||||
_, key, keyname = event.pull("key_down")
|
||||
until keyname == "enter" or keyname == "escape"
|
||||
end
|
||||
|
||||
local function installDefaultOS()
|
||||
if fs.exists("/sys") then
|
||||
fs.delete("/sys", true)
|
||||
end
|
||||
|
||||
flagInstalled()
|
||||
|
||||
print("CapyOS installed!")
|
||||
promptKey()
|
||||
installOS()
|
||||
alert("Default OS installed!")
|
||||
end
|
||||
|
||||
term.setBlink(false)
|
||||
|
|
@ -163,8 +166,9 @@ local function setupScreen()
|
|||
},
|
||||
{
|
||||
"Install default OS",
|
||||
installOS,
|
||||
installDefaultOS,
|
||||
},
|
||||
{},
|
||||
{
|
||||
"Exit setup",
|
||||
exit,
|
||||
|
|
@ -176,26 +180,40 @@ local function setupScreen()
|
|||
}
|
||||
|
||||
local selection = 1
|
||||
local function redraw()
|
||||
term.setForeground(fg)
|
||||
term.setBackground(bg)
|
||||
local function redraw(noDrawSel)
|
||||
local w, h = term.getSize()
|
||||
term.setForeground(setupfg)
|
||||
term.setBackground(setupbg)
|
||||
term.clear()
|
||||
term.setPos(1,2)
|
||||
writeCenter("Capy64 Setup")
|
||||
|
||||
term.setPos(1,3)
|
||||
|
||||
term.setForeground(accent)
|
||||
|
||||
for k, v in ipairs(options) do
|
||||
local _, y = term.getPos()
|
||||
term.setPos(1, y + 1)
|
||||
term.clearLine()
|
||||
|
||||
if selection == k then
|
||||
writeCenter("[ " .. v[1] .. " ]")
|
||||
else
|
||||
writeCenter(v[1])
|
||||
if #v > 0 then
|
||||
if selection == k and not noDrawSel then
|
||||
writeCenter("[ " .. v[1] .. " ]")
|
||||
else
|
||||
writeCenter(v[1])
|
||||
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
|
||||
|
||||
while true do
|
||||
|
|
@ -203,9 +221,16 @@ local function setupScreen()
|
|||
local ev = { coroutine.yield("key_down") }
|
||||
if ev[3] == "up" then
|
||||
selection = selection - 1
|
||||
if options[selection] and #options[selection] == 0 then
|
||||
selection = selection - 1
|
||||
end
|
||||
elseif ev[3] == "down" then
|
||||
selection = selection + 1
|
||||
if options[selection] and #options[selection] == 0 then
|
||||
selection = selection + 1
|
||||
end
|
||||
elseif ev[3] == "enter" then
|
||||
redraw(true)
|
||||
options[selection][2]()
|
||||
elseif ev[3] == "escape" then
|
||||
exit()
|
||||
|
|
@ -226,7 +251,7 @@ local function bootScreen()
|
|||
term.setPos(1,2)
|
||||
writeCenter("Capy64")
|
||||
term.setPos(1,4)
|
||||
writeCenter("Powered by Capybaras")
|
||||
writeCenter("(c) 2023 AlexDevs")
|
||||
|
||||
term.setPos(1, h - 1)
|
||||
writeCenter("Press F2 to open setup")
|
||||
|
|
@ -245,10 +270,6 @@ end
|
|||
|
||||
audio.beep(1000, 0.2, 0.2, "square")
|
||||
|
||||
if shouldInstallOS() then
|
||||
installOS()
|
||||
end
|
||||
|
||||
bootScreen()
|
||||
|
||||
term.clear()
|
||||
51
Capy64/Assets/Lua/firmware.lua
Normal file
51
Capy64/Assets/Lua/firmware.lua
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
-- This file is part of Capy64 - https://github.com/Capy64/Capy64
|
||||
-- Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
--
|
||||
-- Licensed under the Apache License, Version 2.0 (the "License").
|
||||
-- you may not use this file except in compliance with the License.
|
||||
-- You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing, software
|
||||
-- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
|
||||
local event = require("event")
|
||||
local coroutine = coroutine
|
||||
|
||||
-- Declare event functions
|
||||
function event.pull(...)
|
||||
local ev = table.pack(coroutine.yield(...))
|
||||
if ev[1] == "interrupt" then
|
||||
error("Interrupted", 2)
|
||||
end
|
||||
return table.unpack(ev)
|
||||
end
|
||||
|
||||
function event.pullRaw(...)
|
||||
return coroutine.yield(...)
|
||||
end
|
||||
|
||||
-- Set task awaiter
|
||||
|
||||
local function awaiter(task)
|
||||
local status = task:getStatus()
|
||||
local uuid = task:getID()
|
||||
if status == "running" then
|
||||
local _, taskId, result, err
|
||||
repeat
|
||||
_, taskId, result, err = event.pull("task_finish")
|
||||
until taskId == uuid
|
||||
return result, err
|
||||
elseif status == "succeeded" then
|
||||
return task:getResult(), nil
|
||||
elseif status == "failed" then
|
||||
return nil, task:getError()
|
||||
end
|
||||
end
|
||||
|
||||
-- Second argument freezes the awaiter function, so it cannot be modified
|
||||
event.setAwaiter(awaiter, true)
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"EngineMode": 0,
|
||||
"SafeMode": false,
|
||||
"Window": {
|
||||
"Scale": 2
|
||||
},
|
||||
|
|
|
|||
|
|
@ -21,14 +21,15 @@ using Capy64.Integrations;
|
|||
using Capy64.PluginManager;
|
||||
using Capy64.Runtime;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using MonoGame.Extended;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Capy64.Utils;
|
||||
|
||||
|
|
@ -40,10 +41,9 @@ public enum EngineMode
|
|||
Free
|
||||
}
|
||||
|
||||
|
||||
public class Capy64 : Game, IGame
|
||||
public class Capy64 : Game
|
||||
{
|
||||
public const string Version = "0.0.10-alpha";
|
||||
public const string Version = "1.1.2-beta";
|
||||
|
||||
public static class DefaultParameters
|
||||
{
|
||||
|
|
@ -51,12 +51,14 @@ public class Capy64 : Game, IGame
|
|||
public const int Height = 240;
|
||||
public const float Scale = 2f;
|
||||
public const float BorderMultiplier = 1.5f;
|
||||
public readonly static EngineMode EngineMode = EngineMode.Classic;
|
||||
public static readonly EngineMode EngineMode = EngineMode.Classic;
|
||||
|
||||
public const int ClassicTickrate = 20;
|
||||
public const int ClassicTickrate = 30;
|
||||
public const int FreeTickrate = 60;
|
||||
}
|
||||
|
||||
public static readonly string AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
public static readonly string AssetsPath = Path.Combine(AssemblyPath, "Assets");
|
||||
|
||||
public static string AppDataPath
|
||||
{
|
||||
|
|
@ -64,12 +66,15 @@ public class Capy64 : Game, IGame
|
|||
{
|
||||
string baseDir =
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData, Environment.SpecialFolderOption.Create) :
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create) :
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ?
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData, Environment.SpecialFolderOption.Create) :
|
||||
"./";
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData,
|
||||
Environment.SpecialFolderOption.Create) :
|
||||
"./";
|
||||
|
||||
return Path.Combine(baseDir, "Capy64");
|
||||
}
|
||||
|
|
@ -88,8 +93,11 @@ public class Capy64 : Game, IGame
|
|||
public LuaState LuaRuntime { get; set; }
|
||||
public Eventing.EventEmitter EventEmitter { get; private set; }
|
||||
public DiscordIntegration Discord { get; set; }
|
||||
public int TickRate => tickrate;
|
||||
public IConfiguration Configuration { get; private set; }
|
||||
|
||||
public Color BorderColor { get; set; } = Color.Black;
|
||||
|
||||
public Borders Borders = new()
|
||||
{
|
||||
Top = 0,
|
||||
|
|
@ -97,22 +105,23 @@ public class Capy64 : Game, IGame
|
|||
Left = 0,
|
||||
Right = 0,
|
||||
};
|
||||
|
||||
public SpriteBatch SpriteBatch;
|
||||
|
||||
|
||||
private readonly InputManager _inputManager;
|
||||
private RenderTarget2D renderTarget;
|
||||
private readonly GraphicsDeviceManager _graphics;
|
||||
private IServiceProvider _serviceProvider;
|
||||
private ulong _totalTicks = 0;
|
||||
private ulong tickrate = 0;
|
||||
private int tickrate = 0;
|
||||
private int everyTick => 60 / tickrate;
|
||||
|
||||
public Capy64()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
_graphics = new GraphicsDeviceManager(this);
|
||||
Content.RootDirectory = "Content";
|
||||
//Content.RootDirectory = "Content";
|
||||
IsMouseVisible = true;
|
||||
|
||||
EventEmitter = new();
|
||||
|
|
@ -121,11 +130,6 @@ public class Capy64 : Game, IGame
|
|||
Drawing = new();
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceProvider serviceProvider)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public void SetEngineMode(EngineMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
|
|
@ -144,6 +148,9 @@ public class Capy64 : Game, IGame
|
|||
Window.AllowUserResizing = true;
|
||||
break;
|
||||
}
|
||||
|
||||
EngineMode = mode;
|
||||
|
||||
UpdateSize(true);
|
||||
}
|
||||
|
||||
|
|
@ -173,6 +180,12 @@ public class Capy64 : Game, IGame
|
|||
|
||||
private void OnWindowSizeChange(object sender, EventArgs e)
|
||||
{
|
||||
if (EngineMode == EngineMode.Classic)
|
||||
{
|
||||
UpdateSize(true);
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = Window.ClientBounds;
|
||||
|
||||
Width = (int)(bounds.Width / Scale);
|
||||
|
|
@ -196,7 +209,6 @@ public class Capy64 : Game, IGame
|
|||
ResetBorder();
|
||||
UpdateSize();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void ResetBorder()
|
||||
|
|
@ -213,11 +225,26 @@ public class Capy64 : Game, IGame
|
|||
|
||||
protected override void Initialize()
|
||||
{
|
||||
var configuration = _serviceProvider.GetService<IConfiguration>();
|
||||
var configBuilder = new ConfigurationBuilder();
|
||||
|
||||
var settingsPath = Path.Combine(AppDataPath, "settings.json");
|
||||
if (!Directory.Exists(AppDataPath))
|
||||
{
|
||||
Directory.CreateDirectory(AppDataPath);
|
||||
}
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
File.Copy(Path.Combine(AssetsPath, "default.json"), settingsPath);
|
||||
}
|
||||
|
||||
configBuilder.AddJsonFile(Path.Combine(AssetsPath, "default.json"), false);
|
||||
configBuilder.AddJsonFile(settingsPath, false);
|
||||
|
||||
Configuration = configBuilder.Build();
|
||||
|
||||
Window.Title = "Capy64 " + Version;
|
||||
|
||||
Scale = configuration.GetValue("Window:Scale", DefaultParameters.Scale);
|
||||
Scale = Configuration.GetValue("Window:Scale", DefaultParameters.Scale);
|
||||
|
||||
ResetBorder();
|
||||
UpdateSize();
|
||||
|
|
@ -227,12 +254,14 @@ public class Capy64 : Game, IGame
|
|||
|
||||
InactiveSleepTime = new TimeSpan(0);
|
||||
|
||||
SetEngineMode(configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
|
||||
SetEngineMode(Configuration.GetValue<EngineMode>("EngineMode", DefaultParameters.EngineMode));
|
||||
|
||||
Audio = new Audio();
|
||||
|
||||
NativePlugins = GetNativePlugins();
|
||||
Plugins = PluginLoader.LoadAllPlugins("plugins", _serviceProvider);
|
||||
var safeMode = Configuration.GetValue("SafeMode", false);
|
||||
if (!safeMode)
|
||||
Plugins = PluginLoader.LoadAllPlugins(Path.Combine(AppDataPath, "plugins"));
|
||||
|
||||
EventEmitter.RaiseInit();
|
||||
|
||||
|
|
@ -250,9 +279,10 @@ public class Capy64 : Game, IGame
|
|||
|
||||
foreach (var type in types)
|
||||
{
|
||||
var instance = (IComponent)ActivatorUtilities.CreateInstance(_serviceProvider, type)!;
|
||||
var instance = (IComponent)Activator.CreateInstance(type, this);
|
||||
plugins.Add(instance);
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
|
@ -273,7 +303,7 @@ public class Capy64 : Game, IGame
|
|||
{
|
||||
GameTime = gameTime,
|
||||
TotalTicks = _totalTicks,
|
||||
IsActiveTick = _totalTicks % (60 / tickrate) == 0,
|
||||
IsActiveTick = (int)_totalTicks % everyTick == 0,
|
||||
});
|
||||
|
||||
Drawing.End();
|
||||
|
|
@ -289,9 +319,11 @@ public class Capy64 : Game, IGame
|
|||
GraphicsDevice.Clear(BorderColor);
|
||||
|
||||
SpriteBatch.DrawRectangle(renderTarget.Bounds.Location.ToVector2() + new Vector2(Borders.Left, Borders.Top),
|
||||
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black, Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
|
||||
new Size2(renderTarget.Bounds.Width * Scale, renderTarget.Bounds.Height * Scale), Color.Black,
|
||||
Math.Min(renderTarget.Bounds.Width, renderTarget.Bounds.Height), 0);
|
||||
|
||||
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale, SpriteEffects.None, 0);
|
||||
SpriteBatch.Draw(renderTarget, new(Borders.Left, Borders.Top), null, Color.White, 0f, Vector2.Zero, Scale,
|
||||
SpriteEffects.None, 0);
|
||||
|
||||
EventEmitter.RaiseOverlay(new()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<RollForward>Major</RollForward>
|
||||
<PublishReadyToRun>false</PublishReadyToRun>
|
||||
<TieredCompilation>false</TieredCompilation>
|
||||
|
|
@ -26,9 +26,9 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Assets\Lua/**">
|
||||
<Content Include="Assets\Lua\**">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE">
|
||||
|
|
@ -37,26 +37,22 @@
|
|||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.2.8" />
|
||||
<PackageReference Include="KeraLua" Version="1.3.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageReference Include="FontStashSharp.MonoGame" Version="1.3.6" />
|
||||
<PackageReference Include="KeraLua" Version="1.4.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
<PackageReference Include="MonoGame.Extended.Graphics" Version="3.8.0" />
|
||||
<PackageReference Include="MonoGame.Framework.DesktopGL" Version="3.8.1.303" />
|
||||
<PackageReference Include="MonoGame.Content.Builder.Task" Version="3.8.1.303" />
|
||||
<PackageReference Include="System.ComponentModel.Composition" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="Assets\bios.lua">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<EditorConfigFiles Remove="C:\Users\Alex\source\repos\Capy64\Capy64\Capy64\.editorconfig" />
|
||||
</ItemGroup>
|
||||
<Target Name="RestoreDotnetTools" BeforeTargets="Restore">
|
||||
<Message Text="Restoring dotnet tools" Importance="High" />
|
||||
<Exec Command="dotnet tool restore" />
|
||||
</Target>
|
||||
<ItemGroup>
|
||||
<EditorConfigFiles Remove="C:\Users\Alex\source\repos\Capy64\Capy64\Capy64\.editorconfig" />
|
||||
<Folder Include="Assets\Lua\" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(OS)' == 'Windows_NT' ">
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
#----------------------------- Global Properties ----------------------------#
|
||||
|
||||
/outputDir:bin/$(Platform)
|
||||
/intermediateDir:obj/$(Platform)
|
||||
/platform:DesktopGL
|
||||
/config:
|
||||
/profile:Reach
|
||||
/compress:False
|
||||
|
||||
#-------------------------------- References --------------------------------#
|
||||
|
||||
|
||||
#---------------------------------- Content ---------------------------------#
|
||||
|
||||
|
|
@ -29,12 +29,12 @@ public class Audio : IDisposable
|
|||
Noise
|
||||
}
|
||||
|
||||
public const int SampleRate = 16000;
|
||||
public const int SampleRate = 24000;
|
||||
public const int HQSampleRate = 48000;
|
||||
public const AudioChannels AudioChannel = AudioChannels.Mono;
|
||||
public const int ChannelsCount = 8;
|
||||
public readonly DynamicSoundEffectInstance[] Channels = new DynamicSoundEffectInstance[ChannelsCount];
|
||||
private bool[] freeChannels = new bool[ChannelsCount];
|
||||
private readonly bool[] freeChannels = new bool[ChannelsCount];
|
||||
|
||||
public readonly DynamicSoundEffectInstance HQChannel = new(HQSampleRate, AudioChannel);
|
||||
|
||||
|
|
@ -126,6 +126,7 @@ public class Audio : IDisposable
|
|||
|
||||
public TimeSpan SubmitHQ(byte[] buffer)
|
||||
{
|
||||
|
||||
HQChannel.SubmitBuffer(buffer);
|
||||
return HQChannel.GetSampleDuration(buffer.Length);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class Drawing : IDisposable
|
|||
private Texture2D _whitePixel;
|
||||
private RenderTarget2D _canvas;
|
||||
private bool _isDrawing;
|
||||
private HashSet<Texture2D> _disposeTextures = new();
|
||||
private readonly HashSet<Texture2D> _disposeTextures = new();
|
||||
public RenderTarget2D Canvas
|
||||
{
|
||||
get => _canvas;
|
||||
|
|
@ -54,7 +54,7 @@ public class Drawing : IDisposable
|
|||
public Drawing()
|
||||
{
|
||||
_fontSystem = new FontSystem();
|
||||
_fontSystem.AddFont(File.ReadAllBytes(@"Assets/font.ttf"));
|
||||
_fontSystem.AddFont(File.ReadAllBytes(Path.Combine(Capy64.AssetsPath, "font.ttf")));
|
||||
}
|
||||
|
||||
public void Begin()
|
||||
|
|
@ -201,7 +201,7 @@ public class Drawing : IDisposable
|
|||
_spriteBatch.Draw(_whitePixel, position3, null, color, rotation, Vector2.Zero, scale, SpriteEffects.None, layerDepth);
|
||||
}
|
||||
|
||||
public void DrawBuffer(uint[] buffer, Rectangle rect, Rectangle? source = null, Color? color = null, float rotation = 0f, Vector2? origin = null, float scale = 1f, SpriteEffects spriteEffects = 0)
|
||||
public void DrawBuffer(uint[] buffer, Rectangle rect, Rectangle? source = null, Color? color = null, float rotation = 0f, Vector2? origin = null, Vector2? scale = null, SpriteEffects spriteEffects = 0)
|
||||
{
|
||||
var texture = new Texture2D(_graphicsDevice, rect.Width, rect.Height, false, SurfaceFormat.Color);
|
||||
texture.SetData(buffer);
|
||||
|
|
@ -213,7 +213,7 @@ public class Drawing : IDisposable
|
|||
color ?? Color.White, // Color
|
||||
rotation, // Rotation
|
||||
origin ?? Vector2.Zero, // Origin
|
||||
scale, // Scale
|
||||
scale ?? Vector2.One, // Scale
|
||||
spriteEffects, // Flip effects
|
||||
0f // layer depth
|
||||
);
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ public class InputManager
|
|||
Ctrl = LCtrl | RCtrl,
|
||||
}
|
||||
|
||||
private static Keys[] IgnoredTextInputKeys =
|
||||
private static readonly Keys[] IgnoredTextInputKeys =
|
||||
{
|
||||
Keys.Enter,
|
||||
Keys.Back,
|
||||
|
|
@ -74,7 +74,7 @@ public class InputManager
|
|||
};
|
||||
|
||||
public Texture2D Texture { get; set; }
|
||||
public float WindowScale => Capy64.Instance.Scale;
|
||||
public static float WindowScale => Capy64.Instance.Scale;
|
||||
public const int MouseScrollDelta = 120;
|
||||
|
||||
private Point mousePosition;
|
||||
|
|
@ -82,7 +82,7 @@ public class InputManager
|
|||
private int hMouseScroll;
|
||||
|
||||
private Modifiers keyboardMods = 0;
|
||||
private HashSet<Keys> pressedKeys = new();
|
||||
private readonly HashSet<Keys> pressedKeys = new();
|
||||
|
||||
private readonly Game _game;
|
||||
private readonly EventEmitter _eventEmitter;
|
||||
|
|
|
|||
|
|
@ -105,7 +105,7 @@ public partial class SDL2
|
|||
}
|
||||
char* chars = stackalloc char[len];
|
||||
int strLen = System.Text.Encoding.UTF8.GetChars((byte*)s, len, chars, len);
|
||||
string result = new string(chars, 0, strLen);
|
||||
string result = new(chars, 0, strLen);
|
||||
#endif
|
||||
|
||||
/* Some SDL functions will malloc, we have to free! */
|
||||
|
|
|
|||
|
|
@ -1,50 +0,0 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using Capy64.Integrations;
|
||||
using Capy64.Runtime;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Capy64;
|
||||
|
||||
public interface IGame
|
||||
{
|
||||
Capy64 Game { get; }
|
||||
EngineMode EngineMode { get; }
|
||||
IList<IComponent> NativePlugins { get; }
|
||||
IList<IComponent> Plugins { get; }
|
||||
GameWindow Window { get; }
|
||||
Drawing Drawing { get; }
|
||||
Audio Audio { get; }
|
||||
LuaState LuaRuntime { get; set; }
|
||||
Eventing.EventEmitter EventEmitter { get; }
|
||||
void ConfigureServices(IServiceProvider serviceProvider);
|
||||
|
||||
int Width { get; set; }
|
||||
int Height { get; set; }
|
||||
float Scale { get; set; }
|
||||
void UpdateSize(bool resize = true);
|
||||
|
||||
event EventHandler<EventArgs> Exiting;
|
||||
void Run();
|
||||
void Exit();
|
||||
|
||||
// Integrations
|
||||
DiscordIntegration Discord { get; }
|
||||
}
|
||||
|
|
@ -25,29 +25,35 @@ namespace Capy64.Integrations;
|
|||
public class DiscordIntegration : IComponent
|
||||
{
|
||||
public DiscordRpcClient Client { get; private set; }
|
||||
public readonly bool Enabled;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public DiscordIntegration(IConfiguration configuration)
|
||||
public DiscordIntegration(Capy64 game)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_configuration = game.Configuration;
|
||||
|
||||
var discordConfig = _configuration.GetSection("Integrations:Discord");
|
||||
Client = new(discordConfig["ApplicationId"]);
|
||||
Enabled = discordConfig.GetValue("Enable", false);
|
||||
|
||||
Client.Logger = new ConsoleLogger() { Level = DiscordRPC.Logging.LogLevel.Warning };
|
||||
Client = new(discordConfig["ApplicationId"])
|
||||
{
|
||||
Logger = new ConsoleLogger() { Level = LogLevel.Warning }
|
||||
};
|
||||
|
||||
Client.OnReady += OnReady;
|
||||
|
||||
Capy64.Instance.Discord = this;
|
||||
|
||||
if (discordConfig.GetValue("Enable", false))
|
||||
{
|
||||
if (Enabled)
|
||||
Client.Initialize();
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
public void SetPresence(string details, string? state = null)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
Client.SetPresence(new RichPresence()
|
||||
{
|
||||
Details = details,
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Capy64.PluginManager
|
|||
{
|
||||
class PluginLoadContext : AssemblyLoadContext
|
||||
{
|
||||
private AssemblyDependencyResolver _resolver;
|
||||
private readonly AssemblyDependencyResolver _resolver;
|
||||
|
||||
public PluginLoadContext(string pluginPath)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
|
@ -33,7 +32,7 @@ internal class PluginLoader
|
|||
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))
|
||||
Directory.CreateDirectory(pluginsPath);
|
||||
|
|
@ -47,7 +46,7 @@ internal class PluginLoader
|
|||
{
|
||||
if (typeof(IComponent).IsAssignableFrom(type))
|
||||
{
|
||||
IComponent result = ActivatorUtilities.CreateInstance(provider, type) as IComponent;
|
||||
IComponent result = Activator.CreateInstance(type, Capy64.Instance) as IComponent;
|
||||
plugins.Add(result);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,34 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System.IO;
|
||||
|
||||
using var game = new Capy64.Capy64();
|
||||
using IHost host = Host.CreateDefaultBuilder(args)
|
||||
.ConfigureAppConfiguration((context, c) =>
|
||||
{
|
||||
var settingsPath = Path.Combine(Capy64.Capy64.AppDataPath, "settings.json");
|
||||
if (!Directory.Exists(Capy64.Capy64.AppDataPath))
|
||||
{
|
||||
Directory.CreateDirectory(Capy64.Capy64.AppDataPath);
|
||||
}
|
||||
if (!File.Exists(settingsPath))
|
||||
{
|
||||
File.Copy("Assets/default.json", settingsPath);
|
||||
}
|
||||
|
||||
c.AddJsonFile("Assets/default.json", false);
|
||||
c.AddJsonFile(settingsPath, false);
|
||||
})
|
||||
.ConfigureServices((hostContext, services) =>
|
||||
{
|
||||
services.AddSingleton<IGame>(game);
|
||||
services.AddHostedService<Worker>();
|
||||
})
|
||||
.Build();
|
||||
|
||||
await host.RunAsync();
|
||||
game.Run();
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ namespace Capy64.Runtime;
|
|||
public class Constants
|
||||
{
|
||||
public const int MULTRET = -1;
|
||||
public const int MINSTACK = 20;
|
||||
public const int MAXLENNUM = 200;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.Core;
|
||||
using Capy64.Eventing.Events;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
|
|
@ -24,8 +23,8 @@ namespace Capy64.Runtime;
|
|||
|
||||
internal class EventEmitter
|
||||
{
|
||||
private Eventing.EventEmitter _eventEmitter;
|
||||
private LuaState _runtime;
|
||||
private readonly Eventing.EventEmitter _eventEmitter;
|
||||
private readonly LuaState _runtime;
|
||||
private const int rebootDelay = 30;
|
||||
private int heldReboot = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -97,17 +97,4 @@ public static class Utils
|
|||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
[Obsolete("This method does not work as intended and requires more research")]
|
||||
public static void PushManagedObject<T>(this Lua L, T obj)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
var members = type.GetMembers().Where(m => m.MemberType == MemberTypes.Method);
|
||||
L.CreateTable(0, members.Count());
|
||||
foreach (var m in members)
|
||||
{
|
||||
L.PushCFunction(L => (int)type.InvokeMember(m.Name, BindingFlags.InvokeMethod, null, obj, new object[] { L }));
|
||||
L.SetField(-2, m.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,18 +21,18 @@ using static Capy64.Core.Audio;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class Audio : IComponent
|
||||
public class AudioLib : IComponent
|
||||
{
|
||||
private const int queueLimit = 8;
|
||||
|
||||
private static IGame _game;
|
||||
public Audio(IGame game)
|
||||
private static Capy64 _game;
|
||||
public AudioLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
_game.EventEmitter.OnClose += OnClose;
|
||||
}
|
||||
|
||||
private static LuaRegister[] AudioLib = new LuaRegister[]
|
||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -85,10 +85,18 @@ public class Audio : IComponent
|
|||
private static int OpenLib(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
L.NewLib(AudioLib);
|
||||
L.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert 8bit PCM to 16bit PCM
|
||||
private static void PCMTo16bit(Span<byte> buffer, int i, byte value)
|
||||
{
|
||||
var value16bit = value / sbyte.MaxValue * short.MaxValue;
|
||||
buffer[2 * i - 2] = (byte)(value16bit & 0xff);
|
||||
buffer[2 * i - 1] = (byte)(value16bit >> 8);
|
||||
}
|
||||
|
||||
private static int L_Play(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -97,18 +105,24 @@ public class Audio : IComponent
|
|||
|
||||
if (L.IsString(1))
|
||||
{
|
||||
buffer = L.CheckBuffer(1);
|
||||
var inputBuffer = L.CheckBuffer(1);
|
||||
buffer = new byte[inputBuffer.Length * 2];
|
||||
var span = new Span<byte>(buffer);
|
||||
for (int i = 1; i < inputBuffer.Length; i++)
|
||||
{
|
||||
PCMTo16bit(span, i, inputBuffer[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
L.CheckType(1, LuaType.Table);
|
||||
var len = L.RawLen(1);
|
||||
buffer = new byte[len];
|
||||
buffer = new byte[len * 2];
|
||||
var span = new Span<byte>(buffer);
|
||||
for (int i = 1; i <= len; i++)
|
||||
{
|
||||
L.GetInteger(1, i);
|
||||
var value = L.CheckInteger(-1);
|
||||
buffer[i - 1] = (byte)value;
|
||||
PCMTo16bit(span, i, (byte)L.CheckNumber(-1));
|
||||
L.Pop(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -133,7 +147,12 @@ public class Audio : IComponent
|
|||
L.Error(ex.Message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
var playTime = _game.Audio.HQChannel.GetSampleDuration(buffer.Length);
|
||||
|
||||
L.PushBoolean(true);
|
||||
L.PushNumber(playTime.TotalSeconds);
|
||||
|
||||
return 2;
|
||||
}
|
||||
|
||||
private static int L_Beep(IntPtr state)
|
||||
|
|
@ -20,7 +20,7 @@ using System;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class Event : IComponent
|
||||
public class EventLib : IComponent
|
||||
{
|
||||
private const int MaxPushQueue = 64;
|
||||
private static int PushQueue = 0;
|
||||
|
|
@ -29,24 +29,14 @@ public class Event : IComponent
|
|||
|
||||
private static bool FrozenTaskAwaiter = false;
|
||||
|
||||
private static IGame _game;
|
||||
public Event(IGame game)
|
||||
private static Capy64 _game;
|
||||
public EventLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
private static LuaRegister[] EventLib = new LuaRegister[]
|
||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
name = "pull",
|
||||
function = L_Pull,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "pullRaw",
|
||||
function = L_PullRaw
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "push",
|
||||
|
|
@ -88,54 +78,10 @@ public class Event : IComponent
|
|||
private static int OpenLib(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
L.NewLib(EventLib);
|
||||
L.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int LK_Pull(IntPtr state, int status, IntPtr ctx)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
if (L.ToString(1) == "interrupt")
|
||||
{
|
||||
L.Error("interrupt");
|
||||
}
|
||||
|
||||
var nargs = L.GetTop();
|
||||
|
||||
return nargs;
|
||||
}
|
||||
|
||||
public static int L_Pull(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var nargs = L.GetTop();
|
||||
for (int i = 1; i <= nargs; i++)
|
||||
{
|
||||
L.CheckString(i);
|
||||
}
|
||||
|
||||
L.YieldK(nargs, 0, LK_Pull);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int L_PullRaw(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var nargs = L.GetTop();
|
||||
for (int i = 1; i <= nargs; i++)
|
||||
{
|
||||
L.CheckString(i);
|
||||
}
|
||||
|
||||
L.Yield(nargs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int L_Push(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -192,11 +138,15 @@ public class Event : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = TaskMeta.CheckTask(L, false);
|
||||
L.CheckType(1, LuaType.Table);
|
||||
|
||||
L.GetField(1, "task");
|
||||
|
||||
var task = TaskMeta.CheckTask(L, -1, false);
|
||||
L.CheckAny(2);
|
||||
L.ArgumentCheck(!L.IsNil(2), 2, "value cannot be nil");
|
||||
|
||||
if(!task.UserTask)
|
||||
if (!task.UserTask)
|
||||
{
|
||||
L.Error("attempt to fulfill machine task");
|
||||
}
|
||||
|
|
@ -218,7 +168,11 @@ public class Event : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = TaskMeta.CheckTask(L, false);
|
||||
L.CheckType(1, LuaType.Table);
|
||||
|
||||
L.GetField(1, "task");
|
||||
|
||||
var task = TaskMeta.CheckTask(L, -1, false);
|
||||
var error = L.CheckString(2);
|
||||
|
||||
if (!task.UserTask)
|
||||
|
|
@ -226,7 +180,7 @@ public class Event : IComponent
|
|||
L.Error("attempt to reject machine task");
|
||||
}
|
||||
|
||||
if(task.Status != TaskMeta.TaskStatus.Running)
|
||||
if (task.Status != TaskMeta.TaskStatus.Running)
|
||||
{
|
||||
L.Error("attempt to reject a finished task");
|
||||
}
|
||||
|
|
@ -24,11 +24,11 @@ using System.Linq;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class FileSystem : IComponent
|
||||
public class FileSystemLib : IComponent
|
||||
{
|
||||
public static string DataPath = Path.Combine(Capy64.AppDataPath, "data");
|
||||
|
||||
public FileSystem()
|
||||
public FileSystemLib()
|
||||
{
|
||||
if (!Directory.Exists(DataPath))
|
||||
{
|
||||
|
|
@ -37,7 +37,7 @@ public class FileSystem : IComponent
|
|||
}
|
||||
|
||||
// functions to add to the library, always end libraries with null
|
||||
private LuaRegister[] FsLib = new LuaRegister[] {
|
||||
private readonly LuaRegister[] Library = new LuaRegister[] {
|
||||
new()
|
||||
{
|
||||
name = "list",
|
||||
|
|
@ -111,6 +111,8 @@ public class FileSystem : IComponent
|
|||
new(), // NULL
|
||||
};
|
||||
|
||||
public FileSystemLib(Capy64 _) { }
|
||||
|
||||
public void LuaInit(Lua state)
|
||||
{
|
||||
// Add "fs" library to lua, not global (uses require())
|
||||
|
|
@ -120,7 +122,7 @@ public class FileSystem : IComponent
|
|||
private int Open(IntPtr state)
|
||||
{
|
||||
var l = Lua.FromIntPtr(state);
|
||||
l.NewLib(FsLib);
|
||||
l.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -132,6 +134,9 @@ public class FileSystem : IComponent
|
|||
// Get drive root (C:\ for Windows, / for *nix)
|
||||
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
|
||||
// Relative paths are resolved here (es. ../ and ./)
|
||||
var absolutePath = Path.GetFullPath(path, rootPath);
|
||||
|
|
@ -426,19 +431,26 @@ public class FileSystem : IComponent
|
|||
L.Error("path not found");
|
||||
}
|
||||
|
||||
var attr = File.GetAttributes(path);
|
||||
if (attr.HasFlag(FileAttributes.Directory))
|
||||
try
|
||||
{
|
||||
if (!recursive && Directory.GetFileSystemEntries(path).Any())
|
||||
var attr = File.GetAttributes(path);
|
||||
if (attr.HasFlag(FileAttributes.Directory))
|
||||
{
|
||||
L.Error("directory not empty");
|
||||
return 0;
|
||||
if (!recursive && Directory.GetFileSystemEntries(path).Any())
|
||||
{
|
||||
L.Error("directory not empty");
|
||||
return 0;
|
||||
}
|
||||
Directory.Delete(path, recursive);
|
||||
}
|
||||
else
|
||||
{
|
||||
File.Delete(path);
|
||||
}
|
||||
Directory.Delete(path, recursive);
|
||||
}
|
||||
else
|
||||
catch (Exception e)
|
||||
{
|
||||
File.Delete(path);
|
||||
return L.Error(e.Message);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -14,30 +14,27 @@
|
|||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using Capy64.Runtime.Objects;
|
||||
using KeraLua;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class GPU : IComponent
|
||||
public class GPULib : IComponent
|
||||
{
|
||||
|
||||
private static IGame _game;
|
||||
public GPU(IGame game)
|
||||
private static Capy64 _game;
|
||||
public GPULib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
private LuaRegister[] gpuLib = new LuaRegister[] {
|
||||
private readonly LuaRegister[] Library = new LuaRegister[] {
|
||||
new()
|
||||
{
|
||||
name = "getSize",
|
||||
|
|
@ -49,6 +46,11 @@ public class GPU : IComponent
|
|||
function = L_SetSize,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "isResizable",
|
||||
function = L_IsResizable,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getPixel",
|
||||
function = L_GetPixel,
|
||||
|
|
@ -119,6 +121,11 @@ public class GPU : IComponent
|
|||
function = L_NewBuffer,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "bufferFrom",
|
||||
function = L_BufferFrom,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "drawBuffer",
|
||||
function = L_DrawBuffer,
|
||||
|
|
@ -144,14 +151,12 @@ public class GPU : IComponent
|
|||
public int OpenLib(IntPtr state)
|
||||
{
|
||||
var l = Lua.FromIntPtr(state);
|
||||
l.NewLib(gpuLib);
|
||||
l.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
public static void GetColor(uint c, out byte r, out byte g, out byte b)
|
||||
{
|
||||
/*if (_game.EngineMode == EngineMode.Classic)
|
||||
c = ColorPalette.GetColor(c);*/
|
||||
Utils.UnpackRGB(c, out r, out g, out b);
|
||||
}
|
||||
|
||||
|
|
@ -171,8 +176,7 @@ public class GPU : IComponent
|
|||
|
||||
if (_game.EngineMode == EngineMode.Classic)
|
||||
{
|
||||
L.PushBoolean(false);
|
||||
return 1;
|
||||
return L.Error("Screen is not resizable");
|
||||
}
|
||||
|
||||
var w = L.CheckInteger(1);
|
||||
|
|
@ -188,6 +192,15 @@ public class GPU : IComponent
|
|||
return 1;
|
||||
}
|
||||
|
||||
private static int L_IsResizable(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
L.PushBoolean(_game.EngineMode != EngineMode.Classic);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetPixel(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -287,7 +300,7 @@ public class GPU : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var x1 = (int)L.CheckNumber(1);
|
||||
var x1 = (int)L.CheckNumber(1) - 1; // do not question, please
|
||||
var y1 = (int)L.CheckNumber(2);
|
||||
var x2 = (int)L.CheckNumber(3);
|
||||
var y2 = (int)L.CheckNumber(4);
|
||||
|
|
@ -460,6 +473,57 @@ public class GPU : IComponent
|
|||
return 1;
|
||||
}
|
||||
|
||||
private static int L_BufferFrom(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
L.CheckType(1, LuaType.Table);
|
||||
var width = (int)L.CheckInteger(2);
|
||||
var height = (int)L.CheckInteger(3);
|
||||
|
||||
if (width <= 0)
|
||||
{
|
||||
return L.ArgumentError(2, "width must be a positive integer.");
|
||||
}
|
||||
|
||||
if (height <= 0)
|
||||
{
|
||||
return L.ArgumentError(3, "height must be a positive integer.");
|
||||
}
|
||||
|
||||
var buffer = new uint[width * height];
|
||||
|
||||
var tableSize = L.RawLen(1);
|
||||
L.ArgumentCheck(tableSize == buffer.Length, 1, "table length does not match buffer size");
|
||||
|
||||
for (int i = 1; i <= tableSize; i++)
|
||||
{
|
||||
L.GetInteger(1, i);
|
||||
var value = (uint)L.CheckInteger(-1);
|
||||
L.Pop(1);
|
||||
// ARGB to ABGR
|
||||
value =
|
||||
(value & 0xFF_00_00_00U) |
|
||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_00_00_FFU) << 16); // move B
|
||||
|
||||
buffer[i - 1] = value;
|
||||
}
|
||||
|
||||
var gpuBuffer = new GPUBufferMeta.GPUBuffer
|
||||
{
|
||||
Buffer = buffer,
|
||||
Width = width,
|
||||
Height = height,
|
||||
};
|
||||
|
||||
ObjectManager.PushObject(L, gpuBuffer);
|
||||
L.SetMetaTable(GPUBufferMeta.ObjectType);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_DrawBuffer(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -469,13 +533,107 @@ public class GPU : IComponent
|
|||
var x = (int)L.CheckInteger(2) - 1;
|
||||
var y = (int)L.CheckInteger(3) - 1;
|
||||
|
||||
Rectangle? source = null;
|
||||
Color color = Color.White;
|
||||
float rotation = 0;
|
||||
Vector2 origin = Vector2.Zero;
|
||||
Vector2 scale = Vector2.One;
|
||||
SpriteEffects effects = SpriteEffects.None;
|
||||
|
||||
if (L.IsTable(4))
|
||||
{
|
||||
if (L.GetField(-1, "source") == LuaType.Table)
|
||||
{
|
||||
int sx, sy, sw, sh;
|
||||
|
||||
if (L.GetInteger(-1, 1) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
sx = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetInteger(-1, 2) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
sy = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetInteger(-1, 3) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
sw = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetInteger(-1, 4) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
sh = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
source = new(sx, sy, sw, sh);
|
||||
}
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetField(-1, "color") == LuaType.Number)
|
||||
{
|
||||
var c = (uint)L.ToNumber(-1);
|
||||
GetColor(c, out var r, out var g, out var b);
|
||||
color = new Color(r, g, b);
|
||||
}
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetField(-1, "rotation") == LuaType.Number)
|
||||
{
|
||||
rotation = (float)L.ToNumber(-1);
|
||||
}
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetField(-1, "origin") == LuaType.Table)
|
||||
{
|
||||
int ox, oy;
|
||||
|
||||
if (L.GetInteger(-1, 1) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
ox = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetInteger(-1, 2) != LuaType.Number)
|
||||
L.CheckNumber(-1);
|
||||
oy = (int)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
origin = new Vector2(ox, oy);
|
||||
}
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetField(-1, "scale") == LuaType.Table)
|
||||
{
|
||||
float sx = 1;
|
||||
float sy = 1;
|
||||
|
||||
if (L.GetInteger(-1, 1) == LuaType.Number)
|
||||
sx = (float)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetInteger(-1, 2) == LuaType.Number)
|
||||
sy = (float)L.ToNumber(-1);
|
||||
L.Pop(1);
|
||||
|
||||
scale = new(sx, sy);
|
||||
}
|
||||
L.Pop(1);
|
||||
|
||||
if (L.GetField(-1, "effects") == LuaType.Number)
|
||||
{
|
||||
var flags = L.CheckInteger(-1);
|
||||
effects = (SpriteEffects)flags;
|
||||
}
|
||||
L.Pop(1);
|
||||
}
|
||||
|
||||
_game.Drawing.DrawBuffer(buffer.Buffer, new()
|
||||
{
|
||||
X = x,
|
||||
Y = y,
|
||||
Width = buffer.Width,
|
||||
Height = buffer.Height,
|
||||
});
|
||||
}, source, color, rotation, origin, scale, effects);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -486,7 +644,7 @@ public class GPU : IComponent
|
|||
|
||||
var path = L.CheckString(1);
|
||||
|
||||
path = FileSystem.Resolve(path);
|
||||
path = FileSystemLib.Resolve(path);
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
|
|
@ -25,21 +25,20 @@ using System.Net.Http;
|
|||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
#nullable enable
|
||||
public class HTTP : IComponent
|
||||
public class HTTPLib : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
private static HttpClient _httpClient;
|
||||
private static Capy64 _game = null!;
|
||||
private static HttpClient _httpClient = null!;
|
||||
private static long _requestId;
|
||||
public static readonly HashSet<WebSocketClient.Client> WebSocketConnections = new();
|
||||
|
||||
public static readonly string UserAgent = $"Capy64/{Capy64.Version}";
|
||||
|
||||
private static IConfiguration _configuration;
|
||||
private readonly LuaRegister[] HttpLib = new LuaRegister[]
|
||||
private static IConfiguration _configuration = null!;
|
||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -58,13 +57,13 @@ public class HTTP : IComponent
|
|||
},
|
||||
new(),
|
||||
};
|
||||
public HTTP(IGame game, IConfiguration configuration)
|
||||
public HTTPLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
_requestId = 0;
|
||||
_httpClient = new();
|
||||
_httpClient.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
||||
_configuration = configuration;
|
||||
_configuration = game.Configuration;
|
||||
}
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
|
|
@ -76,7 +75,7 @@ public class HTTP : IComponent
|
|||
private int Open(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
L.NewLib(HttpLib);
|
||||
L.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +170,7 @@ public class HTTP : IComponent
|
|||
options["method"] = L.CheckString(-2);
|
||||
break;
|
||||
case "binary":
|
||||
options["binary"] = L.IsBoolean(-2) ? L.ToBoolean(-2) : false;
|
||||
options["binary"] = L.ToBoolean(-2);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -22,15 +22,15 @@ using System;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class Machine : IComponent
|
||||
public class MachineLib : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
public Machine(IGame game)
|
||||
private static Capy64 _game;
|
||||
public MachineLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
||||
private static LuaRegister[] MachineLib = new LuaRegister[]
|
||||
private static readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -83,7 +83,7 @@ public class Machine : IComponent
|
|||
private static int OpenLib(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
L.NewLib(MachineLib);
|
||||
L.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -94,10 +94,14 @@ public class Machine : IComponent
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static int L_Reboot(IntPtr _)
|
||||
private static int L_Reboot(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
RuntimeManager.Reboot();
|
||||
|
||||
L.Yield(0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Runtime.Objects;
|
||||
using KeraLua;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
public class TCP : IComponent
|
||||
{
|
||||
private static int Counter = 0;
|
||||
private static IGame _game;
|
||||
public TCP(IGame game)
|
||||
{
|
||||
_game = game;
|
||||
Counter = 0;
|
||||
}
|
||||
|
||||
private static LuaRegister[] TCPLib = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
name = "connectAsync",
|
||||
function = L_Connect,
|
||||
},
|
||||
new(),
|
||||
};
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
{
|
||||
//L.RequireF("tcp", OpenLib, false);
|
||||
}
|
||||
|
||||
public int OpenLib(nint state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
L.NewLib(TCPLib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_Connect(nint state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var host = L.CheckString(1);
|
||||
var port = (int)L.CheckInteger(2);
|
||||
L.ArgumentCheck(port >= 0 && port <= 0xffff, 2, "port must be in range 0-65535");
|
||||
|
||||
var client = new TcpClient();
|
||||
var id = Counter++;
|
||||
var task = client.ConnectAsync(host, port);
|
||||
|
||||
task.ContinueWith(t =>
|
||||
{
|
||||
if (client.Connected)
|
||||
{
|
||||
_game.LuaRuntime.QueueEvent("tcp_connect", LK =>
|
||||
{
|
||||
/*var handle = new FileHandle
|
||||
{
|
||||
Stream = client.GetStream(),
|
||||
DefaultSize = client.ReceiveBufferSize,
|
||||
};
|
||||
|
||||
LK.PushInteger(id);
|
||||
ObjectManager.PushObject(L, handle);
|
||||
L.SetMetaTable("file");*/
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_game.LuaRuntime.QueueEvent("tcp_failure", LK =>
|
||||
{
|
||||
LK.PushInteger(id);
|
||||
LK.PushString(t.Exception.Message);
|
||||
return 2;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
L.PushInteger(id);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using Capy64.Eventing.Events;
|
||||
using KeraLua;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
|
@ -24,7 +23,7 @@ using static Capy64.Utils;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
internal class Term : IComponent
|
||||
internal class TermLib : IComponent
|
||||
{
|
||||
private struct Char
|
||||
{
|
||||
|
|
@ -50,11 +49,11 @@ internal class Term : IComponent
|
|||
public static Color BackgroundColor { get; set; }
|
||||
private static Char?[] CharGrid;
|
||||
|
||||
private static IGame _game;
|
||||
private static Capy64 _game;
|
||||
private static bool cursorState = false;
|
||||
private static bool enableCursor = true;
|
||||
private static Texture2D cursorTexture;
|
||||
public Term(IGame game)
|
||||
public TermLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
|
||||
|
|
@ -73,7 +72,7 @@ internal class Term : IComponent
|
|||
_game.EventEmitter.OnScreenSizeChange += OnScreenSizeChange;
|
||||
}
|
||||
|
||||
private LuaRegister[] TermLib = new LuaRegister[]
|
||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -100,6 +99,10 @@ internal class Term : IComponent
|
|||
name = "setSize",
|
||||
function = L_SetSize,
|
||||
},
|
||||
new() {
|
||||
name = "isResizable",
|
||||
function = L_IsResizable,
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "getForeground",
|
||||
|
|
@ -171,7 +174,7 @@ internal class Term : IComponent
|
|||
public int Open(IntPtr state)
|
||||
{
|
||||
var l = Lua.FromIntPtr(state);
|
||||
l.NewLib(TermLib);
|
||||
l.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -217,12 +220,12 @@ internal class Term : IComponent
|
|||
_game.Drawing.DrawString(charpos, ch.ToString(), fg);
|
||||
|
||||
}
|
||||
catch (ArgumentException ex) // UTF-16 fuckery
|
||||
catch (ArgumentException) // UTF-16 fuckery
|
||||
{
|
||||
_game.Drawing.DrawString(charpos, "\xFFFD", fg);
|
||||
}
|
||||
|
||||
if(underline)
|
||||
if (underline)
|
||||
{
|
||||
_game.Drawing.DrawLine(charpos + new Vector2(0, CharHeight), charpos + new Vector2(CharWidth, CharHeight), fg);
|
||||
}
|
||||
|
|
@ -313,7 +316,7 @@ internal class Term : IComponent
|
|||
if (cursorState)
|
||||
{
|
||||
var realpos = ToRealPos(CursorPosition - Vector2.One);
|
||||
var charpos = (realpos * _game.Scale) + (CharOffset + new Vector2(0, 2)) * _game.Scale;
|
||||
var charpos = (realpos * _game.Scale) + ((CharOffset + new Vector2(0, 2)) * _game.Scale);
|
||||
charpos += new Vector2(Capy64.Instance.Borders.Left, Capy64.Instance.Borders.Top);
|
||||
_game.Game.SpriteBatch.Draw(cursorTexture, charpos, null, ForegroundColor, 0f, Vector2.Zero, _game.Scale, SpriteEffects.None, 0);
|
||||
}
|
||||
|
|
@ -396,10 +399,9 @@ internal class Term : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
if(_game.EngineMode == EngineMode.Classic)
|
||||
if (_game.EngineMode == EngineMode.Classic)
|
||||
{
|
||||
L.PushBoolean(false);
|
||||
return 1;
|
||||
return L.Error("Terminal is not resizable");
|
||||
}
|
||||
|
||||
var w = (int)L.CheckNumber(1);
|
||||
|
|
@ -420,6 +422,15 @@ internal class Term : IComponent
|
|||
return 1;
|
||||
}
|
||||
|
||||
private static int L_IsResizable(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
L.PushBoolean(_game.EngineMode != EngineMode.Classic);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_GetForegroundColor(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -538,8 +549,6 @@ internal class Term : IComponent
|
|||
|
||||
private static int L_Clear(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
Clear();
|
||||
|
||||
return 0;
|
||||
|
|
@ -553,8 +562,6 @@ internal class Term : IComponent
|
|||
|
||||
private static int L_ClearLine(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
ClearLine();
|
||||
|
||||
return 0;
|
||||
|
|
@ -21,9 +21,15 @@ using System.Collections.Concurrent;
|
|||
|
||||
namespace Capy64.Runtime.Libraries;
|
||||
|
||||
class Timer : IComponent
|
||||
class TimerLib : IComponent
|
||||
{
|
||||
private LuaRegister[] TimerLib = new LuaRegister[]
|
||||
public class Timer
|
||||
{
|
||||
public int RemainingTicks = 0;
|
||||
public TaskMeta.RuntimeTask Task = null!;
|
||||
}
|
||||
|
||||
private readonly LuaRegister[] Library = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -44,25 +50,54 @@ class Timer : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
private static IGame _game;
|
||||
private static Capy64 _game;
|
||||
private static uint _timerId = 0;
|
||||
|
||||
private static ConcurrentDictionary<uint, System.Timers.Timer> timers = new();
|
||||
public Timer(IGame game)
|
||||
private static readonly ConcurrentDictionary<uint, Timer> timers = new();
|
||||
public TimerLib(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
|
||||
_game.EventEmitter.OnTick += OnTick;
|
||||
}
|
||||
|
||||
private void OnTick(object sender, Eventing.Events.TickEvent e)
|
||||
{
|
||||
if (e.IsActiveTick)
|
||||
{
|
||||
foreach (var t in timers)
|
||||
{
|
||||
var timer = t.Value;
|
||||
timer.RemainingTicks--;
|
||||
if (timer.RemainingTicks <= 0)
|
||||
{
|
||||
if (timer.Task == null)
|
||||
{
|
||||
_game.LuaRuntime.QueueEvent("timer", lk =>
|
||||
{
|
||||
lk.PushInteger(t.Key);
|
||||
|
||||
return 1;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
timer.Task.Fulfill(lk =>
|
||||
{
|
||||
lk.PushInteger(t.Key);
|
||||
});
|
||||
}
|
||||
|
||||
timers.TryRemove(t.Key, out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void LuaInit(Lua state)
|
||||
{
|
||||
_timerId = 0;
|
||||
|
||||
foreach (var pair in timers)
|
||||
{
|
||||
pair.Value.Stop();
|
||||
pair.Value.Dispose();
|
||||
}
|
||||
|
||||
timers.Clear();
|
||||
|
||||
state.RequireF("timer", Open, false);
|
||||
|
|
@ -71,36 +106,23 @@ class Timer : IComponent
|
|||
private int Open(IntPtr state)
|
||||
{
|
||||
var l = Lua.FromIntPtr(state);
|
||||
l.NewLib(TimerLib);
|
||||
l.NewLib(Library);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static int L_StartTimer(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var delay = L.CheckNumber(1);
|
||||
L.ArgumentCheck(delay > 0, 1, "delay must be greater than 0");
|
||||
|
||||
var timerId = _timerId++;
|
||||
var timer = new System.Timers.Timer
|
||||
|
||||
timers[timerId] = new Timer
|
||||
{
|
||||
AutoReset = false,
|
||||
Enabled = true,
|
||||
Interval = delay,
|
||||
};
|
||||
|
||||
timers[timerId] = timer;
|
||||
|
||||
timer.Elapsed += (o, e) =>
|
||||
{
|
||||
_game.LuaRuntime.QueueEvent("timer", lk =>
|
||||
{
|
||||
lk.PushInteger(timerId);
|
||||
|
||||
return 1;
|
||||
});
|
||||
timers.TryRemove(timerId, out _);
|
||||
RemainingTicks = (int)(delay * Capy64.Instance.TickRate)
|
||||
};
|
||||
|
||||
L.PushInteger(timerId);
|
||||
|
|
@ -112,26 +134,15 @@ class Timer : IComponent
|
|||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var delay = L.CheckNumber(1);
|
||||
L.ArgumentCheck(delay > 0, 1, "delay must be greater than 0");
|
||||
|
||||
var task = TaskMeta.Push(L, "timer");
|
||||
|
||||
var timerId = _timerId++;
|
||||
var timer = new System.Timers.Timer
|
||||
{
|
||||
AutoReset = false,
|
||||
Enabled = true,
|
||||
Interval = delay,
|
||||
};
|
||||
|
||||
timers[timerId] = timer;
|
||||
|
||||
timer.Elapsed += (o, e) =>
|
||||
timers[timerId] = new Timer
|
||||
{
|
||||
task.Fulfill(lk => {
|
||||
lk.PushInteger(timerId);
|
||||
});
|
||||
timers.TryRemove(timerId, out _);
|
||||
RemainingTicks = (int)(delay * Capy64.Instance.TickRate),
|
||||
Task = task,
|
||||
};
|
||||
|
||||
return 1;
|
||||
|
|
@ -60,7 +60,6 @@ public class LuaState : IDisposable
|
|||
if (yieldTimedOut)
|
||||
{
|
||||
L.Error("no yield timeout");
|
||||
Console.WriteLine("tick");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,25 +23,39 @@ namespace Capy64.Runtime;
|
|||
|
||||
public class ObjectManager : IComponent
|
||||
{
|
||||
private static ConcurrentDictionary<nint, object> _objects = new();
|
||||
private static readonly ConcurrentDictionary<nint, object> _objects = new();
|
||||
|
||||
private static IGame _game;
|
||||
public ObjectManager(IGame game)
|
||||
private static Capy64 _game;
|
||||
public ObjectManager(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
_game.EventEmitter.OnClose += OnClose;
|
||||
}
|
||||
|
||||
public static void PushObject<T>(Lua L, T obj)
|
||||
public static nint PushObject<T>(Lua L, T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
L.PushNil();
|
||||
return;
|
||||
return nint.Zero;
|
||||
}
|
||||
|
||||
var p = L.NewUserData(1);
|
||||
_objects[p] = obj;
|
||||
return p;
|
||||
}
|
||||
|
||||
public static T GetObject<T>(nint address, bool freeGCHandle = false)
|
||||
{
|
||||
if (!_objects.ContainsKey(address))
|
||||
return default(T);
|
||||
|
||||
var reference = (T)_objects[address];
|
||||
|
||||
if (freeGCHandle)
|
||||
_objects.Remove(address, out _);
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
public static T ToObject<T>(Lua L, int index, bool freeGCHandle = true)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,11 @@
|
|||
using Capy64.API;
|
||||
using KeraLua;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using static Capy64.Runtime.Constants;
|
||||
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
|
|
@ -24,7 +28,7 @@ public class FileHandle : IComponent
|
|||
{
|
||||
public const string ObjectType = "file";
|
||||
|
||||
private static LuaRegister[] Methods = new LuaRegister[]
|
||||
private static readonly LuaRegister[] Methods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -59,7 +63,7 @@ public class FileHandle : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
private static readonly LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -84,6 +88,8 @@ public class FileHandle : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
public FileHandle(Capy64 _) { }
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
{
|
||||
CreateMeta(L);
|
||||
|
|
@ -133,7 +139,16 @@ public class FileHandle : IComponent
|
|||
|
||||
private static bool ReadNumber(Lua L, Stream stream)
|
||||
{
|
||||
return false;
|
||||
var str = ReadHelper.ReadNumber(stream);
|
||||
if (L.StringToNumber(str))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
L.PushNil();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ReadLine(Lua L, Stream stream, bool chop)
|
||||
|
|
@ -187,53 +202,66 @@ public class FileHandle : IComponent
|
|||
return 2;
|
||||
}
|
||||
|
||||
return G_Read(L, stream, 2);
|
||||
}
|
||||
|
||||
private static int G_Read(Lua L, Stream f, int first)
|
||||
{
|
||||
var nargs = L.GetTop() - 1;
|
||||
if (nargs == 0)
|
||||
{
|
||||
L.PushString("l");
|
||||
nargs = 1;
|
||||
}
|
||||
int n;
|
||||
bool success;
|
||||
|
||||
for (int i = 2; i <= nargs + 1; i++)
|
||||
if (nargs == 0) // no arguments?
|
||||
{
|
||||
bool success;
|
||||
if (L.Type(i) == LuaType.Number)
|
||||
success = ReadLine(L, f, true);
|
||||
n = first + 1; // to return 1 result
|
||||
}
|
||||
else
|
||||
{
|
||||
// ensure stack space for all results and for auxlib's buffer
|
||||
L.CheckStack(nargs + MINSTACK, "too many arguments");
|
||||
success = true;
|
||||
for (n = first; (nargs-- > 0) && success; n++)
|
||||
{
|
||||
success = ReadChars(L, stream, (int)L.ToNumber(2));
|
||||
}
|
||||
else
|
||||
{
|
||||
var p = L.CheckString(i);
|
||||
var mode = CheckMode(p);
|
||||
switch (mode)
|
||||
if (L.Type(n) == LuaType.Number)
|
||||
{
|
||||
case 'n':
|
||||
success = ReadNumber(L, stream);
|
||||
break;
|
||||
case 'l':
|
||||
success = ReadLine(L, stream, true);
|
||||
break;
|
||||
case 'L':
|
||||
success = ReadLine(L, stream, false);
|
||||
break;
|
||||
case 'a':
|
||||
ReadAll(L, stream);
|
||||
success = true;
|
||||
break;
|
||||
default:
|
||||
return L.ArgumentError(i, "invalid format");
|
||||
var l = (int)L.CheckInteger(n);
|
||||
success = (l == 0) ? f.Position == f.Length : ReadChars(L, f, l);
|
||||
}
|
||||
else
|
||||
{
|
||||
var p = L.CheckString(n);
|
||||
var mode = CheckMode(p);
|
||||
switch (mode)
|
||||
{
|
||||
case 'n': // number
|
||||
success = ReadNumber(L, f);
|
||||
break;
|
||||
case 'l': // line
|
||||
success = ReadLine(L, f, true);
|
||||
break;
|
||||
case 'L': // line with end-of-line
|
||||
success = ReadLine(L, f, false);
|
||||
break;
|
||||
case 'a': // file
|
||||
ReadAll(L, f); // read entire file
|
||||
success = true; // always success
|
||||
break;
|
||||
default:
|
||||
return L.ArgumentError(n, "invalid format");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
L.Pop(1);
|
||||
L.PushNil();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nargs;
|
||||
if (!success)
|
||||
{
|
||||
L.Pop(1);
|
||||
L.PushNil();
|
||||
}
|
||||
|
||||
return n - first;
|
||||
}
|
||||
|
||||
private static int L_Write(IntPtr state)
|
||||
|
|
@ -268,7 +296,6 @@ public class FileHandle : IComponent
|
|||
|
||||
private static int L_Lines(IntPtr state)
|
||||
{
|
||||
return 0;
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var maxargn = 250;
|
||||
|
||||
|
|
@ -279,11 +306,51 @@ public class FileHandle : IComponent
|
|||
L.PushInteger(n);
|
||||
L.PushBoolean(false);
|
||||
L.Rotate(2, 3);
|
||||
L.PushCClosure(null, 3 + n); // todo
|
||||
L.PushCClosure(IO_ReadLine, 3 + n);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int IO_ReadLine(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var stream = ObjectManager.ToObject<Stream>(L, Lua.UpValueIndex(1), false);
|
||||
int i;
|
||||
int n = (int)L.ToInteger(Lua.UpValueIndex(2));
|
||||
if (stream is null)
|
||||
{
|
||||
return L.Error("file is already closed");
|
||||
}
|
||||
L.SetTop(1);
|
||||
L.CheckStack(n, "too many arguments");
|
||||
for (i = 1; i <= n; i++)
|
||||
{
|
||||
L.PushCopy(Lua.UpValueIndex(3 + i));
|
||||
}
|
||||
n = G_Read(L, stream, 2);
|
||||
Debug.Assert(n > 0);
|
||||
if (L.ToBoolean(-n))
|
||||
{
|
||||
return n;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (n > 1)
|
||||
{
|
||||
return L.Error(L.ToString(-n + 1));
|
||||
}
|
||||
if (L.ToBoolean(Lua.UpValueIndex(3)))
|
||||
{
|
||||
L.SetTop(0);
|
||||
L.PushCopy(Lua.UpValueIndex(1));
|
||||
stream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static int L_Flush(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
|
@ -348,9 +415,124 @@ public class FileHandle : IComponent
|
|||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var stream = ToStream(L, true);
|
||||
if (stream is not null)
|
||||
stream.Close();
|
||||
stream?.Close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static class ReadHelper
|
||||
{
|
||||
static bool isdigit(char c)
|
||||
{
|
||||
return "0123456789"
|
||||
.Contains(c, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
static bool isxdigit(char c)
|
||||
{
|
||||
return "0123456789abcdef"
|
||||
.Contains(c, StringComparison.CurrentCultureIgnoreCase);
|
||||
}
|
||||
|
||||
static bool isspace(char c)
|
||||
{
|
||||
return new char[] {
|
||||
' ',
|
||||
'\n',
|
||||
'\t',
|
||||
'\v',
|
||||
'\f',
|
||||
'\r',
|
||||
}.Contains(c);
|
||||
}
|
||||
|
||||
static bool test_eof(Lua L, Stream f)
|
||||
{
|
||||
L.PushString("");
|
||||
return f.Position != f.Length;
|
||||
}
|
||||
|
||||
static bool nextc(RN rn)
|
||||
{
|
||||
if (rn.n >= 200) // buffer overflow?
|
||||
{
|
||||
rn.buff[0] = '\0'; // invalidate result
|
||||
return false; // fail
|
||||
}
|
||||
else
|
||||
{
|
||||
rn.buff[rn.n] = rn.c; // save current char
|
||||
rn.n++;
|
||||
rn.c = (char)rn.f.ReadByte(); // read next one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static bool test2(RN rn, string set)
|
||||
{
|
||||
if (rn.c == set[0] || rn.c == set[1])
|
||||
{
|
||||
return nextc(rn);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static int readdigits(RN rn, bool hex)
|
||||
{
|
||||
int count = 0;
|
||||
while ((hex ? isxdigit(rn.c) : isdigit(rn.c)) && nextc(rn))
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// https://www.lua.org/source/5.4/liolib.c.html#read_number
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReadNumber(Stream stream)
|
||||
{
|
||||
RN rn = new()
|
||||
{
|
||||
buff = new char[201]
|
||||
};
|
||||
|
||||
int count = 0;
|
||||
bool hex = false;
|
||||
string decp = "..";
|
||||
rn.f = stream;
|
||||
rn.n = 0;
|
||||
|
||||
do
|
||||
{
|
||||
rn.c = (char)rn.f.ReadByte();
|
||||
} while (isspace(rn.c)); /* skip spaces */
|
||||
|
||||
test2(rn, "+-"); /* optional sign */
|
||||
if (test2(rn, "00"))
|
||||
{
|
||||
if (test2(rn, "xX")) hex = true; /* numeral is hexadecimal */
|
||||
else count = 1; /* count initial '0' as a valid digit */
|
||||
}
|
||||
count += readdigits(rn, hex); // integral part
|
||||
if (test2(rn, decp)) // decimal point?
|
||||
count += readdigits(rn, hex); // fractional part
|
||||
if (count > 0 && test2(rn, (hex ? "pP" : "eE")))
|
||||
{ /* exponent mark? */
|
||||
test2(rn, "-+"); /* exponent sign */
|
||||
readdigits(rn, false); /* exponent digits */
|
||||
}
|
||||
rn.f.Position += -1;
|
||||
return new string(rn.buff);
|
||||
}
|
||||
|
||||
public class RN
|
||||
{
|
||||
public Stream f;
|
||||
public char c;
|
||||
public int n;
|
||||
public char[] buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@
|
|||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Capy64.Core;
|
||||
using KeraLua;
|
||||
using System;
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ public class GPUBufferMeta : IComponent
|
|||
public int Height { get; set; }
|
||||
}
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
private static readonly LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -67,8 +66,8 @@ public class GPUBufferMeta : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
private static IGame _game;
|
||||
public GPUBufferMeta(IGame game)
|
||||
private static Capy64 _game;
|
||||
public GPUBufferMeta(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
|
@ -138,8 +137,9 @@ public class GPUBufferMeta : IComponent
|
|||
|
||||
var value = buffer.Buffer[key];
|
||||
|
||||
// ABGR to RGB
|
||||
// ABGR to ARGB
|
||||
value =
|
||||
(value & 0xFF_00_00_00U) |
|
||||
((value & 0x00_00_00_FFU) << 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_FF_00_00U) >> 16); // move B
|
||||
|
|
@ -174,13 +174,12 @@ public class GPUBufferMeta : IComponent
|
|||
var value = (uint)L.ToInteger(3);
|
||||
value = GetColor(value);
|
||||
|
||||
// RGB to ABGR
|
||||
// ARGB to ABGR
|
||||
value =
|
||||
(value & 0xFF_00_00_00U) |
|
||||
((value & 0x00_FF_00_00U) >> 16) | // move R
|
||||
(value & 0x00_00_FF_00U) | // move G
|
||||
((value & 0x00_00_00_FFU) << 16) | // move B
|
||||
0xFF_00_00_00U;
|
||||
|
||||
((value & 0x00_00_00_FFU) << 16); // move B
|
||||
|
||||
buffer.Buffer[key] = value;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,6 @@
|
|||
using Capy64.API;
|
||||
using KeraLua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
|
|
@ -33,18 +29,18 @@ public class Socket : IDisposable
|
|||
|
||||
public class SocketLib : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
public SocketLib(IGame game)
|
||||
private static Capy64 _game = null!;
|
||||
public SocketLib(Capy64 game)
|
||||
{
|
||||
|
||||
_game = game;
|
||||
}
|
||||
|
||||
private static LuaRegister[] Methods = new LuaRegister[] {
|
||||
private static readonly LuaRegister[] Methods = new LuaRegister[] {
|
||||
|
||||
new(),
|
||||
};
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[] {
|
||||
private static readonly LuaRegister[] MetaMethods = new LuaRegister[] {
|
||||
new()
|
||||
{
|
||||
name = "__index",
|
||||
|
|
|
|||
|
|
@ -14,22 +14,15 @@
|
|||
// limitations under the License.
|
||||
|
||||
using Capy64.API;
|
||||
using Cyotek.Drawing.BitmapFont;
|
||||
using KeraLua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Runtime.Objects;
|
||||
|
||||
public class TaskMeta : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
public TaskMeta(IGame game)
|
||||
private static Capy64 _game;
|
||||
public TaskMeta(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
|
@ -50,6 +43,7 @@ public class TaskMeta : IComponent
|
|||
}
|
||||
|
||||
public Guid Guid { get; set; } = Guid.NewGuid();
|
||||
public nint Pointer { get; set; } = nint.Zero;
|
||||
public string Name { get; set; }
|
||||
public TaskStatus Status { get; set; } = TaskStatus.Running;
|
||||
public string Error { get; private set; }
|
||||
|
|
@ -69,7 +63,7 @@ public class TaskMeta : IComponent
|
|||
|
||||
var container = tasks.NewThread();
|
||||
lk(container);
|
||||
if(container.IsNoneOrNil(-1))
|
||||
if (container.IsNoneOrNil(-1))
|
||||
{
|
||||
throw new Exception("Task result cannot be nil");
|
||||
}
|
||||
|
|
@ -86,7 +80,6 @@ public class TaskMeta : IComponent
|
|||
DataIndex = tasks.GetTop();
|
||||
}
|
||||
|
||||
|
||||
_game.LuaRuntime.QueueEvent("task_finish", LK =>
|
||||
{
|
||||
LK.PushString(Guid.ToString());
|
||||
|
|
@ -123,7 +116,7 @@ public class TaskMeta : IComponent
|
|||
}
|
||||
}
|
||||
|
||||
private static LuaRegister[] Methods = new LuaRegister[]
|
||||
private static readonly LuaRegister[] Methods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -158,7 +151,7 @@ public class TaskMeta : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
private static LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
private static readonly LuaRegister[] MetaMethods = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
{
|
||||
|
|
@ -210,26 +203,58 @@ public class TaskMeta : IComponent
|
|||
|
||||
var task = new RuntimeTask(typeName);
|
||||
|
||||
ObjectManager.PushObject(L, task);
|
||||
L.NewTable();
|
||||
task.Pointer = ObjectManager.PushObject(L, task);
|
||||
L.SetMetaTable(ObjectType);
|
||||
L.SetField(-2, "task");
|
||||
|
||||
L.SetMetaTable(ObjectType);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
public static RuntimeTask ToTask(Lua L, bool gc = false)
|
||||
public static RuntimeTask ToTask(Lua L, int index = 1, bool gc = false)
|
||||
{
|
||||
return ObjectManager.ToObject<RuntimeTask>(L, 1, gc);
|
||||
RuntimeTask task;
|
||||
if (L.Type(index) != LuaType.Table)
|
||||
{
|
||||
if (L.TestUserData(index, ObjectType) == nint.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
task = ObjectManager.ToObject<RuntimeTask>(L, index, gc);
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
L.GetField(index, "task");
|
||||
task = task = ObjectManager.ToObject<RuntimeTask>(L, -1, gc);
|
||||
}
|
||||
return task;
|
||||
}
|
||||
|
||||
public static RuntimeTask CheckTask(Lua L, bool gc = false)
|
||||
public static RuntimeTask CheckTask(Lua L, int index = 1, bool gc = false)
|
||||
{
|
||||
var obj = ObjectManager.CheckObject<RuntimeTask>(L, 1, ObjectType, gc);
|
||||
if (obj is null)
|
||||
RuntimeTask task;
|
||||
if (L.Type(index) != LuaType.Table)
|
||||
{
|
||||
task = ObjectManager.CheckObject<RuntimeTask>(L, index, ObjectType, gc);
|
||||
}
|
||||
else
|
||||
{
|
||||
L.GetField(index, "task");
|
||||
task = ObjectManager.CheckObject<RuntimeTask>(L, -1, ObjectType, gc);
|
||||
}
|
||||
|
||||
if (task is null)
|
||||
{
|
||||
L.Error("attempt to use a closed task");
|
||||
return null;
|
||||
}
|
||||
return obj;
|
||||
return task;
|
||||
}
|
||||
|
||||
private static int FindSpot()
|
||||
|
|
@ -262,7 +287,9 @@ public class TaskMeta : IComponent
|
|||
|
||||
private static void WaitForTask(Lua L)
|
||||
{
|
||||
L.PushCFunction(Libraries.Event.L_Pull);
|
||||
L.PushString("coroutine");
|
||||
L.GetTable(-1);
|
||||
L.GetField(-1, "yield");
|
||||
L.PushString("task_finish");
|
||||
L.CallK(1, 4, 0, LK_Await);
|
||||
}
|
||||
|
|
@ -273,7 +300,7 @@ public class TaskMeta : IComponent
|
|||
|
||||
L.Warning("Native task awaiter should be avoided", false);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
if (task.Status == TaskStatus.Succeeded)
|
||||
{
|
||||
|
|
@ -300,7 +327,9 @@ public class TaskMeta : IComponent
|
|||
private static int LK_Await(IntPtr state, int status, nint ctx)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var task = CheckTask(L, false);
|
||||
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
var taskId = L.CheckString(3);
|
||||
|
||||
if (task.Guid.ToString() != taskId)
|
||||
|
|
@ -316,7 +345,7 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
L.PushString(task.Guid.ToString());
|
||||
|
||||
|
|
@ -327,7 +356,7 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
L.PushString(task.Name);
|
||||
|
||||
|
|
@ -338,7 +367,7 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
L.PushString(task.Status.ToString().ToLower());
|
||||
|
||||
|
|
@ -349,7 +378,7 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
if (task.Status == TaskStatus.Succeeded)
|
||||
{
|
||||
|
|
@ -369,7 +398,7 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = CheckTask(L, false);
|
||||
var task = CheckTask(L, 1, false);
|
||||
|
||||
if (task.Status == TaskStatus.Failed)
|
||||
{
|
||||
|
|
@ -387,7 +416,11 @@ public class TaskMeta : IComponent
|
|||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = ToTask(L, true);
|
||||
L.CheckType(1, LuaType.Table);
|
||||
|
||||
L.GetField(1, "task");
|
||||
|
||||
var task = CheckTask(L, -1, true);
|
||||
if (task is null)
|
||||
return 0;
|
||||
|
||||
|
|
@ -405,7 +438,9 @@ public class TaskMeta : IComponent
|
|||
private static int LM_ToString(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
|
||||
var task = ToTask(L);
|
||||
|
||||
L.PushString("Task<{0}>: {1} ({2})", task?.Name, task?.Guid, task?.Status);
|
||||
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ using Capy64.API;
|
|||
using Capy64.Runtime.Libraries;
|
||||
using KeraLua;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
|
||||
|
|
@ -74,6 +73,8 @@ public class WebSocketClient : IComponent
|
|||
new(),
|
||||
};
|
||||
|
||||
public WebSocketClient(Capy64 _) { }
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
{
|
||||
CreateMeta(L);
|
||||
|
|
@ -153,7 +154,7 @@ public class WebSocketClient : IComponent
|
|||
});
|
||||
});
|
||||
|
||||
HTTP.WebSocketConnections.Remove(client);
|
||||
HTTPLib.WebSocketConnections.Remove(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,33 +21,33 @@ namespace Capy64.Runtime;
|
|||
public class PanicScreen
|
||||
{
|
||||
public static Color ForegroundColor = Color.White;
|
||||
public static Color BackgroundColor = new Color(0, 51, 187);
|
||||
public static Color BackgroundColor = new(0, 51, 187);
|
||||
|
||||
public static void Render(string error, string details = null)
|
||||
{
|
||||
Term.ForegroundColor = ForegroundColor;
|
||||
Term.BackgroundColor = BackgroundColor;
|
||||
Term.SetCursorBlink(false);
|
||||
Term.SetSize(57, 23);
|
||||
Term.Clear();
|
||||
TermLib.ForegroundColor = ForegroundColor;
|
||||
TermLib.BackgroundColor = BackgroundColor;
|
||||
TermLib.SetCursorBlink(false);
|
||||
TermLib.SetSize(57, 23);
|
||||
TermLib.Clear();
|
||||
|
||||
var title = " Capy64 ";
|
||||
var halfX = (Term.Width / 2) + 1;
|
||||
Term.SetCursorPosition(halfX - (title.Length / 2), 2);
|
||||
Term.ForegroundColor = BackgroundColor;
|
||||
Term.BackgroundColor = ForegroundColor;
|
||||
Term.Write(title);
|
||||
var halfX = (TermLib.Width / 2) + 1;
|
||||
TermLib.SetCursorPosition(halfX - (title.Length / 2), 2);
|
||||
TermLib.ForegroundColor = BackgroundColor;
|
||||
TermLib.BackgroundColor = ForegroundColor;
|
||||
TermLib.Write(title);
|
||||
|
||||
Term.ForegroundColor = ForegroundColor;
|
||||
Term.BackgroundColor = BackgroundColor;
|
||||
Term.SetCursorPosition(1, 4);
|
||||
TermLib.ForegroundColor = ForegroundColor;
|
||||
TermLib.BackgroundColor = BackgroundColor;
|
||||
TermLib.SetCursorPosition(1, 4);
|
||||
Print(error + '\n');
|
||||
|
||||
if (details is not null)
|
||||
{
|
||||
Print(details);
|
||||
}
|
||||
Term.SetCursorPosition(1, 19);
|
||||
TermLib.SetCursorPosition(1, 23);
|
||||
Print("Hold CTRL + ALT + INSERT to reboot.");
|
||||
}
|
||||
|
||||
|
|
@ -55,12 +55,12 @@ public class PanicScreen
|
|||
{
|
||||
foreach (var ch in txt)
|
||||
{
|
||||
Term.Write(ch.ToString());
|
||||
if (Term.CursorPosition.X >= Term.Width || ch == '\n')
|
||||
TermLib.Write(ch.ToString());
|
||||
if (TermLib.CursorPosition.X >= TermLib.Width || ch == '\n')
|
||||
{
|
||||
Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1);
|
||||
TermLib.SetCursorPosition(1, (int)TermLib.CursorPosition.Y + 1);
|
||||
}
|
||||
}
|
||||
Term.SetCursorPosition(1, (int)Term.CursorPosition.Y + 1);
|
||||
TermLib.SetCursorPosition(1, (int)TermLib.CursorPosition.Y + 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
|
||||
using Capy64.API;
|
||||
using Capy64.Eventing.Events;
|
||||
using Capy64.Extensions;
|
||||
using Capy64.Runtime.Libraries;
|
||||
using KeraLua;
|
||||
using System;
|
||||
|
|
@ -33,8 +32,8 @@ internal class RuntimeManager : IComponent
|
|||
private static bool close = false;
|
||||
private static bool inPanic = false;
|
||||
|
||||
private static IGame _game;
|
||||
public RuntimeManager(IGame game)
|
||||
private static Capy64 _game;
|
||||
public RuntimeManager(Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
|
||||
|
|
@ -98,6 +97,8 @@ internal class RuntimeManager : IComponent
|
|||
{
|
||||
_game.Discord.SetPresence("Booting up...");
|
||||
|
||||
InstallOS(false);
|
||||
|
||||
luaState = new LuaState();
|
||||
_game.LuaRuntime = luaState;
|
||||
luaState.Init();
|
||||
|
|
@ -112,19 +113,18 @@ internal class RuntimeManager : IComponent
|
|||
|
||||
emitter.Register();
|
||||
|
||||
LoadFirmware();
|
||||
|
||||
luaState.Thread.PushCFunction(L_OpenDataFolder);
|
||||
luaState.Thread.SetGlobal("openDataFolder");
|
||||
|
||||
luaState.Thread.PushCFunction(L_ShouldInstallOS);
|
||||
luaState.Thread.SetGlobal("shouldInstallOS");
|
||||
|
||||
luaState.Thread.PushCFunction(L_FlagInstalled);
|
||||
luaState.Thread.SetGlobal("flagInstalled");
|
||||
luaState.Thread.PushCFunction(L_InstallOS);
|
||||
luaState.Thread.SetGlobal("installOS");
|
||||
|
||||
luaState.Thread.PushCFunction(L_Exit);
|
||||
luaState.Thread.SetGlobal("exit");
|
||||
|
||||
var status = luaState.Thread.LoadFile("Assets/bios.lua");
|
||||
var status = luaState.Thread.LoadFile(Path.Combine(Capy64.AssetsPath, "Lua/bios.lua"));
|
||||
if (status != LuaStatus.OK)
|
||||
{
|
||||
throw new LuaException(luaState.Thread.ToString(-1));
|
||||
|
|
@ -133,11 +133,10 @@ internal class RuntimeManager : IComponent
|
|||
|
||||
private void InitOS()
|
||||
{
|
||||
_game.Discord.SetPresence("On CapyOS");
|
||||
|
||||
luaState = new LuaState();
|
||||
_game.LuaRuntime = luaState;
|
||||
luaState.Init();
|
||||
CopyHostLibraries();
|
||||
|
||||
emitter = new(_game.EventEmitter, luaState);
|
||||
|
||||
|
|
@ -149,7 +148,14 @@ internal class RuntimeManager : IComponent
|
|||
|
||||
emitter.Register();
|
||||
|
||||
var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua"));
|
||||
LoadFirmware();
|
||||
|
||||
if (!File.Exists(Path.Combine(FileSystemLib.DataPath, "init.lua")))
|
||||
{
|
||||
throw new LuaException("Operating System not found\nMissing init.lua");
|
||||
}
|
||||
|
||||
var initContent = File.ReadAllText(Path.Combine(FileSystemLib.DataPath, "init.lua"));
|
||||
var status = luaState.Thread.LoadString(initContent, "=init.lua");
|
||||
if (status != LuaStatus.OK)
|
||||
{
|
||||
|
|
@ -157,6 +163,36 @@ 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()
|
||||
{
|
||||
var firmwareContent = File.ReadAllText(Path.Combine(Capy64.AssetsPath, "Lua/firmware.lua"));
|
||||
var errored = luaState.Thread.DoString(firmwareContent);
|
||||
if (errored)
|
||||
{
|
||||
throw new LuaException(luaState.Thread.ToString(-1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void Reset()
|
||||
{
|
||||
close = true;
|
||||
|
|
@ -174,9 +210,19 @@ internal class RuntimeManager : IComponent
|
|||
_game.Exit();
|
||||
}
|
||||
|
||||
public static void InstallOS(bool force = false)
|
||||
{
|
||||
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
|
||||
if (!File.Exists(installedFilePath) || force)
|
||||
{
|
||||
FileSystemLib.CopyDirectory(Path.Combine(Capy64.AssetsPath, "Lua/CapyOS"), FileSystemLib.DataPath, true, true);
|
||||
File.Create(installedFilePath).Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private static int L_OpenDataFolder(IntPtr state)
|
||||
{
|
||||
var path = FileSystem.DataPath;
|
||||
var path = FileSystemLib.DataPath;
|
||||
switch (Environment.OSVersion.Platform)
|
||||
{
|
||||
case PlatformID.Win32NT:
|
||||
|
|
@ -190,22 +236,9 @@ internal class RuntimeManager : IComponent
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static int L_ShouldInstallOS(IntPtr state)
|
||||
private static int L_InstallOS(IntPtr state)
|
||||
{
|
||||
var L = Lua.FromIntPtr(state);
|
||||
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
|
||||
|
||||
L.PushBoolean(!File.Exists(installedFilePath));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static int L_FlagInstalled(IntPtr state)
|
||||
{
|
||||
var installedFilePath = Path.Combine(Capy64.AppDataPath, ".installed");
|
||||
if (!File.Exists(installedFilePath))
|
||||
File.Create(installedFilePath).Dispose();
|
||||
|
||||
InstallOS(true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,20 @@ internal class Sandbox
|
|||
L.SetTable(-3);
|
||||
|
||||
L.Pop(1);
|
||||
|
||||
// Replace debug.debug with a dummy function to avoid stalling the program
|
||||
L.GetGlobal("debug");
|
||||
|
||||
L.PushString("debug");
|
||||
L.PushCFunction(L_Dummy);
|
||||
L.SetTable(-3);
|
||||
|
||||
L.Pop(1);
|
||||
}
|
||||
|
||||
internal static int L_Dummy(IntPtr state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal static int L_Searcher(IntPtr state)
|
||||
|
|
@ -192,7 +206,7 @@ internal class Sandbox
|
|||
var errorMessage = new StringBuilder();
|
||||
foreach (var possiblePath in possiblePaths)
|
||||
{
|
||||
var path = FileSystem.Resolve(possiblePath);
|
||||
var path = FileSystemLib.Resolve(possiblePath);
|
||||
var info = new FileInfo(path);
|
||||
if (!info.Exists)
|
||||
{
|
||||
|
|
@ -229,7 +243,7 @@ internal class Sandbox
|
|||
bool hasMode = !L.IsNone(2);
|
||||
bool hasEnv = !L.IsNone(3);
|
||||
|
||||
var path = FileSystem.Resolve(filename);
|
||||
var path = FileSystemLib.Resolve(filename);
|
||||
|
||||
var fileInfo = new FileInfo(path);
|
||||
if (!fileInfo.Exists)
|
||||
|
|
|
|||
|
|
@ -1,73 +0,0 @@
|
|||
// This file is part of Capy64 - https://github.com/Ale32bit/Capy64
|
||||
// Copyright 2023 Alessandro "AlexDevs" Proto
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License").
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64;
|
||||
|
||||
public class Worker : IHostedService
|
||||
{
|
||||
private readonly IGame _game;
|
||||
private readonly IHostApplicationLifetime _appLifetime;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
||||
public Worker(IGame game, IHostApplicationLifetime appLifetime, IServiceProvider serviceProvider)
|
||||
{
|
||||
_game = game;
|
||||
_appLifetime = appLifetime;
|
||||
_serviceProvider = serviceProvider;
|
||||
}
|
||||
|
||||
public Task StartAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_appLifetime.ApplicationStarted.Register(OnStarted);
|
||||
_appLifetime.ApplicationStopping.Register(OnStopping);
|
||||
_appLifetime.ApplicationStopped.Register(OnStopped);
|
||||
|
||||
_game.Exiting += OnGameExiting;
|
||||
|
||||
_game.ConfigureServices(_serviceProvider);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnGameExiting(object sender, EventArgs e)
|
||||
{
|
||||
StopAsync(new CancellationToken());
|
||||
}
|
||||
|
||||
public Task StopAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
_appLifetime.StopApplication();
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void OnStarted()
|
||||
{
|
||||
_game.Run();
|
||||
}
|
||||
|
||||
private void OnStopping()
|
||||
{
|
||||
}
|
||||
|
||||
private void OnStopped()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<EnableDynamicLoading>true</EnableDynamicLoading>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
using Capy64;
|
||||
using Capy64.API;
|
||||
using Capy64.API;
|
||||
using KeraLua;
|
||||
|
||||
namespace ExamplePlugin;
|
||||
|
||||
public class MyPlugin : IComponent
|
||||
{
|
||||
private static IGame _game;
|
||||
public MyPlugin(IGame game)
|
||||
private static Capy64.Capy64 _game;
|
||||
public MyPlugin(Capy64.Capy64 game)
|
||||
{
|
||||
_game = game;
|
||||
}
|
||||
|
|
|
|||
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>
|
||||
19
default.nix
Normal file
19
default.nix
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{ pkgs ? import <nixpkgs> { system = builtins.currentSystem; } }:
|
||||
pkgs.buildDotnetModule rec {
|
||||
pname = "Capy64";
|
||||
version = "1.1.0-beta";
|
||||
|
||||
src = ./.;
|
||||
|
||||
projectFile = "Capy64/Capy64.csproj";
|
||||
nugetDeps = ./deps.nix;
|
||||
|
||||
dotnet-sdk = pkgs.dotnetCorePackages.sdk_7_0;
|
||||
dotnet-runtime = pkgs.dotnetCorePackages.runtime_7_0;
|
||||
|
||||
meta = with pkgs.lib; {
|
||||
homepage = "https://github.com/Ale32bit/Capy64";
|
||||
description = "Capy64";
|
||||
license = with licenses; [ asl20 ];
|
||||
};
|
||||
}
|
||||
53
deps.nix
Normal file
53
deps.nix
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
# This file was automatically generated by passthru.fetch-deps.
|
||||
# Please dont edit it manually, your changes might get overwritten!
|
||||
|
||||
{ fetchNuGet }: [
|
||||
(fetchNuGet { pname = "Cyotek.Drawing.BitmapFont"; version = "2.0.4"; sha256 = "04n0lq9sqfjzyvkqav6qrc8v9fb7jv1n7jk0j4r6ivfbmffzq8if"; })
|
||||
(fetchNuGet { pname = "DiscordRichPresence"; version = "1.1.3.18"; sha256 = "0p4bhaggjjfd4gl06yiphqgncxgcq2bws4sjkrw0n2ldf3hgrps3"; })
|
||||
(fetchNuGet { pname = "FontStashSharp.Base"; version = "1.1.8"; sha256 = "00b4x3hcldp1f6g32khhyv32z127ab6rcgmd4b88k1534vv128bp"; })
|
||||
(fetchNuGet { pname = "FontStashSharp.MonoGame"; version = "1.2.8"; sha256 = "0qgp1i54zm27qxilxbpznv9s3mgf831x7xz5z3w66zcp53qgyk93"; })
|
||||
(fetchNuGet { pname = "FontStashSharp.Rasterizers.StbTrueTypeSharp"; version = "1.1.8"; sha256 = "1rnnwx748378hdbfvldxzacwc710ivvlcjzm9qi2pdh8qcz4v6db"; })
|
||||
(fetchNuGet { pname = "KeraLua"; version = "1.3.3"; sha256 = "1y76f6582fld1jpbkawv26344p6xwdk07xj3im25p8yf2qcw8yln"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration"; version = "7.0.0"; sha256 = "0n1grglxql9llmrsbbnlz5chx8mxrb5cpvjngm0hfyrkgzcwz90d"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Abstractions"; version = "7.0.0"; sha256 = "1as8cygz0pagg17w22nsf6mb49lr2mcl1x8i3ad1wi8lyzygy1a3"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Binder"; version = "7.0.3"; sha256 = "1n59jk6kqqy5f0gfx99b4j2m2clylznvxj1dwm1fjn3gmh2pi35v"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.CommandLine"; version = "7.0.0"; sha256 = "1pmgjrvwdzqrxjb24cg3fd624r64lgywbqc9symd5hyl4175pwk8"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.EnvironmentVariables"; version = "7.0.0"; sha256 = "0nhh7rnh45s39x8sjn88czg7nyfpry85pkm0g619j8b468zj8nb4"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.FileExtensions"; version = "7.0.0"; sha256 = "1fk7dcz6gfhd1k1d8ksz22rnjvj1waqjzk29ym4i3dz73rsq8j1i"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.Json"; version = "7.0.0"; sha256 = "05zjmrpp99l128wijp1fy8asskc11ls871qaqr4mjnz3gbfycxnj"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Configuration.UserSecrets"; version = "7.0.0"; sha256 = "0ks7lcyvfvr3ar36f5gp89bnnblxzic5vawppfcrvhw1ivas4mp1"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection"; version = "7.0.0"; sha256 = "121zs4jp8iimgbpzm3wsglhjwkc06irg1pxy8c1zcdlsg34cfq1p"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.DependencyInjection.Abstractions"; version = "7.0.0"; sha256 = "181d7mp9307fs17lyy42f8cxnjwysddmpsalky4m0pqxcimnr6g7"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.FileProviders.Abstractions"; version = "7.0.0"; sha256 = "0ff20yklyjgyjzdyv7sybczgqhgd557m05dbwxzjznr0x41b180d"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.FileProviders.Physical"; version = "7.0.0"; sha256 = "1f1h0l47abw0spssd64qkhgd7b54pyzslyb586zp21milimcfmgv"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.FileSystemGlobbing"; version = "7.0.0"; sha256 = "1812vnkn8n0i4yr3k5azcxcfx1bbpcsmms95rdyxjfrzfksr05ai"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Hosting"; version = "7.0.1"; sha256 = "1044pm14ark2d7xiqll1cykawmp6m2f3ba9w6nfw0r3a2wfbmnxn"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Hosting.Abstractions"; version = "7.0.0"; sha256 = "1h5szfsr1dalsvdj9c18y6362853chisfns0hfpsq44hz0pr8j9q"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging"; version = "7.0.0"; sha256 = "1bqd3pqn5dacgnkq0grc17cgb2i0w8z1raw12nwm3p3zhrfcvgxf"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Abstractions"; version = "7.0.0"; sha256 = "1gn7d18i1wfy13vrwhmdv1rmsb4vrk26kqdld4cgvh77yigj90xs"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Configuration"; version = "7.0.0"; sha256 = "1f5fhpvzwyrwxh3g1ry027s4skmklf6mbm2w0p13h0x6fbmxcb24"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Console"; version = "7.0.0"; sha256 = "1m8ri2m3vlv9vzk0068jkrx0vkk4sqmk1kxmn8pc3wys38d38qaf"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.Debug"; version = "7.0.0"; sha256 = "14p7hrhdd58fxdhjbyjwmlzr00vs03bmns3sf2f6alsgpvbf2h1i"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.EventLog"; version = "7.0.0"; sha256 = "0q1cgi456shngxs70ar0ibshpm5qk8whw369jrl6xdxnf1vxkkq8"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Logging.EventSource"; version = "7.0.0"; sha256 = "11rskmrijf6xv78slm38zywj6l3wjlm017kijhan1kfg56f1kvdk"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Options"; version = "7.0.1"; sha256 = "0ghz4y4gxnf2vw8yvhz9nkw21p6q2qqwh19phkk1xwxywyilr3mq"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Options.ConfigurationExtensions"; version = "7.0.0"; sha256 = "1liyprh0zha2vgmqh92n8kkjz61zwhr7g16f0gmr297z2rg1j5pj"; })
|
||||
(fetchNuGet { pname = "Microsoft.Extensions.Primitives"; version = "7.0.0"; sha256 = "1b4km9fszid9vp2zb3gya5ni9fn8bq62bzaas2ck2r7gs0sdys80"; })
|
||||
(fetchNuGet { pname = "Microsoft.NETCore.Platforms"; version = "2.0.0"; sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0"; })
|
||||
(fetchNuGet { pname = "Microsoft.Win32.Registry"; version = "4.5.0"; sha256 = "1zapbz161ji8h82xiajgriq6zgzmb1f3ar517p2h63plhsq5gh2q"; })
|
||||
(fetchNuGet { pname = "MonoGame.Content.Builder.Task"; version = "3.8.1.303"; sha256 = "17r9aamc3wlxnvjf8s5ndsz9p6qynyklkwhawjkxsij2cw1x1syp"; })
|
||||
(fetchNuGet { pname = "MonoGame.Extended"; version = "3.8.0"; sha256 = "0xgk6z3d091wd6naqiwlhp649qxvp113w5f0nddm02mh5f63mmxw"; })
|
||||
(fetchNuGet { pname = "MonoGame.Extended.Graphics"; version = "3.8.0"; sha256 = "0kj4adbhds2mvd9z1sym35ms0kr4f4b62gnpbws2whc29nixs5fi"; })
|
||||
(fetchNuGet { pname = "MonoGame.Framework.DesktopGL"; version = "3.8.1.303"; sha256 = "12x7ajkh254x6l1s3i4q0j3pm5p7n95xysix19wvx3x2y6pp10nd"; })
|
||||
(fetchNuGet { pname = "Newtonsoft.Json"; version = "12.0.3"; sha256 = "17dzl305d835mzign8r15vkmav2hq8l6g7942dfjpnzr17wwl89x"; })
|
||||
(fetchNuGet { pname = "Newtonsoft.Json"; version = "13.0.1"; sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb"; })
|
||||
(fetchNuGet { pname = "StbImageSharp"; version = "2.27.13"; sha256 = "0zqnzgnkx3c9ycyk25jjcrl8wrg3pcgk06lxf3hvsplwxwldc8fa"; })
|
||||
(fetchNuGet { pname = "StbTrueTypeSharp"; version = "1.26.11"; sha256 = "1wh2jmym3pvyw8ywrwas3qgqswc6kr4az1bxbn9vb1m3vba0qjn4"; })
|
||||
(fetchNuGet { pname = "System.ComponentModel.Composition"; version = "7.0.0"; sha256 = "1gkn56gclkn6qnsvaw5fzw6qb45pa7rffxph1gyqhq7ywvmm0nc3"; })
|
||||
(fetchNuGet { pname = "System.Diagnostics.DiagnosticSource"; version = "7.0.1"; sha256 = "1ajh5y33apcypz5pnvzxsx44n95ciskzpvbxk0kfg7n9li3nfs1v"; })
|
||||
(fetchNuGet { pname = "System.Diagnostics.EventLog"; version = "7.0.0"; sha256 = "16p8z975dnzmncfifa9gw9n3k9ycpr2qvz7lglpghsvx0fava8k9"; })
|
||||
(fetchNuGet { pname = "System.Security.AccessControl"; version = "4.5.0"; sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0"; })
|
||||
(fetchNuGet { pname = "System.Security.Principal.Windows"; version = "4.5.0"; sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86"; })
|
||||
(fetchNuGet { pname = "System.Text.Encodings.Web"; version = "7.0.0"; sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl"; })
|
||||
(fetchNuGet { pname = "System.Text.Json"; version = "7.0.0"; sha256 = "0scb0lp7wbgcinaa4kqiqs7b8i5nx4ppfad81138jiwd1sl37pyp"; })
|
||||
]
|
||||
Loading…
Add table
Reference in a new issue