This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
BigSHinyToys's profile picture

How dose terminal redirect work

Started by BigSHinyToys, 10 July 2012 - 08:30 PM
BigSHinyToys #1
Posted 10 July 2012 - 10:30 PM
I was looking at the term API and don't realty understand a word of it. I can make the terminal display information and redirect the terminal to monitor but don't understand the underlying functions / processes that make that happen. If some one has the time to explain in great detail how the functions are passed to the monitor i would be very grateful.

I hope to make a program that redirects not to a monitor but to a rednet packet as part of a SSH without modifying every terminal function eg print write term.getCursorPos ect.I am not a pro just a armature that has been coding only for a few months so assume i know almost nothing.


Thanks for your time
Big Shiny Toys
MysticT #2
Posted 10 July 2012 - 10:41 PM
Here's the code in the term api with some comments (added by me):

native = term.native or term -- save the native term

local redirectTarget = native -- redirectTarget stores the current output screen (monitor or terminal)
local tRedirectStack = {} -- redirection stack (to restore after redirecting)

local function wrap( _sFunction ) -- wraps a function
    return function( ... ) -- return a function that...
        return redirectTarget[ _sFunction ]( ... ) -- calls the function on the current target (like doing term.write, or monitor.clear, etc.)
    end
end

local term = {} -- table to store the api
term.redirect = function( _object ) -- the redirect function
    tRedirectStack[#tRedirectStack + 1] = redirectTarget -- add the current target to the stack
    redirectTarget = _object -- set the new output target
end
term.restore = function() -- restore function
    if #tRedirectStack > 0 then -- if there's a target to restore in the stack
        redirectTarget = tRedirectStack[#tRedirectStack]  -- set the target to the previous one
        tRedirectStack[#tRedirectStack] = nil -- remove it from the stack
    end
end
for k,v in pairs( native ) do -- for each method in the native api (term)
    if type( k ) == "string" and type( v ) == "function" then -- if it's a function
        if term[k] == nil then -- and it's not in the api (the one that's loading)
            term[k] = wrap( k ) -- wrap the function and add it to the api
        end
    end
end
    
local env = getfenv() -- get the environment
for k,v in pairs( term ) do -- for each pair in the term api (the one that was just created)
    env[k] = v -- add the function to the environment (so the os adds it to the api)
end

Basically, it just wraps the functions to output to the current target, wich is a table that contains the functions to actually write to the screen.
You could create a table and redirect the ouput to a file (or anything you want, like rednet) if you want.
Example:
redirect to rednet

local target = {}

function target.write(s)
  rednet.broadcast(s)
end

-- add all the functions here (clear, scroll, etc.)

term.redirect(target)
BigSHinyToys #3
Posted 10 July 2012 - 10:49 PM
That is exactly what i was looking for thanks
xuma202 #4
Posted 10 July 2012 - 10:58 PM
I had a short look into the API itself

Spoiler

native = term.native or term
local redirectTarget = native
local tRedirectStack = {}
local function wrap( _sFunction )
return function( ... )
  return redirectTarget[ _sFunction ]( ... )
end
end
local term = {}
term.redirect = function( _object )
tRedirectStack[#tRedirectStack + 1] = redirectTarget
redirectTarget = _object
end
term.restore = function()
if #tRedirectStack > 0 then
  redirectTarget = tRedirectStack[#tRedirectStack]
  tRedirectStack[#tRedirectStack] = nil
end
end
for k,v in pairs( native ) do
if type( k ) == "string" and type( v ) == "function" then
  if term[k] == nil then
   term[k] = wrap( k )
  end
end
end

local env = getfenv()
for k,v in pairs( term ) do
env[k] = v
end

I've not completely understood the code but the wrap function seems important to me. To transfer the function call into a rednet message you can simply call the term.redirect with an array of functions that represent the term functions like so:


myRednetMonitor =
{["write"] = function(_s) rednet.broadcast("write" .. _s) end,
["clear"] = function() rednet.broadcast("clear") end
}

But I have no idea if this will really work. otherwise you can maybe read out the function name and the parameters in the warp function an pack them as you need to.


local function wrap( _sFunction )
return function( ... )
  return redirectTarget[ _sFunction ]( ... )
end
end

_sFunction is the name of the called function and … is an array/table of parameters.


EDIT: Basically the same as what MysticT posted earlier.
BigSHinyToys #5
Posted 20 July 2012 - 01:43 PM
so due to a recent series of unfortunate events i was unable to work on this after all. I have some time now thought and have attempted to make something here is my code currently it only clear works and the others cause crash any ideas??
client


local function openRednet()
local listOfSides = rs.getSides()
for i = 1,6 do
  if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
   rednet.open(listOfSides[i])
   return listOfSides[i]
  end
end
end
modemOn = openRednet()
if not modemOn then
print("No WIFI ModemnPress any key to return to menu.")
os.pullEvent("key")
return
else
print("Opened wifi on "..modemOn.." side")
end

while true do
e1,e2,e3,e4,e5 = os.pullEvent()
if e1 == "rednet_message" then
  local sTest = string.sub(e3,1,3)
  if sTest == "WRT" then
   write(string.sub(e3,4,#e3))
  elseif sTest == "CLR" then
   term.clear()
  elseif sTest == "CLL" then
   term.clearline()
  elseif sTest == "SCB" then
   local boolin = string.sub(e3,4,#e3)
   if boolin == "true" then
    term.setCursorBlink(true)
   elseif boolin == "false" then
    term.setCursorBlink(false)
   end
  elseif sTest == "SCP" then
   local curP = textutils.unserialize(string.sub(e3,4,#e3))
   term.setCursorPos(curP[1],curP[2])
  elseif sTest == "GSZ" then -- work on this
   rednet.broadcast("SIZ"..textutils.serialize({term.getSize()}))
  elseif sTest == "GCP" then
   rednet.broadcast("POS"..textutils.serialize({term.getCursorPos()}))
  elseif sTest == "SCR" then
   term.scroll()
  end 
end
if e1 == "key" or e1 == "char" then
  rednet.broadcast(tostring(e1)..tostring(e2))
end
end
sever


local function openRednet()
local listOfSides = rs.getSides()
for i = 1,6 do
  if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
   rednet.open(listOfSides[i])
   return listOfSides[i]
  end
end
end
modemOn = openRednet()
if not modemOn then
print("No WIFI ModemnPress any key to return to menu.")
os.pullEvent("key")
return
else
print("Opened wifi on "..modemOn.." side")
end
-- Original = os.pullEventRaw
function os.pullEventRaw()
e1,e2,e3,e4,e5 = coroutine.yield()
if e1 == "rednet_message" then
  if string.sub(e3,1,3) == "key" then
   return string.sub(e3,1,3),tonumber(string.sub(e3,4,string.len(e3)))
  elseif string.sub(e3,1,4) == "char" then
   return string.sub(e3,1,4),string.sub(e3,5,string.len(e3))
  end
end
return e1,e2,e3,e,e5
end
local target = {}
function target.write(s)
rednet.broadcast("WRT"..s)
end
function target.clear()
rednet.broadcast("CLR")
end
function target.clearLine()
rednet.broadcast("CLL")
end
function target.setCursorBlink(s)
rednet.broadcast("SCB"..tostring(s))
end
function target.setCursorPos(x,y)
rednet.broadcast("SCP"..textutils.serialize({x,y}))
end
function target.getSize()
rednet.broadcast("GSZ")
while true do
  local w1,w2,w3 = os.pullEvent("rednet_message")
  if string.sub(w3,1,3) == "SIZ" then
   return textutils.unserialize(string.sub(w3,4,string.len(w3)))
  end
end
end
function target.getCursorPos()
rednet.broadcast("GCP")
while true do
  local w1,w2,w3 = os.pullEvent("rednet_message")
  if string.sub(w3,1,3) == "POS" then
   return textutils.unserialize(string.sub(w3,4,string.len(w3)))
  end
end
end
function target.scroll()
rednet.broadcast("SCR")
end
-- add all the functions here (clear, scroll, etc.)
--[[ notes
write -- WRT
clear -- CLR
clearLine -- CLL
setCursorBlink -- SCB
setCursorPos -- SCP
getSize -- GSZ
getCursorPos -- GCP
scroll -- SCR
]]--
term.redirect(target)
term.clear()
local x,y = term.getSize()
term.setCursorPos(1,1)
print("hello")
-- shell.run("rom/user/BENCH1.lua")
os.pullEventRaw = Original
term.restore()
--[[
while true do
local a = {os.pullEvent()}
for i = 1,#a do
  write(tostring(a[i]).." ")
end
write("n")
end
]]--
Lyqyd #6
Posted 20 July 2012 - 06:46 PM
The LyqydNet API has terminal redirection over rednet. You may be able to adapt it to your purposes. The redirect target and packet handler functions are in the connection API, near the bottom.
BigSHinyToys #7
Posted 21 July 2012 - 02:47 AM
I was hoping for something that worked with all programs with out altering the program its self. But i will look into LyqydNet API
MysticT #8
Posted 21 July 2012 - 03:08 AM
And what's the error you get with your code? Knowing that makes easier to find the error.
Lyqyd #9
Posted 21 July 2012 - 04:13 AM
I was hoping for something that worked with all programs with out altering the program its self. But i will look into LyqydNet API

Well, my point wasn't necessarily that you should use the LyqydNet API (which may be a good idea also; it is handy), but more that since it's already complete there, it may be easier to adapt it to a non-LyqydNet version rather than starting from scratch. Or it may help you see something different to try.
BigSHinyToys #10
Posted 21 July 2012 - 04:20 AM
And what's the error you get with your code? Knowing that makes easier to find the error.
It gives no error just crashes. I think this is because the program changes write so it cant write properly what error happened.

Any way I have found the problem and fixed it. I forgot to put e4 i put e insted in my lets call it "fake event code". and now it is working Thanks for you help Here is the latest ver.

RUN this in new world as rednet interference will be a problem

Client
Spoiler

local function openRednet()
local listOfSides = rs.getSides()
for i = 1,6 do
  if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
   rednet.open(listOfSides[i])
   return listOfSides[i]
  end
end
end
modemOn = openRednet()
if not modemOn then
print("No WIFI ModemnPress any key to return to menu.")
os.pullEvent("key")
return
else
print("Opened wifi on "..modemOn.." side")
end
term.clear()
term.setCursorPos(1,1)
while true do
e1,e2,e3,e4,e5 = os.pullEvent()
if e1 == "rednet_message" then
  local sTest = string.sub(e3,1,3)
  if sTest == "WRT" then
   write(string.sub(e3,4,#e3))
  elseif sTest == "CLR" then
   term.clear()
  elseif sTest == "CLL" then
   term.clearline()
  elseif sTest == "SCB" then
   local boolin = string.sub(e3,4,#e3)
   if boolin == "true" then
    term.setCursorBlink(true)
   elseif boolin == "false" then
    term.setCursorBlink(false)
   end
  elseif sTest == "SCP" then
   local curP = textutils.unserialize(string.sub(e3,4,#e3))
   term.setCursorPos(curP[1],curP[2])
  elseif sTest == "GSZ" then -- work on this
   rednet.broadcast("SIZ"..textutils.serialize({term.getSize()}))
  elseif sTest == "GCP" then
   rednet.broadcast("POS"..textutils.serialize({term.getCursorPos()}))
  elseif sTest == "SCR" then
   term.scroll()
  end 

elseif e1 == "key" then
  rednet.broadcast("key"..tostring(e2))
elseif e1 == "char" then
  rednet.broadcast("cha"..e2)
end
end
Sever – change the program in the shell.run at the bottom or it will crash
Spoiler

local function openRednet()
local listOfSides = rs.getSides()
for i = 1,6 do
  if peripheral.isPresent(listOfSides[i]) and peripheral.getType(listOfSides[i]) == "modem" then
   rednet.open(listOfSides[i])
   return listOfSides[i]
  end
end
end
modemOn = openRednet()
if not modemOn then
print("No WIFI ModemnPress any key to return to menu.")
os.pullEvent("key")
return
else
print("Opened wifi on "..modemOn.." side")
end
Original = os.pullEventRaw
function os.pullEventRaw(...)
local e1,e2,e3,e4,e5 = coroutine.yield(...)
if e1 == "rednet_message" then
  local a = string.sub(e3,1,3)
  if a == "key" then
   return "key",tonumber(string.sub(e3,4,string.len(e3)))
  elseif a == "cha" then
   return "char",string.sub(e3,5,string.len(e3))
  end
end
return e1,e2,e3,e4,e5
end
remote = {}
function remote.write(s)
rednet.broadcast("WRT"..s)
end
function remote.clear()
rednet.broadcast("CLR")
end
function remote.clearLine()
rednet.broadcast("CLL")
end
function remote.setCursorBlink(s)
rednet.broadcast("SCB"..tostring(s))
end
function remote.setCursorPos(x,y)
rednet.broadcast("SCP"..textutils.serialize({x,y}))
end
function remote.getSize()
rednet.broadcast("GSZ")
while true do
  local w1,w2,w3 = os.pullEvent("rednet_message")
  if string.sub(w3,1,3) == "SIZ" then
   local a = textutils.unserialize(string.sub(w3,4,string.len(w3)))
   return a[1],a[2]
  end
end
end
function remote.getCursorPos()
rednet.broadcast("GCP")
while true do
  local w1,w2,w3 = os.pullEvent("rednet_message")
  if string.sub(w3,1,3) == "POS" then
   local a = textutils.unserialize(string.sub(w3,4,string.len(w3)))
   return a[1],a[2]
  end
end
end
function remote.scroll()
rednet.broadcast("SCR")
end
-- add all the functions here (clear, scroll, etc.)
--[[ notes
write -- WRT
clear -- CLR
clearLine -- CLL
setCursorBlink -- SCB
setCursorPos -- SCP
getSize -- GSZ
getCursorPos -- GCP
scroll -- SCR
]]--
term.redirect(remote)
print("hello")
term.write("hello there")
local x,y = term.getSize()
print(x.." "..y)
os.pullEvent()
term.clear()
term.setCursorPos(1,1)

shell.run("rom/user/BENCH1.lua")
os.pullEventRaw = Original
term.restore()
--[[
while true do
local a = {os.pullEvent()}
for i = 1,#a do
  write(tostring(a[i]).." ")
end
write("n")
end
]]--