Some of my scripts for daily use on a linux desktop

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={
{h=130},
}



Another box with the 4 parameters :
local boxes_settings={
{x=3,y=35,h=95,w=85},
}




It's quite easy to use more than one box :
local boxes_settings={
{x=3,y=35,h=95,w=85},
{x=92,y=35,h=95,w=117},
}



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={
{x=3,h=25,w=conky_window.width-6},
{x=3,y=35,h=95,w=85},
{x=92,y=35,h=95,w=117},
}



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},
{shape-top-right,radius-top-right},
{shape-bottom-left,radius-bottom-left},
{shape-bottom-right,radius-bottom-right}
}

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 :
table={P,C,A}
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:
colour={{0,0x0000ff,1}}

for a 2-colours table :
colour={
{0,0x0000ff,1},
{1,0xff00ff,1}
}

for a 3-colours table:
colour={
{0,0x0000ff,1},
{0.5,0xff00ff,1},
{1,0xff00ff,1}
}


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)
{x1,y1,x2,y2}
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 :
     {x=10,y=10,w=250,h=100,
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
linear_gradient={0,0,0,100} }

gives this output:


This code :
     {x=10,y=10,w=250,h=100,
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
linear_gradient={0,0,0,50} }

gives this output:


This code :
     {x=10,y=10,w=250,h=100,
colour={ {0,0xFFFF00,1}, {0.5,0xFF0000,1}} ,
linear_gradient={0,0,0,50} }

gives this output:


This code, with a radial gradient :
     {x=10,y=10,w=250,h=90,
colour={ {0,0xFFFF00,1}, {1,0xFF0000,1}} ,
radial_gradient={0,0,0,0,0,90} }

gives this output :


This code:
     {x=10,y=110,w=300,h=10,
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 :
     {x=300,y=70,w=250,h=100,
linear_gradient={0,80,0,100},
colour={{0,0x000000,1},{0.5,0xFFFFFF,1}},
},

{x=200,y=70,w=100,h=100,
radial_gradient={100,100,0,100,100,20},
colour={{0.5,0xFFFFFF,1},{1,0x000000,1}},
},

{x=200,y=170,w=100,h=100,
linear_gradient={80,0,100,0},
colour={{0.5,0xFFFFFF,1},{0,0x000000,1}},
},
{x=200,y=270,w=100,h=100,
radial_gradient={100,0,0,100,0,20},
colour={{0.5,0xFFFFFF,1},{1,0x000000,1}},
},
{x=300,y=270,w=250,h=100,
linear_gradient={0,0,0,20},
colour={{1,0x000000,1},{0.5,0xFFFFFF,1}},
},
{x=290,y=160,h=120,
corners={{"circle",10}},
operator="clear" }

for this output :




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

{w=200,h=200,
colour=gradient,
linear_gradient= {0,0,0,200} },

{x=10,y=10,w=180,h=180,
corners={ {"circle",90} },
colour=gradient ,
linear_gradient= {180,180,10,10} },

{x=20,y=20,w=160,h=160,
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)
end

mt.__declared = {}

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

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.."'")
end
mt.__declared[n] = true
end
rawset(t, n, v)
end

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


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 Mameo.org
Memory Leaks with Cairo and imlib2 on londonali's blog.