Programming with RiscLua: Remarks on a little WIMP program

posted in: Programming, Software | 1

Gavin Wraith of RiscLua fame takes us through writing WIMP programs in RiscLua. WIMP, if you’re unfamilar with it stands for Windows, Icons, Menus and Pointers, there’s a nicely written intro to WIMP located here if you’d like to learn more about it.

Is this the shortest WIMP program possible?

local task, QUIT in require “task”
local greet = task “briefly”
greet.handler[0] = QUIT
greet:init ( )
greet:alert “hello and goodbye”
greet:run ( )

Here is what it produces… —->

All it does is display an alert box saying hello and goodbye. This is not much, but it will serve as a peg on which to hang some remarks. To run it you need RiscLua, which can be download from here. You may also need the Shared C Library if your system doesn’t have it already.

Once these are installed, create a textfile containing the above program, save it to a file with filetype Lua, and doubleclick on the file’s icon.

To explain this program we need to rehearse some ideas about Lua. It has a special value, called nil whose job is to be different from all other values. It is the value of an undefined expression. A proper value is one that is not nil .

Lua has a single datastructure, the table . BASIC and C let you access memory, so that you can define datastructures by means of arrays and pointers.

By contrast Lua forbids such access, on the grounds of security and portability, but offers the single notion of tables instead. How tables are implemented is not the programmer’s business. You could say that BASIC’s arrays are a bit like tables, except that…

  • The indices, or keys , of a table do not have to be integers. They can be any proper value. Keys in the same table must be distinct. You can have empty tables, with no keys. A table can have itself as one of its keys.
  • The values of a table at an index can be any proper value, including the table itself.
  • Tables can grow or contract dynamically. They do not have to be declared before use.

A statement x = { } creates an empty table. A statement x[key] = value will create a key for the table x with the given value, or update an existing key’s value. A key can be deleted from a table by setting its value to nil.

You can create a table with pre-assigned keys and values with a statement of the form.

x = { [key_1] = val_1, . . . , [key_n] = val_n }

Note that each use of matched braces creates a different table…

x = { }
y = x
print ( x == y ) --> true

but

x = { }
y = { }
print ( x == y ) --> false

It is useful to picture an assignment a[k] = b, where a and b are tables like this:

If we think of the tables as (not necessarily connected) blobs of memory then a key can be thought of as a reference – a pointer. It should be clear how datastructures can be realised in Lua, mimicking anything defined in C using structs and pointers, even though Lua does not permit explicit memory access.

When a key of a table is a string that can be the name of a variable (so does not contain spaces or other forbidden signs) Lua provides a special syntax, whereby x[“foo”] can be written more succinctly as x.foo and inside braces [“foo”] = y can be written as foo = y.

Of course, this looks just like an ordinary assignment. Indeed, global variables are held in a table, so all assignments in Lua are either to local variables or to keys in a table. When tables are used to bundle up values, the table is often referred to as a library.

When x.foo is a function the expression:

x.foo (foo, . . . )

can be written as

x:foo ( . . . )

This notation is handy for implementing notions of object-oriented programming. Note that there is no separate function x:foo and no separate colon operator. It is just a shorthand.

An expression of the form:

{ a_1, . . . ,a_n}

is interpreted as

{ [1] = a_1, . . . , [n] = a_n }

Another notational convention is that if single arguments are literal strings, or are enclosed in braces, then the parentheses round them may be omitted. Examples of this usage can be seen in lines 1,2 and 5 of the program.

Libraries may well be defined in separate files, holding C code or Lua code. The same function, require , is used for loading in both cases. When called with the library file’s name as argument it first checks to see whether the library has already been loaded. If it has it returns that library, as a table.

Otherwise it searches for the library file in the directories specified by the system variables LUA_PATH and LUA_CPATH, and if successful it compiles the code and returns the library as a table. If it fails, it returns nil . So libraries do not get loaded twice, unnecessarily.

The little word in is a useful one. The first line:

local task, QUIT in require "task"

is equivalent to

local lib = require "task"
local task, QUIT = lib.task, lib.QUIT

Do not be put off by the use of the same word task in three different settings

  • as a filename
  • as a key in a table
  • as a local variable

The value QUIT is a handler function. That is to say, it takes as first argument a wimp-task. If it returns nil the wimp-task continues, otherwise the wimp-task will close down. In the second line the function task is used to define a table called greet. This is our wimp-task. It has the title briefly .Wimp-tasks come equipped with special keys

  • init¬†registers the wimp-task with the task-manager.
  • run hands execution over to the task-manager. This can only be called in the last line of the program, since it does not return. It can only be used after init has been called.
  • handler an array of handler functions indexed by wimp-event codes to describe what happens when execution is passed back to the wimp-task by the task-manager.
  • alert runs an auxiliary wimp-task to display a message.

Note how the order of the statements in the program does not strictly reflect the order in which things happen. This is typical of wimp programs, as they have to share execution with the task-manager. In the diagram below the blue blob indicates the wimp-task, the purple blob the task-manager, and the arrows show the transfer of control.


A wimp-task will generally display things on the screen. As far as RISC OS is concerned we can say that a location is an icon in a window.

There are special windows, the desktop and the iconbar, and a window’s work area may count as a special icon. But generally a location is a pair (window, icon).

A window is identified by its window handle , a number assigned by the window manager when the window is created. An icon is identified by a number, the ordinal (starting with 0) of its data in its window’s block. A trigger is a user-action that can take place at a location: typically a click or a drag, into or from.

A WIMP program must specify what happens when a trigger is activated at a location.

This is often described as registering a handler for the trigger. In the RiscLua wimp libraries triggers start with the prefix on_ . For example, can you guess what this might mean?

mytask:register (window, icon)
{
on_Menu = menu.open (icon_menu);
on_Select = mytask:myclickHandler ( );
on_DragSelect = dragobject:start (drag_icon);
}

Menus and drag-objects are all defined by tables, of course.

When I first came across Lua, in 2001, it was clear that it would be a good candidate for porting to RISC OS. This is because it grew out of a software project for PetroBras, the Brazilian state oil company, which did not have a large budget; so the software had to run on a multiplicity of systems.

Portability, therefore, was one of Lua’s primary design goals. In fact Lua is designed to compile on any system with an ANSI C compiler.

What I wanted to do was extend Lua with features that would allow it to access RISC OS, just as BBC BASIC can with its SYS command. There were two difficulties: arithmetic and memory access. The ARM CPUs that RISC OS was designed for had no floating point arithmetic in hardware; RISC OS uses 32-bit integers everywhere.

Lua has for its default doubles, 64-bit floating point numbers. So earlier versions of RiscLua were compiled with 32-bit integers as the standard number type. With version 5.3 of Lua it became easier to cater for the arithmetic side of things. However, on the RISC OS side, only GCC currently supports floating point hardware for the newer ARM CPUs.

The memory access problem is a dichotomy: Lua was designed to prevent it, RISC OS was designed to require it. BASIC’s SYS command would be of little use without DIM, ?, ! and $. It took me some while to find out how to get round the dichotomy properly.

Eventually the modifications needed to marry Lua with RISC OS were put in a single library called riscos . I decided to make its syntax follow closely that of BASIC’s, with sys, dim, ?, !, $ , so that those who are familiar with BBC BASIC should feel at home.

There are some extra twists: dimmed blocks of memory can be collected as garbage when no longer needed, and there are more flexible ways of reading strings from such blocks. Keep in mind that Lua implements strings in a fashion totally different from BASIC. Lua strings are immutable values. Comparison of strings in Lua is as quick as comparison of numbers.

Where BASIC has operators ?, !, $ RiscLua has tables. The byte at an address x is ?[x] . It is as if there were a table ? with addresses for keys and bytes for values. Actually this is an illusion concocted by using metatables , a cunning feature unique to Lua, that allows a certain amount of control over syntax.

From the start I wanted to implement libraries to make wimp programming easier. But I held back, hoping that other people would have a go, reluctant to commit others to something that would almost certainly need updating.

Some brave souls did go ahead: Michael Gerbracht produced LuaFox and Stephan Kleinert produced Mitosis, which both use the toolbox.

It became apparent to me that certain features of Lua, that functions are first-class citizens and that it has lexical scoping, were a huge advantage for hiding the complexities of WIMP programming, and that languages, like BASIC and C, which lack them are at a grave disadvantage.

The whole point of a library, of course, is to hide the implementation details of useful general purpose values, so that wheels do not have to be reinvented. I hope that what this little program does is clear, even if how it does it is not. Note that an explicit number only occurs in the program once, on the third line; 0 is the null wimp-event code (a magic number).

There are no SWIs mentioned, nor the buffers needed for passing data between the wimp-task and the task-manager. RiscLua is well-adapted for implementing abstract notions, such as wimp-task , but, equally important, it is well adapted for hiding detail within libraries.

The same applies to other RiscLua libraries dealing with templates, windows, menus and draggable icons. See here for more details. The libraries in their present form are by no means complete. They are intended as a showcase for what is possible. But I have held off from further development, partly out of idleness and partly because I know that development without feedback can be wasted effort.

One Response

  1. A fascinating article. I’m definitely going to have a play with Lua on RISC OS!

Leave a Reply