LSLua 0.6

by doy (jluehrs2 at uiuc dot edu)
updated by THC4k (http://www.ls-themes.org)
http://dev.litestep.com/projects/LSLua/

1 - Introduction

LSLua is a scripting interface to LiteStep using the Lua scripting language. Lua is a cross platform, open source, embeddable scripting language. This module provides an interface to the LSAPI through Lua scripts.

This document explains what LSLua as a module provides; for convenience, each of lua code, litestep config code, and LSAPI code are highlighted.

See history.txt for a list of bugs and changes in LSLua.

To see how LSLua may be used, with examples, see http://wiki.litestep.com/Modules:LSLua.

Information about Lua

See the Lua 5.1 manual for a quick reference on using Lua. See also the book Programming in Lua. Code snippets and general information can be found at the lua-users wiki. Finally, for more specific questions not covered by those resources, feel free to ask for help in #lua on irc.freenode.net

2 - Step.rc Settings and Bang Commands

To load LSLua, put a line resembling *NetLoadModule lslua-0.5 load lslua.dll in your theme.rc. The load lslua.dll part is important, since, due to restrictions in NetLoadModule, all of the modules that come with LSLua must have a .dll extension for NetLoadModule to unpack them to the correct place. Loading LSLua threaded may be required to allow concurrent operation of things in Lua (such as the timer module). Threading can likely cause scripts in general to break, however. Try it with and without threading and see for yourself.


*LuaFile <filename>

Executes the given file. This registers any functions that are declared, and executes any statements not declared in a function. Any function whose name begins with bang_ is also registered as a bang command, named with the rest of the function name. For example, the function bang_test would be registered as !test. Bang functions take one optional string argument consisting of the entire argument string passed to them.


*LuaBinaryModuleDir <directory>

Declares a directory to be added to the search path for binary modules (package.cpath). Uses an extension of .dll.


*LuaScriptModuleDir <directory>

Declares a directory to be added to the search path for Lua script modules (package.path). Uses an extension of .lua or .lua.dll.


*LuaModuleDir <directory>

Equivalent to both of *LuaBinaryModuleDir and *LuaScriptModuleDir.


*LuaPackageDir <directory>

This is deprecated. Same as *LuaModuleDir.


!LuaExec <lua chunk>

Executes the argument as a Lua chunk. Note that new bangs are not registered using this method (this may change in the future).

3 - LSLua Functions

LSLua defines the global table lslua which contains some important functions and variables.


lslua.message_box (text, title)

Creates a message box with text of text and optional title of title. Quotes do work correctly, unlike !alert.


lslua.exec (str)

Executes str as a command, like LSXCommand or !execute [].

Uses LSExecute().


lslua.res ()

Returns a table such that lslua.res().x is the x resolution, lslua.res().y is the y resolution, and lslua.res().bpp is the bits per pixel.


lslua.mouse ()

Returns a table such that lslua.mouse().x is the x coordinate of the mouse and lslua.mouse().y is the y coordinate of the mouse.


lslua.milli ()

Returns the current number of milliseconds since the last second (use os.date to get the rest of the time information).


lslua.version

a string containing the lslua version.


lslua.get_evar (name)

Get an evar's value in memory, not file. Returns a string, or nil if evar is not defined. Similar to what you expect $name$ would give.

Uses GetRCString().

Note: the preferred way to use this function is through the evar module.


lslua.get_line (evar_name)

Get an evar's raw value in memory, not file. Returns a string, or nil if evar is not defined. Similar to get_evar but does not return first token.

Uses GetRCLine().


lslua.set_evar (name, value)

Set an evar's value in memory, not file.

Uses LSSetVariable().

Note: the preferred way to use this function is through the evar module.


lslua.rc_lines (prefix, file)

Get lines with given prefix from rc file. Use empty string for prefix to return all lines. If file not specified, defaults to settings already loaded in memory. Returns a table of strings.

Uses LCReadNextCommand().


lslua.init

A user defined value that is called at the very end of module load, so that bangs are registered. It is also executed in a coroutine, so timers should work.


lslua.quit

Similar to lslua.init, but is called when lslua is unloaded.


During bang execution, !LuaExec execution, and lslua.init() and lslua.quit(), it also declares the global variable CURRENT_THREAD, which is the coroutine that contains the bang execution. See the thread module for how this can be used.

4 - Modules (for LsLua, not LiteStep)

LSLua comes with eight modules designed to make certain tasks easier. To use one, include the line require "<modulename>" at the top of your Lua script. All functions provided by the modules are provided in a global table with the same name as the module, so for example, you would access the pause() function through timer.pause().

4.1 - args module

The args module defines some useful argument parsing functions.


args.strjoin (table, sep)

Takes two arguments: a table of strings, and a string to use as the separator. Returns a string containing all of the elements of the table concatenated together, with elements separated by the separator string passed as the second argument. For example, args.strjoin( { "this", "is", "a", "test" }, " " ) == "this is a test"


args.strsplit (string, sep)

Takes two arguments: a string, and a delimiter (a separator). It splits the string up into a table of substrings based on the delimiter, and returns that table. For example, args.strsplit( "this is a test", " " ) == { "this", "is", "a", "test" }


args.mstrsplit (string, sep)

The same as args.strsplit, except it returns the results as multiple return values rather than as a table.


args.qsplit (string [, delimiters [, leftquotes [, rightquotes ]]])

Takes between 1 and 4 arguments, some of them optional. first argument is a string, the second is a table of strings to use as delimiters, and the third and fourth are tables of strings to use as left and right quotes, respectively. If the fourth argument is omitted, it defaults to the same as the third, and if they are both omitted, they default to { "\"", "'" }. If the second argument is omitted, it defaults to { " " }. If any of the second, third, or fourth arguments are strings, they are automatically converted to tables holding those strings. This function splits the string into substrings based on the delimiter like args.strsplit does, but it takes into account quote characters as well. For example,

args.qsplit( "this is a 'string with quotes'" ) ==
    { "this", "is", "a", "string with quotes" }
args.qsplit( "%{hello},%{world}", ",", "%{", "}" ) == { "hello", "world" }
args.qsplit( "[this is][a test]", "", "[", "]" ) == { "this is", "a test" }

args.mqsplit (string [, delimiters [, leftquotes [, rightquotes ]]])

The same as args.qsplit, except it returns the results as multiple return values rather than as a table.

4.2 - evar module

The evar module allows access to and (re)assignment of LiteStep evars.


evar.toboolean (evar)

Takes one argument, a string for an evar's name. It returns a boolean evaluating the given evar as a boolean as how LiteStep core would in RC 'scripting'/preprocessing: It returns false for an evar whose value is one of these strings: "0", "false", "off", or "no". Otherwise it returns true.

If for some reason you need to access the evar named "toboolean", access it with a different casing, like "ToBoolean".


Evar Access

The evar table itself can be indexed to access the values of evars. For example, evar.LiteStepDir == evar[ "LiteStep" .. "Dir" ] --is "C:\LiteStep\" (on my system).

The result is nil for both undefined evars and those evars previously set to nil through the evar table (a magic value is replaced with nil).

Evar Assignment

Keys of the evar table can be assigned to; doing so will set an evar's value. tostring() is applied to this value. For example, evar.nameOfEvar = 13.

Setting an evar to nil this way will actually set the evar to a magic value.

Notes on evar access and assignment and why a magic value for nil:

There is no way (aside from !reload or !recycle or restarting LS) to undefine an evar, so the evar module has its own notion of 'undefined'; It does so using its own magic value.

So it makes no sense to set an evar to nil, because evars are just key-value pairs where both key and value are strings --omitting one or the other or both makes no sense.

The choice to handle nil specially is because of the addition of evar.toboolean(); nil should be recognized as false; why not handle nil in assignment as well?

To avoid this issue, don't associate the idea that an evar can hold nil.

4.3 - evarutils module

The evarutil module provides functions that work with evars. You can use this module to save evars to a file.


evarutils.savevar(filename, varname, value [, comment])

Sets all occurances of varname in file filename to value and appends the specified comment if it is provided.


evarutils.getvalue(filename, varname)

Reads file filename and tries to find varname. Returns the var's value or nil


evarutils.deletevar(filename,varname)

Removes all occurances of varname from file filename.


evarutils.config

evarutils.config provides a simple shortcut for writing config lines to a file without having to construct filenames.

evarutils.config.[<relative folders>.*]<filename>.<evarname> = <value>

The file path is constructed from evar.ThemeDir .. folders .. filename .. ".rc".Then evarutils.savevar(path, evarname, value) is called.

Folder example:

evarutils.config.settings.buttons.topbuttons is expanded to $ThemeDir$settings/buttons/buttons.rc

evarutils.config.settings.buttons.buttonset1.button1link = "http://www.ls-themes.org" calls

evarutils.savevar(evar.themedir .. 'settings/buttons/buttons.rc', button1link, "http://www.ls-themes.org").


4.4 - textedit module

This is designed to emulate the textedit.dll/xTextedit.dll modules in Lua. It can search through files, replace lines, insert lines, delete lines, etc. The main difference is that this module uses Lua pattern matching instead of standard regexes like textedit.dll does.


textedit.append (filename, str)

Adds str as a line at the end of the file

filename.

textedit.insert (filename, pattern, str, mode)

Searches through the file filename for lines matching pattern, and inserts str there. 'There' is determined by part of the string mode. The mode string can be any combination of the letters g, i, b, and G. g means 'global', and means that the function will be applied to every matching line in the file. i means case insensitive, which should be self-explanitory. b means before, and only applies to insert. If b is given, str will be inserted before the matching line, otherwise it will be inserted after. G means line global, and only applies for textedit.replace(). It means that textedit.replace() should replace each match on a given line, instead of the first.

Returns the number of insertions made.


textedit.delete (filename, pattern, mode)

Deletes lines in filename that match pattern, following the mode rules from textedit.insert(). Returns the number of deletions made.


textedit.replace (filename, pattern, replace_pattern, mode)

Searches the file filename for lines that match pattern and replaces pattern with replace_pattern on those lines. Captures and stuff like that from string.gsub() are all valid here. Returns the number of times it replaced pattern.


textedit.find (filename, pattern, mode)

Searches filename for pattern, and returns either the number of the first line that matches, or a table containing all lines that match if the mode string contains g.


textedit.delete_line (filename, line_num)

Deletes line number line_num from the file filename. It returns true if the line was successfully deleted and false otherwise.


textedit.insert_line (filename, str, line_num, before)

Inserts str into the file filename either before line_num if before is true, or after line_num if before is false. Returns true if the insertion was successful and false otherwise.


textedit.get_line (filename, line_num)

Returns a string containing the contents of line line_num in file filename.

4.5 - thread module

The thread module doesn't provide anything useful to users of LSLua, but it provides an important interface for module writers. It allows scripters to access the global coroutines defined by LSLua each time a user defined bang is called, allowing scripts to be started and resumed by name.


thread.yield ()

Yields the current bang execution, with a value of a string that is passed to it as an argument. If you declare your own coroutines in your code, you should always pass arguments back through coroutine.yield(), so that you can check to be sure that what is yielding is in fact your code, and not a thread trying to yield. See the animation module for an example of this.


thread.resume ()

Resumes the execution of the function that yielded with the name that is passed to resume.

4.6 - timer module

The timer module is an interface to the timer LiteStep module, and allows for pauses to work without freezing anything or stopping LSLua from functioning.


timer.pause (delay)

Takes a single argument, defining how long to pause for, using the timer.dll syntax for time. For example, pause(500) pauses for 500 milliseconds, pause("3s") pauses for 3 seconds, etc.


timer.resume ()

The function that timer.dll calls when the timer.pause() is over. It shouldn't ever be called manually, unless you want to end a timer.pause() early.

4.7 - lsmodule module

Implements an abstract, object oriented wrapper factory. Primary supports Andymon's xModules, but any other module can be implemented in a very simple way. Basically this means that evars are mapped to class attributes and bangs are mapped to class methods, providing simple and direct access to these without the need to construct bangs manually from strings and run them with lslua.exec. The concrete wappers for modules are construced by a maze of clever metatables on access and are cached.

A wrapper class for a supported module can be created like this:

wrapper = lsmodule.<module name>.<module instance name>

Now any of the wrapped module's bangs can be accessed with simple names ( "<module name><bang>" becomes "<bang>" ). If the called method has side effects (i.e. "!Move" changes the x and y position) all changed attributes are set accordingly.

wrapper.<simple bang name>( <bang parameter1> , < more bangparameters>, .. )

Evars are also accessible directly through the wrapper with simple names ( "<module instance><evar>" becomes "<evar>" ). "Current" Evars are used first.

attribute = wrapper.<simple evar name>

Changes to the state of a wrapped module can be automatically saved to a file if the object has a varfile attribute with the path to a file.

Examples

mylabel = lsmodule.xlabel.SomeLabel -- maps to a xlabel created with "*xlabel SomeLabel"
mylabel:moveby( -100, 0 ) -- maps to "!labelmoveby SomeLabel -100 0"

-- you always have to give the instance name, even if the module does not always need them
taskbar = lsmodule.xtaskbar.xtaskbar

-- if there is a 'varfile' settings are saved automatically
mylabel.varfile = evar.themedir .. "mylabelvarfile.rc"
mylabel.settext( "save me!") -- Updates 'mylabelvarfile.rc' with SomeLabeltext "Save me!"
taskbar.Alwaysontop = False -- Updates 'mylabelvarfile.rc' with xTaskbarAlwaysonTop false

there are some more examples in the source file lsmodule.lua