Roku Developer Program

Developers and content creators—a complete solution for growing an audience directly.
cancel
Showing results for 
Search instead for 
Did you mean: 
brybott
Level 8

sending info from scene graph to main thread

I understand that to pass information from a scene graph node, I can set observeField with the field name and a message port to listen on. What I don't understand is how to assign the same message port in observeField that I am listening on in the roSGScreen's event loop. If I create the message port in the main thread so I can set it to the roSGScreen, how can I send it into the scene graph's thread so it can be set on observeField? Below is the basic code I'm working with:

Main:

'create the screen graph component and set it up with a message port
screen = CreateObject("roSGScreen")
sceneGraphPort = CreateObject("roMessagePort")
screen.SetMessagePort(sceneGraphPort)

'set the xml component by name, that will be displayed on the screen
screen.createScene("appScene")

'display the scene on the screen
screen.show()


'listen for any events associated with the scene
while true
msg = wait(0, sceneGraphPort)
if type(msg) = "roSGScreenEvent"
if msg.isScreenClosed()
return
end if
else if type(msg) = "roSGNodeEvent"
print "node "; msg.getNode()
print "field name "; msg.getField()
print "data "; msg.data()
end if
end while


Scene Graph:

<?xml version="1.0" encoding="utf-8" ?>
<component name="appScene" extends="Scene" >
<script type="text/brightscript" >
<![CDATA[
sub init()
print "-----------STARTING GRAPH SCENE-----------"
m.leftRectangle = m.top.findNode("leftRectangle")
m.leftRectangle.observeField("color", sceneGraphPort)
m.top.setFocus(true)
end sub

function onKeyEvent(key as String, press as Boolean) as Boolean
handled = false
if press then
if (key = "OK")
m.leftRectangle.color = "0x0000FFFF"
handled = true
end if
end if
return handled
end function
]]>
</script>
<children>
<Rectangle
id="leftRectangle"
width="300"
height="300"
color="0xFF0000FF"
translation="[100,100]" />
</children>
</component>


The end result is once the scene graph is loaded, if the user presses "ok", the square should turn from green to blue.
The issue is, unsurprisingly, sceneGraphPort is uninitialized in the scene graph thread. Any advice how to set the port in the scene graph thread, that I'm listening to in the main thread? Or am I thinking about this all wrong? Did I miss something in the documentation that explains how to do this?
0 Kudos
6 Replies
TheEndless
Level 7

Re: sending info from scene graph to main thread

When observing a field in the SceneGraph thread, you don't specify a port or listen for roSGNode events. Those are strictly for listening from the main BrightScript thread. If you're trying to observe "color" inside the SceneGraph thread, use the version of observeField() that takes a field name and the name of the function that handles the event. And, of course, make sure you have a function with that name in the defined in the Scene.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
brybott
Level 8

Re: sending info from scene graph to main thread

Thank you for taking the time to respond TheEndless.

If you're trying to observe "color" inside the SceneGraph thread...

I'm actually trying to observe for a change to the color field in the main thread.

In a general sense, I'm trying to understand how to send information between the main thread and the scene graph thread, if it's possible (though the docs seem to imply that it's possible).

If I were to set up observeField() in the main thread, as I understand it, I would need to call that from an SGNode in the main thread. Ok, but that means I need to create the node in the main thread, which means it doesn't exist in the scene graph thread, so I'm still not able to listen for events that occur in the scene graph thread while in the main thread's event loop.
0 Kudos
TheEndless
Level 7

Re: sending info from scene graph to main thread

All messages should automatically be sent to the main BrightScript thread, if you've registered as an observer (they all use the same port). If the field you're setting is on a component instead of on the scene, then you may need to either search for that component to observe the field, or marshal it through the scene via a second custom field that you can easily observe from the main thread.
My Channels: http://roku.permanence.com - Twitter: @TheEndlessDev
Instant Watch Browser (NetflixIWB), Aquarium Screensaver (AQUARIUM), Clever Clocks Screensaver (CLEVERCLOCKS), iTunes Podcasts (ITPC), My Channels (MYCHANNELS)
0 Kudos
brybott
Level 8

Re: sending info from scene graph to main thread

Well, it took a bunch experimentation, but I figured out how to send information from the main thread to the scene graph thread. Thanks to TheEndless for dropping some bread crumbs and leading me in the right direction. I've posted the following basic example, in case it might help someone else.

Main:

sub Main()
showChannelSGScreen()
end sub

sub showChannelSGScreen()
screen = CreateObject("roSGScreen")
m.port = CreateObject("roMessagePort")
screen.setMessagePort(m.port)
scene = screen.CreateScene("appScene")


screen.show()
scene.findNode("leftRectangle").observeField("width", m.port)

while(true)

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

if msgType = "roSGScreenEvent"

if msg.isScreenClosed() then return

else if msgType = "roSGNodeEvent"
print "node "; msg.getNode()
print "field name "; msg.getField()
print "data "; msg.getData()
scene.findNode(msg.getNode()).height = 400
end if

end while
end sub


Scene Graph:

<?xml version="1.0" encoding="utf-8" ?>
<component name="appScene" extends="Scene">
<script type="text/brightscript" >
<![CDATA[

sub init()
print "-----------STARTING SCENE GRAPH-----------"
m.leftRectangle = m.top.findNode("leftRectangle")
m.leftRectangle.observeField("color", "someFunction")
m.leftRectangle.observeField("width", "someOtherFunction")
m.top.setFocus(true)
end sub

sub someFunction()
print "color was changed"
end sub

sub someOtherFunction()
print "width was changed"
end sub

function onKeyEvent(key as String, press as Boolean) as Boolean
handled = false
if press then
if (key = "OK")
m.leftRectangle.setField("color", "0x0000FFFF")
m.leftRectangle.setField("width", 350)
handled = true
end if
end if
return handled
end function

]]>
</script>
<children>
<Rectangle
id="leftRectangle"
width="300"
height="300"
color="0xFF0000FF"
translation="[100,100]" />
</children>
</component>


The key is to set the observeField() in the main thread after you call show() on the roSGScreen and before you enter the event loop for the roSGScreen. Additionally you have to observe field based on the relationship to the scene you've created and sent to the roSGScreen.createScene method.
0 Kudos
rynop
Level 7

Re: sending info from scene graph to main thread

You my friend, are the man!  Thanks for following up, this helped me SO much.  Docs are not clear (from what I can find) on this crucial topic of sending messages from Render thread to main thread and vice versa.  Thanks again!
0 Kudos
jasondixon
Level 7

Re: sending info from scene graph to main thread

This was perfect. Thank you so much for posting your solution. Was banging my head on this for longer than I'd like to admit. Thanks again so much.
0 Kudos