Add scripting to your tool with Lua

It’s been a while since I last wrote a programming article! I’m working on a big visual fx tool for my company, and I recently started to think that it would be nice to be able to script it. For example, create a small script to convert all texture paths of the effect from absolute to relative, etc.

And I found out that it’s really simple. I decided to use Lua which I (re)discovered through the wonderfull Premake project. So first thing first, go to Lua’s website and download it. Then just create a simple console application, and add all the .c files found in lua/src to your project, except lua.c and luac.c !

For my purpose, I just need to be able to execute some Lua, and access my tool’s functionalities from it. The first step is really simple:

// init lua
lua_State* lua = luaL_newstate();
// load the default libs (io, etc.)
luaL_openlibs(lua);

// then you can load and execute an existing script:
luaL_dofile(lua, "test.lua");
// or directly execute something:
luaL_dostring(lua, "io.write(\"Hello World!\")");

// and close
lua_close(lua);

Yes, it’s that simple.

Then the second part is allowing a Lua script to call a C function. This is also pretty simple:

// our function. All functions that will be called from Lua
// need to have the same signature.
int test(lua_State* lua)
{
    // since lua allow calling a function with variable arguments,
    // get the number of arguments it was called with.
    int num_args = lua_gettop(lua);

    // do some tests (we could also just print a message and return)
    // note: arguments are retrieved from the lua state object. Since the
    // very first value is the number of arguments, the arguments start
    // at index 1.
    assert(num_args >= 1 && num_args <= 2);
    assert(lua_isnumber(lua, 1));

    // get the first parameter
    int a = lua_tointeger(lua, 1);

    // check if we need to print
    if(num_args == 2 && lua_isboolean(lua, 2) && lua_toboolean(lua, 2))
    {
        std::cout << "result: " << a * a << std::endl;
    }

    // push as many return values that you want, and return the number of
    // values that you pushed. In our case it's only 1, but it could be
    // really powerful: need to return a list of selected items ? Just
    // push all of them and return the number of items.
    lua_pushnumber(lua, a * a);
    return 1;
}

// somewhere else, start Lua
lua_State* lua = luaL_newstate();
luaL_openlibs(lua);

// push our function on top of the lua state object
lua_pushcfunction(lua, testFunction);
// then make this function globally accessible through the name "test"
lua_setglobal(lua, "test");

// call our function. That's all :)
luaL_dostring(lua, "test(10, true)");
luaL_dostring(lua, "io.write(test(10))");

// close lua
lua_close(lua);

Now if you want to go further, here are a few links:

  • Reference: The Lua 5.2 reference manual.
  • LuaJIT: From what I understood, this is a replacement of the default Lua compiler/VM that is a lot faster and more efficient. I didn’t have time to investigate but it seems really good. Only drawback is that it’s Lua 5.1 only.

ImageViewer version 0.8 : drag & drop et plein d’autres !

Au menu de cette nouvelle version, de gros changements. Le plus gros changement vient du code : j’ai implémenté un système qui permet de récupérer les évènements qui se passent sur le disque dur, et de pouvoir enregistrer divers “écouteurs” sur ces évènements, qui reçoivent donc ensuite de manière automatique des évènements du style “ce fichier a été effacé”, etc.

Le premier changement que ça a entrainé, c’est que toutes les fonctions de couper/copier/coller, drag&drop, suppression de fichier sont devenues beaucoup plus simples à gérer : je m’occupe simplement de faire ce qu’il faut d’un fichier, et ensuite l’interface sera automatiquement notifiée du changement par le système, et se mettra à jour automatiquement. Bref, c’est génial. Et donc, côté utilisateur, ça se traduit par les 2 grosses fonctionnalités suivantes :

  • Le nombre d’images d’un répertoire est maintenant toujours synchronisé. C’est à dire que si vous supprimez des images, ou en ajoutez, le nombre se mettra à jour automatiquement, et ce, même si vous supprimez une image depuis le Finder !
  • Sur le même principe, les onglets sont synchro avec le contenu du disque : si des images sont supprimées du disque, l’ImageViewer se met automatiquement à jour, sans perdre votre sélection (sauf si celle-ci a disparut du disque ^^)
  • copier/coller et couper/coller sont de retour, ainsi que le drag & drop : vous pouvez dragger des images depuis la vue par onglet vers un répertoire de votre choix.
  • Rien à voir avec le système dont j’ai parlé, mais grosse amélioration : on peut maintenant enregistrer l’ImageViewer comme application par défaut pour les images, et donc l’ouvrir directement en plein écran en double cliquant sur une image. Ca manquait, c’est réparé ! ^^

Il y a quelques autres fonctionnalités, mais j’ai la flemme de les détailler ici. Donc comme d’habitude, pour le téléchargement du code source et de l’exécutable, c’est ici que ça se passe : http://blog.pcitron.fr/tools/macosx-imageviewer/