Skip to content

Instantly share code, notes, and snippets.

@Vftdan
Last active August 4, 2025 02:00
Show Gist options
  • Select an option

  • Save Vftdan/ab61531879c3660911537e3c73d7ef71 to your computer and use it in GitHub Desktop.

Select an option

Save Vftdan/ab61531879c3660911537e3c73d7ef71 to your computer and use it in GitHub Desktop.
ComputerCraft monitor & keyboard environment
local keyboardEvents = {["char"] = true, ["key"] = true, ["key_up"] = true}
local touchButtonId = 1
local useParentKeyboard = true
local useTmKeyboards = {}
local monName = nil
local scale = nil
local seat = {monitor = nil, co = nil, monitorName = nil}
local args = {...}
local argi = 0
local posArg = #args + 1
while argi < #args do
argi = argi + 1
local opt = args[argi]
if opt == "--monitor" or opt == "-m" then
argi = argi + 1
monName = args[argi]
if not monName then
printError("Monitor name expected")
return
end
elseif opt == "--no-parent-keys" or opt == "-n" then
useParentKeyboard = false
elseif opt == "--keyboard" or opt == "-k" then
argi = argi + 1
local kbName = args[argi]
if not kbName then
printError("Keyboard device name expected")
return
end
useTmKeyboards[kbName] = true
elseif opt == "--scale" or opt == "-s" then
argi = argi + 1
local scaleStr = args[argi]
if not scaleStr then
printError("Scale value expected")
return
end
scale = tonumber(scaleStr)
if scale == nil then
printError("Scale is not a number")
return
elseif scale < 0.5 or scale > 5 then
printError("Monitor scale is out of range")
return
end
elseif opt == "--button" or opt == "-b" then
argi = argi + 1
local btnStr = args[argi]
if not btnStr then
printError("Mouse button id expected")
return
end
if btnStr == "left" then
touchButtonId = 1
elseif btnStr == "right" then
touchButtonId = 2
elseif btnStr == "middle" then
touchButtonId = 3
else
touchButtonId = tonumber(btnStr)
if not touchButtonId then
printError("Invalid mouse button")
return
end
if touchButtonId < 1 or touchButtonId > 3 then
printError("Mouse button is out of range")
return
end
end
elseif opt == "--help" or opt == "-h" then
print("Usage: startseat [OPTIONS] [--] [COMMAND]")
print("Options:")
print(" -m, --monitor <NAME|auto> Use this monitor instead of the parent terminal.")
print(" -n, --no-parent-keys Don't pass parent terminal keyboard events through.")
print(" -k, --keyboard <NAME|all> Convert Tom's Peripherals keyboard events into terminal events.")
print(" -s, --scale <NUM> Set the monitor scale.")
print(" -b, --button <NUM|left|right|middle> Convert monitor touches to this mouse button.")
return
elseif opt == "--" then
posArg = argi + 1
break
elseif opt:sub(1, 1) == "-" then
printError("Unknown option: " .. opt)
return
else
posArg = argi
break
end
end
if scale and not monName then
printError("Cannot set scale when not using a monitor")
return
end
local cmd = {table.unpack(args, posArg)}
if monName then
if monName == "auto" then
seat.monitor = peripheral.find("monitor")
if not seat.monitor then
printError("No monitors attached")
return
end
seat.monitorName = peripheral.getName(seat.monitor)
else
if peripheral.getType(monName) ~= "monitor" then
printError("Not a monitor: " .. monName)
return
end
seat.monitor = peripheral.wrap(monName)
seat.monitorName = monName
end
if scale then
seat.monitor.setTextScale(scale)
end
else
local w, h = term.getSize()
seat.monitor = window.create(term.current(), 1, 1, w, h)
end
seat.co = coroutine.create(function()
local prg = "shell"
if multishell then
prg = "multishell"
end
local prgPath = shell.resolveProgram(prg)
os.run({shell = shell}, prgPath, table.unpack(cmd))
end)
local function resume(seat, ...)
local oldTerm = term.redirect(seat.monitor)
local ok, wantEvent = coroutine.resume(seat.co, ...)
term.redirect(oldTerm)
if not ok then
printError(wantEvent)
end
return wantEvent
end
local function notDead(co)
return coroutine.status(co) ~= "dead"
end
local function eventLoop(seat)
local mouseClicks = {}
local wantEvent = resume(seat)
while notDead(seat.co) do
local ev = table.pack(os.pullEventRaw())
local virtEv = ev
if ev[1] == "timer" and mouseClicks[ev[2]] then
local click = mouseClicks[ev[2]]
virtEv = table.pack("mouse_up", table.unpack(click, 1, click.n))
elseif ev[1] == "monitor_touch" and ev[2] == seat.monitorName then
local click = table.pack(touchButtonId, table.unpack(ev, 3, ev.n))
mouseClicks[os.startTimer(0.1)] = click
virtEv = table.pack("mouse_click", table.unpack(click, 1, click.n))
elseif ev[1] == "monitor_resize" and ev[2] == seat.monitorName then
virtEv = table.pack("term_resize")
elseif ev[1] == "term_resize" then
if seat.monitorName then
virtEv = nil
else
local w, h = term.getSize()
seat.monitor.reposition(1, 1, w, h, term.current())
end
elseif not useParentKeyboard and ev[1] and keyboardEvents[ev[1]] then
virtEv = nil
elseif ev[1] and ev[2] and ev[1]:find("^tm_keyboard_") and (useTmKeyboards.all or useTmKeyboards[ev[2]]) then
virtEv = table.pack(ev[1]:match("^tm_keyboard_(.+)$"), table.unpack(ev, 3, ev.n))
end
if virtEv then
if not wantEvent or virtEv[1] == wantEvent then
wantEvent = resume(seat, table.unpack(virtEv, 1, virtEv.n))
end
end
end
end
eventLoop(seat)
-- vim: ts=4 et
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment