This tutorial requires knowledge of basic Lua C api and C/C++.
This tutorial is a walkthrough for developing simple plugin for Windows Simulator or Windows desktop app. We would implement wrapper around Tolk, open source library allowing applications to output text through screen reader software (assistive technology for the blind and visually impaired).
Tolk provides C bindings, described in Tolk.h.
One would have to obtain latest CoronaEnterprise daily build as well as Tolk distribution. Visual Studio 2013 is used to compile a plugin.
Daily build 2017.3043
contains significant improvements to Windows plugin template. This tutorial would not work with earlier Corona Enterprise templates as-is.
Unpack CoronaEnterprise
to you Projects folder and copy CoronaEnterprise\ProjectTemplates\App
next to it.
It is important to have copied App
template next to CoronaEnterprise
folder in order for Windows template to compile. Rename App
into TolkPlugin
to have more specific project name.
Usually plugins are required by Lua code like
local tolk = require("plugin.tolk")
When encountering such code one of the things Corona Simulator would attempt is to locate a library named plugin_tolk.dll
and run luaopen_plugin_tolk
function from it. This, in a nutshell, how native Lua plugins work.
Windows project template is now located in TolkPlugin\win32\
folder. Open Plugin.sln
with Visual Stodio 2013. In Solution Explorer one can see that currently plugin contains both Lua and C++ code. Our plugin woul have only single C++ source file.
First our step would be to delete existing source code in a plugin. In Solution Explorer, select all 3 files under Source Files (one .lua
, one .cpp
and one .h
file), then select Remove from context menu. Confirm the removal.
Now it is time to add a new source file which would contain our plugin. Right click on "Source Files" and select "Add" → "New Item…". In pop-up dialog select "C++ File (.cpp)" and type in a name, "TolkPlugin" for example.
We would be presented with empty C++ file. Time to write some code:
// TolkPlugin.cpp - simple Tolk wrapper for Corona SDK
#include "CoronaLua.h"
#include "CoronaMacros.h"
int test(lua_State *L)
{
lua_pushstring(L, "Hello, World!");
return 1;
}
CORONA_EXPORT int luaopen_plugin_tolk(lua_State *L)
{
static const luaL_Reg kVTable[] =
{
{ "test", test },
{ NULL, NULL }
};
luaL_openlib(L, "plugin.tolk", kVTable, 0);
return 1;
}
Before compiling the code we would have to change output name from plugin_library.dll
to plugin_tolk.dll
. To do that right click on Plugin project in Solution Explorer and select Properties. In top of the Properties window, select "All Configurations" from Configurations drop-down, then change value of "Target Name" from plugin_library
to plugin_tolk
(it is in "Configuration Properties" → "General" section).
Press OK and select Build → "Build Solution". We just made our first Windows plugin. It has single function "test" which just returns "Hello, World!"
string.
Now we can test our plugin. Start Corona Simulator and create a new project.
local tolk = require("plugin.tolk")
print( tolk.test() )
This should print out "Hello, World!" into Corona Simulator Output window.
Note that even if Corona would produce a warning that plugin isn't set up in build.settings, it will still work. Please, ignore missing plugin warning while developing a plugin.
Hint: in order to debug a plugin you can attach to running Simulator with "Debug" → "Attach To Process…" or setup "Command" in Project Properties, Debugging section to Corona Simulator executable. There is handy "<Browse..>" option in a drop-down selection box. This way, pressing green triangle would start Corona Simulator, and automatically attach debugger to it.
Our plugin doesn't do much. We would have to add more functionality to it. From this point I would provide snippets from my TolkPlugin.cpp
with explanations. Full source code is available on-line on Github
We will be using Tolk.lib
and Tolk.h
from Tolk distribution. Copy of them should be in root of the project (inside win32
folder, next to Plugin.sln).
Visual Studio provides handy way to link project to static library:
#include <windows.h>
#include "Tolk.h"
#pragma comment(lib, "Tolk.lib")
First two lines include header files, and last line links Tolk.lib
to our dll.
As per documentation, Tolk library requires Tolk_Load()
to be called before using any of its APIs and Tolk_Unload()
after. Calling Tolk_Load()
when library is loaded is rather easy. Unloading library is trickier. To achieve that, we will create an userdata, and attach metatable with __gc
(Garbage Collection) method to it. Corona Enterprise provides handy functions for it:
int Finalizer(lua_State*)
{
Tolk_Unload();
}
CORONA_EXPORT int luaopen_plugin_tolk(lua_State *L)
{
// This will macro creates "unique" string with filename and line nubmer in it
const char kMetatableName[] = __FILE__;
// This call will create metatable named kMetatableName and GC callback "Finalizer"
CoronaLuaInitializeGCMetatable(L, kMetatableName, Finalizer);
// This pushes pushes new userdata and assigns metatable to it
// we're using nullptr: we don't care about contents of userdata
// we just need a garbage collection callback on it when it is
// no longer held by the plugin
CoronaLuaPushUserdata(L, nullptr, kMetatableName);
static const luaL_Reg kVTable[] =
{
//...
};
Tolk_Load();
// note last 1. This means that we will push our single userdata as "upvalue"
// for this library (plugin). It means, that lifetime of userdata would be bound
// to lifetime of library. So when library is released (e.g. Simulator is reloaded)
// userdata would be freed alongside with library and Finalizer called
luaL_openlib(L, "plugin.tolk", kVTable, 1);
return 1;
}
This technique is very common in Corona plugins. Often userdata would contain some state we want to pass around, unlike nullptr
in our case. This can be data structure containing listeners user set up. For such example check out iOS plugin code.
All lua strings are UTF8. Tolk library works with windows wide wide character encoding, where each character is represented by 16-bit. Windows provides several functions to translate text from one encoding to another. This is why we included windows.h
earlier:
// function to actually output a text
static int Output(lua_State *L)
{
// check if first parameter is actually a string
if (lua_type(L, 1) == LUA_TSTRING) {
size_t utf8Len = 0;
// retrieve string and it's length
const char * utf8Text = lua_tolstring(L, 1, &utf8Len);
// create buffer for wide char string
// It will always have less elements than UTF 8 string
wchar_t *wcharText = new wchar_t[utf8Len+1]();
// function to convert UTF8 to wide char
MultiByteToWideChar(CP_UTF8, 0, utf8Text, utf8Len, wcharText, utf8Len);
// send converted text to Tolk
Tolk_Output(wcharText, interrupt);
// delete the buffer we just allocated
delete wcharText;
}
return 0;
}
- Explore CoronaEnterprise and Lua native APIs: https://docs.coronalabs.com/native/
- Try making macOS cross-platform plugin. You can try writing plugins which would work on all Corona supported platforms with single code base. Check out memoryBitmap as an example of such plugin.
Corona expects plugins to be in "%APPDATA%\Corona Labs\Corona Simulator\Plugins"
. You can manually copy dll there.