Some of my scripts for daily use on a linux desktop

Friday, February 18, 2011

HowTo run multiple Lua scripts in a Conky


Here is a little HOWTO for running more than one Lua script at the same time.

1. Method for two scripts with Conky objects

This can be done for two scripts with the two functions of Conky : lua_draw_hook_pre and lua_draw_hook_post

lua_load /home/wlourf/lua/scriptA.lua
lua_draw_hook_pre functionA

lua_load /home/wlourf/lua/scriptB.lua
lua_draw_hook_post functionB

lua_draw_hook_pre is called before Conky parse the TEXT section and lua_draw_hook_post is called after the Conky parse the TEXT section. So effects are not very nice because the two calls are not synchronized.

2. Method with a Lua script for 2 or more scripts

Now, here is an example for 2 scripts, but you can modify it for three, four ... scripts :
Save the two scripts (scriptA.lua with conky_functionA() and scriptB.lua with conky_functionB() in the same folder : /home/wlourf/multi/ for my example :
Create a third script loadAll.lua, and paste that :

package.path = "/home/wlourf/multi/?.lua"
require 'scriptA' --for scriptA.lua ".lua" is not required here
require 'scriptB'

function conky_main()

and in the conkyrc, call the conky_main function with lua_draw_hook_pre or lua_draw_hook_post, like this :

lua_draw_hook_post main

3. With some variables

Now, sometimes, we need to use the same parameters in more than one script (colours, x and y position ...). So we have to write them in the conky_main() function so they can be used in the functions conky_functionA() and conky_functionB()

To avoid memory leaks, we have to declare the variables in local, so these variables will be available only for the conky_main() function, then we have to "pass" them to the functions, like that :

function conky_main()
local varX = 25
local varY = 50

and in the functions :
function conky_functionA(varX,varY)
table_config ={...,

Ok, passing functions from the conky_main() function to other functions can be boring when we have a lot of parameters. The easiest way to pass the parameters is to group them into a table and to pass the table :
function conky_main()
local table_param={
varX = 25,
varY = 50

and in the functions :
function conky_functionA(t)
table_config ={...,
y=t.varY, ...

t.varX is the variable varX from the table t, it can be written t.["varX"]

That's all. I hope this little HowTo will help you to use multiple Lua scripts. This is my own way to manage multiple scripts, it can be done with others methods dofile, loadfile or instances !

Happy Conkying ;-)

Thursday, January 27, 2011

Box widget

Back to basics with Lua and Cairo! This widget draws only a customizable box. It is inspired from Londonali's Background script.

The script can be downloaded on deviantArt : here.

As usual, the widget uses a table to get the parameters : boxes_settings

The minimum setup is an empty table :
local boxes_settings={

This one draws a box with the default parameters : same size of the conky window, white background with opacity set to 50 %.

Parameters for positioning the box are x, y, w and h, relatives to the top-left corner of the Conky's window.
The same box as above but with a shorter height:
local boxes_settings={

Another box with the 4 parameters :
local boxes_settings={

It's quite easy to use more than one box :
local boxes_settings={

Tha Lua API for Conky can get some informations about Conky's window, for example, to get the window's height or width, use conky_window.height or conky_window.width in the Lua scripts :
local boxes_settings={

So, with the above code, when the Conky's window increases, the top box is still nice :

Each corner of a box can be set individually in a table {shape,radius}
The four corners are join into a table named corners:
corners={ {shape-top-left,radius-top-left},

Available shapes are : "curve", "circle" and "line"

If the corners table has less than 4 elements, the last element is used for missing elements:
Box with one corner defined:
{w=400,h=120, corners={ {"circle",40} } }

The boxes can be draw with only a border if the border parameter is greater than zero :

Some dashes can be added to the border if the dash parameter is greater than zero:

The box can be rotated around his top-left corner with the angle parameter :

The rotation can be arround the x axis or the y axis with the skew_x and skew_y parameters :

An interesting feature of Cairo is the compositing operator :
In this script, the compositing operator is called with the operator parameter, and the available arguments are "clear", "source", "over", "in", "out", "atop", "dest", "dest_over", "dest_in", "dest_out", "dest_atop", "xor", "add", "saturate". Default is "over".

NOTE : in your conkyrc, you need the parameter
own_window_argb_visual yes

Let's start with theses two boxes :
     {x=10,y=10,w=150,h=90, },
{x=50,y=20,w=60,h=150, corners={{ "curve",10}} },

for this output :

The "clear" operator :
     {x=10,y=10,w=150,h=90, },
{x=50,y=20,w=60,h=150, corners={{ "curve",10}} , operator="clear" },

The "out" operator :
     {x=10,y=10,w=150,h=90, },
{x=50,y=20,w=60,h=150, corners={{ "curve",10}} , operator="out" },

The "add" operator :
     {x=10,y=10,w=150,h=90, },
{x=50,y=20,w=60,h=150, corners={{ "curve",10}} , operator="add" },

and so on ...

Last part of the boxes are the colour, colours are set in the colour tables.
Each "table colour" contains one or more tables which defines the colours of a gradient :
P = position inside the linear gradient (0 = start of the gradient, 1= end of the gradient)
C = hexadecimal colour
A = alpha (opacity) of colour (0=invisible,1=opacity 100%)

For example, for a single colour table:

for a 2-colours table :

for a 3-colours table:

The output of this box :
     {x=10,y=10,w=250,h=90, colour={ {0,0xFFFF00,1} } }

is a yellow rectangle :

The output of this box
   {x=10,y=10,w=250,h=90, colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}  } }

is a yellow rectangle again because we haven't set up the gradient to use.

Gradients are defined with one of this parameters linear_gradient or radial_gradient

linear_gradient - table with the coordinates of two points to define a linear gradient,
points are relative to top-left corner of the box, (not the conky's window)
radial_gradient - table with the coordinates of two circle to define a radial gradient,
points are relative to top-left corner of the box, (not the conky window)
{x1,y1,r1,x2,y2,r2} (r=radius)

This code :
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
linear_gradient={0,0,0,100} }

gives this output:

This code :
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
linear_gradient={0,0,0,50} }

gives this output:

This code :
colour={ {0,0xFFFF00,1}, {0.5,0xFF0000,1}} ,
linear_gradient={0,0,0,50} }

gives this output:

This code, with a radial gradient :
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
radial_gradient={0,0,0,0,0,90} }

gives this output :

This code:
colour={ {0,0x0000FF,0}, {0.5,0x00FF00,1}, {1,0x0000FF,0},} ,
radial_gradient={0,5,0,0,5,300} }

gives this output :

Other parameters added recently, while writing this note :
scale_x and scale_y to rescale the box, useful for drawing elipses
draw_me : if set to false, box is not drawn (default = true or 1)
it can be used with a conky string, if the string returns 1, the box is drawn :
example : "${if_empty ${wireless_essid wlan0}}${else}1$endif",

Some examples of amazing setup :


operator="clear" }

for this output :

A button :
 local gradient = { {0,0x858585,1}, {1,0x000000,1}}
local table_settings={

linear_gradient= {0,0,0,200} },

corners={ {"circle",90} },
colour=gradient ,
linear_gradient= {180,180,10,10} },

corners={ {"circle",80} },
colour=gradient ,
linear_gradient= {20,20,160,160} },


for this output :

If you create some nice conkys with this widget, or any other, feel free to post a link in the comments ;-)

Thursday, January 13, 2011

Memory Leaks in Conky/Lua/Cairo

A user named "Creamy Goodness" of Nokia N900 phone uses conky on his phone and this phone does not have much memory. He looked at my scripts a little closer and gave me some pointers to reduce memory leaks :

1. Avoid global variables.
If you don't specify local in front of a variable, the variable is set to global. When conky runs the script, the global variable is set but it is not destroy at the end of the script (even if you set the variable to nil). At each loop a variable is created so memory usage increase !

But, it can be very interesting to have global variables with conky because each time a Lua script is called (ie at every update of the conky), the variable is still in memory and you can use it. This is the way I do when I draw graphs : the script start with an empty table and at each loop a new element popup the values in the table. Not very efficient but it works.

2. Declare variables in local
It's a good habit to declare variables in local, even in a local function, so they are destroyed when the script exits. I'm not sure but a local variable outside a function is OK too.

3. Don't forget "cairo_pattern_destroy"
When creating patterns, dont' forget to destroy them when the drawing is finished with cairo_pattern_destroy(pat) and of course destroy contexts and surfaces with cairo_destroy(cr) and cairo_surface_destroy(cs).

4. Use strict.lua to check use of undeclared variables
The script strict.lua can be found here or here or in the Lua5.1 package.
Creamy Godness made a modified version of this script to print a warning when it finds undeclared variables :
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
-- From Lua distribution (etc/strict.lua)

local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget

local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)

mt.__declared = {}

local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"

mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
print("assign to undeclared variable '"..n.."'")
mt.__declared[n] = true
rawset(t, n, v)

mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
print("variable '"..n.."' is not declared")
return rawget(t, n)

To use it, simply call the script in the script you want to check with
require 'strict'
if both scripts are in the same folder.

5. Workaround
You can use a cron job to run a script that will killall conky and restarts your conkys.

6. Further reading :
Lua documentation : Detecting Undefined Variables
The conky for the phone with the comments of Creamy Goodness, on Ubuntu Forums or on
Memory Leaks with Cairo and imlib2 on londonali's blog.