Author SHA1 Message Date
mcplayer3 0ae00dd61a Apparently I left in some unused variables that were supposed to be removed by Tema 2026-06-20 22:21:14 +10:00
mcplayer3 593e8a8512 Started to add custom colour palettes
I have literally no idea why it wont work but if anyone picks this up you'll know it works when the old Halyde colours show up
2026-06-20 22:19:36 +10:00
107 changed files with 883 additions and 1566 deletions
View File
+1
View File
@@ -0,0 +1 @@
{"palette":{"dark":{"0":"171421","1":"c01c28","2":"26a269","3":"a2734c","4":"12488b","5":"a347ba","6":"2aa1b3","7":"d0cfcc"},"bright":{"0":"5e5c64","1":"f66151","2":"33d17a","3":"e9ad0c","4":"2a7bde","5":"c061cb","6":"33c7de","7":"ffffff"}}}
+842
View File
@@ -0,0 +1,842 @@
--[[
TODO:
```bash
echo -e "\033[?25l" # hide
echo -e "\033[?25h" # show
```
]]
local module = {}
function module.check()
return true -- Usually always loaded, but maybe it would be worth it to check if the computer has a GPU or not? I'm not sure.
end
function module.init()
local serialize = require("serialize")
local unicode = require("unicode")
local event = require("event")
local fs = require("filesystem")
local json = require("json")
local component = require("component")
local gpu = component.gpu
_G._PUBLIC.terminal = {}
local readHistory = {}
function _PUBLIC.terminal.getHistory(id)
checkArg(1,id,"string")
return table.copy(readHistory[id])
end
function _PUBLIC.terminal.setHistory(id,hist)
checkArg(1,id,"string")
checkArg(2,hist,"table")
for i=1,#hist do
hist[i]=tostring(hist[i])
end
readHistory[id]=hist
end
function _PUBLIC.terminal.addToHistory(id,hist)
checkArg(1,id,"string")
checkArg(2,hist,"string")
table.insert(readHistory[id],hist)
end
if not fs.exists("/halyde/config/terminal.json") then
fs.copy("/halyde/config/generate/terminal.json", "/halyde/config/terminal.json")
end
local config = ""
local tmpdata
local handle = fs.open("/halyde/config/terminal.json")
repeat
tmpdata = handle:read(math.huge)
config = config .. (tmpdata or "")
until not tmpdata
config = json.decode(config)
require("log").terminal.info("Loaded config file: " .. serialize(config, " "))
local function getColorPalette(depth)
if depth == 1 then
return {
["dark"] = {
[0] = 0x000000,
[1] = 0xffffff,
[2] = 0xffffff,
[3] = 0xffffff,
[4] = 0xffffff,
[5] = 0xffffff,
[6] = 0xffffff,
[7] = 0xffffff,
},
["bright"] = {
[0] = 0x000000,
[1] = 0xffffff,
[2] = 0xffffff,
[3] = 0xffffff,
[4] = 0xffffff,
[5] = 0xffffff,
[6] = 0xffffff,
[7] = 0xffffff,
}
}
end
if depth == 4 then
return {
-- Closest colors to the 4 bit OC pallete
-- Better than outright failure
["dark"] = {
[0] = 0x000000, -- black
[1] = 0x663300, -- brown (dark red)
[2] = 0x336600, -- green (dark green)
[3] = 0x336600, -- green (dark yellow)
[4] = 0x333399, -- blue (dark blue)
[5] = 0x9933CC, -- purple (dark purple)
[6] = 0x333399, -- blue (dark cyan)
[7] = 0xCCCCCC -- silver (dark white)
},
["bright"] = {
[0] = 0x333333, -- gray (bright black)
[1] = 0xff3333, -- red
[2] = 0x33cc33, -- lime (green)
[3] = 0xffff33, -- yellow
[4] = 0x333399, -- blue
[5] = 0xcc66cc, -- magenta (purple)
[6] = 0x336699, -- cyan
[7] = 0xffffff -- white
}
}
end
if depth == 8 then
local palette = {["dark"] = {}, ["bright"] = {}}
for i = 0, 7 do
palette["dark"][i] = tonumber(config.palette.dark[tostring(i)], 16)
end
for i = 0, 7 do
palette["bright"][i] = tonumber(config.palette.bright[tostring(i)], 16)
end
require("log").terminal.info("Set colour palette: " .. serialize(palette, " "))
return palette
--[[ return {
["dark"] = {
[0] = 0x0f0f0f, -- black
[1] = 0xcc2424, -- dark red
[2] = 0x339280, -- dark green
[3] = 0x996d00, -- dark yellow
[4] = 0x004980, -- dark blue
[5] = 0x9949c0, -- dark purple
[6] = 0x33b6c0, -- dark cyan
[7] = 0xc3c3c3 -- dark white
},
["bright"] = {
[0] = 0x666d80, -- brighter black
[1] = 0xff6d40, -- red
[2] = 0x33db80, -- green
[3] = 0xffb600, -- yellow
[4] = 0x336dff, -- blue
[5] = 0xcc6dc0, -- purple
[6] = 0x33dbc0, -- cyan
[7] = 0xffffff -- white
}
} ]]
end
--[[ Original color palette:
{
["dark"] = {
[0] = 0x171421,
[1] = 0xc01c28,
[2] = 0x26a269,
[3] = 0xa2734c,
[4] = 0x12488b,
[5] = 0xa347ba,
[6] = 0x2aa1b3,
[7] = 0xd0cfcc
},
["bright"] = {
[0] = 0x5e5c64,
[1] = 0xf66151,
[2] = 0x33d17a,
[3] = 0xe9ad0c,
[4] = 0x2a7bde,
[5] = 0xc061cb,
[6] = 0x33c7de,
[7] = 0xffffff
}
}
]]
-- Shouldn't reach here
error()
end
local ANSIColorPalette = getColorPalette(gpu.maxDepth())
local cursor = { x = 1, y = 1, X = nil, Y = nil } -- X and Y are managed by ESC s and ESC u
local printState = 0 -- 0:none 1:in ESC 2:in CSI
local color = {
FG = ANSIColorPalette["bright"][7], BG = ANSIColorPalette["dark"][0],
fg = nil, bg = nil, reverse = false
}
require("log").terminal.info("FG and BG: " .. color.FG .. " " .. color.BG)
color.fg = color.FG
color.bg = color.BG
local current_codepoint = 0
local bytes_remaining = 0
local seq = {}
local writeBuf = {}
local function update_gpu_colors()
if color.reverse then
gpu.setForeground(color.bg)
gpu.setBackground(color.fg)
else
gpu.setForeground(color.fg)
gpu.setBackground(color.bg)
end
end
local width, height = gpu.getResolution()
function _G._PUBLIC.terminal.getResolution()
return width, height
end
gpu.setForeground(color.fg)
gpu.setBackground(color.bg)
local function scroll()
if gpu.copy(1, 2, width, height - 1, 0, -1) then
gpu.setForeground(color.FG)
gpu.setBackground(color.BG)
gpu.fill(1, height, width, 1, " ")
gpu.setForeground(color.fg)
gpu.setBackground(color.bg)
cursor.y = height
end
end
local function check_wrap_and_scroll()
if cursor.x > width then
cursor.x = 1
cursor.y = cursor.y + 1
end
while cursor.y > height do
scroll()
end
end
local function exec_csi()
local params = {}
local op = 0
local current_num = 0
local have_num = false
for i = 1, #seq do
local byte = seq[i]
if 0x30 <= byte and byte <= 0x39 then
current_num = current_num * 10 + (byte - 0x30)
have_num = true
elseif byte == 0x3b then
table.insert(params, have_num and current_num or 0)
current_num = 0
have_num = false
else
if have_num then
table.insert(params, current_num)
end
if 0x40 <= byte and byte <= 0x7e then
op = byte
end
break
end
end
local function get_param(idx, default)
if idx <= #params and params[idx] ~= nil then
return params[idx]
end
return default
end
if op == 0x48 or op == 0x66 then
local row = get_param(1, 1)
local col = get_param(2, 1)
cursor.y = row
cursor.x = col
if cursor.x < 1 then cursor.x = 1 end
if cursor.y < 1 then cursor.y = 1 end
if cursor.x > width then cursor.x = width end
if cursor.y > height then cursor.y = height end
return
end
if op == 0x41 then
local n = get_param(1, 1)
cursor.y = cursor.y - n
if cursor.y < 1 then cursor.y = 1 end
return
end
if op == 0x42 then
local n = get_param(1, 1)
cursor.y = cursor.y + n
if cursor.y > height then cursor.y = height end
return
end
if op == 0x43 then
local n = get_param(1, 1)
cursor.x = cursor.x + n
if cursor.x > width then cursor.x = width end
return
end
if op == 0x44 then
local n = get_param(1, 1)
cursor.x = cursor.x - n
if cursor.x < 1 then cursor.x = 1 end
return
end
if op == 0x47 then
local col = get_param(1, 1)
cursor.x = col
if cursor.x < 1 then cursor.x = 1 end
if cursor.x > width then cursor.x = width end
return
end
if op == 0x4a then
local mode = get_param(1, 0)
if mode == 0 then
update_gpu_colors()
gpu.fill(cursor.x, cursor.y, width - cursor.x + 1, height - cursor.y + 1, " ")
elseif mode == 1 then
update_gpu_colors()
gpu.fill(1, 1, cursor.x, cursor.y, " ")
elseif mode == 2 then
update_gpu_colors()
gpu.fill(1, 1, width, height, " ")
cursor.x = 1
cursor.y = 1
end
return
end
if op == 0x4b then
local mode = get_param(1, 0)
if mode == 0 then
update_gpu_colors()
gpu.fill(cursor.x, cursor.y, width - cursor.x + 1, 1, " ")
elseif mode == 1 then
update_gpu_colors()
gpu.fill(1, cursor.y, cursor.x, 1, " ")
elseif mode == 2 then
update_gpu_colors()
gpu.fill(1, cursor.y, width, 1, " ")
end
return
end
if op == 0x60 then
local col = get_param(1, 1)
cursor.x = col
if cursor.x < 1 then cursor.x = 1 end
if cursor.x > width then cursor.x = width end
return
end
if op == 0x64 then
local row = get_param(1, 1)
cursor.y = row
if cursor.y < 1 then cursor.y = 1 end
if cursor.y > height then cursor.y = height end
return
end
if op == 0x6d then
local j = 1
local function parse_extended_color()
local mode = get_param(j + 1, -1)
if mode == 5 then
local idx = get_param(j + 2, 0)
j = j + 2
if idx < 8 then
return ANSIColorPalette["dark"][idx]
elseif idx < 16 then
return ANSIColorPalette["bright"][idx - 8]
elseif idx < 232 then
local i = idx - 16
local b = (i % 6) * 51
local g = ((i // 6) % 6) * 51
local r = (i // 36) * 51
return (r << 16) | (g << 8) | b
else
local v = (idx - 232) * 10 + 8
return (v << 16) | (v << 8) | v
end
elseif mode == 2 then
local r = get_param(j + 2, 0)
local g = get_param(j + 3, 0)
local b = get_param(j + 4, 0)
j = j + 4
return (r << 16) | (g << 8) | b
end
return nil
end
if #params == 0 then
color.reverse = false
color.fg = color.FG
color.bg = color.BG
update_gpu_colors()
return
end
while j <= #params do
local p = params[j] or 0
if p == 0 then
color.reverse = false
color.fg = color.FG
color.bg = color.BG
elseif p == 1 then
elseif p == 2 then
elseif p == 3 then
elseif p == 4 then
elseif p == 5 or p == 6 then
elseif p == 7 then
color.reverse = true
elseif p == 8 then
color.fg = color.bg
elseif p == 9 then
elseif p == 21 then
elseif p == 22 then
elseif p == 23 then
elseif p == 24 then
elseif p == 25 then
elseif p == 27 then
color.reverse = false
elseif p == 28 then
color.fg = color.FG
elseif p == 29 then
elseif 30 <= p and p <= 37 then
color.fg = ANSIColorPalette["dark"][p - 30]
elseif p == 38 then
local c = parse_extended_color()
if c then color.fg = c end
elseif p == 39 then
color.fg = color.FG
elseif 40 <= p and p <= 47 then
color.bg = ANSIColorPalette["dark"][p - 40]
elseif p == 48 then
local c = parse_extended_color()
if c then color.bg = c end
elseif p == 49 then
color.bg = color.BG
elseif p == 58 then
parse_extended_color()
elseif p == 59 then
elseif 90 <= p and p <= 97 then
color.fg = ANSIColorPalette["bright"][p - 90]
elseif 100 <= p and p <= 107 then
color.bg = ANSIColorPalette["bright"][p - 100]
end
j = j + 1
end
update_gpu_colors()
return
end
if op == 0x73 then
cursor.X = cursor.x
cursor.Y = cursor.y
return
end
if op == 0x75 then
if cursor.X and cursor.Y then
cursor.x = cursor.X
cursor.y = cursor.Y
if cursor.x < 1 then cursor.x = 1 end
if cursor.y < 1 then cursor.y = 1 end
if cursor.x > width then cursor.x = width end
if cursor.y > height then cursor.y = height end
end
return
end
end
function _G._PUBLIC.terminal.writec(byte)
if byte == 0x1b then
_PUBLIC.terminal.flush()
printState = 1
seq = {}
return
end
if printState == 1 then
if byte == 0x5b then
printState = 2
else
printState = 0
end
return
end
if printState == 2 then
table.insert(seq, byte)
if 0x40 <= byte and byte <= 0x7e then
exec_csi()
printState = 0
seq = {}
end
return
end
if byte == 0xa then
_PUBLIC.terminal.flush()
cursor.y = cursor.y + 1
cursor.x = 1
check_wrap_and_scroll()
return
end
if byte == 0xd then
_PUBLIC.terminal.flush()
cursor.x = 1
return
end
if byte == 0x8 then
_PUBLIC.terminal.flush()
if cursor.x > 1 then
cursor.x = cursor.x - 1
end
return
end
if byte == 0x9 then
_PUBLIC.terminal.flush()
cursor.x = ((cursor.x - 1) // 8) * 8 + 9
if cursor.x < 1 then cursor.x = 1 end
if cursor.x > width then cursor.x = width end
return
end
if byte >= 0x20 and byte <= 0x7F then
table.insert(writeBuf, string.char(byte))
cursor.x = cursor.x + 1
check_wrap_and_scroll()
if cursor.y ~= writeBufY or #writeBuf >= 32 then
_PUBLIC.terminal.flush()
end
elseif byte >= 0xC2 and byte <= 0xDF then
current_codepoint = (byte & 0x1F)
bytes_remaining = 1
elseif byte >= 0xE0 and byte <= 0xEF then
current_codepoint = (byte & 0x0F)
bytes_remaining = 2
elseif byte >= 0xF0 and byte <= 0xF7 then
current_codepoint = (byte & 0x07)
bytes_remaining = 3
elseif byte >= 0x80 and byte <= 0xBF and bytes_remaining > 0 then
current_codepoint = (current_codepoint << 6) | (byte & 0x3F)
bytes_remaining = bytes_remaining - 1
if bytes_remaining == 0 then
table.insert(writeBuf, utf8.char(current_codepoint))
cursor.x = cursor.x + 1
check_wrap_and_scroll()
if cursor.y ~= writeBufY or #writeBuf >= 32 then
_PUBLIC.terminal.flush()
end
current_codepoint = 0
end
else
current_codepoint = 0
bytes_remaining = 0
end
end
function _G._PUBLIC.terminal.write(text)
text = tostring(text)
for i = 1, #text do
_PUBLIC.terminal.writec(string.byte(text, i))
end
end
function _G._PUBLIC.terminal.clear()
update_gpu_colors()
gpu.fill(1, 1, width, height, " ")
writeBuf = {}
cursor.x = 1
cursor.y = 1
end
function _G._PUBLIC.terminal.flush()
if #writeBuf == 0 then return end
update_gpu_colors()
gpu.set(cursor.x - #writeBuf, cursor.y, table.concat(writeBuf))
writeBuf = {}
end
function _G.print(...)
local args = {...}
local stringArgs = {}
for _, arg in pairs(args) do
if type(arg)=="table" then
table.insert(stringArgs, serialize(arg))
elseif tostring(arg) then
table.insert(stringArgs, tostring(arg))
end
end
_PUBLIC.terminal.write(table.concat(stringArgs, "\t") .. "\n")
end
function _G._PUBLIC.terminal.read(options)
checkArg(1, options, "table", "nil")
local function checkOption(name, value, neededType)
assert(not value or type(value) == neededType, ("%s option must be %s, %s provided"):format(name, neededType, type(value)))
end
if not options then
options = {}
end
checkOption("readHistoryType", options.readHistoryType, "string")
checkOption("prefix", options.prefix, "string")
checkOption("maxChars", options.maxChars, "number")
checkOption("defaultText", options.defaultText, "string")
checkOption("censor", options.censor, "string")
options.maxChars = options.maxChars or math.huge
local text = options.defaultText or ""
_G._PUBLIC.terminal.flush()
local historyIdx
if options.readHistoryType then
if not readHistory[options.readHistoryType] then
readHistory[options.readHistoryType] = {text}
elseif readHistory[options.readHistoryType][#readHistory[options.readHistoryType] ] ~= text then
table.insert(readHistory[options.readHistoryType], text)
end
historyIdx = #readHistory[options.readHistoryType]
end
local function updateHistory()
if not options.readHistoryType then return end
if historyIdx ~= #readHistory[options.readHistoryType] then return end
readHistory[options.readHistoryType][historyIdx]=text
end
local cur = unicode.len(text)+1
if options.prefix then _PUBLIC.terminal.write(options.prefix) end
local startX, startY = cursor.x, cursor.y
local fg, bg = gpu.getForeground(), gpu.getBackground()
local cursorBlink = true
local function checkScroll(y)
for i=1,y-height do
scrollDown()
startY=startY-1
end
return math.min(y,height)
end
local function set(index, character, invertedColors) -- HACK: Currently, this will uncensor all spaces in the inputted text.
if character==nil or character=="" then return end
if options.censor then
character = character:gsub("[^ ]", options.censor)
end
if invertedColors then
gpu.setForeground(bg)
gpu.setBackground(fg)
else
gpu.setForeground(fg)
gpu.setBackground(bg)
end
index=startX+index-1
local setX, setY = (index-1)%width+1, startY+((index-1)//width+1)-1
setY = checkScroll(setY)
gpu.set(setX,setY,unicode.sub(character,1,width-setX+1))
for i=1,math.ceil((#character+setX-1)/width)+1 do
gpu.set(1,setY+i,unicode.sub(character,2-setX+i*width,width+i*width-setX))
setY = checkScroll(setY)
end
end
local function strDef(a,b)
if #a==0 then return b end
return a
end
local function curPos(cur)
return unicode.wlen(unicode.sub(text,1,cur-1))+1
end
local function add(chr)
if type(chr)~="string" or #chr==0 then return end
if unicode.len(text)>=options.maxChars then return end
if options.maxChars<math.huge then
chr=unicode.sub(chr,1,options.maxChars-unicode.len(text))
end
text=unicode.sub(text,1,cur-1)..chr..unicode.sub(text,cur)
set(curPos(cur),chr,false)
cur=math.min(cur+unicode.len(chr),options.maxChars+1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
set(curPos(cur+1),unicode.sub(text,cur+1),false)
end
local function moveCur(dir)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
cur=math.max(math.min(cur+dir,unicode.len(text)+1),1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
end
local function isLetter(chr)
return not string.find("\x09 :@-./_~?&=%+#",chr,1,true)
end
local function nextCur(dir,chr,icur)
if icur==nil then icur=cur end
local next = math.max(math.min(icur+dir,unicode.len(text)+1),1)
if chr then return unicode.sub(text,next,next) end
return next
end
local function curAfterWord(dir)
local ncur = cur
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==1) do
ncur=nextCur(dir,false,ncur)
end
while nextCur(dir,false,ncur)~=ncur and isLetter(nextCur(dir,true,ncur))==(dir==-1) do
ncur=nextCur(dir,false,ncur)
end
return ncur
end
local function moveWord(dir)
if nextCur(dir)==cur then return end
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
cur=curAfterWord(dir)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
end
local function deleteWord(dir)
local after = curAfterWord(dir)
local lenb = unicode.wlen(text)
if dir==1 then
text=unicode.sub(text,1,cur-1)..unicode.sub(text,after)
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
else
text = unicode.sub(text,1,after-1)..unicode.sub(text,cur)
cur=after
set(curPos(cur+1),unicode.sub(text,cur+1)..string.rep(" ",lenb-unicode.wlen(text)+1),false)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
end
updateHistory()
cursorBlink = true
end
local function isLine(chr)
return chr=="\n" or chr=="\r"
end
--[[ gpu.set(startX,startY,unicode.sub(text,1,width-startX))
for i=1,(#text+startX)//width-1 do
gpu.set(startX,startY+i,unicode.sub(text,1+i*width,width-startX+i*width))
end ]]
set(1,text,false)
set(curPos(cur)," ",true)
local function reprint(new)
set(1,new..string.rep(" ",unicode.wlen(text)-unicode.wlen(new)+1),false)
cur=unicode.len(new)+1
text=new
set(curPos(cur)," ",true)
end
while true do
local args = {event.pull("key_down", "clipboard", 0.5)}
local ctrlDown = _PUBLIC.keyboard.getCtrlDown()
if args and args[1] == "key_down" and args[4] then
local key = _PUBLIC.keyboard.keys[args[4]]
if key=="up" and options.readHistoryType then
historyIdx=math.max(historyIdx-1,1)
reprint(readHistory[options.readHistoryType][historyIdx])
elseif key=="down" and options.readHistoryType then
historyIdx=math.min(historyIdx+1,#readHistory[options.readHistoryType])
reprint(readHistory[options.readHistoryType][historyIdx])
elseif key=="left" and ctrlDown then
moveWord(-1)
elseif key=="right" and ctrlDown then
moveWord(1)
elseif key=="left" then
moveCur(-1)
elseif key=="right" then
moveCur(1)
elseif key=="home" then
moveCur(-math.huge)
elseif key=="end" then
moveCur(math.huge)
elseif key=="back" and ctrlDown then
deleteWord(-1)
elseif key=="delete" and ctrlDown then
deleteWord(1)
elseif key=="back" and cur>1 then
text=unicode.sub(text,1,cur-2)..unicode.sub(text,cur)
cur=cur-1
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
set(curPos(cur)+1,unicode.sub(text,cur+1).." ",false)
updateHistory()
elseif key=="delete" then
text = unicode.sub(text,1,cur-1)..unicode.sub(text,cur+1)
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),true)
cursorBlink = true
if cur<=unicode.len(text) then
set(curPos(cur+1),unicode.sub(text,cur+1).." ",false)
end
updateHistory()
elseif key=="enter" then
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),false)
break
elseif not (args[3]<32 or (args[3]>0x7F and args[3]<=0x9F)) then
add(unicode.char(args[3]) or " ")
updateHistory()
end
elseif args and args[1]=="clipboard" then
local clip = args[3]
if not args[3] then goto continue end
while isLine(unicode.sub(clip,1,1)) do clip=unicode.sub(clip,2) end
while isLine(unicode.sub(clip,-1)) do clip=unicode.sub(clip,1,-2) end
add(clip)
updateHistory()
else
cursorBlink=not cursorBlink
set(curPos(cur),strDef(unicode.sub(text,cur,cur)," "),cursorBlink)
end
::continue::
end
if options.readHistoryType then
if readHistory[options.readHistoryType][#readHistory[options.readHistoryType]]=="" then
table.remove(readHistory[options.readHistoryType],#readHistory[options.readHistoryType])
end
if historyIdx<#readHistory[options.readHistoryType] then
-- table.remove(readHistory[options.readHistoryType],historyIdx)
table.insert(readHistory[options.readHistoryType],text)
end
while #readHistory[options.readHistoryType] > 50 do
table.remove(readHistory[options.readHistoryType], 1)
end
end
cursor.x=1
cursor.y=cursor.y+math.ceil((unicode.wlen(text)+startX-1)/width)
if cursor.y>height then scroll() end
return text
end
end
function module.exit()
_G._PUBLIC.terminal = nil
end
return module
View File
+6 -4
View File
@@ -47,13 +47,15 @@ function event.pull(...)
end end
end end
if timeout ~= nil and computer.uptime() >= startTime + timeout then -- Check if we've timed out
return nil if timeout and computer.uptime() >= startTime + timeout then
return nil -- Timed out, return nil
end end
if timeout == nil then -- Yield to allow other processes to run and more events to be added
if timeout and timeout > 0 then
coroutine.yield() coroutine.yield()
elseif timeout > 0 then elseif not timeout then
coroutine.yield() coroutine.yield()
end end
end end
View File
View File
View File
+34 -34
View File
@@ -39,7 +39,7 @@ function raster.init(width, height, bgcolor)
raster.charHeight = height raster.charHeight = height
width, height = raster.units.charToBraille(width, height) width, height = raster.units.charToBraille(width, height)
bgcolor = bgcolor or raster.defaultBackgroundColor bgcolor = bgcolor or raster.defaultBackgroundColor
if bgcolor~=0 then if bgcolor~=0 then
for i=1,width*height do for i=1,width*height do
@@ -204,29 +204,29 @@ end
function raster.drawLine(x1, y1, x2, y2, color) function raster.drawLine(x1, y1, x2, y2, color)
x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2) x1, y1, x2, y2 = math.floor(x1), math.floor(y1), math.floor(x2), math.floor(y2)
local dx = math.abs(x2 - x1) local dx = math.abs(x2 - x1)
local dy = math.abs(y2 - y1) local dy = math.abs(y2 - y1)
local sx = x1 < x2 and 1 or -1 local sx = x1 < x2 and 1 or -1
local sy = y1 < y2 and 1 or -1 local sy = y1 < y2 and 1 or -1
local err = dx - dy local err = dx - dy
while true do while true do
raster.set(x1, y1, color) raster.set(x1, y1, color)
if x1 == x2 and y1 == y2 then if x1 == x2 and y1 == y2 then
break break
end end
local e2 = 2 * err local e2 = 2 * err
if e2 > -dy then if e2 > -dy then
err = err - dy err = err - dy
x1 = x1 + sx x1 = x1 + sx
end end
if e2 < dx then if e2 < dx then
err = err + dx err = err + dx
y1 = y1 + sy y1 = y1 + sy
@@ -266,7 +266,7 @@ function raster.drawCircle(xc, yc, radius, color)
local x = 0 local x = 0
local y = radius local y = radius
local d = 3 - 2 * radius local d = 3 - 2 * radius
while y >= x do while y >= x do
-- Draw 8 symmetric points -- Draw 8 symmetric points
raster.set(xc + x, yc + y, color) raster.set(xc + x, yc + y, color)
@@ -277,7 +277,7 @@ function raster.drawCircle(xc, yc, radius, color)
raster.set(xc - y, yc + x, color) raster.set(xc - y, yc + x, color)
raster.set(xc + y, yc - x, color) raster.set(xc + y, yc - x, color)
raster.set(xc - y, yc - x, color) raster.set(xc - y, yc - x, color)
if d < 0 then if d < 0 then
d = d + 4 * x + 6 d = d + 4 * x + 6
else else
@@ -291,22 +291,22 @@ end
function raster.drawEllipse(x1, y1, x2, y2, color) function raster.drawEllipse(x1, y1, x2, y2, color)
if x1 > x2 then x1, x2 = x2, x1 end if x1 > x2 then x1, x2 = x2, x1 end
if y1 > y2 then y1, y2 = y2, y1 end if y1 > y2 then y1, y2 = y2, y1 end
local xc = math.floor((x1 + x2) / 2) local xc = math.floor((x1 + x2) / 2)
local yc = math.floor((y1 + y2) / 2) local yc = math.floor((y1 + y2) / 2)
local a = math.floor((x2 - x1) / 2) local a = math.floor((x2 - x1) / 2)
local b = math.floor((y2 - y1) / 2) local b = math.floor((y2 - y1) / 2)
if a <= 0 or b <= 0 then if a <= 0 or b <= 0 then
return return
end end
if a == b then if a == b then
raster.drawCircle(xc, yc, a, color) raster.drawCircle(xc, yc, a, color)
return return
end end
if a <= 1 and b <= 1 then if a <= 1 and b <= 1 then
raster.set(xc, yc, color) raster.set(xc, yc, color)
return return
@@ -321,21 +321,21 @@ function raster.drawEllipse(x1, y1, x2, y2, color)
end end
return return
end end
local x = 0 local x = 0
local y = b local y = b
local a2 = a * a local a2 = a * a
local b2 = b * b local b2 = b * b
local d1 = b2 - (a2 * b) + (0.25 * a2) local d1 = b2 - (a2 * b) + (0.25 * a2)
local dx = 2 * b2 * x local dx = 2 * b2 * x
local dy = 2 * a2 * y local dy = 2 * a2 * y
while dx < dy do while dx < dy do
raster.set(xc + x, yc + y, color) raster.set(xc + x, yc + y, color)
raster.set(xc - x, yc + y, color) raster.set(xc - x, yc + y, color)
raster.set(xc + x, yc - y, color) raster.set(xc + x, yc - y, color)
if d1 < 0 then if d1 < 0 then
x = x + 1 x = x + 1
dx = dx + (2 * b2) dx = dx + (2 * b2)
@@ -348,15 +348,15 @@ function raster.drawEllipse(x1, y1, x2, y2, color)
d1 = d1 + dx - dy + b2 d1 = d1 + dx - dy + b2
end end
end end
local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2 local d2 = b2 * (x + 0.5) * (x + 0.5) + a2 * (y - 1) * (y - 1) - a2 * b2
while y >= 0 do while y >= 0 do
raster.set(xc + x, yc + y, color) raster.set(xc + x, yc + y, color)
raster.set(xc - x, yc + y, color) raster.set(xc - x, yc + y, color)
raster.set(xc + x, yc - y, color) raster.set(xc + x, yc - y, color)
raster.set(xc - x, yc - y, color) raster.set(xc - x, yc - y, color)
if d2 > 0 then if d2 > 0 then
y = y - 1 y = y - 1
dy = dy - (2 * a2) dy = dy - (2 * a2)
@@ -374,17 +374,17 @@ end
function raster.fillCircle(x, y, r, color) function raster.fillCircle(x, y, r, color)
x, y = math.floor(x + 0.5), math.floor(y + 0.5) x, y = math.floor(x + 0.5), math.floor(y + 0.5)
r = math.floor(r + 0.5) r = math.floor(r + 0.5)
if r <= 0 then return end if r <= 0 then return end
local minX, maxX = x - r, x + r local minX, maxX = x - r, x + r
local minY, maxY = y - r, y + r local minY, maxY = y - r, y + r
for py = minY, maxY do for py = minY, maxY do
for px = minX, maxX do for px = minX, maxX do
local dx, dy = px - x, py - y local dx, dy = px - x, py - y
local distSquared = dx*dx + dy*dy local distSquared = dx*dx + dy*dy
if distSquared <= r*r then if distSquared <= r*r then
raster.set(px, py, color) raster.set(px, py, color)
end end
@@ -395,33 +395,33 @@ end
function raster.fillEllipse(x1, y1, x2, y2, color) function raster.fillEllipse(x1, y1, x2, y2, color)
local centerX = (x1 + x2) / 2 local centerX = (x1 + x2) / 2
local centerY = (y1 + y2) / 2 local centerY = (y1 + y2) / 2
local a = math.abs(x2 - x1) / 2 local a = math.abs(x2 - x1) / 2
local b = math.abs(y2 - y1) / 2 local b = math.abs(y2 - y1) / 2
centerX = math.floor(centerX + 0.5) centerX = math.floor(centerX + 0.5)
centerY = math.floor(centerY + 0.5) centerY = math.floor(centerY + 0.5)
a = math.floor(a + 0.5) a = math.floor(a + 0.5)
b = math.floor(b + 0.5) b = math.floor(b + 0.5)
if a <= 0 or b <= 0 then return end if a <= 0 or b <= 0 then return end
if a == b then if a == b then
raster.fillCircle(centerX, centerY, a, color) raster.fillCircle(centerX, centerY, a, color)
return return
end end
local minX = centerX - a local minX = centerX - a
local maxX = centerX + a local maxX = centerX + a
local minY = centerY - b local minY = centerY - b
local maxY = centerY + b local maxY = centerY + b
for y = minY, maxY do for y = minY, maxY do
for x = minX, maxX do for x = minX, maxX do
local dx = x - centerX local dx = x - centerX
local dy = y - centerY local dy = y - centerY
local value = (dx*dx)/(a*a) + (dy*dy)/(b*b) local value = (dx*dx)/(a*a) + (dy*dy)/(b*b)
if value <= 1 then if value <= 1 then
raster.set(x, y, color) raster.set(x, y, color)
end end
View File

Some files were not shown because too many files have changed in this diff Show More