Close Channel

Trying to close a channel from a menu. The user clicks ok on a button (on screen) to exit the channel. Not really getting it, can somebody point me down the correct path. I have a function in the main.brs

function home()
?"IN HOME"
screen = CreateObject("roSGScreen")
port = CreateObject("roMessagePort")
screen.setMessagePort(port)
scene =screen.CreateScene("HomeScene")
screen.show()
[size=85][font=Helvetica Neue, Helvetica, Arial, sans-serif]    while (true)[/font][/size]
     msg = wait(0, port)
      msgType = type(msg)
      ?msgType
       if msgType = "roSGScreenEvent"
          if msg.isScreenClosed()
            EXIT WHILE
[size=85][font=Helvetica Neue, Helvetica, Arial, sans-serif]      end if[/font][/size]

    end if
end while
end function

So later on in the channel if the user wants to exit instead of hitting the home button a menu option on the screen could be picked. In order to do this I have created a task to handle the button selection but not really getting anything to work.

<component name = "exitapp" extends = "Task" >
  <interface>
    <field id = "Content" type = "node" />
 </interface>
 <script type="text/brightscript" uri="pkg:/source/main.brs" />

  <script type = "text/brightscript" >
    <![CDATA[

sub init()
PRINT "IN EXIT TASK"
m.top.functionName = "exitout"
end sub

function exitout()
screen.close()
end function

]]>
  </script>

</component>

This is what I use:


' Terminate the channel, exiting back to the Roku Home screen
' The first Home keypress is needed to exit the screensaver
' The second Home key press will exit to the Home screen
'
sub sendHomeKeypress()
    ut = CreateObject("roUrlTransfer")
    ut.SetUrl("http://localhost:8060/keypress/Home")
    ut.PostFromString("")
    Sleep(2500)
    ut.PostFromString("")
end sub

Excellent belltown. Just what I was looking for. Wasn’t thinking about using a url transfer was trying to close the screen. Thanks again

btw, you don’t need the Sleep() and 2nd call to PostFromString() if this is being called as a result of a user menu item selection, as there won’t be a screensaver running then. In my case, I have a sleep timer which calls the routine when it expires, so there may be a screensaver active.

Thanks for the info. I had removed it.

I may not know what I’m talking about or maybe it’s different for video channels or scenegraph, but for my game, I simply RETURN from the Main() function (which is “AS VOID”), and since it’s over, the channel exits.

When the use selects “exit game” from the menu, it sets a flag, and a line at end of my main while loop within Main() always checks for that flag, and if it sees it, RETURN.

Maybe my method is unreliable or susceptible to future software changes or something, and I should switch to using “keypress/Home” approach.

“Komag” wrote:
I may not know what I’m talking about or maybe it’s different for video channels or scenegraph, but for my game, I simply RETURN from the Main() function (which is “AS VOID”), and since it’s over, the channel exits.

When the use selects “exit game” from the menu, it sets a flag, and a line at end of my main while loop within Main() always checks for that flag, and if it sees it, RETURN.

Maybe my method is unreliable or susceptible to future software changes or something, and I should switch to using “keypress/Home” approach.
If only Scream Graph were that simple. Unfortunately, Main() spins up an roSGScreen, which creates a Scene to handle the UI, running in a separate thread. You might have multiple Tasks running in their own threads as well, and a screensaver. While you can theoretically call screen.Close() within Main(), you’d first need to set up a way to signal Main() from the scene that it’s time to close, and even then calling Close() only closes the main thread; the screensaver (and Tasks) continue running. Furthermore, if your screensaver is running in SD mode, the Roku then switches back to HD mode (because that’s what it normally does when the main thread exits), so now you have a mini-screensaver doing its thing. And if that isn’t enough … the channel is unresponsive to remote keypresses, and the only way to get back to the Home screen is to re-boot the device. It makes me want to SCREAM.

By far the most reliable way to programmatically exit a Scene Graph application, including the screensaver and all associated Tasks, is to simulate a pair of Home keypresses.

It’s nothing you need to worry about if you’re using the old API or 2D API.

Holy cow, I had no idea!

So you can’t just use “END” in the script section of a callback? Does that leave processes running?

“destruk” wrote:
So you can’t just use “END” in the script section of a callback? Does that leave processes running?
Putting END in a script callback function just exits the callback. It doesn’t stop anything running, even the Scene it’s running in - unless I misunderstand what you’re saying.

I’ll put together an example when I get a moment showing how tasks and screensavers keep running if you exit from Main(), locking up the device and needing a re-boot.

Are there code changes I need to do to the SceneGraph SDK so as to not have the channel locking up users device trying to exit the channel? : https://github.com/rokudev/videoplayer-channel

Is there another SceneGraph template like this that is more up to date that i should be using to be ready for the future changes? Thanks

“destruk” wrote:
So you can’t just use “END” in the script section of a callback? Does that leave processes running?
Here’s a demo channel (HD). Set your screensaver timeout to one minute and see what happens.

Main() creates MainScene. Main() prints to the console every 3 seconds to show that it’s running. After 100 seconds (long enough for screensaver activation), Main() closes the scene and exits.

Meanwhile, MainScene has a timer that runs every 15 seconds. The timer callback calls END, which does indeed exit the callback. However, it can be seen in the console output that the timer keeps firing every 15 seconds until screensaver activation.

MainScene has a Task that runs continuously printing to the console every 15 seconds.

The key point is that even though Main() exits, the Task and the screensaver keep running. The UI is unresponsive requiring a re-boot.

source/Main.brs


sub Main()
    screen = CreateObject("roSGScreen")
    scene = screen.createScene("MainScene")
    port = CreateObject("roMessagePort")
    screen.SetMessagePort(port)
    screen.Show()
    ts = CreateObject("roTimespan")
    while true
        msg = Wait(3000, port)
        print "Main() is running"
        if ts.TotalSeconds() > 100
            print "Closing the screen"
            screen.Close()
            exit while
        end if
        if Type(msg) = "roSGScreenEvent"
            print "roSGScreenEvent"
            if msg.IsScreenClosed()
                exit while
            end if
        end if
    end while
    print "** Main() terminating ***"
end sub

components/MainScene.xml


<?xml version="1.0" encoding="UTF-8"?>

<component name="MainScene" extends="Scene">
  <script type="text/brightscript">
    <![CDATA[

    sub init()
      m.top.findNode("label").font.size = 120
      m.timer = m.top.findNode("timer")
      m.timer.ObserveField("fire", "onFire")
      m.timer.control = "start"
    end sub

    sub onFire()
      print "***** Main Scene timer fired *****"
      END
      print "***** Should not get here *****"
    end sub

    ]]>
</script>
  <children>
    <Label
      id="label"
      width="1280"
      height="720"
      text="Hello, World!"
      font="font:LargeBoldSystemFont"
      horizAlign="center"
      vertAlign="center" />
    <TaskRunning />
    <Timer
      id="timer"
      repeat="true"
      duration="15" />
  </children>
</component>

components/TaskRunning.xml


<?xml version="1.0" encoding="UTF-8"?>

<component name="TaskRunning" extends="Task">
  <script type="text/brightscript">
    <![CDATA[

    sub init()
        m.top.functionName = "taskRun"
        m.top.control = "RUN"
    end sub

    function taskRun() as void
        port = CreateObject("roMessagePort")
        while true
            Wait(5000, port)
            print "Task is running"
        end while
    end function

    ]]>
  </script>
</component>

components/Screensaver.xml


<?xml version="1.0" encoding="UTF-8"?>

<component name="Screensaver" extends="Scene">
  <script type="text/brightscript">
    <![CDATA[

    sub init()
      m.top.findNode("seq").control = "start"
    end sub

    ]]>
  </script>
  <children>
    <Group>
      <Poster
        id="poster"
        uri="https://c1.staticflickr.com/9/8505/8418942879_d6f9e365b8_z.jpg"
        translation="[370, 120]"
        width="500"
        height="500" />
      <SequentialAnimation
        id="seq"
        repeat="true">
        <Animation
          duration="8"
          easeFunction="outCubic">
          <FloatFieldInterpolator
            key="[0,1]"
            keyValue="[1,0]"
            fieldToInterp="poster.opacity" />
        </Animation>
        <Animation
          duration="8"
          easeFunction="inCubic">
          <FloatFieldInterpolator
            key="[0,1]"
            keyValue="[0,1]"
            fieldToInterp="poster.opacity" />
        </Animation>
      </SequentialAnimation>
    </Group>
  </children>
</component>

source/RunScreenSaver.brs


sub RunScreenSaver()
    screen = CreateObject("roSGScreen")
    port = CreateObject("roMessagePort")
    screen.SetMessagePort(port)
    scene = screen.CreateScene("Screensaver")
    screen.Show()
    while true
        msg = Wait(0, port)
        if Type(msg) = "roSGScreenEvent"
            if msg.IsScreenClosed()
                exit while
            end if
        end if
    end while
end sub

argh… :disappointed_face: Thanks Belltown

“belltown” wrote:

“destruk” wrote:
So you can’t just use “END” in the script section of a callback? Does that leave processes running?
Putting END in a script callback function just exits the callback. It doesn’t stop anything running, even the Scene it’s running in - unless I misunderstand what you’re saying.
Good grief - END does not exit the program?
Out of curiosity - what does STOP cause in such situation?

“EnTerr” wrote:

“belltown” wrote:

“destruk” wrote:
So you can’t just use “END” in the script section of a callback?  Does that leave processes running?
Putting END in a script callback function just exits the callback. It doesn’t stop anything running, even the Scene it’s running in - unless I misunderstand what you’re saying.
Good grief - END does not exit the program?
Out of curiosity - what does STOP cause in such situation?
It appears that STOP behaves the same as END, the only difference being that when side-loaded, the channel will break into the debugger for a STOP. When run from a channel-store channel, STOP and END seem to behave the same way. I have a Timer in the main Scene that displays a message then calls END or STOP. The code after END/STOP does not execute, but the Scene and Timer remain active, and the message display continues updating whenever the timer fires. If I exit from Main() while the screensaver is active, the screensaver continues running, but the Roku does not respond to remote commands and has to be re-booted.

“belltown” wrote:
It appears that STOP behaves the same as END, the only difference being that when side-loaded, the channel will break into the debugger for a STOP. When run from a channel-store channel, STOP and END seem to behave the same way. I have a Timer in the main Scene that displays a message then calls END or STOP. The code after END/STOP does not execute, but the Scene and Timer remain active, and the message display continues updating whenever the timer fires.
seems like a bug to me - but i can easily imagine someone dismissing it “works as designed, won’t fix”

If I exit from Main() while the screensaver is active, the screensaver continues running, but the Roku does not respond to remote commands and has to be re-booted.
clearly a bug!
whose screensaver btw? channel’s own or the system one

Not meaning to hijack my own thread but I have another situation somewhat in the same lines as my original. The app starts in the main.brs and creates a scene (spinner) as the spinner is running, the app continues to the next function which starts configuration stuff. If a particular situation is present, a scene is created that creates a dialog box displaying to the users. Once the user is done reading the box, he/she can press the back or options button to close the dialog and continue into the app or home.brs (thru a task). All this works except that in order to continue into the home.brs, the user has to press the button twice (not intended). The first press closes the dialog box and leaves a gray screen. The second press enters the home.brs. I am under the impression that when the first button click occurs, the dialog would close and the original spinner would be visible as home.brs starts. Apparently the spinner is being closed or most likely I am not doing something right. I am posting min code with hopes somebody could point out the flaw.

Sub RunUserInterface()
Print "RUNUSERINTERFACE"
OpeningDialog()
End Sub
''*************************************************************'
Function OpeningDialog()
?"In OpeningDialog"
screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
screen.setMessagePort(m.port)
scene = screen.CreateScene("OpeningDialog")
screen.show()
ConfigureStuff()

while(true)
  msg = wait(0, m.port)
  msgType = type(msg)

  if msgType = "roSGScreenEvent"

    if msg.isScreenClosed() then return ""
  end if
end while
end function
'*********************************************************
function configurestuff()
code stuff
Condition Exist so creae "WarnUser"

screen = CreateObject("roSGScreen")
port = CreateObject("roMessagePort")
screen.setMessagePort(port)
scene = screen.CreateScene("WarnUser")
screen.show()
    while (true)
     msg = wait(0, port)
      msgType = type(msg)
      ?msgType
       if msgType = "roSGScreenEvent"
          if msg.isScreenClosed()
      EXIT WHILE
    end if
    end if
end while
end function
'*****************************************************

<component name = "Warnuser" extends = "Scene" >
  <interface>
    <field id="Content" type="node"  />
    </interface>
  <script type="text/brightscript" uri="pkg:/source/main.brs" />

  <script type = "text/brightscript" >
   <![CDATA[
    sub init()
    dialogWarn()
    end sub

    sub dialogWarn()
      m.background  = m.top.findNode("Background")
      example = m.top.findNode("instructLabel")
      examplerect = example.boundingRect()
      centerx = (1280 - examplerect.width) / 2
      centery = (720 - examplerect.height) / 2
      example.translation = [ centerx, centery ]
      m.top.setFocus(true)
      di = CreateObject ("roDeviceInfo")
      serial=di.GetDeviceUniqueId()
      dialog = createObject("roSGNode", "Dialog")
      dialog.title = "  Channel Not Activated"
      dialog.optionsDialog = true
      dialog.message = "Danger Will Robinson"
      m.top.dialog = dialog
    end sub

    function onKeyEvent(key as String, press as Boolean) as Boolean
          if press then
            if key = "options"
            m.hometaskRegistryNode = m.top.findNode("hometaskRegistry")
            m.hometaskRegistryNode.control="RUN"
              return true
            end if
          end if

          return false
        end function

    ]]>

“EnTerr” wrote:

“belltown” wrote:
It appears that STOP behaves the same as END, the only difference being that when side-loaded, the channel will break into the debugger for a STOP. When run from a channel-store channel, STOP and END seem to behave the same way. I have a Timer in the main Scene that displays a message then calls END or STOP. The code after END/STOP does not execute, but the Scene and Timer remain active, and the message display continues updating whenever the timer fires.
seems like a bug to me - but i can easily imagine someone dismissing it “works as designed, won’t fix”

If I exit from Main() while the screensaver is active, the screensaver continues running, but the Roku does not respond to remote commands and has to be re-booted.
clearly a bug!
whose screensaver btw? channel’s own or the system one
It’s the channel’s own, private screensaver, the same one I used in the demo code a few posts back.

“btpoole” wrote:
most likely I am not doing something right

A couple of quotes from the documentation:

Important

There must be only one Scene Graph component extended from a scene node class in the pkg:/components directory.

While it is technically possible to have more than one scene per channel, we recommend you only have one roSGScreen and one Scene node. Child nodes of the scene can be treated as different “scenes” where you can then implement transitions between them.
If you fully grasp this principle, it will make things much easier going forward. In the old SDK, there was a concept of a “screen stack” where you could keep creating screens anywhere and everywhere as much as you liked. Each screen would stack on top of its parent, and when you closed it you’d be back in the parent screen right where you were before you created the child. This is how you’re trying to use Scene Graph. However, SG is a totally different paradigm. You need to think in terms of the whole application being one super-Scene, a single Scene that contains all the components that make up the whole UI. You can show and hide components to give the appearance of multiple “screens”, but they really all fall under the same Scene. A good way to familiarize yourself with this concept would be to run through a number of the Roku sample channels; the ones in https://github.com/rokudev tend to be a bit more representative of real-world situations than the ones in the Scene Graph XML Tutorial.

“belltown” wrote:

“btpoole” wrote:
most likely I am not doing something right

A couple of quotes from the documentation:

Important

There must be only one Scene Graph component extended from a scene node class in the pkg:/components directory.

While it is technically possible to have more than one scene per channel, we recommend you only have one roSGScreen and one Scene node. Child nodes of the scene can be treated as different “scenes” where you can then implement transitions between them.
If you fully grasp this principle, it will make things much easier going forward. In the old SDK, there was a concept of a “screen stack” where you could keep creating screens anywhere and everywhere as much as you liked. Each screen would stack on top of its parent, and when you closed it you’d be back in the parent screen right where you were before you created the child. This is how you’re trying to use Scene Graph. However, SG is a totally different paradigm. You need to think in terms of the whole application being one super-Scene, a single Scene that contains all the components that make up the whole UI. You can show and hide components to give the appearance of multiple “screens”, but they really all fall under the same Scene. A good way to familiarize yourself with this concept would be to run through a number of the Roku sample channels; the ones in https://github.com/rokudev tend to be a bit more representative of real-world situations than the ones in the Scene Graph XML Tutorial.
bellotwn, thank you for the explanation. If I understand correctly, there should be one main scene (my Home scene) in this case which is called from the main.brs. Once the Home is created all components of the app would be created , waiting in the wings to be shown. I think I understand. now just moving things around to do this might be tricky. Thanks again.