mirror of
https://github.com/Ale32bit/Capy64.git
synced 2025-01-18 18:46:43 +00:00
Added paste event.
Added proper shell Added shell commands
This commit is contained in:
parent
6b26e8f66f
commit
ad2166831b
19 changed files with 389 additions and 23 deletions
17
Capy64/Assets/Lua/bin/cd.lua
Normal file
17
Capy64/Assets/Lua/bin/cd.lua
Normal file
|
@ -0,0 +1,17 @@
|
|||
local args = {...}
|
||||
local fs = require("fs")
|
||||
|
||||
local dir = args[1]
|
||||
|
||||
if #args == 0 then
|
||||
dir = shell.homePath
|
||||
end
|
||||
|
||||
dir = fs.combine(shell.getDir(), dir)
|
||||
|
||||
if not fs.exists(dir) or not fs.getAttributes(dir).isDirectory then
|
||||
error("No such directory: " .. dir, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
shell.setDir(dir)
|
5
Capy64/Assets/Lua/bin/clear.lua
Normal file
5
Capy64/Assets/Lua/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)
|
1
Capy64/Assets/Lua/bin/exit.lua
Normal file
1
Capy64/Assets/Lua/bin/exit.lua
Normal file
|
@ -0,0 +1 @@
|
|||
shell.exit()
|
32
Capy64/Assets/Lua/bin/ls.lua
Normal file
32
Capy64/Assets/Lua/bin/ls.lua
Normal file
|
@ -0,0 +1,32 @@
|
|||
local args = { ... }
|
||||
local fs = require("fs")
|
||||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local dir = shell.getDir()
|
||||
|
||||
if args[1] then
|
||||
dir = fs.combine(shell.getDir(), args[1])
|
||||
end
|
||||
|
||||
if not fs.exists(dir) then
|
||||
error("No such directory: " .. dir, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local attr = fs.getAttributes(dir)
|
||||
if not attr.isDirectory then
|
||||
error("No such directory: " .. dir, 0)
|
||||
return false
|
||||
end
|
||||
|
||||
local files = fs.list(dir)
|
||||
for k,v in ipairs(files) do
|
||||
local attr = fs.getAttributes(fs.combine(dir, v))
|
||||
if attr.isDirectory then
|
||||
term.setForeground(colors.lightBlue)
|
||||
print(v .. "/")
|
||||
else
|
||||
term.setForeground(colors.white)
|
||||
print(v)
|
||||
end
|
||||
end
|
|
@ -30,13 +30,13 @@ for k, v in pairs(package.loaded) do
|
|||
end
|
||||
|
||||
term.setForeground(colours.yellow)
|
||||
print("Interactive Lua prompt.")
|
||||
print(_VERSION .. " interactive prompt")
|
||||
print("Call exit() to exit.")
|
||||
term.setForeground(colours.white)
|
||||
|
||||
while bRunning do
|
||||
term.setForeground( colours.yellow )
|
||||
write("lua> ")
|
||||
write("> ")
|
||||
term.setForeground( colours.white )
|
||||
|
||||
local s = io.read(nil, tCommandHistory)
|
||||
|
|
14
Capy64/Assets/Lua/bin/mkdir.lua
Normal file
14
Capy64/Assets/Lua/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 = fs.combine(shell.getDir(), args[1])
|
||||
if fs.exists(dir) then
|
||||
error("Path already exists", 0)
|
||||
end
|
||||
|
||||
fs.makeDir(dir)
|
1
Capy64/Assets/Lua/bin/pwd.lua
Normal file
1
Capy64/Assets/Lua/bin/pwd.lua
Normal file
|
@ -0,0 +1 @@
|
|||
print(shell.getDir())
|
7
Capy64/Assets/Lua/bin/reboot.lua
Normal file
7
Capy64/Assets/Lua/bin/reboot.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
local timer = require("timer")
|
||||
|
||||
print("Goodbye!")
|
||||
|
||||
timer.sleep(1000)
|
||||
|
||||
os.shutdown(true)
|
10
Capy64/Assets/Lua/bin/rm.lua
Normal file
10
Capy64/Assets/Lua/bin/rm.lua
Normal file
|
@ -0,0 +1,10 @@
|
|||
local fs = require("fs")
|
||||
|
||||
local args = {...}
|
||||
if #args == 0 then
|
||||
print("Usage: rm <file>")
|
||||
return
|
||||
end
|
||||
|
||||
local file = fs.combine(shell.getDir(), args[1])
|
||||
fs.delete(file, true)
|
|
@ -1,32 +1,131 @@
|
|||
local term = require("term")
|
||||
local colors = require("colors")
|
||||
local event = require("event")
|
||||
local io = require("io")
|
||||
local fs = require("fs")
|
||||
|
||||
local exit = false
|
||||
local shell = {}
|
||||
|
||||
shell.path = "/bin/?.lua"
|
||||
shell.path = "./?.lua;./?;/bin/?.lua"
|
||||
shell.homePath = "/home"
|
||||
|
||||
local currentDir = shell.homePath
|
||||
|
||||
local function buildEnvironment()
|
||||
return setmetatable({
|
||||
shell = shell,
|
||||
}, { __index = _G })
|
||||
end
|
||||
|
||||
local function printError(...)
|
||||
local cfg = {term.getForeground()}
|
||||
local cbg = {term.getBackground()}
|
||||
|
||||
term.setForeground(colors.red)
|
||||
term.setBackground(colors.black)
|
||||
print(...)
|
||||
term.setForeground(table.unpack(cfg))
|
||||
term.setBackground(table.unpack(cbg))
|
||||
end
|
||||
|
||||
local function tokenise(...)
|
||||
local sLine = table.concat({ ... }, " ")
|
||||
local tWords = {}
|
||||
local bQuoted = false
|
||||
for match in string.gmatch(sLine .. "\"", "(.-)\"") do
|
||||
if bQuoted then
|
||||
table.insert(tWords, match)
|
||||
else
|
||||
for m in string.gmatch(match, "[^ \t]+") do
|
||||
table.insert(tWords, m)
|
||||
end
|
||||
end
|
||||
bQuoted = not bQuoted
|
||||
end
|
||||
return tWords
|
||||
end
|
||||
|
||||
function shell.getDir()
|
||||
|
||||
return currentDir
|
||||
end
|
||||
|
||||
function shell.setDir(path)
|
||||
|
||||
currentDir = path
|
||||
end
|
||||
|
||||
function shell.resolve(path)
|
||||
|
||||
if path:sub(1, 1) == "/" then
|
||||
return path
|
||||
end
|
||||
|
||||
function shell.run(path, args)
|
||||
|
||||
for seg in shell.path:gmatch("[^;]+") do
|
||||
local resolved = seg:gsub("%?", path)
|
||||
if fs.exists(resolved) then
|
||||
return resolved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function shell.run(...)
|
||||
local args = tokenise(...)
|
||||
local command = args[1]
|
||||
local path = shell.resolve(command)
|
||||
|
||||
while true do
|
||||
term.setForeground(colors.yellow)
|
||||
write("$ ")
|
||||
if not path then
|
||||
printError("Command not found: " .. command)
|
||||
return false
|
||||
end
|
||||
|
||||
local env = buildEnvironment()
|
||||
|
||||
local func, err = loadfile(path, "t", env)
|
||||
|
||||
if not func then
|
||||
printError(err)
|
||||
return false
|
||||
end
|
||||
|
||||
local ok, err = pcall(func, table.unpack(args, 2))
|
||||
if not ok then
|
||||
printError(err)
|
||||
return false
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function shell.exit()
|
||||
exit = true
|
||||
end
|
||||
|
||||
local history = {}
|
||||
local lastExecSuccess = true
|
||||
while not exit do
|
||||
term.setForeground(colors.white)
|
||||
local line = io.read()
|
||||
write(":")
|
||||
term.setForeground(colors.lightBlue)
|
||||
local currentDir = shell.getDir()
|
||||
if currentDir == shell.homePath then
|
||||
write("~")
|
||||
else
|
||||
write(currentDir)
|
||||
end
|
||||
|
||||
if lastExecSuccess then
|
||||
term.setForeground(colors.yellow)
|
||||
else
|
||||
term.setForeground(colors.red)
|
||||
end
|
||||
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
|
||||
lastExecSuccess = shell.run(line)
|
||||
end
|
||||
end
|
7
Capy64/Assets/Lua/bin/shutdown.lua
Normal file
7
Capy64/Assets/Lua/bin/shutdown.lua
Normal file
|
@ -0,0 +1,7 @@
|
|||
local timer = require("timer")
|
||||
|
||||
print("Goodbye!")
|
||||
|
||||
timer.sleep(1000)
|
||||
|
||||
os.shutdown(false)
|
32
Capy64/Assets/Lua/bin/wget.lua
Normal file
32
Capy64/Assets/Lua/bin/wget.lua
Normal file
|
@ -0,0 +1,32 @@
|
|||
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 = fs.combine(shell.getDir(), outputName)
|
||||
|
||||
if not http.checkURL(args[1]) then
|
||||
error("Invalid URL", 0)
|
||||
end
|
||||
|
||||
local response, err = http.get(args[1])
|
||||
if not response then
|
||||
error(err, 0)
|
||||
end
|
||||
|
||||
local file = fs.open(outputPath, "w")
|
||||
file:write(response:readAll())
|
||||
file:close()
|
||||
response:close()
|
||||
|
||||
print("File written to " .. outputPath)
|
|
@ -10,4 +10,20 @@ term.setPos(1,1)
|
|||
|
||||
print(os.version())
|
||||
|
||||
dofile("/bin/lua.lua")
|
||||
local func, err = loadfile("/bin/shell.lua")
|
||||
if func then
|
||||
while true do
|
||||
local ok, err = pcall(func)
|
||||
if not ok then
|
||||
print(err)
|
||||
end
|
||||
end
|
||||
else
|
||||
print(err)
|
||||
end
|
||||
|
||||
|
||||
|
||||
print("Press any key to continue...")
|
||||
coroutine.yield("key_down")
|
||||
os.shutdown(false)
|
|
@ -1,4 +1,5 @@
|
|||
using Capy64.Eventing;
|
||||
using Capy64.Core;
|
||||
using Capy64.Eventing;
|
||||
using Capy64.Eventing.Events;
|
||||
using Capy64.LuaRuntime;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
|
@ -109,6 +110,20 @@ internal class RuntimeInputEvents
|
|||
BypassFilter = true,
|
||||
});
|
||||
}
|
||||
else if (e.Key == Keys.V)
|
||||
{
|
||||
if (SDL.HasClipboardText())
|
||||
{
|
||||
var text = SDL.GetClipboardText();
|
||||
_runtime.PushEvent(new LuaEvent()
|
||||
{
|
||||
Name = "paste",
|
||||
Parameters = new[] {
|
||||
text,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
21
Capy64/Core/SDL.cs
Normal file
21
Capy64/Core/SDL.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Capy64.Extensions.Bindings;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Capy64.Core;
|
||||
|
||||
public class SDL
|
||||
{
|
||||
public static string GetClipboardText()
|
||||
{
|
||||
return SDL2.UTF8_ToManaged(SDL2.Native_SDL_GetClipboardText(), true);
|
||||
}
|
||||
|
||||
public static bool HasClipboardText()
|
||||
{
|
||||
return SDL2.SDL_HasClipboardText() == 1;
|
||||
}
|
||||
}
|
|
@ -9,9 +9,83 @@ public partial class SDL2
|
|||
|
||||
[LibraryImport(SDL)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
public static partial void SDL_MaximizeWindow(IntPtr window);
|
||||
internal static partial void SDL_MaximizeWindow(IntPtr window);
|
||||
|
||||
[LibraryImport(SDL)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
public static partial uint SDL_GetWindowFlags(IntPtr window);
|
||||
internal static partial uint SDL_GetWindowFlags(IntPtr window);
|
||||
|
||||
[LibraryImport(SDL, EntryPoint = "SDL_GetClipboardText")]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial IntPtr Native_SDL_GetClipboardText();
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns>0 is false; 1 is true</returns>
|
||||
[LibraryImport(SDL)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial int SDL_HasClipboardText();
|
||||
|
||||
[LibraryImport(SDL)]
|
||||
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvCdecl) })]
|
||||
internal static partial void SDL_free(IntPtr memblock);
|
||||
|
||||
/// <summary>
|
||||
/// https://github.com/flibitijibibo/SDL2-CS/blob/master/src/SDL2.cs#L128
|
||||
/// </summary>
|
||||
/// <param name="s"></param>
|
||||
/// <param name="freePtr"></param>
|
||||
/// <returns></returns>
|
||||
public static unsafe string UTF8_ToManaged(IntPtr s, bool freePtr = false)
|
||||
{
|
||||
if (s == IntPtr.Zero)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/* We get to do strlen ourselves! */
|
||||
byte* ptr = (byte*)s;
|
||||
while (*ptr != 0)
|
||||
{
|
||||
ptr++;
|
||||
}
|
||||
|
||||
/* TODO: This #ifdef is only here because the equivalent
|
||||
* .NET 2.0 constructor appears to be less efficient?
|
||||
* Here's the pretty version, maybe steal this instead:
|
||||
*
|
||||
string result = new string(
|
||||
(sbyte*) s, // Also, why sbyte???
|
||||
0,
|
||||
(int) (ptr - (byte*) s),
|
||||
System.Text.Encoding.UTF8
|
||||
);
|
||||
* See the CoreCLR source for more info.
|
||||
* -flibit
|
||||
*/
|
||||
#if NETSTANDARD2_0
|
||||
/* Modern C# lets you just send the byte*, nice! */
|
||||
string result = System.Text.Encoding.UTF8.GetString(
|
||||
(byte*) s,
|
||||
(int) (ptr - (byte*) s)
|
||||
);
|
||||
#else
|
||||
/* Old C# requires an extra memcpy, bleh! */
|
||||
int len = (int)(ptr - (byte*)s);
|
||||
if (len == 0)
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
char* chars = stackalloc char[len];
|
||||
int strLen = System.Text.Encoding.UTF8.GetChars((byte*)s, len, chars, len);
|
||||
string result = new string(chars, 0, strLen);
|
||||
#endif
|
||||
|
||||
/* Some SDL functions will malloc, we have to free! */
|
||||
if (freePtr)
|
||||
{
|
||||
SDL_free(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -117,7 +117,16 @@ public class FileSystem : IPlugin
|
|||
var absolutePath = Path.GetFullPath(path, rootPath);
|
||||
|
||||
// Trim root from path
|
||||
return absolutePath.Remove(0, rootPath.Length);
|
||||
string localPath;
|
||||
try
|
||||
{
|
||||
localPath = absolutePath.Remove(0, rootPath.Length);
|
||||
}
|
||||
catch
|
||||
{
|
||||
localPath = absolutePath;
|
||||
}
|
||||
return Path.Join("/", localPath);
|
||||
}
|
||||
|
||||
public static string Resolve(string path)
|
||||
|
@ -210,7 +219,7 @@ public class FileSystem : IPlugin
|
|||
parts.Add(pathPart);
|
||||
}
|
||||
|
||||
var result = Path.Join(parts.ToArray());
|
||||
var result = Path.Combine(parts.ToArray());
|
||||
if (string.IsNullOrEmpty(result))
|
||||
{
|
||||
L.PushString("");
|
||||
|
|
|
@ -16,6 +16,7 @@ public class HTTP : IPlugin
|
|||
private static HttpClient _client;
|
||||
private static long RequestId;
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly LuaRegister[] HttpLib = new LuaRegister[]
|
||||
{
|
||||
new()
|
||||
|
@ -36,10 +37,12 @@ public class HTTP : IPlugin
|
|||
RequestId = 0;
|
||||
_client = new();
|
||||
_client.DefaultRequestHeaders.Add("User-Agent", $"Capy64/{Capy64.Version}");
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public void LuaInit(Lua L)
|
||||
{
|
||||
if (_configuration.GetValue<bool>("HTTP:Enable"))
|
||||
L.RequireF("http", Open, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,11 +120,14 @@ public class Runtime
|
|||
{
|
||||
if (Disposing)
|
||||
return false;
|
||||
if (status == LuaStatus.Yield)
|
||||
{
|
||||
filters = new string[pars];
|
||||
for (int i = 0; i < pars; i++)
|
||||
{
|
||||
filters[i] = Thread.OptString(i + 1, null);
|
||||
}
|
||||
}
|
||||
Thread.Pop(pars);
|
||||
return status == LuaStatus.Yield;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue