Last active
January 11, 2026 03:10
-
-
Save MineRobber9000/6886835b990fca3bb1dddeb3ae794a49 to your computer and use it in GitHub Desktop.
Mirror a ComputerCraft computer screen to a monitor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| -- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe, 2025 minerobber | |
| -- | |
| -- SPDX-License-Identifier: LicenseRef-CCPL | |
| local function printUsage() | |
| local programName = arg[0] or fs.getName(shell.getRunningProgram()) | |
| print("Usage: " .. programName .. " <name> <program> <arguments>") | |
| return | |
| end-- SPDX-FileCopyrightText: 2017 Daniel Ratcliffe | |
| -- | |
| -- SPDX-License-Identifier: LicenseRef-CCPL | |
| local function printUsage() | |
| local programName = arg[0] or fs.getName(shell.getRunningProgram()) | |
| print("Usage:") | |
| print(" " .. programName .. " <name> <program> <arguments>") | |
| --print(" " .. programName .. " scale <name> <scale>") | |
| return | |
| end | |
| local tArgs = { ... } | |
| if #tArgs < 2 then | |
| if tArgs[1]=="define" then | |
| -- shell completion | |
| local completion = require("cc.shell.completion") | |
| shell.setCompletionFunction(shell.getRunningProgram(), completion.build( | |
| { completion.peripheral, true }, | |
| { completion.programWithArgs, 3, many=true } | |
| )) | |
| -- settings | |
| settings.define("mirror.fill_character", { | |
| description = "The character that fills background of the monitor outside the terminal window."; | |
| default = "\127"; | |
| type = "string"; | |
| }) | |
| settings.define("mirror.fill_fg", { | |
| description = "The color to fill the foreground with, as a hex color."; | |
| default = "0"; | |
| type = "string"; | |
| }) | |
| settings.define("mirror.fill_bg", { | |
| description = "The color to fill the background with, as a hex color."; | |
| default = "f"; | |
| type = "string"; | |
| }) | |
| return | |
| end | |
| printUsage() | |
| return | |
| end | |
| local sName = tArgs[1] | |
| if peripheral.getType(sName) ~= "monitor" then | |
| print("No monitor named " .. sName) | |
| return | |
| end | |
| local termWidth, termHeight = term.getSize() | |
| peripheral.call(sName, "setTextScale", 1) | |
| local monWidth, monHeight = peripheral.call(sName, "getSize") | |
| local monScale = math.min(math.floor((monWidth / termWidth) * 2) / 2, math.floor((monHeight / termHeight) * 2) / 2) | |
| peripheral.call(sName, "setTextScale", monScale) | |
| monWidth, monHeight = peripheral.call(sName, "getSize") | |
| local sProgram = tArgs[2] | |
| local sPath = shell.resolveProgram(sProgram) | |
| if sPath == nil then | |
| print("No such program: " .. sProgram) | |
| return | |
| end | |
| print("Mirroring " .. sProgram .. " on monitor " .. sName .. "...") | |
| sleep(3) | |
| local monOffsetX = math.floor((monWidth - termWidth)/2) | |
| local monOffsetY = math.floor((monHeight - termHeight)/2) | |
| local previousTerm | |
| local redirect = setmetatable({ | |
| setCursorPos = function(x, y) | |
| if not previousTerm then error("attempt to set cursor position before redirect") end | |
| peripheral.call(sName, "setCursorPos", x+monOffsetX, y+monOffsetY) | |
| return previousTerm.setCursorPos(x, y) | |
| end; | |
| },{ | |
| __index=function(_, k) | |
| return function(...) | |
| if not previousTerm then return term[k](...) end | |
| peripheral.call(sName, k, ...) | |
| return previousTerm[k](...) | |
| end | |
| end; | |
| }) | |
| for y=1,monHeight do | |
| peripheral.call(sName, "setCursorPos", 1, y) | |
| peripheral.call(sName, "blit", settings.get("mirror.fill_character","\127"):sub(1,1):rep(monWidth), settings.get("mirror.fill_fg","0"):sub(1,1):rep(monWidth), settings.get("mirror.fill_bg","f"):rep(monWidth)) | |
| end | |
| local win = window.create(redirect, 1, 1, termWidth, termHeight, false) | |
| previousTerm = term.redirect(win) | |
| win.setVisible(true) | |
| term.clear() | |
| term.setCursorPos(1,1) | |
| local co = coroutine.create(function() | |
| (shell.execute or shell.run)(sProgram, table.unpack(tArgs, 3)) | |
| end) | |
| local function resume(...) | |
| local ok, param = coroutine.resume(co, ...) | |
| if not ok then | |
| printError(param) | |
| end | |
| return param | |
| end | |
| local timers = {} | |
| local ok, param = pcall(function() | |
| local sFilter = resume() | |
| while coroutine.status(co) ~= "dead" do | |
| local tEvent = table.pack(os.pullEventRaw()) | |
| if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then | |
| sFilter = resume(table.unpack(tEvent, 1, tEvent.n)) | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then | |
| if tEvent[1] == "monitor_touch" and tEvent[2] == sName then | |
| timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] } | |
| sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n)) | |
| end | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then | |
| if tEvent[1] == "monitor_resize" and tEvent[2] == sName then | |
| sFilter = resume("term_resize") | |
| end | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then | |
| if tEvent[1] == "timer" and timers[tEvent[2]] then | |
| sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2)) | |
| timers[tEvent[2]] = nil | |
| end | |
| end | |
| end | |
| end) | |
| term.redirect(previousTerm) | |
| term.clear() | |
| term.setCursorPos(1,1) | |
| if not ok then | |
| printError(param) | |
| end | |
| local tArgs = { ... } | |
| if #tArgs < 2 then | |
| if tArgs[1]=="define" then | |
| -- shell completion | |
| local completion = require("cc.shell.completion") | |
| shell.setCompletionFunction(shell.getRunningProgram(), completion.build( | |
| { completion.peripheral, true }, | |
| { completion.programWithArgs, 3, many=true } | |
| )) | |
| -- settings | |
| settings.define("mirror.fill_character", { | |
| description = "The character that fills background of the monitor outside the terminal window."; | |
| default = "\127"; | |
| type = "string"; | |
| }) | |
| settings.define("mirror.fill_fg", { | |
| description = "The color to fill the foreground with, as a hex color."; | |
| default = "0"; | |
| type = "string"; | |
| }) | |
| settings.define("mirror.fill_bg", { | |
| description = "The color to fill the background with, as a hex color."; | |
| default = "f"; | |
| type = "string"; | |
| }) | |
| return | |
| end | |
| printUsage() | |
| return | |
| end | |
| local sName = tArgs[1] | |
| if peripheral.getType(sName) ~= "monitor" then | |
| print("No monitor named " .. sName) | |
| return | |
| end | |
| local termWidth, termHeight = term.getSize() | |
| local monWidth, monHeight = peripheral.call(sName, "getSize") | |
| if monWidth<termWidth or monHeight<termHeight then | |
| print("Monitor " .. sName .. " too small") | |
| return | |
| end | |
| local sProgram = tArgs[2] | |
| local sPath = shell.resolveProgram(sProgram) | |
| if sPath == nil then | |
| print("No such program: " .. sProgram) | |
| return | |
| end | |
| print("Mirroring " .. sProgram .. " on monitor " .. sName .. "...") | |
| sleep(3) | |
| local monOffsetX = math.floor((monWidth - termWidth)/2) | |
| local monOffsetY = math.floor((monHeight - termHeight)/2) | |
| local previousTerm | |
| local redirect = setmetatable({ | |
| setCursorPos = function(x, y) | |
| if not previousTerm then error("attempt to set cursor position before redirect") end | |
| peripheral.call(sName, "setCursorPos", x+monOffsetX, y+monOffsetY) | |
| return previousTerm.setCursorPos(x, y) | |
| end; | |
| },{ | |
| __index=function(_, k) | |
| return function(...) | |
| if not previousTerm then return term[k](...) end | |
| peripheral.call(sName, k, ...) | |
| return previousTerm[k](...) | |
| end | |
| end; | |
| }) | |
| for y=1,monHeight do | |
| peripheral.call(sName, "setCursorPos", 1, y) | |
| peripheral.call(sName, "blit", settings.get("mirror.fill_character","\127"):sub(1,1):rep(monWidth), settings.get("mirror.fill_fg","0"):sub(1,1):rep(monWidth), settings.get("mirror.fill_bg","f"):rep(monWidth)) | |
| end | |
| local win = window.create(redirect, 1, 1, termWidth, termHeight, false) | |
| previousTerm = term.redirect(win) | |
| win.setVisible(true) | |
| term.clear() | |
| term.setCursorPos(1,1) | |
| local co = coroutine.create(function() | |
| (shell.execute or shell.run)(sProgram, table.unpack(tArgs, 3)) | |
| end) | |
| local function resume(...) | |
| local ok, param = coroutine.resume(co, ...) | |
| if not ok then | |
| printError(param) | |
| end | |
| return param | |
| end | |
| local timers = {} | |
| local ok, param = pcall(function() | |
| local sFilter = resume() | |
| while coroutine.status(co) ~= "dead" do | |
| local tEvent = table.pack(os.pullEventRaw()) | |
| if sFilter == nil or tEvent[1] == sFilter or tEvent[1] == "terminate" then | |
| sFilter = resume(table.unpack(tEvent, 1, tEvent.n)) | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_click") then | |
| if tEvent[1] == "monitor_touch" and tEvent[2] == sName then | |
| timers[os.startTimer(0.1)] = { tEvent[3], tEvent[4] } | |
| sFilter = resume("mouse_click", 1, table.unpack(tEvent, 3, tEvent.n)) | |
| end | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "term_resize") then | |
| if tEvent[1] == "monitor_resize" and tEvent[2] == sName then | |
| sFilter = resume("term_resize") | |
| end | |
| end | |
| if coroutine.status(co) ~= "dead" and (sFilter == nil or sFilter == "mouse_up") then | |
| if tEvent[1] == "timer" and timers[tEvent[2]] then | |
| sFilter = resume("mouse_up", 1, table.unpack(timers[tEvent[2]], 1, 2)) | |
| timers[tEvent[2]] = nil | |
| end | |
| end | |
| end | |
| end) | |
| term.redirect(previousTerm) | |
| term.clear() | |
| term.setCursorPos(1,1) | |
| if not ok then | |
| printError(param) | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment