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

How to handle Programs Redirecting to Windows --Multitasking

Started by KingofGamesYami, 04 November 2015 - 11:17 PM
KingofGamesYami #1
Posted 05 November 2015 - 12:17 AM
I have this program, not nearly finished, that's going to be a coroutine manager.

the program

local tUserEvents = {
  mouse_click = true,
  mouse_drag = true,
  mouse_up = true,
  key = true,
  char = true,
  key_up = true,
  monitor_touch = true,
  mouse_scroll = true,
  paste = true,
}

local tApps, sCurrent = {}

function getRunningApp()
  for i, app in ipairs( tApps ) do
  	if coroutine.status( app.co ) == "running" then
  		return app.name
  	end
  end
end

function listRunningApps()
	return table.protect( tApps )
end

function launchApp( appPath, ... )
  local app = {}
  app.window = term.current()
  app.name = fs.getName( appPath )
  local func = loadfile( appPath )
  app.co = coroutine.create( func )
  _, app.filter = coroutine.resume( app.co, ... )
  tApps[ #tApps + 1 ] = app
end

function setCurrent( appName )
  for i, app in ipairs( tApps ) do
    if app.name == appName then
      sCurrent = appName
      return true
    end
  end
  return false
end

function runApps()
  local event = {}
  while #tApps > 0 do
    for i, app in ipairs( tApps ) do
      if (app.name == sCurrent or not tUserEvents[ event[ 1 ] ]) and (app.filter == event[ 1 ] or not app.filter) then
        term.redirect( app.window )
        _, app.filter = coroutine.resume( app.co, unpack(event) )
      end
      if coroutine.status( app.co ) == "dead" then
        table.remove( tApps, i )
      end
    end
    event = {os.pullEvent()}
  end
end

…however, it doesn't exactly work as planned when a program uses term.redirect itself. The program in question works perfectly fine when not being run by this manager, but rendering is very screwed up when it is - given that it has an internal window. For now, I am using the default window API in this program, changing it would not help as any program that does use it will continue to have incorrect behavior.
Lyqyd #2
Posted 05 November 2015 - 01:03 AM
You need to redirect to the window you want the app to run in prior to resuming it for the first time. I'd assume that the program with the messed up rendering creates the window prior to its first yield, so at that point, term.current() won't be pointing at where you'd really like it to be without an initial redirect.

Either that or you're not showing us all of the relevant code.
KingofGamesYami #3
Posted 05 November 2015 - 01:30 AM
I'm running the app in the default term at the moment. ( notice the line app.window = term.current() ). The program is, therefor, redirected to the current window when launched - because the apps window becomes whatever term.current() was at the time of launch.

I've created a demonstration program that should show the problem I ran into:

test

local maxx, maxy = term.getSize()
local w = window.create( term.current(), 1, 4, maxx, maxy - 3 )
term.redirect( w )
term.setBackgroundColor( colors.white )
term.clear()
sleep( 1 )
term.clear()

runitall

os.loadAPI( "appManager" ) --#this is the API I posted in the OP
appManager.launchApp( "test" ) --#this is the test program above
appManager.runApps()

Running the program test directly will cover an area of the screen in white, wait a second, then end.

Running the runitall program will cover an area of the screen in white, wait a second, cover the remaining part in white, then end.
Edited on 05 November 2015 - 12:31 AM
Bomb Bloke #4
Posted 05 November 2015 - 03:35 AM
After resuming each coroutine, you should be immediately performing "app.window = term.current()" again.
Creator #5
Posted 05 November 2015 - 05:44 AM
Also, the getRunning function isn't very helpful if you call it from outside because it will return suspended for all the coroutines. Also, to make it faster, you could add a var running and before a routine is resumed, set it to the number of the routine. I hope this will increase performance.
KingofGamesYami #6
Posted 05 November 2015 - 01:50 PM
Thanks BB, that fixed it!

Creator, that function is meant to be called within a running coroutine - it's part of my security.
Edited on 05 November 2015 - 12:52 PM
Creator #7
Posted 05 November 2015 - 02:16 PM
Ok. What security?
KingofGamesYami #8
Posted 05 November 2015 - 03:29 PM
Certain programs will have write access for fs, others will not. It's not released yet, you'll just have to wait and see :)/>

(Although, I probably will go with your suggestion of using a variable)
KingofGamesYami #9
Posted 09 November 2015 - 05:18 AM
So… I broke this again somehow. Same problem as before with windows in programs. Went so far as to actually open minecraft and test it on the actual mod instead of an emulator. Still broken.

Relevant Code:
SpoilerGithub Repo

1. Make the directory "SecureOS"
2. Get these files from the repo
SecureOS/startup
SecureOS/desktop
SecureOS/appManager
SecureOS/simpleButton
3. Run SecureOS/desktop –this will show you what *should* happen
4. Run SecureOS/startup –this will show you what is *broken*
Bomb Bloke #10
Posted 09 November 2015 - 07:00 AM
Just at a glance, I'm seeing two copies of most of your function definitions within "startup". Beats me whether they're exactly the same or not, but obviously only the second ones will apply.

Also note that this:

appManager = table.protect( appManager )

… will not replace _G.appManager, but rather will define a new global variable within your script's environment.

Edit:

On actually running your code, I can't see what symptoms you're referring to - it's the same whether I run "desktop" directly or via "startup". You may need to elaborate on your problem.
Edited on 09 November 2015 - 11:55 AM
KingofGamesYami #11
Posted 09 November 2015 - 07:47 PM
I fixed the double definition thing on github. I have no idea when that happened.

As for it working exactly the same, did you actually click on anything? The bug doesn't happen immediately, rather, it occurs after a button is clicked.
Bomb Bloke #12
Posted 09 November 2015 - 11:14 PM
Yes, though I didn't see anything too obvious.

Looking at the appManager code though, I see my answer is the same as before: You need to perform "app.window = term.current()" immediately after resuming each coroutine. Including after line 36.
Lyqyd #13
Posted 10 November 2015 - 01:01 AM
You may be better off creating one central function that resumes coroutines (passing to it all relevant information about the coroutine and the current event), and only ever resume them from that function. The initial resume to "launch" the program is then just passing the newly created coroutine an empty event in the master resume function. That way, you can ensure consistent behavior with your coroutines and only have to change things in one place.
KingofGamesYami #14
Posted 10 November 2015 - 02:33 AM
I added the line you specified after line 36, which fixed the problem I was having. Odd that you didn't encounter the bug I did.

Anyway, thanks for all the help.