Added reboot, OS improvements

This commit is contained in:
Alessandro Proto 2023-01-08 00:40:33 +01:00
parent 84cba789a3
commit 29c55500bd
13 changed files with 444 additions and 451 deletions

View file

@ -0,0 +1 @@
os.print = print

View file

@ -0,0 +1 @@
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path

View file

@ -0,0 +1,79 @@
local term = require("term")
local expect = require("expect").expect
function write(sText)
expect(1, sText, "string", "number")
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 print(...)
local nLinesPrinted = 0
local nLimit = select("#", ...)
for n = 1, nLimit do
local s = tostring(select(n, ...))
if n < nLimit then
s = s .. "\t"
end
nLinesPrinted = nLinesPrinted + write(s)
end
nLinesPrinted = nLinesPrinted + write("\n")
return nLinesPrinted
end

View file

@ -0,0 +1,8 @@
local timer = require("timer")
function timer.sleep(n)
local timerId = timer.start(n)
repeat
local _, par = coroutine.yield("timer")
until par == timerId
end

View file

@ -0,0 +1,13 @@
local term = require("term")
local colors = require("colors")
term.setSize(51, 19)
term.setForeground(0x59c9ff)
term.setBackground(colors.black)
term.clear()
term.setPos(1,1)
print(os.version())
dofile("/bin/shell.lua")

View file

@ -1,446 +1,6 @@
local term = require("term") local fs = require("fs")
local timer = require("timer")
os.print = print
package.path = "/lib/?.lua;/lib/?/init.lua;" .. package.path
local event = require("event")
local colors = require("colors")
local expect = require("expect").expect
function timer.sleep(n)
local timerId = timer.start(n)
repeat
local _, par = coroutine.yield("timer")
until par == timerId
end
function write(sText)
expect(1, sText, "string", "number")
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 print(...)
local nLinesPrinted = 0
local nLimit = select("#", ...)
for n = 1, nLimit do
local s = tostring(select(n, ...))
if n < nLimit then
s = s .. "\t"
end
nLinesPrinted = nLinesPrinted + write(s)
end
nLinesPrinted = nLinesPrinted + write("\n")
return nLinesPrinted
end
function 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.getForeground()
oldBg = term.getBackground()
term.setForeground(colors.white)
term.setBackground(colors.gray)
end
if sReplace then
term.write(string.rep(sReplace, #sCompletion))
else
term.write(sCompletion)
end
if not _bClear then
term.setForeground(oldText)
term.setBackground(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 param1 == "enter" then
-- Enter/Numpad Enter
if nCompletion then
clear()
uncomplete()
redraw()
end
break
elseif param1 == "left" then
-- Left
if nPos > 0 then
clear()
nPos = nPos - 1
recomplete()
redraw()
end
elseif param1 == "right" then
-- Right
if nPos < #sLine then
-- Move right
clear()
nPos = nPos + 1
recomplete()
redraw()
else
-- Accept autocomplete
acceptCompletion()
end
elseif param1 == "up" or param1 == "down" then
-- Up or down
if nCompletion then
-- Cycle completions
clear()
if param == "up" then
nCompletion = nCompletion - 1
if nCompletion < 1 then
nCompletion = #tCompletions
end
elseif param == "down" then
nCompletion = nCompletion + 1
if nCompletion > #tCompletions then
nCompletion = 1
end
end
redraw()
elseif _tHistory then
-- Cycle history
clear()
if param1 == "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 param1 == "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 param1 == "home" then
-- Home
if nPos > 0 then
clear()
nPos = 0
recomplete()
redraw()
end
elseif param1 == "delete" then
-- Delete
if nPos < #sLine then
clear()
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
recomplete()
redraw()
end
elseif param1 == "end" then
-- End
if nPos < #sLine then
clear()
nPos = #sLine
recomplete()
redraw()
end
elseif param1 == "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 printError(...)
local r, g, b = term.getForeground()
term.setForeground(colors.red)
print(...)
term.setForeground(r, g, b)
end
local tEnv = {
["_echo"] = function(...)
return ...
end,
p = function(t, sv)
for k, v in pairs(t) do
if sv then
print(k, v)
else
print(k, type(v))
end
end
end,
fs = require("fs"),
}
setmetatable(tEnv, { __index = _ENV })
local tCommandHistory = {}
term.setSize(51, 19)
term.setForeground(colors.yellow)
term.setBackground(colors.black)
term.clear()
term.setPos(1,1)
print("Lua prompt")
while true do
term.setForeground(colors.yellow)
write("> ")
term.setForeground(colors.white)
local s = read(nil, tCommandHistory)
if s:match("%S") and tCommandHistory[#tCommandHistory] ~= s then
table.insert(tCommandHistory, s)
end
local nForcePrint = 0
local func, e = load(s, "=lua", "t", tEnv)
local func2 = load("return _echo(" .. s .. ");", "=lua", "t", tEnv)
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(tostring(value))
n = n + 1
end
else
printError(tResults[2])
end
else
printError(e)
end
local files = fs.list("/boot")
for k, v in ipairs(files) do
dofile("/boot/" .. v)
end end

View file

@ -0,0 +1,283 @@
local expect = require("expect").expect
local event = require("event")
local term = require("term")
local io = {}
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.getForeground()
oldBg = term.getBackground()
term.setForeground(colors.white)
term.setBackground(colors.gray)
end
if sReplace then
term.write(string.rep(sReplace, #sCompletion))
else
term.write(sCompletion)
end
if not _bClear then
term.setForeground(oldText)
term.setBackground(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 param1 == "enter" then
-- Enter/Numpad Enter
if nCompletion then
clear()
uncomplete()
redraw()
end
break
elseif param1 == "left" then
-- Left
if nPos > 0 then
clear()
nPos = nPos - 1
recomplete()
redraw()
end
elseif param1 == "right" then
-- Right
if nPos < #sLine then
-- Move right
clear()
nPos = nPos + 1
recomplete()
redraw()
else
-- Accept autocomplete
acceptCompletion()
end
elseif param1 == "up" or param1 == "down" then
-- Up or down
if nCompletion then
-- Cycle completions
clear()
if param == "up" then
nCompletion = nCompletion - 1
if nCompletion < 1 then
nCompletion = #tCompletions
end
elseif param == "down" then
nCompletion = nCompletion + 1
if nCompletion > #tCompletions then
nCompletion = 1
end
end
redraw()
elseif _tHistory then
-- Cycle history
clear()
if param1 == "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 param1 == "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 param1 == "home" then
-- Home
if nPos > 0 then
clear()
nPos = 0
recomplete()
redraw()
end
elseif param1 == "delete" then
-- Delete
if nPos < #sLine then
clear()
sLine = string.sub(sLine, 1, nPos) .. string.sub(sLine, nPos + 2)
recomplete()
redraw()
end
elseif param1 == "end" then
-- End
if nPos < #sLine then
clear()
nPos = #sLine
recomplete()
redraw()
end
elseif param1 == "tab" then
-- Tab (accept autocomplete)
acceptCompletion()
end
elseif sEvent == "mouse_down" or sEvent == "mouse_move" 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
return io

View file

@ -5,6 +5,10 @@ local bootSleep = 2000
local bg = 0x0 local bg = 0x0
local fg = 0xffffff local fg = 0xffffff
term.setForeground(fg)
term.setBackground(bg)
term.clear()
local function sleep(n) local function sleep(n)
local timerId = timer.start(n) local timerId = timer.start(n)
repeat repeat

View file

@ -26,7 +26,8 @@ public class Bios : IPlugin
private EventEmitter _eventEmitter; private EventEmitter _eventEmitter;
private RuntimeInputEvents _runtimeInputEvents; private RuntimeInputEvents _runtimeInputEvents;
private Drawing _drawing; private Drawing _drawing;
private bool CloseRuntime = false; private static bool CloseRuntime = false;
private static bool OpenBios = false;
public Bios(IGame game) public Bios(IGame game)
{ {
@ -47,12 +48,18 @@ public class Bios : IPlugin
if (CloseRuntime) if (CloseRuntime)
{ {
_runtimeInputEvents.Unregister(); _runtimeInputEvents.Unregister();
_game.LuaRuntime.Close(); _game.LuaRuntime.Close();
CloseRuntime = false; CloseRuntime = false;
StartLuaOS(); if (OpenBios)
{
OpenBios = false;
RunBIOS();
}
else
{
StartLuaOS();
}
} }
Resume(); Resume();
@ -150,6 +157,12 @@ public class Bios : IPlugin
_game.Exit(); _game.Exit();
} }
public static void Reboot()
{
CloseRuntime = true;
OpenBios = true;
}
private int L_OpenDataFolder(IntPtr state) private int L_OpenDataFolder(IntPtr state)
{ {
var path = FileSystem.DataPath; var path = FileSystem.DataPath;

View file

@ -20,8 +20,8 @@ namespace Capy64;
public class Capy64 : Game, IGame public class Capy64 : Game, IGame
{ {
public const string Version = "Capy64 a0.0.1";
public Game Game => this; public Game Game => this;
public string Version => "Capy64 a0.0.1";
public IList<IPlugin> NativePlugins { get; private set; } public IList<IPlugin> NativePlugins { get; private set; }
public IList<IPlugin> Plugins { get; private set; } public IList<IPlugin> Plugins { get; private set; }
public int Width { get; set; } = 400; public int Width { get; set; } = 400;
@ -102,6 +102,8 @@ public class Capy64 : Game, IGame
protected override void Initialize() protected override void Initialize()
{ {
Window.Title = Version;
UpdateSize(); UpdateSize();
Window.AllowUserResizing = false; Window.AllowUserResizing = false;

View file

@ -11,7 +11,6 @@ namespace Capy64;
public interface IGame public interface IGame
{ {
Game Game { get; } Game Game { get; }
public string Version { get; }
IList<IPlugin> NativePlugins { get; } IList<IPlugin> NativePlugins { get; }
IList<IPlugin> Plugins { get; } IList<IPlugin> Plugins { get; }
GameWindow Window { get; } GameWindow Window { get; }

View file

@ -20,14 +20,43 @@ public class OS : IPlugin
public void LuaInit(Lua state) public void LuaInit(Lua state)
{ {
state.GetGlobal("os"); state.GetGlobal("os");
state.PushString("version");
state.PushCFunction(L_Version);
state.SetTable(-3);
state.PushString("shutdown"); state.PushString("shutdown");
state.PushCFunction(L_Shutdown); state.PushCFunction(L_Shutdown);
state.SetTable(-3); state.SetTable(-3);
} }
private static int L_Version(IntPtr state)
{
var L = Lua.FromIntPtr(state);
L.PushString(Capy64.Version);
return 1;
}
private static int L_Shutdown(IntPtr state) private static int L_Shutdown(IntPtr state)
{ {
BIOS.Bios.Shutdown(); var L = Lua.FromIntPtr(state);
var doReboot = false;
if (L.IsBoolean(1))
{
doReboot = L.ToBoolean(1);
}
if (doReboot)
{
BIOS.Bios.Reboot();
}
else
{
BIOS.Bios.Shutdown();
}
return 0; return 0;
} }

View file

@ -46,6 +46,7 @@ public class Runtime
public void Init() public void Init()
{ {
Parent.SetTop(0);
var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua")); var initContent = File.ReadAllText(Path.Combine(FileSystem.DataPath, "init.lua"));
var status = Thread.LoadString(initContent, "=init.lua"); var status = Thread.LoadString(initContent, "=init.lua");
if (status != LuaStatus.OK) if (status != LuaStatus.OK)