@atilim, @bowerandy, right, forgot to mention a small but significant detail.
To try live coding (or debugging) on the device you need to include mobdebug.lua in the project (I think @bowerandy also included socket.lua, but not sure if it's needed as you seem to support luasocket).
Then you start the application on the device; it will start a debugging session in the IDE (you should see a green arrow). Only **then** you press Ctrl-F6 to switch to live coding.
Also I may have a recommendation: if the output of 'gdrbridge isconntected' is already 1, then don't start the desktop player and directly play the .gproj file.
@atlim. To run on a real device you don't use Start Debugging. What you do it to find out your desktop machine's IP address and stick in as a parameter to the mob debug.start call. e.g.
require('mobdebug').start("192.168.1.101")
You have to launch from Gideros Studio. Select main.lua in Gideros and in ZBS and start the player on the device. The press run in Gideros and switch back to ZBS. After a short time you should seen the green breakpoint cursor appear in the ZBS editor and you are good to debug as normal.
@paulclinger, I couldn't get Run as Scratchpad to be enabled when I'm running on a device, only the desktop player. Is there any way around this?
@bowerandy, @atilim, get the latest from the repo and give it a try. When you start the IDE, don't forget to select Project | Start Debugger Server, otherwise the client won't be able to connect to start debugging.
After the debugging is initiated (follow @bowerandy's instructions above), "Run as Scratchpad" option should be enabled. When you select it, you switch to scratchpad mode.
Let me know how it goes. Hope it's all for today .
Also I may have a recommendation: if the output of 'gdrbridge isconntected' is already 1, then don't start the desktop player and directly play the .gproj file.
@atilim, makes sense. This may allow to start even remote Scratchpad using Ctrl-F6 as long as the player is running and the bridge is connected. There are some technical issues to resolve, but I'll give it a try.
@paulclinger. Ok, I'm having a bit of difficulty putting together a little demo for this live coding. I can get your modification of Sleeping Bodies going but, as you've obviously spotted, there is some trickiness needed when replacing methods that are given out to addEventListener() and other such places.
So I thought I would build a much simpler example to explore what exactly can and can't be done. However, when I do this I can't get any live changes to register. I find myself wondering what it is exactly that changing something in the scratchpad pane does. So here's my code in "main.lua":
ifnot(BhLiveCode)then-- Don't refine a class if it is already defined
BhLiveCode=Core.class(Sprite)require("mobdebug").start("192.168.1.101")endfunction BhLiveCode:init(scale)local image=Bitmap.new(Texture.new("Widget.png"))
image:setScale(1.5*scale)
self:addChild(image)
stage:addChild(self)
BhLiveCode.sharedInstance=self
endif BhLiveCode.sharedInstance then-- If we already have a shared instance then destroy it to start over
BhLiveCode.sharedInstance:removeFromParent()
BhLiveCode.sharedInstance=nilend
BhLiveCode.new(1.1)
So I have been assuming that if I change anything in the live code pane then the whole file is recompiled and re-run. Is that right? Re-running the above should:
a) Not redefine the class. I'm not sure what this would do but it seems like a bad idea.
b) Redefine function init() in the BhLiveCode table
c) Removed any old instance from the stage which should be GC'd
d) Run the new() again to create a new instance with the new code.
So I would expect to be able to drag the sliders on the 1.1 and 1.5 numbers above and see the a new image be added with a new size? However, nothing appears to happen.
@bowerandy, I think you are on the right track, but it doesn't quite work like this. To reload the updated code from the IDE, the debugger needs to do two things:
(1) it needs to abort the running process. (2) it needs to re-evaluate the new code fragment in the context of your application.
It can only execute step 2 when it can abort the running process. In your case there are no Lua code fragments that get executed, so there is nothing to interrupt to allow the new fragment to be loaded. In the earlier example, we had ENTER_FRAME and TIMER events registered that allowed the script to be interrupted.
Adding one of those should allow you to stop and reload the script. It won't guarantee that it will work though as you are trying to do something similar to what I was trying and couldn't quite do yesterday (like removing and adding listeners, which seem to work for you). For some reason Gideros didn't like my world/stage modifications and the player would crash.
@paulclinger, I was thinking exactly as @bowerandy and after reading your comment, I've added an empty ENTER_FRAME function to BhLiveCode and then it worked as expected.
ifnot(BhLiveCode)then
BhLiveCode=Core.class(Sprite)require("mobdebug").start("192.168.1.101")endfunction BhLiveCode:onEnterFrame()-- Comment in this line to see if active movement can be altered (I find it can)-- self.image:setY(self.image:getY()+9)endfunction rgb(r, g, b)return((r*256)+g)*256+b
endfunction BhLiveCode:init(x, y)
application:setBackgroundColor(rgb(150, 146, 97))local image=Bitmap.new(Texture.new("MyPicture.png"))
image:setScale(0.53)
image:setAnchorPoint(0.5, 0.5)
image:setPosition(x, y)
image:setRotation(4)
self.image=image
self:addChild(image)
stage:addChild(self)
self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
self:addEventListener(Event.MOUSE_DOWN, self.onMouseDown, self)endfunction Application:clearStage()while stage:getNumChildren()>0do
stage:removeChildAt(1)endend
application:clearStage()
BhLiveCode.new(162, 416)
BhLiveCode.new(446, 476)
This works fine for me on an iOS device and in desktop player on Mac. If, it crashes for you in Windows/Android, any chance @atilim could take a look at it? It would be a shame to only be able to use this on iOS.
Paul, are there any other slider types apart from the numerical one? You can see I've added an RGB colour feature because I didn't know whether there was a colour palette available. I wonder if there's any way we could slide through a table based on it's keys. Not sure how that would work.
Anyway, I'm getting closer to being able put up a demo.
@atilim, #2 is a great idea; why didn't I think of that?
With #3, why do you need to do that? I saw that @paulcllinger did it in yesterday's example but I didn't understand why. I was trying to avoid as many magic incantations as possible to avoid confusing too many people.
If you do #1, surely that means that you never get to reach the rest of file so any live editing won't have an effect. Or am I missing something?
after changing the MySprite:onEnterFrame, it continues to execute the original one. Because although MySprite:onEnterFrame function is changed, MySprite object holds the original function reference internally. Currently, I don't know if there is another easy way.
And about #1, you're right. I just want to execute main.lua once even when it's changed. Maybe #1 is not necessary at all.
@atilim, in my example that rebuilds everything from scratch, I don't think this is necessary. Try just commenting in the line in the onEnterFrame() handler and you should be able to fiddle with the speed slider (9) in that method.
@bowerandy, there are no other sliders at the moment, but it *may* be possible to add more; I was thinking about selecting a code fragment (for example, "1,2,3") and then using right click menu to select what you want to change. It may show you a color wheel and then adjust those numbers according to the position on the wheel, which will update the script. The rest is already done.
I'm not sure I'm doing everything correctly with @bowerandy's example. First of all, the example references self.onMouseDown, which I don't see in the code, so I removed that listener. Second, I get an error on self.image:setY saying that self.image is nil, so I put it behind "if", but it doesn't do anything on the screen.
If I fix these two issues (and also fix IP address in "start") and run the example as is, it crashes for me. Even after I move addEventListener call and wrap it as @atilim suggested and as I did yesterday, it still crashes.
It's interesting that it works for @atilim. Maybe I need to upgrade to a newer version of Gideros (mine is from 9/5). Let me try with a newer version.
oh yes, you're right. And your example is like a realtime scene editor. It's really fun to play with it (and still I can't believe how this is possible )
@atilim, @bowerandy, yes, it works after upgrade to 2012.8.4. I can move both of the boxes and see the immediate effect.
This is the code that I'm running (I've incorporated some of @atilim's suggestions):
BhLiveCode = BhLiveCode or Core.class(Sprite)function BhLiveCode:onEnterFrame()-- Comment in this line to see if active movement can be altered (I find it can)-- self.image:setY(self.image:getY()+9)endfunction rgb(r, g, b)return((r*256)+g)*256+b
endfunction BhLiveCode:init(x, y)
application:setBackgroundColor(rgb(150, 146, 97))local image=Bitmap.new(Texture.new("MyPicture.png"))
image:setScale(0.53)
image:setAnchorPoint(0.5, 0.5)
image:setPosition(x, y)
image:setRotation(4)
self.image=image
self:addChild(image)
stage:addChild(self)endfunction Application:clearStage()while stage:getNumChildren()>0do
stage:removeChildAt(1)endend
application:clearStage()
BhLiveCode.new(149, 387)
BhLiveCode.new(232, 276)if initialized thenreturnend
initialized =truerequire("mobdebug").start()
stage:addEventListener(Event.ENTER_FRAME, function(...) BhLiveCode.onEnterFrame(...)end)
But @paulclinger, I think that is as a result of editing stuff in the code window and not knowing it's changed. Do you think it would be a good idea to offer the ability to revert to the original version after messing with Live Code?
@bowerandy, you can still use Undo (Cmd-Z/Ctrl-Z) to undo your actions, although it may be too many to undo if you spent quite a bit of time making changes. The file is saved before starting debugging, so you can simply close and re-open it.
I can possibly add a hotkey/button for "return to the last saved state" (which is not the same as 'reload' because you can still keep you Redo history in case you want a replay), but I need to experiment with it a bit. Also, I'm hesitant to add too many features quickly as I'm looking for support from multiple users for features before adding them.
Another thought is that if you can find a good lua-based diff'er, I may be able to include it to show differences between the current content in the editor tab and a saved version.
Just back from the pub and looking at this demo program. I'm getting a program exit if I try and edit the onEnterFrame method and there is a syntax error. Presumably, if a method doesn't compile it should abort and not install it and leave the previous method present? And also not run the following code?
@bowerandy, try it with the script I posted. You probably don't need/want to re-register event listeners; you just want to use a wrapper that will make the same listener to work with the new function.
If there are any compilation errors, the code won't be executed at all (and you will see the error in the Output window). If there are some run-time errors, then whatever code got already executed, will have the effect, but the rest of the code will not (and your app may end up in some weird state. For example, you can remove listener successfully, but not register a new one because you have a run-time error on that line. There is not much I can do with that other than hope that whatever next change you do will fix that state.
> @atilim, if I do this then will it be possible to remove that handler? In this case, you need to hold a reference to the registered function like:
self.penterFrame =function(...)pcall(self.onEnterFrame, ...)end
self:addEventListener(Event.ENTER_FRAME, self.penterFrame, self)-- and later
self:removeEventListener(Event.ENTER_FRAME, self.penterFrame, self)
But I'm planning to implement removeAllEventListeners function to remove all registered event listeners or remove registered event listeners with a specific type. e.g.
self:removeAllEventListeners()-- remove all event listeners
self:removeAllEventListeners(Event.ENTER_FRAME)-- remove all ENTER_FRAME event listeners
> Guys, I'm doing a "promo" video for this live coding stuff. Do you have > any high res images of the Gideros and ZeroBrane logos that you can send me? Yay! You can use the logos here: http://www.giderosmobile.com/press
This would remove the ENTER_FRAME listener(s) for a particular target. I always wondered why we didn't have that capability. I think that needing to know the registered function rather than just the event is a bit odd?
BTW, rather than me overriding EventDispatcher:registerEventHandler to wrap the passed function in a pcall (when using live coding), is there any way for me to override dispatchEvent() to do the pcall instead (such that the whole system will use it while live coding).
And my initial thought was if it's possible to register multiple listeners for a particular event type, then they also should be removed one by one. I just want addEventListener and removeEventListener be totally symmetric to each other.
Overriding dispatchEvent doesn't help because it's not used internally to dispatch events like ENTER_FRAME, MOUSE_DOWN, etc.
But in the future I can add a function to Application class to suppress runtime errors like:
application:suppressErrors(true)
or forward errors to a custom error handler function like:
Yes, that's true but it's unlikely that you would be registering three different listeners for the same event AND the same target. And if you did, I think it would still be "symmetrical" to remove all the listeners for that event AND that target with one call. Otherwise it would work just as it does now.
Overriding dispatchEvent doesn't help because it's not used internally to dispatch events like ENTER_FRAME, MOUSE_DOWN, etc.
Ok, understood.
But in the future I can add a function to Application class to suppress runtime errors like:
application:suppressErrors(true)
or forward errors to a custom error handler function like:
Comments
To try live coding (or debugging) on the device you need to include mobdebug.lua in the project (I think @bowerandy also included socket.lua, but not sure if it's needed as you seem to support luasocket).
Then you start the application on the device; it will start a debugging session in the IDE (you should see a green arrow). Only **then** you press Ctrl-F6 to switch to live coding.
@atilim, see @bowerandy's instructions on how to run on the device here: http://giderosmobile.com/forum/discussion/comment/12680#Comment_12680
I don't guarantee this will work (as I have never tried this with Gideros), but this is how it works with some other frameworks I tested with.
require('mobdebug').start("192.168.1.101")
You have to launch from Gideros Studio. Select main.lua in Gideros and in ZBS and start the player on the device. The press run in Gideros and switch back to ZBS. After a short time you should seen the green breakpoint cursor appear in the ZBS editor and you are good to debug as normal.
@paulclinger, I couldn't get Run as Scratchpad to be enabled when I'm running on a device, only the desktop player. Is there any way around this?
best regards
@bowerandy, I'll need to check the conditions that enable Run as Scratchpad for external connections. I'll post an update when I figure it out.
After the debugging is initiated (follow @bowerandy's instructions above), "Run as Scratchpad" option should be enabled. When you select it, you switch to scratchpad mode.
Let me know how it goes. Hope it's all for today .
So I thought I would build a much simpler example to explore what exactly can and can't be done. However, when I do this I can't get any live changes to register. I find myself wondering what it is exactly that changing something in the scratchpad pane does. So here's my code in "main.lua":
a) Not redefine the class. I'm not sure what this would do but it seems like a bad idea.
b) Redefine function init() in the BhLiveCode table
c) Removed any old instance from the stage which should be GC'd
d) Run the new() again to create a new instance with the new code.
So I would expect to be able to drag the sliders on the 1.1 and 1.5 numbers above and see the a new image be added with a new size? However, nothing appears to happen.
Any ideas?
(1) it needs to abort the running process.
(2) it needs to re-evaluate the new code fragment in the context of your application.
It can only execute step 2 when it can abort the running process. In your case there are no Lua code fragments that get executed, so there is nothing to interrupt to allow the new fragment to be loaded. In the earlier example, we had ENTER_FRAME and TIMER events registered that allowed the script to be interrupted.
Adding one of those should allow you to stop and reload the script. It won't guarantee that it will work though as you are trying to do something similar to what I was trying and couldn't quite do yesterday (like removing and adding listeners, which seem to work for you). For some reason Gideros didn't like my world/stage modifications and the player would crash.
btw, is player still crashing on your side?
Paul, are there any other slider types apart from the numerical one? You can see I've added an RGB colour feature because I didn't know whether there was a colour palette available. I wonder if there's any way we could slide through a table based on it's keys. Not sure how that would work.
Anyway, I'm getting closer to being able put up a demo.
best regards
btw, as a reference, I apply these steps before live editing:
1. Add
2. Define the classes as:
3. Register the events as:
With #3, why do you need to do that? I saw that @paulcllinger did it in yesterday's example but I didn't understand why. I was trying to avoid as many magic incantations
as possible to avoid confusing too many people.
If you do #1, surely that means that you never get to reach the rest of file so any live editing won't have an effect. Or am I missing something?
best regards
And about #1, you're right. I just want to execute main.lua once even when it's changed. Maybe #1 is not necessary at all.
best regards
I'm not sure I'm doing everything correctly with @bowerandy's example. First of all, the example references self.onMouseDown, which I don't see in the code, so I removed that listener. Second, I get an error on self.image:setY saying that self.image is nil, so I put it behind "if", but it doesn't do anything on the screen.
If I fix these two issues (and also fix IP address in "start") and run the example as is, it crashes for me. Even after I move addEventListener call and wrap it as @atilim suggested and as I did yesterday, it still crashes.
It's interesting that it works for @atilim. Maybe I need to upgrade to a newer version of Gideros (mine is from 9/5). Let me try with a newer version.
I hope everything will work after upgrading. Also I can try to run desktop player in debug mode. So that maybe I can catch a clue.
This is the code that I'm running (I've incorporated some of @atilim's suggestions):
But @paulclinger, I think that is as a result of editing stuff in the code window and not knowing it's changed. Do you think it would be a good idea to offer the ability to revert to the original version after messing with Live Code?
best regards
I can possibly add a hotkey/button for "return to the last saved state" (which is not the same as 'reload' because you can still keep you Redo history in case you want a replay), but I need to experiment with it a bit. Also, I'm hesitant to add too many features quickly as I'm looking for support from multiple users for features before adding them.
Just back from the pub and looking at this demo program. I'm getting a program exit if I try and edit the onEnterFrame method and there is a syntax error. Presumably, if a method doesn't compile it should abort and not install it and leave the previous method present? And also not run the following code?
best regards
If there are any compilation errors, the code won't be executed at all (and you will see the error in the Output window). If there are some run-time errors, then whatever code got already executed, will have the effect, but the rest of the code will not (and your app may end up in some weird state. For example, you can remove listener successfully, but not register a new one because you have a run-time error on that line. There is not much I can do with that other than hope that whatever next change you do will fix that state.
Guys, I'm doing a "promo" video for this live coding stuff. Do you have any high res images of the Gideros and ZeroBrane logos that you can send me?
best regards
Likes: gorkem
> @atilim, if I do this then will it be possible to remove that handler?
In this case, you need to hold a reference to the registered function like:
> any high res images of the Gideros and ZeroBrane logos that you can send me?
Yay! You can use the logos here: http://www.giderosmobile.com/press
I think you could also make removeEventListener() do the following without any compatibility issues:
BTW, rather than me overriding EventDispatcher:registerEventHandler to wrap the passed function in a pcall (when using live coding), is there any way for me to override dispatchEvent() to do the pcall instead (such that the whole system will use it while live coding).
best regards
good idea!
It's possible to add more then one listener for a particular event type like:
Overriding dispatchEvent doesn't help because it's not used internally to dispatch events like ENTER_FRAME, MOUSE_DOWN, etc.
But in the future I can add a function to Application class to suppress runtime errors like:
best regards