Inline Lua


Lua scripting may be used "inline" in Rainmeter. This is done by using a call to a Lua script as a [SectionVariable], referencing the Script measure containing the ScriptFile option for the .lua file. The value returned by Lua will "replace" the inline Lua section variable where it is used.

These may be used anywhere a [SectionVariable] is allowed. This can be in almost any options in measures or meters, and any bangs in an action.

As with any [SectionVariable], these will be resolved, in this case executed, when the skin is loaded, and on each update of the measure or meter containing them. As with any [SectionVariable], DynamicVariables=1 must be used on any measure or meter containing them.

Calling a Function()

Functions in a Lua script may be executed "inline", by using a call to a single function() within the Lua script, with optional parameters.

The Lua function will be executed, using the provided parameters, and the single number or string value returned by the function() in the Lua script will replace the [Section Variable] where it is used.

The way to consider this is as a simple [SectionVariable], that works just like any other [SectionVariable]. The only difference is that a [SectionVariable] is used to reference a number or string value returned by some measure in the skin, and this is used to reference a number or string value returned by some function() in the Lua hosted by a Script measure in the skin.

As the inline Lua can reference any function() within the .lua file, and can pass parameters specific to where the inline Lua is used, a single Script measure, and single .lua file, can host any number of functions, and a single function can return context specific values.

Note that this is not designed to execute stand-alone Lua statements, but is used to execute a function() code block in the Lua script file that returns a value. For example:

function SomeFunction(arg1, arg2)
outValue = arg1 + arg2
return outValue
end

The syntax of an inline Lua call to a function() is:

[&ScriptMeasureName:LuaFunctionName(numberParameter, 'stringParameter', ...)]

Examples:
[&MeasureMyScript:GetCharaceterInString('Rainmeter', 5)]
[&MeasureMyScript:ConvertTemperature([&MeasureCurrentTemp], 'C')]

The types of parameters that can be passed to the Lua function() are:

Number   Any number literal, formula or variable that resolves to a number.
String   Any 'string' literal, or variable that resolves to a string, enclosed in 'single quotes'.
Boolean   One of either true or false.

Retrieving a Variable

Inline Lua can also be used to simply retrieve the current value of any variable that that you may set in the Lua, either in Update() or in a Function() that you call with !CommandMeasure or other inline Lua.

This can greatly simplify getting any number of variable values that you may set in Lua. You don't need a return for each of them, just have your Lua set variable values, and have some inline Lua in measures or meters go "get them".

Example:
[&MeasureMyScript:myVariable]

Nested Variables in Rainmeter

It must be remembered that the entire inline Lua functionality is contained within a [SectionVariable]. This means that any #Variable# or other measure [SectionVariable] you use in the parameters to the Lua function will cause "nesting" of variables within variables. The standard way of expressing #VariableName# and / or [MeasureName] in Rainmeter cannot be "nested", and won't work.

This is solved by using the alternative Nesting Variables syntax in any inline Lua.

That link has a full explanation, but in short:

[#VarName]   Replaces: #VarName#
[&MeasureName]   Replaces: [MeasureName]
[$MouseVar]   Replaces: $MouseVar$

In general, anything useful that you do with this functionality is going to entail passing variables of one form or another to the Lua function, and just always using the Nested Variables syntax with any inline Lua will save a lot of frustration and confusion.

Inline Lua Notes

DynamicVariables=1 MUST be set on any measures or meters where inlineLua is used.

String values in parameters must be explicitly passed as strings to the Lua function, by enclosing them in 'single quotes'. Numbers or formulas may, and should, be passed without quotes, but any string passed without quoting will be seen by the Lua as a Lua variable name with a nil value, and will fail.

The Lua boolean data type may be passed to the Lua function with true or false, and any return to Rainmeter of a boolean true will be 1 and false will be 0.

Lua tables, myTable[index], may not be directly used in a parameter to the Lua function, you will need to pass the table name and the index number or string as separate parameters and use them to access the table in Lua. However, the Lua may return a single value from a table with return myTable[index]. The TranslateDay example below demonstrates this.

The Lua nil data type, indicating that a value doesn't exist, may not be used in a parameter, nor returned to Rainmeter. In some cases, the boolean value of false may be used to indicate NOT, in place of nil.

Important Final Note

Any inline Lua used in options in measures or meters are resolved when the skin is first read and created on load or refresh. This is the same for all [SectionVariables] in Rainmeter.

During this "initialization" phase of the skin, before the first actual "update" of the skin, the value of all measures will be set to an initial numeric value of 0, and if applicable, a string value of "". This will normally be transparent, as before any "result" of the inline Lua is "used", before the skin is "drawn" for the first time, all measures and meters will have been updated and the correct and expected results will happen. However some care should be taken to ensure that a one-time initial value of 0 being passed to your Lua doesn't cause any logic or formula errors.

In addition, the Initialize() function in a Lua script will only be executed during the first update cycle of the skin. This means that if you are defining and setting some default values for variables in Initialize() in your Lua, those values will not be visible before the first skin update, which will be after any inline Lua in measures or meters are first run. This may cause "nil" values to be seen and result in errors.

In many cases, this can be resolved by defining and setting these variables in the global scope of the Lua script, outside of any function block. This global scope will be executed during the initialization phase of the skin, and the values will be immediately available. The global scope of Lua has not been much needed before in Rainmeter, but with inline Lua it can be valuable.

Again, in both cases, this all will generally be transparent and of no consequence, since by the time the skin is first drawn, at the end of the first skin update, all will be well. This issue should be kept in mind however, if you find you are getting single initial error messages in the log.

Inline Lua used in bangs in actions in the skin will only be executed when the action is executed, and so will not display this behavior. They simply can't be executed before the first update of the skin.

Examples

The best way to explain inline Lua is to disassemble a few simple examples:

SimpleLua

SimpleLua.ini:

[Rainmeter]
Update=1000
AccurateText=1
DynamicWindowSize=1

[MeasureGetUserName]
Measure=Plugin
Plugin=SysInfo
SysInfoType=USER_NAME
UpdateDivider=-1

[MeasureScript]
Measure=Script
ScriptFile=SimpleLua.lua
Disabled=1

[MeterWelcome]
Meter=String
FontSize=20
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=[&MeasureScript:GetWelcome('[&MeasureGetUserName]')]
DynamicVariables=1

So we have a pretty simple skin, which gets the name of the user account logged into Windows with the SysInfoType=USER_NAME option on the SysInfo plugin measure [MeasureGetUserName].

And we have a Script measure, [MeasureScript], which will load and make available the Lua script file SimpleLua.lua. Note that since we don't use or need an Update() function in the Lua, we can just disable this Script measure. There is nothing it can or should return on each update of the measure itself.

Then in our [MeterWelcome] String meter, we use an inline Lua section variable. Let's look at the construction of that.

Text=[&MeasureScript:GetWelcome('[&MeasureGetUserName]')]

First, we reference our Script measure name with [&MeasureScript. That is followed with a : colon, to indicate a modifier, as with any [SectionVariable]. Note that we are using the Nested Variables syntax to reference our Script measure, as we are going to be "nesting" another [SectionVariable] in this.

Then we reference the function() in the Lua script file that we want to call. In this case it is a function called GetWelcome(), which requires a 'string' parameter.

We want to use the value of our [MeasureGetUserName] measure as the 'string' parameter to the Lua function, so we use the Nested Variables syntax to pass the value of that measure to the Lua as a 'string', with the section variable ('[&MeasureGetUserName]'). Then we end the initial section variable, our inline Lua, with a ] square bracket.

SimpleLua.lua:

function GetWelcome(inArg)

hourOfDay = tonumber(os.date('%H'))

if hourOfDay < 12 then
greetingText = 'Good Morning'
elseif hourOfDay < 17 then
greetingText = 'Good Afternoon'
else
greetingText = 'Good Evening'
end

welcomeText = greetingText..', '..inArg

return welcomeText

end

If we pretend that the value of our [MeasureGetUserName] measure is the string RainmeterTeam, then the function GetWelcome('RainmeterTeam') will be executed in the Lua script.

The Lua script uses the current hour to choose a greeting appropriate for the time of day, and constructs a string to return as the section variable value to the String meter in Rainmeter. That is done with the return function in Lua, as return welcomeText. What would be seen in the skin at nine in the morning is "Good Morning, RainmeterTeam".

FlipCoin

This example is a little more complex, showing how we can combine the use of an "action" in the Rainmeter skin, with inline Lua section variables, to get a result "on demand", and how we can just retrieve variables with Inline Lua, instead of calling a function().

With this one, we will just explain using ;comments in the Rainmeter code, and --comments in the Lua code, as it might be easier to follow the explanation in context.

FlipCoin.ini:

[Rainmeter]
Update=1000
DynamicWindowSize=1
AccurateText=1

; Demonstrates combining executing some Lua based on an "action"
; in the skin, with some inline Lua section variables to get
; different parts of the result.

[MeasureScript]
Measure=Script
ScriptFile=FlipCoin.lua
; There is no Update() function in the Lua, and we don't need
; one, so there is no reason to enable this measure. It's a
; "host" for the .lua file we use with !CommandMeasure and
; inline Lua later on.
Disabled=1

[MeterFlip]
Meter=String
FontSize=13
FontWeight=400
FontColor=255,255,255,255
SolidColor=47,47,47,255
Padding=5,5,5,5
AntiAlias=1
Text=Flip Coin
; We don't want the coin to flip on every update of the skin, or the meters with
; the inline Lua section variables, we want to flip the coin on demand. So we
; Use a !CommandMeasure bang with a LeftMouseUpAction to execute the
; FlipCoin() function in the Lua, which will get a random number between 0 and 1,
; and set the value of the coin to "heads" or "tails".
LeftMouseUpAction=[!CommandMeasure MeasureScript "FlipCoin()"][!UpdateMeterGroup Coins][!Redraw]

[MeterCoin]
Meter=String
Group=Coins
X=15R
FontSize=20
FontWeight=700
FontColor=255,255,255,255
SolidColor=0,0,0,1
AntiAlias=1
; Now we use an inline Lua section variable to ask for the current value,
; 'Heads' or 'Tails' of the coin, by asking for the current value of the
; variable flipText in the Lua.

; This will get that value, which was created and changed by FlipCoin() and
; is global, and replace the inline Lua section variable in the Text
; option of the meter.
Text=It's [&MeasureScript:flipText]
DynamicVariables=1

[MeterTotal]
Meter=String
Group=Coins
X=0
Y=10R
FontSize=17
FontWeight=400
FontColor=255,255,255,255
SolidColor=0,0,0,1
AntiAlias=1
; When we flip the coin with our !CommandMeasure action, the Lua keeps track
; of how many times the function has been called. We can then ask the Lua for the
; current value of the variable totalFlips, and that value will replace the
; inline Lua section variable in the Text option.
Text=Total Flips: [&MeasureScript:totalFlips]
DynamicVariables=1

[MeterCount]
Meter=String
Group=Coins
X=0
Y=10R
FontSize=17
FontWeight=400
FontColor=255,255,255,255
SolidColor=0,0,0,1
AntiAlias=1
; When we flip the coin with our !CommandMeasure action, the Lua also keeps track
; of how many times it was 0 or 1. We can then ask the Lua for the current value
; of either headsCount or tailsCount to return either the number of 'Heads' or 'Tails',
; and those values will replace the inline Lua section variables in the Text option.
Text=Heads: [&MeasureScript:headsCount] Tails: [&MeasureScript:tailsCount]
DynamicVariables=1

FlipCoin.lua:

math.randomseed(tonumber(tostring(os.time()):reverse():sub(1,6)))

-- Since the skin will be asking for these variables before the FlipCoin() function is ever
-- called, we need to set these to some initial value in the global scope, so they are not
-- initially nil, which would raise an error.

flipText = 'None'
headsCount = 0
tailsCount = 0
totalFlips = 0

function FlipCoin()

flipNumber = math.random(0, 1)

-- When we "flip the coin", the variables we want are set by the function, and
-- since they are not defined as local, they will be available in the global scope
-- when we ask for them with the inline Lua in the skin.

if flipNumber == 0 then
flipText = 'Heads'
headsCount = headsCount + 1
else
flipText = 'Tails'
tailsCount = tailsCount + 1
end

totalFlips = headsCount + tailsCount

end

TranslateDay

This example adds a few more pieces, showing how we can use both [&SectionVariables] and [#Variables] (remember our Nesting Variables syntax!) embedded in the inline Lua, and how to pass multiple parameters to the Lua function, both number and 'string'.

TranslateDay.ini:

[Rainmeter]
Update=1000
AccurateText=1
DynamicWindowSize=1

[Variables]
MainLang=English

[MeasureDay]
Measure=Time
Format=%w

[MeasureScript]
Measure=Script
ScriptFile=TranslateDay.lua
; We are not doing anything on every skin update, and there is no Update() function
; in the .lua file, so there is no need to enable this measure. In this case, it's
; just a "host" for the function() in the script we will call "inline" later.
Disabled=1

[MeterBack]
Meter=Shape
Shape=Rectangle 0.5,0.5,220,110,12 | Fill Color 47,47,47,255 | StrokeWidth 1.5 | Stroke Color 150,150,150,255

[MeterHeader]
Meter=String
X=10
Y=10
FontSize=13
FontWeight=400
FontColor=255,255,255,255
AntiAlias=1
; In this we use an inline Lua variable to call the TranslateHeader() function hosted
; by our [MeasureScript] measure, and pass it the the string for the variable [#MainLang].
; It will "return" the result, a lookup translation of the text "Today is", and replace that
; in the value of the Text option.
Text=[&MeasureScript:TranslateHeader('[#MainLang])']
DynamicVariables=1

[MeterLang1]
Meter=String
X=10
Y=10R
FontSize=11
FontWeight=400
FontColor=255,255,255,255
AntiAlias=1
; In this we use an inline Lua variable to call the TranslateDayOfWeek() function hosted
; by our [MeasureScript] measure, pass it the day of the week as a number, and
; the string 'Language'. It will "return" the result and replace that in the value
; of the Text option.

; Note that [&MeasureDay] represents a number and we pass it as a number. The value of
; the variable [#MainLang] is a string, and we pass it as a string with single quotes.
Text=[#MainLang]: [&MeasureScript:TranslateDayOfWeek([&MeasureDay], '[#MainLang]')]
DynamicVariables=1

[MeterLang2]
Meter=String
X=0r
Y=5R
FontSize=11
FontWeight=400
FontColor=255,255,255,255
AntiAlias=1
; This is were the power of this feature shines. Note that since we are
; passing different "parameters" to the function in different places where
; we "use it", the result can be specifically tailored to the context
; it it used in. So "English" before, and "French" here...
Text=French: [&MeasureScript:TranslateDayOfWeek([&MeasureDay], 'French')]
DynamicVariables=1

[MeterLang3]
Meter=String
X=0r
Y=5R
FontSize=11
FontWeight=400
FontColor=255,255,255,255
AntiAlias=1
Text=Russian: [&MeasureScript:TranslateDayOfWeek([&MeasureDay], 'Russian')]
DynamicVariables=1

TranslateDay.lua:

function Initialize()

translateToday = {
english = 'Today is',
german = 'Heute ist',
french = 'Aujourd\'hui, c\'est',
spanish = 'Hoy es',
russian = 'Cегодня'
}

translateDays = {
english = {'Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'},
german = {'Sonntag','Montag','Dienstag','Mittwoch','Donnerstag','Freitag','Samstag'},
french = {'dimanche','lundi','mardi','mercredi','jeudi','vendredi','samedi'},
spanish = {'domingo','lunes','martes','mercredi','miércoles','viernes','sábado'},
russian = {'воскресенье','понедельник','вторник','среда','четверг','пятница'}
}

end

function TranslateHeader(langArg)

return translateToday[string.lower(langArg)]

end

function TranslateDayOfWeek(dayArg, langArg)

return translateDays[string.lower(langArg)][dayArg+1]

end

In these examples, we concentrated on using the inline Lua to set Text options on a String meter, as they are easy to understand and demonstrate. Remember though, that inline Lua can be used on any measure or meter option, and in any bang. Anywhere that a [SectionVariable] can be used. So this might be X or FontColor on a String meter, Drive on a FreeDiskSpace measure, some value in an ActionTimer measure, the uses are only limited by how clever you are.

Download Examples

You can download the above examples, and a few others, as a .rmskin.