Hello guys, Im new to Corona and can’t figure out how to create your own plugins on Native code and include it into my Lua project, tried to read docs page for 10 times but can’t understand how to make a bridge and etc. if someone can provide some step by step guide how to create some native plugin and import it into Lua project it would be cool cuz I can’t find one, even some ‘Hello $username’ would be cool <3
Thank you!!
I’m going to work this week on rebuilding our iOS Enterprise Tutorial so that it’s Corona Native friendly. Stay tuned.
Rob
Hello Rob! Thank you a lot!!! Hope there would be some step by step guide <3
And sorry for off top but just want to ask why native elements are prior in elements hierarchy
Corona’s display objects (display.newCircle, display.newImageRect etc. and everything based on them like widgets) are done in OpenGL. On all systems, OpenGL is it’s own window or canvas. You cannot mix native objects like native.newTextField() with OpenGL because they don’t generate OpenGL textures that can be pushed through the graphics pipeline. OpenGL is processed mostly by the devices GPU (Graphics Processing Unit) while native objects are exclusively done with the CPU (main computer chip). They just can’t mix.
We have set things up where you can put native.* objects into a display.newGroup() so that when you move the group, the native.* objects move with it, but that’s the only feature you get with grouping.
Rob
Hey Rob, how u doin, any news about this stuff?
I made a blog post on this and send over to corona. Hopefully the blog will be published.
thx mate hope is there any link available to see the post after publish??
Today is Labor Day in the us, which is a work holiday. I don’t think a lot of the corona staff is here today.
Scott is right, we are on a holiday in the US. I’ve glanced over his submission and to set reasonable expectations, it could be a week or more before this gets published. Let me explain.
We are working on a new public release. All hands will be involved in testing this week. So there is no bandwidth until we complete testing.
Next, we re no longer putting tutorials in our blog. They will be published to our Tutorials section on our Docs site. Why? The blog does a horrible job at formatting code and it’s hard to maintain them. We might publish a referring post on the blog to let people know about the new tutorial. For this to happen, I have to convert Scott’s post to Markdown and then it will have to go trough editing that will have to be scheduled in with all of our other publishing work. Depending on my release testing this week, I can probably get it converted to Markdown laster this week, but I can’t see it being published this week.
Scott, what might be more efficient is for you to post this to your website. If you want this up ASAP, that’s going to be your best route. We just need time to get this done.
Rob
I forgot to add, I’ve converted the original iOS Enterprise tutorial to our new system, it’s waiting on editing.
Rob
Thanks Rob. I have never done a guest blog post before so I had no idea. azbik999 I will pm you a link to the google docs file. My site is all html based so creating a new blog section on my site would be tricking. I want it to be seen more so I think would be better on Corona’s site.
thx alot Scott! I will wait for your message <3
Just my quick $0.02 - I’m also really interested to see this tutorial as I’m also struggling to get up to speed with Corona Native.
Many thanks to Rob & Scott for helping out like this, can’t wait for the post.
Also Scott, I’d also love to see the original Google Doc for this if the link is still available
Our iOS Native tutorial is now live: https://docs.coronalabs.com/tutorial/native/iosIntro/index.html
It takes you through the process of making a plugin, then integrating it into a native built Corona app. We are going to begin working on re-imagining the old Android Enterprise tutorial. It’s going to take a little longer for a couple of reasons. First, it’s ANT based not Android Studio based, so we are going to have to get all those instructions re-captured with screen shots, etc. that we didn’t have before (command line is your friend Secondly, that tutorial didn’t make a plugin. It just let you call native code from Corona. If we want to have it support making a plugin, we need to come up with a simple plugin to do.
Rob
Just working through the tutorial now - looks like an excellent reference, awesome job Scott (& Rob).
Can’t wait for the Android one as well
Hi Rob / Scott,
Sorry to hijack the thread but I’ve worked through the tutorial and got a test plugin working for simple cases - ie I can register a function, call it from my Lua code and have it generate an event which get’s picked up by the listener function registered when I call the plugin .init function.
However when I want to take it to the next level I’m hitting an issue. I’m developing a plugin with a simple interface.
plugin.init( listener ) -- initialise the plugin and register the event listener plugin.start() -- start a monitoring process that continually sends events back to the listener plugin.stop() -- stop the monitoring process
To implement the monitoring / recording process in my Objective C code in the start function I use an NSTimer scheduledTimerWithTimeInterval : block to basically create a closure and this appears to work fine - the block repeats fine and will happily NSLog() until I call the stop() function which calls the invalidate method on the above NSTimer instance.
However when I attempt to create a new event from within the block the code stops at the following line
Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) );
With an EXC_BAD_ACCESS (code=1, address=0x0b) error - as far as I can tell it’s getting a null reference from somewhere but I can’t work out why.
Here’s the code from my plugin
class PluginLibrary { protected: PluginLibrary(); static int Finalizer( lua\_State \*L ); public: typedef PluginLibrary Self; static const char kName[]; static const char kEvent[]; bool Initialize( CoronaLuaRef listener ); CoronaLuaRef GetListener() const { return fListener; } static int Open( lua\_State \*L ); static Self \*ToLibrary( lua\_State \*L ); static int init( lua\_State \*L ); static int start( lua\_State \*L ); static int stop( lua\_State \*L ); static int trigger( lua\_State \*L ); private: static void sendMessage( lua\_State \*L, NSString \*message ); static void sendFrequency( lua\_State \*L, double frequency ); static int updateCounter(); CoronaLuaRef fListener; }; // ---------------------------------------------------------------------------- int fakeFreqValue; bool recording; NSTimer \*heartbeat; // ---------------------------------------------------------------------------- const char PluginLibrary::kName[] = "plugin.tuner"; // This corresponds to the name of the library, e.g. [Lua] require "plugin.library" const char PluginLibrary::kEvent[] = "Tuner"; // This corresponds to the event name, e.g. [Lua] event.name PluginLibrary::PluginLibrary() : fListener( NULL ) { fakeFreqValue = 0; recording = NO; heartbeat = nil; } bool PluginLibrary::Initialize( CoronaLuaRef listener ) { // Can only initialize listener once bool result = ( NULL == fListener ); if ( result ) { fListener = listener; } return result; } int PluginLibrary::Open( lua\_State \*L ) { // Register \_\_gc callback const char kMetatableName[] = \_\_FILE\_\_; // Globally unique string to prevent collision CoronaLuaInitializeGCMetatable( L, kMetatableName, Finalizer ); // Functions in library const luaL\_Reg kVTable[] = { { "init", init }, { "start", start }, { "stop", stop }, { "trigger", trigger }, { NULL, NULL } }; // Set library as upvalue for each library function Self \*library = new Self; CoronaLuaPushUserdata( L, library, kMetatableName ); luaL\_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack return 1; } int PluginLibrary::Finalizer( lua\_State \*L ) { Self \*library = (Self \*)CoronaLuaToUserdata( L, 1 ); CoronaLuaDeleteRef( L, library-\>GetListener() ); delete library; return 0; } PluginLibrary \*PluginLibrary::ToLibrary( lua\_State \*L ) { // library is pushed as part of the closure Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) ); return library; } // [Lua] library.init( listener ) int PluginLibrary::init( lua\_State \*L ) { int listenerIndex = 1; // TODO - Request permission to use the microphone... if ( CoronaLuaIsListener( L, listenerIndex, kEvent ) ) { Self \*library = ToLibrary( L ); CoronaLuaRef listener = CoronaLuaNewRef( L, listenerIndex ); library-\>Initialize( listener ); } return 0; } int PluginLibrary::updateCounter() { fakeFreqValue += 1; return fakeFreqValue; } // [Lua] library.start( word ) - start sampling int PluginLibrary::start( lua\_State \*L ) { if (!recording) { // Start sampling the mic and set up a regular heartbeat to send the calculated frequency value via the registered listener... NSString \*message = @"Recording..."; // Fake with a simple counter for now... heartbeat = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer \* \_Nonnull timer) { // NSLog(@"Bing %i",updateCounter()); sendFrequency(L, updateCounter()); }]; recording = YES; sendMessage(L, message); } return 0; } // [Lua] library.off( word ) - stop sampling int PluginLibrary::stop( lua\_State \*L ) { if (recording) { // Stop sampling the mic and stop the heartbeat function... NSString \*message = @"Stopped"; [heartbeat invalidate]; heartbeat = nil; recording = NO; fakeFreqValue = 0; sendMessage(L, message); } return 0; } // [Lua] library.trigger( word ) - force a read and transmit via a despatched event... int PluginLibrary::trigger( lua\_State \*L ) { fakeFreqValue += 1; sendFrequency(L, (double)fakeFreqValue); return 0; } // ---------------------------------------------------------------------------- void PluginLibrary::sendMessage( lua\_State \*L, NSString \*message ) { // Create event and add the data to it Self \*library = ToLibrary(L); CoronaLuaNewEvent(L, kEvent); lua\_pushstring(L, [message UTF8String]); lua\_setfield(L,-2,"message"); CoronaLuaDispatchEvent(L, library-\>GetListener(), 0); } void PluginLibrary::sendFrequency( lua\_State \*L, double value ) { NSLog(@"sendFrequency %f",value); lua\_Number frequency = value; // Create event and add the data to it Self \*library = ToLibrary(L); // \<\<-- This is where it blows up when called from within a block CoronaLuaNewEvent(L, kEvent); lua\_pushnumber(L, frequency); lua\_setfield(L,-2,"frequency"); CoronaLuaDispatchEvent(L, library-\>GetListener(), 0); }
Any help or pointers in the right direction would be gratefully appreciated.
I’ll see if I can get an engineer to look into this. FWIW, it would have been better to start a new thread.
Rob
Thanks Rob,
I’m happy to open a new thread and repost my question there.
Hello! Think about this from following perspective:
When timer is called, where is you Lua state? It is probably in the middle of some other function, doing it’s own stuff. Trying to send things to it is rather dangerous, because it is not prepared. I answered to similar issue/question on this thread:
You can fix your code with something like this:
void PluginLibrary::sendFrequency( lua\_State \*L, double value ) { NSLog(@"sendFrequency %f",value); lua\_Number frequency = value; dispatch\_async(dispatch\_get\_main\_queue(), ^{ Self \*library = ToLibrary(L); CoronaLuaNewEvent(L, kEvent); lua\_pushnumber(L, frequency); lua\_setfield(L,-2,"frequency"); CoronaLuaDispatchEvent(L, library-\>GetListener(), 0); }); }
But it is important to understand what is going on here.
Hi Vlads,
Firstly following on from Rob’s comment above - I’ve reposted the question here https://forums.coronalabs.com/topic/70218-plugin-implementation-in-corona-native/
Also - that was the one thing that occurred to me - however I *thought* that the block acted like a lua closure and that the state would still be valid, I was also under the impression that the block ran on the same thread, but in hindsight I can see the problem.
I’ll try your fix and also check out the link above (as well as doing some background reading on the dispatch functionality as well.
Thanks for your help.
UPDATE : I did the fix above as suggested and got the same issue - whilst I understand that the code inside the block (basically the sendFrequency() function) is running on the same thread it’s obvious (and I should have realised) that since some time has now passed and the reference to the lua_State is the same the actual state itself is likely to have changed.
What I don’t understand is what this code does
PluginLibrary \*PluginLibrary::ToLibrary( lua\_State \*L ) { // library is pushed as part of the closure Self \*library = (Self \*)CoronaLuaToUserdata( L, lua\_upvalueindex( 1 ) ); return library; }
Because when XCode throws an error L appears to have a valid value, as does library but when you expand library the fListener reference is 0x0 (and probably the cause of a null pointer exception)
Ahhh - I think now it’s starting to make sense.
int PluginLibrary::Open( lua\_State \*L ) { // Snip... // Set library as upvalue for each library function Self \*library = new Self; CoronaLuaPushUserdata( L, library, kMetatableName ); luaL\_openlib( L, kName, kVTable, 1 ); // leave "library" on top of stack return 1; }
My understanding is that the above creates an instance of the plugin library and then pushes the reference as userdata which is then picked up and returned in the ToLibrary() method - this initially threw me as all the rest of the methods are declared as static.