Skip to content

Instantly share code, notes, and snippets.

@sunjon
Last active November 5, 2021 20:16
Show Gist options
  • Save sunjon/b88e28e964fbae5273e11976b4714383 to your computer and use it in GitHub Desktop.
Save sunjon/b88e28e964fbae5273e11976b4714383 to your computer and use it in GitHub Desktop.
#!/usr/bin/env luajit
local TOTAL_COLORS = 256
local char = string.char
local io_read = io.read
local byte = string.byte
local INDEX_MAX_DIGITS = #(tostring(TOTAL_COLORS)) + 1
-- Ascii decimal value lookup
local ascii = {
BEL = 7,
ESC = 27,
NUM_FOUR = 52,
SEMICOLON = 59,
QUESTIONMARK = 63,
BACKSLASH = 92,
RBRACKET = 93,
}
-- control sequences
local RGB_START = { ascii.ESC, ascii.RBRACKET, ascii.NUM_FOUR, ascii.SEMICOLON }
local RGB_END = { ascii.ESC, ascii.BACKSLASH }
local OS_CMD_START = char(ascii.ESC) .. char(ascii.RBRACKET) .. char(ascii.NUM_FOUR)
local OS_CMD_END = char(ascii.BACKSLASH) .. char(ascii.BEL)
--
local util = {}
util.to_string = function(byte_array)
local s = ""
for i = 1, #byte_array do
s = s .. char(byte_array[i])
end
return s
end
-- shallow check if two tables are identical
util.match = function(a, b)
if #a ~= #b then
return nil
end
for i = 1, #a do
if a[i] ~= b[i] then
return false
end
end
return true
end
-- consumes n bytes from the IO stream
util.chomp = function(n) -- TODO: stop using string
local bytes = assert({ byte(io_read(n), 1, n) }, "read_error")
if bytes[n] then
return bytes
else
return nil
end
end
-- Terminal
local terminal = {}
terminal.setrawmode = function()
return os.execute "stty raw -echo 2> /dev/null"
end
terminal.savemode = function()
local handle = assert(io.popen("stty -g", "r"))
local mode = assert(handle:read "*all")
local success, e, msg = handle:close()
-- print(inspect(mode))
return success and mode or nil, e, msg -- TODO: handle all errors like this
end
terminal.restoremode = function(mode)
return os.execute("stty " .. mode)
end
--
-- Args come in as a single 'index1;rgb1 ... ;indexN;rgbN' string.
--
local function get_color_entry(index)
local b = util
if not b.match(b.chomp(4), RGB_START) then
error "NO HEADER FOUND"
return nil
end
local color_id = {}
local c = nil
for i = 1, INDEX_MAX_DIGITS + 1 do
c = b.chomp(1)[1]
if c == ascii.SEMICOLON then
break
else
color_id[i] = c
end
end
if color_id == {} then
error("Error: Index not found at " .. index .. "/" .. TOTAL_COLORS)
return nil
end
-- get the bytes containing the RGB values
local hex_color
local chunk_bytes = b.chomp(20)
-- check for the end marker
if b.match({ chunk_bytes[19], chunk_bytes[20] }, RGB_END) then
local rgb_values = {
-- parse RGB from chunk: 'rgb:xxRR/xxGG/xxBB'
chunk_bytes[7],
chunk_bytes[8],
chunk_bytes[12],
chunk_bytes[13],
chunk_bytes[17],
chunk_bytes[18],
}
-- id = tonumber(b.to_string(color_id))
hex_color = "#" .. b.to_string(rgb_values)
else
error("DEBUG: " .. b.to_string(chunk_bytes))
end
return hex_color
end
-- send escape sequence to query palette
local function osc_query_palette()
-- build the query string
local query = OS_CMD_START
for idx = 0, TOTAL_COLORS do
query = query .. char(ascii.SEMICOLON) .. idx .. char(ascii.SEMICOLON) .. char(ascii.QUESTIONMARK)
end
query = query .. OS_CMD_END
-- print('DEBUG: '..query)
-- write arguments to stdout, then flush.
io.write(query)
io.flush()
end
local function get_term_colors()
local mode, _ = terminal.savemode()
terminal.setrawmode()
osc_query_palette()
local results = {}
for i = 1, TOTAL_COLORS do
results[i] = get_color_entry(i)
end
-- local results = get_term_colors()
terminal.restoremode(mode)
print(#results)
--
for i, hex_color in ipairs(results) do
print(string.format("[%s] - %s", i - 1, hex_color))
end
end
get_term_colors()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment