Plua
1. Introduction
Plua is a port of Lua 4.0.1 (plus a small IDE) for the Palm Computing platform.
Lua is a programming language designed at TeCGraf, the Computer Graphics
Technology Group of PUC-Rio, Brazil.
More information on Lua can be found at
Lua.
(NOTE: I have no affiliation with TeCGraf or PUC-Rio)
THIS SOFTWARE COMES WITH ABSOLUTELY NO WARRANTY, SO USE IT AT YOUR OWN RISK.
Plua can not be reverse engineered or modified in any way.
To include Plua in any software collection, online or not,
first contact the author for permission. No one is allowed to sell or charge
for redistributing Plua.
Plua is Copyright (c) 2001-2005 Marcio M. Andrade.
2. Operation
Plua can be used in two different modes. The first one provides a
quick read-eval-print loop. In this mode you can type a program
and have it evaluated immediatelly. You type a program in the
lower half of the screen and the results are printed in the upper half.
To evaluate the current program, tap the Run button.
To clear the current program, tap the Clear button.
For example, if you type in the text field this program:
print("Result = ", 2*3)
Plua will print the following in the result area:
Result = 6
The second mode also allows you to run Lua programs,
but with two differences:
- The program is retrieved from a MemoPad record, a Doc file, a Stream file or a file stored in a VFS card.
- The entire screen is available to display results.
Tap the File button to access the file selection screen. On the top right corner you can choose one of the four file types (Memo, Doc, Stream or Card). A list of current available programs of the selected type is displayed.
If you create Memo with Lua code, it must start
with "-- " (without the quotes and with a trailing space) and then the program
name (usually a single word) ended with ".lua", otherwise it will not be
visible in Plua. Doc, Stream and VFS files must also end in ".lua".
Select one program on the list and tap the Run button to run it.
Plua will switch to a full screen mode and run the program.
When it is finished Plua will return to the program selection screen.
If you want to stop a program before it is finished tap the Applications icon.
Plua looks for VFS files in the /PALM/Programs/Plua/src directory on your expansion card.
The button Compile is used to compile a program into a PRC file. The generated
PRC is a standalone application that can be accessed like any other one in
the Application Launcher. Note however that Plua or PluaRT (the Plua runtime)
needs to be installed. You do not need to install both if you just want to run
Plua applications. If neither Plua nor the runtime is installed and a compiled
program is run, an error message will be displayed and the program will be
aborted. Generated PRCs have a fixed pre-defined icon.
Note that it is not necessary to compile a program in order to run it.
If you just to want to run a program from within Plua, you can use the
Run button directly. The Compile button should be used only when you
want to generate a standalone PRC with your program.
Tap the Main button to return to the main screen. For Memos you have two additional
buttons: New and Edit, to create and edit Memos respectively. For Docs, there is
also a Edit button. If you tap this button Plua will open the third-party
application SrcEdit to edit your doc file. If this application is not installed
nothing will happen.
Preferences are accessible via Menu/Preferences in the main screen.
If "Read-only mode" is checked, all attempts to open a database for writing or
removing a database will be aborted, and an error message will be displayed.
If "Clear output" is checked, the Clear button also clears the output area.
The Plua online help system is accessible via the PalmOS standard "Find" button.
If you tap Find while in the main window or editing a memo, Plua will open a
dialog showing all function names. If you select one function name and tap
Goto again Plua will show the function reference. If you tap the Index button,
all function names will be shown again. Tap Done to close the help dialog.
While in the main window or editing a memo, before tapping Find you may first
select a word, in which case the help dialog will open directly in the function
reference. In order to use the help system, you must first install pluahelp.prc.
3. Technical notes
A great effort was put on porting the complete Lua package.
Some portions of the source code were kept almost intact, while others
have been heavily modified, in order to fit the PalmOS architecture.
3.1. Compatibility
Plua requires PalmOS 3.1 or greater.
Indexed color support (2-bit, 4-bit or 8-bit) requires at least
PalmOS 3.5. On other OS versions the screen is considered monochromatic
(colors are 0=white, 1=black), even if the hardware supports grayscale.
3.2. Limitations when compared to the standard Lua distribution
- The following functions of the standard I/O library (liolib) are missing:
debug, execute, getenv, rename, setlocale, tmpname.
The date function is implemented partially (some formatting options
are missing).
- There is no concept of "stdin", the standard input stream. Programs
can read data from files, but not from user input. User input is restricted
to pen/key events and interaction with UI components.
- Decimal is the only supported numerical base.
- Most of the functions in the standard mathematical library (lmathlib)
require MathLib (a third-party library) to work properly.
3.3. Extensions to the standard Lua distribution
In addition to the functions of standard Lua libraries, Plua provides
additional functions to access PalmOS specific features.
In the following lists, optional parameters are denoted by square
brackets.
Database I/O functions:
- listdb(creator, type [,suffix]): returns a table with all databases matching
the specified type and creator, and optional suffix.
Type and creator are either a 4 character
string or an empty string to accept anything.
- opendb(name, mode): opens a PalmOS database. Mode can be "r" for read-only
access, "r+" for read/write access, or "w" for write-only access. If mode
is not read-only, the database is created if it does not exist. This
function returns a handle to the open file and the number
of records in the database. The handle is used as the first parameter in
the other database I/O funtions. Returns nil if an error ocurred.
- closedb(f): closes a database.
Returns true if success or nil if an error ocurred.
- getdbcat(f, n): returns the name of category n (where 0 <= n <= 15) from
already open database f.
- setdbcat(f, n, "cat"): sets the name of category n (where 0 <= n <= 15) to
"cat" on already open database f. Returns the category name just set.
- openrec(f, i): opens the record with index i from an already open database
f. Returns the size of the record or nil if an error occurred.
- createrec(f, size): creates a new record with given size in (already open)
database f. The record is added at the end of the database. The created
record is NOT automatically opened, so it is necessary to call openrec()
to open the just created record before using it.
Returns the index of the new record or nil if an error ocurred.
- deleterec(f, i): deletes the record with index i from (already open)
database f. The record can not be open when it is deleted.
Returns true if success or nil if an error occurred.
- closerec(f): closes the current open record of (already open) database f.
Returns true if success or nil if an error occurred.
- resizerec(f, i, size): resizes the record with index i from (already open)
database f to the specified size. If the record beeing resized is the
currently open record, it is closed, resized and then reopened.
Returns true if success or nil if an error occurred.
- getreccat(f, i): returns the category number for record index i from
already open database f.
- setreccat(f, i, n): sets the category number to n for record index i on
already open database f. Returns the category number just set.
- pgetprefs("creator", id): returns a string with the preferecences specified
by creator and id, or nil if it does not exist.
- psetprefs("creator", id, prefs): sets the preferecences specified by creator
and id to the given string prefs.
Returns nothing.
Directory functions:
- opendir(name): opens a VFS directory for reading.
Returns a handle to the open directory.
- closedir(f): closes a directory.
Returns true if success or nil if an error ocurred.
- readdir(f): reads a directory entry.
Returns the entry name and type (4=directory, 8=regular file, 10=link.
- mkdir(name): creates a VFS directory.
Returns true if success or nil if an error ocurred.
- listdir(name [, suffix]): returns a table with the file names in a VFS
directory. If the optional parameter is specified only files with that
suffix are returned.
Resource functions:
- popenres("type", id [, file]): opens the resource with specified type
(4 character string) and id. The resource is searched in all open databases
or, if the optional file parameter is passed, only in the database named
"file".
Returns a number identifying the resource.
- pcloseres(r): closes the resource identified by number r.
The number r is returned by the popenres() function.
Returns nothing.
- pgetres(r [, start [, end]]): returns a string with the contents of the
resource identified by number r. You can optionally specify just a substring
of the resource, with the start and end parameters.
The number r is returned by the popenres() function.
Returns a string with the resource.
- pgetsize(r): returns the size of the resource identified by number r. If
the resource is a bitmap, two additional numbers are returned: the width and
the height of the bitmap.
The number r is returned by the popenres() function.
- pdrawbmp(r [, mode]): draws the bitmap identified by the numbner r in the
current cursor position. The cursor is advanced to the right of the bitmap.
The optional mode parameter affects how pixels are transfered to screen
(0=paint, 1=erase, 2=mask, 3=invert, 4=overlay, 5=paint inverse).
The number r is returned by the popenres() function.
Returns nothing.
Serial I/O functions:
- openser(name, baud [,mode]): opens an IO port using the New Serial Manager.
Currently name can be either "serial0" or "ir0", to open the serial port
or the infrared port respectivelly, baud is a number and the optinal mode
is a string like "8N1" (the default) or "7E1". The first character is the
word size (5, 6, 7 or 8), the second is the parity (N, E, or O) and the
third is the number of stop bits (1 or 2).
Returns a handle to the open port or nil if there was an error.
Note that PalmOS does not allow more than one port open (you can open
either the serial or the infrared, but not both).
- closeser(f): closes an open serial port.
Returns true if success or nil if an error occurred.
Low-level graphical functions:
- pmode(): returns 4 numbers: screen width, screen height, screen depth
and 1 or 0 indicating if the screen supports color. In interactive mode
the screen height is half of the full-screen mode.
- pclear([c]): erases the screen with the background color or with the
optional c color, and moves the cursor to 0,0.
Returns nothing.
- pcolor(fg [,bg]): sets the foreground color (fg) and optionally the
background color (bg).
Returns nothing.
- prgb(r, g, b): returns the color equivalent to the (Red,Green,Blue)
components.
- ppos(): returns 2 numbers with the current x,y cursor position.
- pmoveto(x, y): moves the screen cursor to the x,y position.
- pline(x1, y1, x2, y2 [,c]): draws a line from x1,y1 to x2,y2 using the
fg color or the optional c color.
- plineto(x, y [,c]): draws a line from current position to x,y using the
fg color or the optional c color.
- pset(x, y [,c]): draws a pixel at position x,y using the
fg color or the optional c color.
- pget(x, y): return a number with the color of pixel at position x,y.
- prect(x, y, dx, dy [,c]): draws a rectangle at x,y, extending dx,dy pixels,
using the fg color or the optional c color.
- pbox(x, y, dx, dy [,c]): draws a filled rectangle at x,y, extending dx,dy
pixels, using the fg color or the optional c color.
- pcircle(x, y, rx, ry [,c]): draws an ellipse centered at x,y, with rx,ry
as x,y radius, using the fg color or the
optional c color. Use rx=ry for a circle.
- pdisc(x, y, rx, ry [,c]): draws a filled ellipse centered at x,y, with rx,ry
as x,y radius, using the fg color or the
optional c color. Use rx=ry for a filled circle.
- pfill(x, y, [,c]): starts a flood fill in the pixel located at x,y with
current color or the optional c color. The filling stops at pixels with a
different color than the initial pixel. Returns nothing.
- pfont(f): sets the text font to number f. Returns two numbers with the
"average" width of a character and the height of a character, both in
pixels. Note that PalmOS fonts are not fixed-width fonts, so if the returned
width is used in calculations, you get just an approximation.
- ptextwidth(text): returns the width of the text string in pixels.
- pheading(g): turns the graphical "turtle" to the specified heading g (in
degrees). 0 points to the right, 90 points up, and so on.
Returns nothing.
- pturn(g): turns the graphical "turtle" g degrees. g can be positive or
negative and it is added to the current heading.
Returns nothing.
- pwalk(d): draws a line from the current cursor position, with extent d
and following the current "turtle" heading. The cursor is positioned
at the end of the line.
Returns nothing.
- pgetbuffer(x, y, dx, dy): saves in a buffer the pixels delimited by
a rectangle at x,y, extending dx,dy pixels. Returns a number identifying
the saved buffer.
- pnewbuffer(dx, dy): creates an blank buffer
extending dx,dy pixels. Returns a number identifying the new buffer.
- pputbuffer(id, x, y [, mode]): draws a saved buffer at coordinates x,y.
The optional mode parameter affects how pixels are transfered to screen
(0=paint, 1=erase, 2=mask, 3=invert, 4=overlay, 5=paint inverse).
Returns nothing.
- pusebuffer([id]): sets a selected buffer for drawing. If the argument is
missing, the screen is selected for drawing. Returns nothing.
- pfreebuffer(id): frees the saved buffer. Returns nothing.
- pwritebuffer(filename [,id]): writes buffer identified by id into file
filename. If id is not specified the current screen is written. Returns
the resulting file size or nil in case of error.
- preadbuffer(filename): reads a buffer from file filename. Returns a number
identifying the new buffer, its width and its height. In case of error,
nil is returned.
- pclip(x, y, dx, dy): sets the clipping region to the rectangle at x,y,
extending dx,dy pixels. If no parameter is passed the clipping region is
reset. Returns nothing.
UI-related functions:
- ptitle([text]): sets the title of the current form. Works only in
full-screen mode. If the parameter is omited, the title is erased.
- pmenu(t): defines the menu items. Currently there is one fixed menu item
that is always available: "About Plua". It is possible to define
additional items by passing a table with strings to pmenu(). If a string
starts with a letter followed by a colon, the letter is used as the
item's shortcut.
- plabel(text): creates a label with given text in the current cursor
position. The cursor is positioned to the right of the component.
Returns the ID of the component or nil if it could not be created.
- pbutton(text [,bmpId]): creates a button with given text in the current
cursor position. The cursor is positioned to the right of the component.
If the optional bmpId is passed and the PalmOS version supports graphic
controls, a graphic button with the given bitmap resource ID is created
instead.
Returns the ID of the component or nil if it could not be created.
- ppbutton(text [,g [, bmpId]]): creates a pushbutton with given text in the
current cursor position. Pushbutons can be optionally placed in a given
group number g. The cursor is positioned to the right of the component.
If the optional bmpId is passed and the PalmOS version supports graphic
controls, a graphic button with the given bitmap resource ID is created
instead.
Returns the ID of the component or nil if it could not be created.
- prbutton(text [,bmpId]): creates a repeating button with given text in the
current cursor position. The cursor is positioned to the right of the
component. If the optional bmpId is passed and the PalmOS version supports
graphic controls, a graphic button with the given bitmap resource ID is
created instead.
Returns the ID of the component or nil if it could not be created.
- pcheckbox(text): creates a pushbutton with given text in the current
cursor position. The cursor is positioned to the right of the component.
Returns the ID of the component or nil if it could not be created.
- pselector(text [,bmpId]): creates a selector trigger with given text in the
current cursor position. The cursor is positioned to the right of the
component. If the optional bmpId is passed and the PalmOS version supports
graphic controls, a graphic button with the given bitmap resource ID is
created instead.
Returns the ID of the component or nil if it could not be created.
- pslider(width, range [,value]): creates a slider control with the specified
width in pixels. The slider values go from 0 to range-1. The optional
value parameter is the initial slider value. The cursor is positioned to
the right of the component.
Returns the ID of the component or nil if it could not be created.
- pfield(lines, cols, max [,text [, e, u]]): creates a text field with given
number of lines and columns in the current cursor position. The field will
accpet at most max chars. The field is optionally initialized with the
specified text. The optional parameter "e" and "u" are values
indicating if the field is editable and underlined, respectively.
A nil value means false, and not nil value means true. If these parameters
are passed, the text parameter must also be passed.
The cursor is positioned below the component.
Returns the ID of the component or nil if it could not be created.
For fields with more than 1 line, a scroll bar is automatically placed
to the right of the field if the user enters more text than the number of
lines can display.
- pfieldattr(id, e, u): sets the attributes of an existing field identified
by the given ID. The next two parameters are the same as in pfield().
Returns nothing.
- psetfocus(id): sets the focus to the field identified by the given ID.
Works only with text fields. Returns nothing.
- plist(lines, cols, t [,sel]): creates a list with given number of lines
and columns in the current cursor position. The list is filled with the
elements of table t. Optionally the selected element is set to index sel.
The cursor is positioned to the right of the component.
Returns the ID of the component or nil if it could not be created.
- ppopup(t [,sel]): creates a popup list in the current cursor position.
The list is filled with the elements of table t. The size is automatically
adjusted. Optionally the selected element is set to index sel.
The cursor is positioned to the right of the component.
Returns the ID of the component or nil if it could not be created.
- pgettext(id): returns a string with the text of the component
identified by the given ID. For labels, buttons, pushbuttons, repeating
buttons and checkboxes, the text is the label of the component.
For field, the the text is its current contents. For list, the text is the
current selected element.
- psettext(id, text): sets the text of the component
identified by the given ID. Works with buttons, pushbuttons, repeating
buttons, checkboxes, labels, fields and lists. For labels, the text is
changed only if the new length is not greater than the current length.
For lists, the text argument must be a table.
Returns nothing.
- pinserttext(id, text): inserts a string text in the field
identified by the given ID.
Returns nothing.
- pgetstate(id): returns a number with the state of the component
identified by the given ID. For pushbuttons and checkboxes, the number is
1 if selected, 0 if not selected. For lists and popups, the number is the
index of the selected item. For fiels, two numbers are returned: the start
and ending position of the selected text.
- psetstate(id, n): sets the state (or index for lists and popups) of the
component identified by the given ID. Works with pushbuttons, checkboxes
lists and popups.
Returns nothing.
- psetstate(id, start [,end]): if only start is passed, sets the insertion
point of the field identified by the given ID. If end is also passed,
selects a portion of the field text (from start to end).
Returns nothing.
- pnl(): positions the cursor at (x,y), where x is the leftmost position
and y is below the "tallest" component of the current "line".
Returns nothing.
- ptab([n]): advances the cursor 8 pixels to the right. It is useful when
separating UI components on the same line. If n is specified the cursor
is advanced 8*n pixels.
Returns nothing.
- pdestroy(): destroys all UI components currently in the screen.
Returns nothing.
- palert(text): opens a information dialog with specified text.
The user must press the Ok button to close the dialog.
Returns nothing.
- pconfirm(text): opens a confirmation dialog with specified text.
The user must press either the Yes or the No button to close the dialog.
Returns 1 if Yes was pressed or nil if No was pressed.
- pinput([text [,initial]]): opens an input dialog with an optional text as
title. A second optional parameter defines the initial value of the input.
The user can enter up to 255 characters of input.
The user must press either the Ok or the Cancel button to close the dialog.
Returns a string with the input entered if Ok was pressed, or nil if
Cancel was pressed.
- pselectdate([text [, y, m, d]]): opens the PalmOS day selection dialog.
Text is an optional title, and y/m/d are the year, month and day initially
shown in the dialog. Returns three numbers with the selected year, month
and day, in this order, or nil if Cancel was pressed.
- pselecttime([text [, h, m]]): opens the PalmOS time selection dialog.
Text is an optional title, and h and m are the hour and minute initially
shown in the dialog. Returns two numbers with the selected hour and
minute, in this order, or nil if Cancel was pressed.
- pselectcolor(text, c): opens the PalmOS color selection dialog.
Text is the dialog title, and c is the initial color shown in the dialog.
Works only in full screen mode, and only on PalmOS 3.5 or greater.
Returns a number with the selected color if Ok was pressed,
or nil if Cancel was pressed.
- pgsi(): creates a Graffiti State Indicator in the current cursor position.
Works only in full screen mode, and only on PalmOS 3.5 or greater.
Returns nothing.
- pdialog(x, y, dx, dy, title): pops up an empty dialog at position x,y with
dimensions dx,dy. Controls can be created inside the dialog.
The dialog must be closed with pdestroy.
Event functions:
- pevent([n]): blocks until an event is generated. Events can be a hard button
press, a pen event, a UI control selection or whether there is IO pending.
The first returned value is always the event type: penDown, penUp, penMove,
keyDown, ctlSelect, ctlRepeat, popSelect, lstSelect, menuSelect, ioPending,
sampleStop
(these constants are already pre-defined). For pen events, 2 more number
are returned: the x,y cursor position. For key events, 1 more number is
returned: the key code. For UI control events, 2 more number are returned:
the control ID and the control state (1=selected, 0=not selected).
For list events and popup events, 2 more number are returned: the list ID and the selected
element. For menu event, the index of the menu item (starting with 1) is
also returned. The IO pending event (nothing else is returned) is a
efficient way for a program to wait for IO without polling. If the
optional n parameter is passed, pevent() will wait at most n millisecons for
an event. If no event happens, it will return the constant nilEvent.
Sound functions:
- pbeep(n): plays the system sound identified by the number n.
Returns nothing.
- psound(freq,len [,volume]): plays a tone with frequency freq (in Hz),
duration len (in millisenconds), and optionally volume (0-64).
Returns nothing.
- pplaysample(filename [,volume]): plays sampled sound stored in a WAV file.
If volume (0-64) is not specified, the default system Game volume is used.
Returns the duration of the sampled sound in seconds, or nil in case of
error. After the sample is played a sampleStop event is generated.
- pstopsample(filename [,volume]): stops playing sampled sound.
Returns nothing.
Bitwise operator functions:
- andb(n1, n2): returns a bitwise AND between integers n1 and n2.
- orb(n1, n2): returns a bitwise OR between integers n1 and n2.
- xorb(n1, n2): returns a bitwise XOR between integers n1 and n2.
- notb(n): returns a bitwise NOT of integer n.
Binary data functions:
- pack("format", data): packs the elements of table data into a binary
string, and the returns this string. The binary data format is specificed by the
string argument format, in which each letter refers to a different encoding:
B=8 bits unsigned integer;
W=big endian 16 bits unsigned integer;
L=big endian 32 bits unsigned integer;
F=big endian 32 bits float;
D=big endian 64 bits double;
S=variable length string, ended with ASCII null.
- unpack("format", string): unpacks a binary string encoded with pack() and returns
a table with the decoded elements.
Misc functions:
- psleep(s): pauses the execution for s seconds (s can be a decimal number).
Returns nothing.
- pcopy(s): copies the string s to the clipboard. Returns nothing.
- ppaste(): returns a string with the contents of the clipboard.
- pmem(): returns used memory and total memory, both in KB.
- loadlib(name): loads a C library built with libkit. Returns a number
identifying the loaded library or nil in case of error.
- removelib(id): unloads a C library loaded with loadlib.
Returns nothing.
Built-in constants:
- _VERSION: a string with the Lua version number (this is a standard Lua
feature).
- _PLUA_VERSION: a string with the Plua version number.
- _OS_VERSION: a string with the PalmOS version number.
4. A small tutorial
This section assumes that you are already familiar with the Lua language,
and focuses on issues related to the Palm platform.
If you don't know the language Lua, don't worry. There is a lot of
documentation on its home page.
If you really want to start programming right away without reading
the docs, here are a few tips:
- Comments start with -- and extend to the end of line.
- Statements are not ended by ";"
- Variables do not need to be declared.
- Variables are not typed, but values are. Values can be numbers,
strings, functions or tables. Values are automatically converted to
the right type when possible. For example, the code 1+"2" evaluates to 3.
- String concatenation is done with the .. operator. For example, the code
"abc".."def" evaluates to "abcdef".
- Multiple assignments are supported. For example, the code x,y=2,3,4
assigns 2 to x and 3 to y. Non-used values are discarded (like 4 above).
- Functions can return multiple values. For example, if function f
return 2 values, they can be retrieved using x,y=f()
- You can print a list of expressions using the function print:
print(123,"abc",4*5,sin(45))
- Take a look at the supplied samples.
- There is much more you can do with Lua, now take a look the docs :-)
4.1. The display
Plua handles your device screen as a bitmapped display where individual
pixels can be addressed. The "stdout" is also mapped to the display,
so when you use print() or write() the characters are written to the display.
Plua stores the current cursor position in a pair of numbers (x,y).
This position is updated whenever you write to the display.
The statement pline(0,0,19,19)
will draw a line from position
(0,0) to (19,19) and will update the current cursor position to (19,19).
If the next statement is write("abc")
the string "abc" will
be printed starting at position (19,19) and the cursor will be placed at
(19,19+d), where d is the width in pixels of string "abc" written with
the current font.
Besides the cursor position, Plua also stores the current foregroung color,
the current background color and the current font. The following example
writes the string "Plua" in red over black with the bold font:
pcolor(prgb(255,0,0), prgb(0,0,0))
pfont(1) print("Plua")
Plua works with 1-bit monochromatic displays, 2-bit or 4-bit grayscale displays
and with 8-bit color displays. The foreground and background colors are
mapped to the closest grayscale tone in your device, if color is not
available. You can find the display properties of your device by calling
the pmode() function:
width, height, depth, hasColor = pmode()
print(width, height, depth, hasColor)
Width and height are the display dimensions in pixels. Depth is 1, 2, 4, or 8,
and hasColor is 1 if the display supports color, or 0 otherwise. On color
devices, you can use the prgb() function to get the index of a color given
its red, green and blue components. The example below will print 125 on 8-bit
color device using the standard color pallete.
red = prgb(255,0,0)
print(red)
Plua supports a Logo-like "turtle" pointer. Using pwalk() and pturn() you
can walk around the display drawing lines. The example below clears the
display, positions the cursor at the middle of the display and
draws a small expiral.
pclear() w,h = pmode() pmoveto(w/2,h/2)
for d = 1,20,1 do
pwalk(d) pturn(-40)
end
The following example shows how to draw a bitmap. 11000 is the resource ID
of the "Taxi" bitmap stored in some Palm ROMs.
pclear()
bmp = popenres("Tbmp", 11000)
pdrawbmp(bmp)
pcloseres(bmp)
One final note on the display: writing at the end of a line does not make it
to wrap, and writing at bottom line does not make the display to scroll up.
4.2. User Interface
The standard PalmOS UI componentes can be easily created and interacted with.
The usual way to do this is to create a few components and then enter
a loop waiting for UI events. The following example does exactly this:
textField = pfield(1,20,20)
lengthButton = pbutton("Length") pnl()
while 1 do
ev,id = pevent()
if ev == ctlSelect and id == lengthButton then
print(strlen(pgettext(textField)))
end
end
The functions pfield() and pbutton() create a 1-line text field and a button,
respectively. The pnl() function positions the cursor below the button.
The infinite loop calls pevent(), which makes the application block until
an UI event is generated. In our example, the "Length" button will generate
the an event (ctlSelect) when it is pressed. pevent() returns this event ID
and the component ID (in this case the ID of the button). The program uses
pgettext() to retrieve the current contents of the text field and prints its
length.
NOTE: since this program enters an infinite loop, the only way to stop
it is to tap the Applications icon.
The example below shows pinput(), palert() and pconfirm().
s = pinput("Write something")
if s ~= nil then
palert("You wrote "..s)
end
if pconfirm("Are you tired ?") then
print("Me too")
else
print("Me neither")
end
In order to set a menu for your application, you can use pmenu() as following.
pmenu({"O:Open", "Preferences", "-", "Q:Quit"})
The menu will have 4 items: the fixed "About Plua" item, a fixed separator,
an "Open" item with "O" as shortcut, a "Preferences" item with no shortcut,
a separator, and a "Quit" item with "Q" as shortcut. Limitations: currently
pmenu() will work only on PalmOS 3.5 or greater (earlier versions do not allow
dynamic menu configuration), it works only in full-screen mode,
and it is not possible to define more than one menu in the same menu bar.
pmenu() can be called any time, and it will replace the current menu
with the new one.
When one of the menu items is selected (except the About
item), a menuSelect event is returned by pevent(). In the example above,
if "Preferences" was selected, pevent() would return menuSelect and the
number 2, meaning that the second user defined item was selected.
4.3. Database I/O
Although PalmOS does not have a true filesystem, Plua provides the Lua virtual
machine the illusion that the underlying OS supports file I/O.
The "stdout" descriptor is mapped to the display.
The "stderr" descriptor is mapped to a dialog box that shows what was printed
on stderr.
Regular files are implemented using the File Stream API of PalmOS, so Lua
programs can open, create, read from and write to files normaly, without
knowing about database types or database creators. The following example
shows this:
f = openfile("MyOutput", "w")
write(f, "This is being written to a PalmOS stream database")
closefile(f)
The database MyOutput is open for writting (it is created if it does not
exist), a string is written to it, and it is closed.
There are also functions that allow a program to manipulate a PalmOS database
directly, if desired. They were listed in the section "Extensions to the
standard Lua distribution" above. The example below iterates through all
MemoPad records and prints the first line of each record:
f,n = opendb("MemoDB", "r")
for i = 0,n-1,1 do
openrec(f, i)
s = read(f, "*l")
print(s)
end
closedb(f)
The read() function belongs to the standard I/O Lua library. The I/O functions
work with both stream files and regular databases. With regular databases,
each record works like a sub-file, that is, they can be read from the
begining to the end, when an EOF conditions is signaled. In order to
continue to read the database, openrec() must be called to open the next
record and so on.
The available modes for opendb() are:
- "r": opens the database for reading only.
- "r+": opens the database for reading and writing. The database is created
if it does not exist. In this case, the creator is inherited from Plua
and the type is always 'data'.
- "w": opens the database for writing only. The database is created
if it does not exist. In this case, the creator is inherited from Plua
and the type is always 'data'.
The first two modes also accept the "b" suffix (like "rb" or "r+b"),
which means binary mode. If a record is read in binary mode, the
last character is ignored if it is ASCII 0 (nul). This is necessary
because applications like MemoPad always write a nul as the last character
of a record. In some cases this nul must be ignored, while in others
(like binary data) it may be significant.
The standard dofile() function works with Doc files, MemoPad records and
compiled applications. If there is a Doc file named "name.lua", for example,
it can be included by using dofile("name.lua"). For MemoPad records, the
record name must be prefixed with the string "memo:". If there is a record
named "-- name.lua", for example, it can be included by using
dofile("memo:name.lua"). For applications, the compiled lua code can be
included by using dofile("appname"), where "appname" is the PRC name.
A note about error handling in file I/O: in case of success, the functions
marked as returning true in fact return an userdata value (which is true,
since any non-nil value is considered true in Lua). The value of this
userdata is not important, it is used just to make it different from false
(nil). In case of failure, all I/O functions
(except for read) return two additional values besides nil.
The second value is a string with the error message and
the third value is the numeric error code. The error messages/codes are
inspired on the Unix C Library (libc) errors. Currently the following errors
may be reported:
- ENOENT (2): No such file or directory
- EIO (5): Input/output error
- EBADF (9): Bad file descriptor
- ENOMEM (12): Cannot allocate memory
- EACCES (13): Permission denied
- EEXIST (17): File exists
- EINVAL (22): Invalid argument
- EMFILE (24): Too many open files
- EFBIG (27): File too large
For example, suppose opendb() is used to open a database, like in
the following code:
f,n,e = opendb("Test", "r")
If there is a database named Test, f will be assigned a handle to the opened
database, n will be assigned the number of records in the database and
e will be assigned nil. However, if there is no databse named Test, f will
assigned nil, n will be assigned the string "No such file or directory" and e
will be assigned 2 (for ENOENT). This example illustrates how a function may
return different number of values (and possibly of different types) depending
on its execution.
4.4. Serial I/O
The function openser() opens an IO port known to the New Serial Manager.
Currently only the serial (RS-232) and infrared (IrComm) ports are supported.
The following example shows how to open the serial port and wait for data
in a efficient way.
f = openser("serial0", 57600, "8N1")
while 1 do
ev = pevent()
if ev == ioPending then
s = read(f, 8)
print(s)
elseif ev == penDown then
break
end
end
closeser(f)
When data is available at the serial port, the ioPending event is sent.
Note that it is not possible to know how much data is available. The read()
function reads at most 8 bytes and returns. If less than 8 bytes are
available, read() returns them without blocking. If more than
8 bytes are available, they remaining bytes are hold in the Serial Manager
buffer. In the next loop interaction, another ioPending event will be sent,
and so on. In this example, the loop is stopped when the user taps anywhere
in the screen with the pen.
The functions openser() and closeser() may also return the error messages/codes
defined for file I/O.
4.5. Unified I/O
The previous sections showed how to use regular databases, stream databases and
serial I/O. Instead of creating new functions for each new PalmOS API, Plua now
supports unified I/O handling using the standard Lua functions (openfile,
read, write and closefile). The unified I/O supports VFS, TCP/IP and any device
accessible by the New Serial Manager. The following table shows all supported
options.
Type |
Syntax |
Example |
Stream database |
openfile("name", "mode") |
openfile("MyDatabase", "r+") |
Doc database |
openfile("name", "mode") |
openfile("MyDoc", "r") |
Memo database |
openfile("memo:name", "mode") |
openfile("memo:Data", "w") |
Serial Manager |
openfile("srm:device:baud:word") |
openfile("srm:serial0:9600:8N1", "w") |
VFS file |
openfile("vfsn:path", "mode") |
openfile("vfs0:/Palm/Programs/Readme.txt", "r") |
TCP socket |
openfile("tcp:host:port") |
openfile("tcp:www.lua.org:80") |
UDP socket |
openfile("udp:host:port") |
openfile("udp:host.com:7") |
4.6. Binary data
Most PalmOS applications save persistent data in one or more PDB's. This information
is written in binary form, that is, a sequence of bytes usually representing the
encoding of a C structure. In Lua, however, information is stored in numbers,
strings and tables, and their internal binary representation is not relevant.
In order to read and write binary data, Plua provides the functions pack() and
unpack(). The following example shows the usage of these functions along with
database access functions.
Storing a a table into a PDB record:
table = {25, "Plua", 3.1416, 9999}
data = pack("BSDW", table)
f = opendb("MyBinaryData", "wb")
index = createrec(f, strlen(data))
openrec(f, index)
write(f, data)
closedb(f)
According to the format string "BSDW", the number 25 (the first element) is packed as
a byte, the string "Plua" is packed as a null-terminated string,
the number 3.1416 is packed as a double and the number 9999 is packed as
a 16 bit word. The returned data is a binary string of 1+5+8+2 = 16 bytes.
This data is stored as record in the MyBinaryData PDB.
Reading the same record into a table:
f = opendb("MyBinaryData", "rb")
openrec(f, index)
data = read(f, "*a")
table = unpack("BSDW", data)
closedb(f)
After this, the returned table will have the same elements as the original one.
4.7. Libraries
You can use compiled Plua applications as libraries. Lets say you have a set of
useful Lua functions and you want to make them available to other developers.
The obvious way is to distribute the source code, but there is an alternative:
place the functions inside a MemoPad record or Doc file and compile it just
like a regular application. For example, if the generated PRC is named
"MyLib", another application can simply use dofile("MyLib") to load the
functions. To distribute your "library", you have to distribute the PRC named "MyLib.prc" that was created when you compiled it.
For a description on how to build C libraries and integrate them into Plua,
please look at the libkit examples.