In an ideal world you would be able to solve any computing problem without leaving the confines of LiveCode. Unfortunately, though, there will always be things that very-high level programming is not suited for. For example, an implementation of an algorithm in LiveCode may be too slow, you may need a specific OS feature that isn’t wrapped by a command or function in LiveCode, or you may just want to access a pre-existing library in LiveCode.
To support such cases, LiveCode has what we call the ‘externals interface’. An external is simply a shared library (dll on Windows, bundle on Mac OS X) written in a lower-level language that can be loaded (at runtime) into the LiveCode environment. Once loaded, your scripting environment is augmented with the commands and functions exported by the external.
In this, the first of a series of articles, I will cover the basics of writing a (cross-platform) external for Windows and Mac OS X in C/C++.
Before we begin
Before going any further you will need:
LiveCode 2.7.x or later
Visual C++ 2005 - any edition including Express (if you want to build externals on Windows)
XCode 2.4.x (if want to build externals on Mac OS X)
The LiveCode externals build environment: http://developer.runrev.com/externals/ExternalsEnvironmentV3.zip
A set of pre-constructed environments for each step in the tutorial article: http://developer.runrev.com/externals/NewsletterArticle1.zip
A cup of coffee (not strictly required, but I find it helps when coding in lower-level languages...)
Note: The need for LiveCode 2.7.x is just because the External Creator assumes the new installation structure is 2.7 when setting up projects – the externals you build will operate in any LiveCode version and edition.
Before you can begin doing anything in a lower-level language, it is important that you set up your ‘build environment’. Modern IDEs such as Visual C++ and XCode make this straightforward when targetting a single platform – but it can be trickier if you want to use the same code base to compile on multiple platforms (or even multiple variants of the same platform such as exist with Mac OS X).
To help with this, a skeleton environment similar to the system we use internally is included. This skeleton has the basic structure allowing you to write externals using both Visual C++ 2005 and XCode 2.4.x.
After unpacking ‘ExternalsEnvironmentV1.zip’ onto a suitable place on your hard-drive, it is worth spending a few moments familiarising yourself with its contents:

The contents you see here serve the following purposes:
External Creator V1.rev – a small LiveCode utility to help you setup an external project
libexternal – this project folder contains the special glue-code that all externals need to be linked with in order to bind to LiveCode. (It is a project in its own right that builds as a static library).
templates – this folder contains template project files used by the External Creator to setup new external projects.
configurations – this folder contains two kinds of file vsprops and xcconfig. These contain settings for different variants of builds on different platforms.
In later sections we will refer to this folder as the environment – in addition to what it currently contains, it will also hold our external projects themselves.
Note: Although the environment folder will unpack to ExternalsEnvironmentV1, you are free to rename it to anything.s
Both Visual Studio and XCode rely on the idea of projects to organize development. A project defines the type of entity to be built along with all the build parameters and source-files needed to do the build.
One concept that will be new to you if you haven’t built projects in lower-level languages before will be that of build configuration. A build configuration is a collection of settings that define both the type of build and also the variant of the current platform to build for.
For example, lower-level languages typically require you to compile in a different way if you wish to debug your code (this is the Debug configuration in our case) or release your project (this is one of the Release configurations in our case).
Furthermore, the existence of single OSes targetting different architectures means that there are generally a multiplicity of Release builds needed. For example, for Mac OS X, we need ones for both PowerPC and Intel, as well as one for both (i.e. Universal).
However, you needn’t worry about all the details here as the skeleton environment comes with a collection of configurations that are completely compatible with LiveCode (i.e. they should just work!).
The first thing to do is to create a skeleton project for our first external. Included with the skeleton environment is a LiveCode stack called External Creator. This little tool helps to start off creating an external by setting up the project (potentially for multiple platforms) including directory structure and build configurations. Furthermore, it creates an empty test stack to load the new external.
So, with the External Creator loaded into LiveCode do the following:
fill in the Name field with rnahello (we use the three-letter prefix rna to stand for LiveCode Newsletter Article)
check the appropriate platforms
choose C++ (no exceptions, no rtti) as the language
ensure the Installation paths are filled in correctly.
You should end up with something like:

When you have configured all your settings, just click Generate.
Exploring our new project
Having generated the project, you should now find an rnahello folder within the environment. It is worth spending a few moments exploring this folder to see what has been created:

Here we have the following:
rnahello.vsproj – the Visual Studio project file (present if you checked Windows)
rnahello.xcodeproj – the XCode project folder (present if you checks Mac OS X)
test_rnahello.rev – an empty stack that will load the external (used for testing and debugging)
src/rnahello.cpp – the outline of our main C++ file that will contain the implementation of our commands and functions
So, now we have the structure in place we can actually add some functionality...
Open up Visual C++ or XCode depending on the platform, and load either externals.sln (into Visual Studio), or rnahello.xcodeproj into XCode. In both cases you should be presented with (what will, at least, become) a familiar interface.
Both IDEs use a hierarchical approach to manage projects – in the case of XCode, the project itself will be the root node, whereas in Visual Studio the root node will be the solution:


You may wish to spend some time looking around the IDEs and the project/solution structure we have created to familiarise yourself with them before going any further.
Adding functionality – our first function
At this stage, you should be looking at the rnahello project created with the External Creator. This project has all the appropriate build configurations configured as well as the necessary setup for launching the test stack on run so you can debug and/or see your external running – basically we have a skeleton external that will work, but not actually provide any functionality to the development environment.
In this section we will add an external function to our external. An external function is exactly the same as a function handler (defined by the function keyword) you will be familiar with in LiveCode, except that it’s implementation is in native code defined in an external. As with a function handler, an external function takes a number of parameters as input and returns a value as a result.
The External Creator will have created an outline main file rnahello.cpp which you may want to familiarise yourself with before continuing.
We will now add our first external function to our project – called rnahellouser. This will take one parameter (a name) and return a friendly message.
The external declarations block – describing the functionality the external provides
The external glue code provided in libexternal provides a number of C/C++-macros that help in the construction of the external declarations block that tells LiveCode what the external exports to it.
A block starts with:
EXTERNAL_BEGIN_DECLARATIONS()
This is followed by a sequence of command or functions declarations:
EXTERNAL_DECLARE_FUNCTION(, )
or
EXTERNAL_DECLARE_COMMAND(, )
Then the block finishes with:
EXTERNAL_END_DECLARATIONS
In our case we wish to declare the existence of an external function called rnahellouser. So, scroll to where the (empty) external declarations block is in the rnahello.cpp file and insert the line:
EXTERNAL_DECLARE_FUNCTION("rnahellouser", rnaHelloUser)
Between the comments '// BEGIN USER DECLARATIONS' and '// END USER DECLARATIONS'. This will cause a call to a function rnahellouser in LiveCode to be mapped to a call to the C++ function rnaHelloUser which we will implement next.
The function definition – implementing an external function
Now we have declared the existence of our external function, we now need to implement it. For any external handler (command or function), the C/C++ prototype is the same:
void handler(char *p_arguments[], int p_argument_count,
char **r_result, Bool *r_pass, Bool *r_err)
Here:
p_arguments is an array of C-strings of length p_argument_count – each element of the array is an argument passed to the external handler.
r_result is a pointer to a C-string variable that should be set to a pointer to the result of the external handler. (The C-string returned in this way must be allocated using malloc/calloc/realloc and ownership of the memory will pass to LiveCode.)
r_pass is a pointer to a Bool variable that should be set to True if the handler invocation should be passed in the message path.
r_err is a pointer to a Bool variable that should be set to True if the invocation of this handler should raise a runtime error in LiveCode.
Add the following function to the rnahello.cpp file between the // BEGIN USER DEFINITIONS and // END USER DEFINITIONS:
In this function we have used a number of standard library functions which we will need to include in our environment. In C/C++ this is done via #include directives, so add the following to the rnahello.cpp file before the #include directive.
#include
#include
#include
This imports memory management (cstdlib), i/o (cstdio) and string manipulation (cstring) functions into our file.
All being well, you should now be able to compile your external and launch it within LiveCode...
Ensure rnahello is the Startup Project
Make sure Debug is chosen in the Solution Configurations drop-down list (on the toolbar)
Click the green Run button
Note: Due to the relaunch feature added to recent versions of LiveCode, you need to make sure the version of LiveCode you are debugging externals in is not already running when you launch a debug session from Visual Studio
Note: You will likely get a message saying 'Unable to find debug symbols for LiveCode.exe' the first time you run your application - do not worry about this, just click to continue and choose not to show the dialog again.
Choose Build mode (either by choosing Build Results from the Build menu, or by clicking on the Build icon in the Page tab in the in the top-left).
Choose rnaHello from the Target drop-down
Choose Release from the Configuration drop-down
Choose Build
Now choose the Debug configuration and click Build and when it has finished click Debug. (The previous two steps shouldn't be necessary, but there seems to be a glitch in XCode that prevents it picking up the correct settings for launching the debug executable if you have only ever built a Debug variant of a project - from now on you won't need to build a release variant before a debug one in this project).
Assuming the above steps were followed correctly, after a short while an empty stack called rnaHelloTest should pop-up inside the development environment.
Testing our function – hooking into our external
At this point you should be looking at the LiveCode IDE with a stack opened up. This stack should have already loaded the external into the LiveCode environment – so now we just need to hook into it.
So do the following:
First add a field called Name to the stack.
Add a button called rnaHelloUser to the stack and place the following into its script:
Now enter any string into the field and click the button – you should get an answer dialog popping up with a friendly message.
Before continuing, make sure you save your test stack (we will be building on it in the next section) and then quit LiveCode.
In the previous section, we looked at adding an external function. In this section we will look at adding an external command instead. Just with external functions, external commands are effectively the same as command handlers (defined with the on keyword) in LiveCode except that their implementation resides in a external.
In reality, there is very little difference between external commands and functions (just like there is little difference between command and function handlers in LiveCode). Indeed, the difference is mainly syntactic, and the fact that you have to use the result to get the return value of a previously executed command. Therefore, we will use this opportunity to introduce how to set and get local variables in an external handler.
The command we will implement will be called rnaHelloUserIndirect and will have syntax:
rnaHelloUserIndirect pUserVariableName, pOutputVariableName
Here pUserVariableName will be the name of a (handler) local variable from which to fetch the user’s name, and pOutputVariableName will be the name of a (handler) local variable in which to put the result.
In addition to providing the necessary ‘glue’ to allow a shared library to be loaded as a LiveCode external, ‘libexternal’ also exports a number of functions that allow some access to the internals of the currently running LiveCode application.
In this section will make use of two of these functions GetVariable and SetVariable. These functions give you access to the contents of the local variables present in the context that called the external handler – as long as the value of those variables are text strings (i.e. not binary data).
The prototypes of these functions are:
char *GetVariable(const char *p_variable_name, int *r_success)
void SetVariable(const char *p_variable, const char *p_value, int *r_success)
The GetVariable call takes a name of a variable (as a C-string) and a pointer to a return variable. If the call succeeds, *r_success will be EXTERNAL_SUCCESS and a copy of the value of the variable as a C-string will be returned. If the call fails, r_success will contain EXTERNAL_FAILURE and NULL will be returned.
NB: The ownership of the memory returned passes to you – i.e. you must free it when you are finished with it.
The SetVariable call takes a name of a variable, the value you wish to set the variable to and a return variable. If the call succeeds *r_success will contain EXTERNAL_SUCCESS and the specified variable will be set to the required value. If the call fails *r_success will contain EXTERNAL_FAILURE and no other change will have occured.
NB: LiveCode copies the string you pass to it in p_value so there is no concern about ownership - i.e. if you've allocated memory to store it, you still have to free it.
As before, we first have to declare the existence our new command by adding an appropriate entry in the external declarations block. Therefore, add the following line in the ‘USER DECLARATIONS’ as before:
EXTERNAL_DECLARE_COMMAND("rnahellouserindirect", rnaHelloUserIndirect)
Next we have to provide an implementation of this command which we do by adding the following to the 'USER HANDLER DEFINITIONS' section of the file:
Again, you should now be able to Run (Visual Studio), or Build and Debug (XCode) your project and after a few moments, the test stack will appear again.
You should again find yourself staring at our test stack, loaded into the LiveCode IDE. Add a new button to the stack called rnaHelloUserIndirect and place the following in its script:
on mouseUp
local tName, tOutput
put field "Name" into tName
rnaHelloUserIndirect "tName", "tOutput"
answer tOutput
end mouseUp
Notice here that we are passing strings to rnaHelloUserIndirect that contain the names of the variables that we wish to access in the external, rather than their values.
Clicking on rnaHelloUserIndirect should now give you exactly the same message as clicking rnaHelloUser - our new command works!
The External Creator helpfully builds us a stack that automatically loads our external when it is opened through either XCode or Visual C++. However, when you get to a point when you wish to start testing any externals you create in the context of actual LiveCode projects, you will need to be able to integrate them with the IDE.
To do this, you will first want to build Release builds of your externals. In Visual Studio, simply choose the Release configuration from the drop-down list on the toolbar, while in XCode you need to switch to the build pane and choose one of Release, Release x86-32 or Release PowerPC-32.
When you have done this, all you need to do then is copy them into appropraite places in your Documents folder for LiveCode to pick up next time it is launched. This can be done by creating the following hierarchy inside the standard Documents folder:
Then inside each Externals folder, put the appropriate build of the external along with a text file called Externals.txt. The externals.txt file is return-delimited list of pairs:
,
For example for rnahello on Windows the file should contains:
Hello External,rnahello.dll
Whereas on Mac OS X it should contain:
Hello External,rnahello.bundle
Note: You only need the Runtime hierarchy for building Standalones containing your externals. Furthermore, you need only create the architecture folders within each platform folder for the architectures that you need.
Note: In general, you should always build a Release (Universal) external on Mac OS X to place in the non-Runtime Externals folder - as this is the folder the IDE uses.
In this article we have covered the basics of writing externals – with particular emphasis on doing so cross-platform for both Mac OS X and Windows. Although we have only covered two API calls (GetVariable and SetVariable) there are numerous other ones – these are all documented in external.h present inside the libexternal/src folder.
In the next part of the series on external writing we will explore using more of these APIs – in particular ones allowing you to access binary data and arrays from LiveCode. We will then put this to good use in producing an external that does something native code is very good at – image processing...
Note: At the time of 'going to press' - the documentation to be included in external.h had not been finalized. We will upload ExternalsEnvironmentV2.zip in a few days with this documentation included.
Visual C++ 2005 is available in several editions as part of the Visual Studio 2005 software suite. Microsoft makes Visual C++ 2005 Express Edition available free of charge, and is available from:
The XCode tools are freely available from Apple’s developer site after registering for Apple’s Developer Connection (also free). The landing page is here:
No. If you are on a slow machine, you may want to setup your test stack and build it as a standalone. You can then change the debugger parameters in either XCode or Visual Studio to run this executable rather than the IDE.
Navigate to the Executables branch of the project tree and Get Infoon the Test node.
Change Executable Path to reference your standalone application.
Go to the Arguments tab and remove all the arguments.
Right-click on the appropriate project in the Solution tree-view and select Properties.
Choose All Configurations from the drop-down list
Expand Configuration Properties and choose Debugging
Select Command, choose Browse and select your test standalone executable
Clear the command arguments and working directory fields
Yes. You are free to use all source-code and project files included with this article (or generated by External Creator V1) for the purposes of producing LiveCode compatible externals.
Yes – this is correct. libexternal is an updated version of the glue-code which is cleaner. It uses exactly the same LiveCode interface as the previous implementation and so will continue to produce externals that will be usable in any LiveCode version.
Many people use C++ as ‘just a better C’ - and don’t take advantage of exception handling or runtime-type information. Used in this form, there is no need for the external glue-code to do anything special. However, if you wish to use C++ exceptions, it is important the glue code automatically handles any before control is returned to the engine and this option does this automatically for you.
Yes – but make sure you choose C++ when creating the skeleton for your external. Most recent implementations of the standard C++ library require exception handling (Visual Studio for example) and are not guaranteed to work correctly if it is disabled. Creating your external with initial support for C++ will ensure that unhandled exceptions in your external code are caught before returning to LiveCode.
In theory any language that can build shared libraries and allows you to link with the provided glue code could be used. However, in practice, some languages will require extra glue-code in order to provide an environment in which their code can run. For example, to access Objective-C you will need to ensure you set up its memory/exception environment in C wrapper functions before calling any Objective-C code.
Yes. Any external that does not depend on any OS APIs should run fine on the Windows 98/ME family of OSes. If you do you use any OS APIs you just need to make sure they are available on that platform by checking in the Platform SDK documentation (since you can’t explicity target a given Windows version, you won’t find out at compile-time if you’ve accidentally used an API that isn’t available).
Yes – but the External Creator only generates project and solution files compatible with the Visual Studio 2005 family.
Yes. Externals are just DLLs which export a specific function and so any compiler should be persuadable to produce externals compatible with LiveCode. Of course, you will have to set up your own environment in this case.
Note: the only requirement that LiveCode puts on a DLL it trys to load as an external is that it exports one symbol whose resulting name is _getXtable. Some compilers don’t automatically prepend an ‘_’ but with appropriate options can be made to do so. (getXtable is defined in libexternal).
In theory, yes. In practice, no. With the advent of Universal Binary you really need to be producing externals that compile to both PowerPC and Intel – this requires a minimum of XCode 2.2 (if I recall correctly). Also, if you wish to support the same range of OS versions that LiveCode supports you need to build them in a specific way:
PowerPC with gcc-3.3 against the 10.2.x SDK
Intel with gcc-4.0.1 against the 10.4u SDK
We use XCode 2.4.1 internally and know these configurations work without problems and so we recommend that you do the same.
Of course, if you are targetting a specific OS version or architecture, then you can choose your version of XCode appropriately, but you may have to setup your environment from scratch.
This depends on the OS versions and architectures that you are targetting. Any environment that is based on the Apple GNU toolchains should be able to produce compatible shared libraries. However, if they are not based on these toolchains, your mileage will vary.
In all these cases, though, you will have to configure your build environment yourself from scratch.
Yes. As long as you checked both platform boxes in the ‘External Creator’ when you initially setup your external a project will have been created for both platforms. To save constantly moving the folder between the two platforms you could (for example) put the environment on a network share. However, I recommend that you look into setting up a version control system such as Subversion to manage your external projects – this isn’t at all hard to do and numerous tutorials and GUIs exist on the web to help.
As a general rule, you should only include ‘source’ files in the version control system - these are the files that cannot be derived from other files. So, in our case you will need:
The top-level folder
Each project folder
The configurations folder and its contents
The externals.sln file (if you are targetting Windows)
The src folders and their contents within each project folder
The vcproj file within each project folder
The vcproj.user file within each project folder
The xcodeproj folder and the project.pbxproj file within it (Mac OS X build only)
The plist file within each project folder (Mac OS X build only)
The rev test stack within each project folder
Any other files that you have explicitly added
You should not add the _build or _cache folders, nor any ‘hidden’ files or .ncb files on Windows. Furthermore, all the files within the xcodeproj folder apart from project.pbxproj should be ignored.
The general principle is this – if a file or folder was not created by you, or the External Creator it shouldn’t go under version control!
To begin with, we decided to focus on the two main desktop platforms. However, we hope to look at the other platforms in future articles.