From e9cf9789b7ddcf5078d508f74fae42030c5fc1ba Mon Sep 17 00:00:00 2001 From: Ponali Date: Sat, 19 Apr 2025 16:09:27 +0200 Subject: [PATCH] 0.5.0 - ANSI color escape code support I got asked to do the ANSI escape codes for color because Wahlolly couldn't find a way to get them to work. This involved making some kind of TTY, and let me be clear, i didn't know it would be so tedious to develop one. This implementation has support for dark and bright colors by using a color palette. There isn't 8-bit and 24-bit color yet, nor is there a shell parser, so please wait a bit more when it gets implemented. --- halyde/config/shell.cfg | 5 + .../{startupapps.txt => startupapps.cfg} | 2 +- halyde/core/boot.lua | 4 +- halyde/core/cormgr.lua | 4 +- halyde/core/datatools.lua | 2 +- halyde/core/shell.lua | 12 +- halyde/lib/termlib.lua | 164 ++++++++++++++++-- 7 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 halyde/config/shell.cfg rename halyde/config/{startupapps.txt => startupapps.cfg} (76%) diff --git a/halyde/config/shell.cfg b/halyde/config/shell.cfg new file mode 100644 index 0000000..4c460ae --- /dev/null +++ b/halyde/config/shell.cfg @@ -0,0 +1,5 @@ +local shellcfg = { + ["prompt"] = "\x1b[92m%s > \x1b[0m" -- shell prompt, %s will be replaced with working directory. example: "%s > " turns to "/current/working/directory > " +} + +return shellcfg diff --git a/halyde/config/startupapps.txt b/halyde/config/startupapps.cfg similarity index 76% rename from halyde/config/startupapps.txt rename to halyde/config/startupapps.cfg index 5be8336..0f8086c 100644 --- a/halyde/config/startupapps.txt +++ b/halyde/config/startupapps.cfg @@ -1,4 +1,4 @@ /halyde/core/datatools.lua /halyde/core/fullkb.lua /halyde/core/evmgr.lua -/halyde/core/shell.lua \ No newline at end of file +/halyde/core/shell.lua diff --git a/halyde/core/boot.lua b/halyde/core/boot.lua index 700300e..8324790 100644 --- a/halyde/core/boot.lua +++ b/halyde/core/boot.lua @@ -1,7 +1,7 @@ local loadfile = ... local filesystem = loadfile("/halyde/lib/filesystem.lua")(loadfile) -_G._OSVERSION = "Halyde 0.4.1" +_G._OSVERSION = "Halyde 0.5.0" function _G.import(module, ...) local args = table.pack(...) @@ -28,4 +28,4 @@ end --assert(handle:write("Bazinga!")) --handle:close() -import("/halyde/core/cormgr.lua") \ No newline at end of file +import("/halyde/core/cormgr.lua") diff --git a/halyde/core/cormgr.lua b/halyde/core/cormgr.lua index ee46034..053fab0 100644 --- a/halyde/core/cormgr.lua +++ b/halyde/core/cormgr.lua @@ -35,7 +35,7 @@ local function runCoroutines() end end -local handle = filesystem.open("/halyde/config/startupapps.txt", "r") +local handle = filesystem.open("/halyde/config/startupapps.cfg", "r") local data = "" local tmpdata repeat @@ -58,4 +58,4 @@ while true do if #_G.cormgr.corList == 0 then computer.shutdown() end -end \ No newline at end of file +end diff --git a/halyde/core/datatools.lua b/halyde/core/datatools.lua index 83eab76..e958ed5 100644 --- a/halyde/core/datatools.lua +++ b/halyde/core/datatools.lua @@ -19,4 +19,4 @@ function table.copy(orig) copy = orig end return copy -end \ No newline at end of file +end diff --git a/halyde/core/shell.lua b/halyde/core/shell.lua index a71e998..8abbcb1 100644 --- a/halyde/core/shell.lua +++ b/halyde/core/shell.lua @@ -1,3 +1,4 @@ +local shellcfg = import("/halyde/config/shell.cfg") import("termlib") local event = import("event") @@ -7,10 +8,11 @@ _G.shell.workingDirectory = "/" print("\n │\n │ ".._OSVERSION..'\n │ Welcome! Type "help" to get started.\n │\n ') while true do coroutine.yield() - print(shell.workingDirectory .. " >") - termlib.nextPosX = #(shell.workingDirectory .. " > ") - termlib.nextPosY = termlib.nextPosY - 1 + -- print(shell.workingDirectory .. " >") + print(shellcfg["prompt"]:format(shell.workingDirectory),false) + -- termlib.cursorPosX = #(shell.workingDirectory .. " > ") + -- termlib.cursorPosY = termlib.cursorPosY - 1 read() - termlib.nextPosX = 1 + termlib.cursorPosX = 1 print("no shell parser yet") -end \ No newline at end of file +end diff --git a/halyde/lib/termlib.lua b/halyde/lib/termlib.lua index 2ca0887..84b4c69 100644 --- a/halyde/lib/termlib.lua +++ b/halyde/lib/termlib.lua @@ -2,40 +2,166 @@ local event = import("event") --local keyboard = import("keyboard") local gpu = component.proxy(component.list("gpu")()) -- replace with component.gpu once implemented -local ocelot = component.proxy(component.list("ocelot")()) +--local ocelot = component.proxy(component.list("ocelot")()) _G.termlib = {} -termlib.nextPosX = 1 -termlib.nextPosY = 1 termlib.cursorPosX = 1 termlib.cursorPosY = 1 -function _G.print(text) - local xRes, yRes = gpu.getResolution() +local width, height = gpu.getResolution() +termlib.width = width +termlib.height = height + +local ANSIColorPalette = { + ["dark"] = { + [0] = 0x000000, + [1] = 0x800000, + [2] = 0x008000, + [3] = 0x808000, + [4] = 0x000080, + [5] = 0x800080, + [6] = 0x008080, + [7] = 0xC0C0C0 + }, + ["bright"] = { + [0] = 0x808080, + [1] = 0xFF0000, + [2] = 0x00FF00, + [3] = 0xFFFF00, + [4] = 0x0000FF, + [5] = 0xFF00FF, + [6] = 0x00FFFF, + [7] = 0xFFFFFF + } +} + +defaultForegroundColor = ANSIColorPalette["dark"][7] +defaultBackgroundColor = ANSIColorPalette["dark"][0] + +gpu.setForeground(defaultForegroundColor) +gpu.setBackground(defaultBackgroundColor) + +function _G.scrollDown() + if gpu.copy(0,1,width,height-1,0,-1) then + gpu.set(0,height-1,string.rep(" ",width)) + end +end + +function _G.newLine() + termlib.cursorPosX=1 + termlib.cursorPosY = termlib.cursorPosY + 1 + if termlib.cursorPosY>height then + _G.scrollDown() + termlib.cursorPosY=height + end +end + +function parseCodeNumbers(code) + o = {} + for num in code:sub(3,-2):gmatch("[^;]+") do + table.insert(o,tonumber(num)) + end + return o +end + +function _G.print(text,endNewLine) + + -- you don't know how tiring this took just for ANSI escape code support + + if endNewLine==nil then + endNewLine = true + end + if not text or not tostring(text) then return end - local printText = tostring(text):gsub("\t", " ") - for line in printText:gmatch("([^\n]*)\n?") do - while #line > xRes do - gpu.set(termlib.nextPosX, termlib.nextPosY, line:sub(1,xRes)) - line = line:sub(xRes+1) - termlib.nextPosY = termlib.nextPosY + 1 - termlib.nextPosX = 1 + text = tostring(text) + readBreak = 0 + -- readBreak is for when, inside the for loop, there normally would have been an increase in the "i" variable because it has read more than one character. + -- unfortunately, changing the "i" variable would have unpredictable effects, so to not risk anything, this workaround was done. + section = "" + + function printSection() + if #section==0 then + return end - gpu.set(termlib.nextPosX, termlib.nextPosY, line) - termlib.nextPosY = termlib.nextPosY + 1 + gpu.set(termlib.cursorPosX,termlib.cursorPosY,section) + termlib.cursorPosX = termlib.cursorPosX+unicode.wlen(section) + if termlib.cursorPosX>width then + newLine() + end + section="" + end + + for i=1,#text do + if readBreak>0 then + readBreak = readBreak - 1 + goto continue + end + + if string.byte(text,i)==10 then + printSection() + newLine() + elseif string.byte(text,i)==13 then + printSection() + termlib.cursorPosX=1 + elseif string.byte(text,i)==0x1b and i<=#text-2 then + printSection() + --ocelot.log("0x1b char detected") + codeType = string.sub(text,i+1,i+1) + if codeType=="[" then + -- Control Sequence Introducer + --ocelot.log("Control Sequence Introducer") + codeEndIdx = string.find(text,"m",i) + code = string.sub(text,i,codeEndIdx) + --ocelot.log("Code: "..code.." ("..i..", "..codeEndIdx..")") + readBreak = readBreak + #code - 1 + nums = parseCodeNumbers(code) + codeEnd = code:sub(-1) + --ocelot.log("Code end: "..codeEnd..", "..#codeEnd) + if codeEnd == "m" then + -- Select Graphic Rendition + --ocelot.log("Select Graphic Rendition, ID "..nums[1]) + if nums[1]>=30 and nums[1]<=37 then + gpu.setForeground(ANSIColorPalette["dark"][nums[1]%10]) + end + if nums[1]==39 or nums[1]==0 then + gpu.setForeground(defaultForegroundColor) + end + if nums[1]>=40 and nums[1]<=47 then + gpu.setBackground(ANSIColorPalette["dark"][nums[1]%10]) + end + if nums[1]==49 or nums[1]==0 then + gpu.setBackground(defaultBackgroundColor) + end + if nums[1]>=90 and nums[1]<=97 then + gpu.setForeground(ANSIColorPalette["bright"][nums[1]%10]) + end + if nums[1]>=100 and nums[1]<=107 then + gpu.setBackground(ANSIColorPalette["bright"][nums[1]%10]) + end + end + end + else + --gpu.set(termlib.cursorPosX,termlib.cursorPosY,string.sub(text,i,i)) + section = section..string.sub(text,i,i) + end + ::continue:: + end + printSection() + if endNewLine then + newLine() end end function _G.clear() local xRes, yRes = gpu.getResolution() gpu.fill(1,1,xRes,yRes," ") - termlib.nextPosX, termlib.nextPosY = 1, 1 + termlib.cursorPosX, termlib.cursorPosY = 1, 1 end function _G.read() local curtext = "" - local nextPosX, nextPosY = termlib.nextPosX, termlib.nextPosY + local cursorPosX, cursorPosY = termlib.cursorPosX, termlib.cursorPosY local cursorWhite = true while true do local args = {event.pull("key_down", 0.5)} @@ -48,17 +174,17 @@ function _G.read() else if key == "back" then curtext = curtext:sub(1, #curtext-1) - termlib.nextPosX, termlib.nextPosY = nextPosX, nextPosY + termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY print(curtext.." ") elseif key == "enter" then return curtext end end - termlib.nextPosX, termlib.nextPosY = nextPosX, nextPosY + termlib.cursorPosX, termlib.cursorPosY = cursorPosX, cursorPosY print(curtext) else cursorWhite = not cursorWhite end end -end \ No newline at end of file +end -- 2.54.0