local monitor = peripheral.find("monitor") local programs = {} local isRunning = false local function addProgram(name, program) table.insert(programs, {name = name, program = program}) end local function runProgram(programOrPath) local programModule if type(programOrPath) == "string" then if fs.exists(programOrPath) then -- Load the file as a chunk. In Lua 5.2+ use the env parameter instead of setfenv. local env = _ENV or _G local programChunk, err = loadfile(programOrPath, "t", env) if not programChunk then print("Error loading program: " .. err) return false end -- Run the chunk to get the module table local success, result = pcall(programChunk) if not success then print("Error executing program file: " .. tostring(result)) return false end programModule = result else print("Program not found: " .. programOrPath) return false end elseif type(programOrPath) == "table" then programModule = programOrPath else print("Invalid program type") return false end if type(programModule) ~= "table" or not programModule.main then print("Error: Program must return a table with at least a 'main' function.") return false end -- Setup Monitor local w, h = monitor.getSize() monitor.setBackgroundColor(colors.black) monitor.clear() -- Draw X button monitor.setCursorPos(w, 1) monitor.setBackgroundColor(colors.red) monitor.setTextColor(colors.white) monitor.write("X") monitor.setBackgroundColor(colors.black) -- Create window for program local win = window.create(monitor, 1, 2, w, h - 1) local oldTerm = term.redirect(win) -- Lifecycle: START if programModule.start then local ok, err = pcall(programModule.start) if not ok then print("Error in start(): " .. tostring(err)) term.redirect(oldTerm) return false end end -- Lifecycle: MAIN (Run in parallel with exit watcher) local function runMain() -- We wrap main in pcall to catch errors without crashing the driver local ok, err = pcall(programModule.main) if not ok then win.setTextColor(colors.red) win.write("Runtime Error: " .. tostring(err)) sleep(2) -- Let user see error end end local function watchExit() while true do local _, _, x, y = os.pullEvent("monitor_touch") if x == w and y == 1 then return end end end parallel.waitForAny(runMain, watchExit) -- Lifecycle: STOP if programModule.stop then pcall(programModule.stop) end term.redirect(oldTerm) return true end local function exitProgram() isRunning = false end local function drawList() monitor.clear() monitor.setCursorPos(1, 1) monitor.write("Available Programs:") for i, program in ipairs(programs) do monitor.setCursorPos(1, i + 2) -- Start from line 3 monitor.write(i .. ". " .. program.name) end monitor.setCursorPos(1, #programs + 4) monitor.write("Click to run") end local function listPrograms() isRunning = true while isRunning do drawList() local event, side, x, y = os.pullEvent("monitor_touch") local programIndex = y - 2 if programIndex > 0 and programIndex <= #programs then local program = programs[programIndex] monitor.clear() monitor.setCursorPos(1,1) monitor.write("Running " .. program.name .. "...") sleep(0.5) runProgram(program.program) end end end return { addProgram = addProgram, runProgram = runProgram, exitProgram = exitProgram, listPrograms = listPrograms }