stable now, graphs might need some tweaks
[monky] / data / graph.lua
1 --[[ GRAPH widget v1.0 by wlourf (31.10.2010)
2         this widget draws some graphs with some effects 
3         http://u-scripts.blogspot.com/2010/10/graph-widget.html
4         
5 To call the script in a conky, use, before TEXT
6         lua_load /path/to/the/script/graph.lua
7         lua_draw_hook_pre main_graph
8 and add one line (blank or not) after TEXT
9
10
11 Parameters are :
12 3 parameters are mandatory
13 name            - the name of the conky variable to display,
14                           for example for {$cpu cpu0}, just write name="cpu"
15 arg                     - the argument of the above variable,
16                           for example for {$cpu cpu1}, just write arg="cpu1"
17                           arg can be a numerical value if name=""
18 max                     - the maximum value the above variable can reach,
19                           for example for {$cpu cpu1}, just write max=100 or less or more
20         
21 Optional parameters:
22 x,y             - coordinates of the bottom-left corner of the graph,
23               relative to the top-left corner of the conky window 
24                           default =  bottom-left corner of the conky window
25 width       - width of the graph, default = 100 pixels
26 height      - height of the graph, default = 20 pixels
27 nb_values   - number of values to display in the graph, default=width 
28               i.e. 1 pixel for 1 value
29 autoscale   - if set to true, calculate the max valeu of the y axis and
30               doesn't use the max parameter above, default=false
31 skew_x      - skew graph around x axis, défaut = 0
32 skew_y      - skew graph around y axis, défaut = 0
33 angle       - angle of rotation of the graph in degress, default = 0
34               i.e. a horizontal graph)
35 inverse     - if set to true, graph are draw from right to left, default=false
36 background  - if set to false, background is not drawn, default=true
37 foreground  - if set to false, foreground is not drawn, default=true
38               foreground = plain graph
39 bg_bd_size  - size of the border of the background, default=0=no border
40 fg_bd_size  - size of the border of the foreground, default=0=no border
41
42
43 Colours tables below are defined into braces :
44 {position in the gradient (0 to 1), colour in hexadecimal, alpha (0 to 1)}
45 example for a single colour table : 
46 {{0,0xFFAA00,1}} position parameter doesn't matter
47 example for a two-colours table : 
48 {{0,0xFFAA00,1},{1,0x00AA00,1}} or {{0.5,0xFFAA00,1},{1,0x00AA00,1}}
49 example for a three-colours table : 
50 {{0,0xFFAA00,1},{0.5,0xFF0000,1},{1,0x00AA00,1}}
51
52 bg_colour       - colour table for background,
53                           default = {{0,0x000000,.5},{1,0xFFFFFF,.5}}
54 fg_colour       - colour table for foreground,
55                           default = {{0,0x00FFFF,1},{1,0x0000FF,1}}
56 bg_bd_colour- colour table for background border,
57                           default = {{1,0xFFFFFF,1}}                      
58 fg_bd_colour- colour table for foreground border,
59                           default = {{1,0xFFFF00,1}}                      
60
61 bg_orientation, bg_bd_orientation, fg_orientation, fg_bd_orientation,
62                 - "orientation" defines the starting point of the gradient,
63                   default="nn"
64                           there are 8 available starting points : 
65                           "nw","nn","ne","ee","se","ss","sw","ww"
66                           (n for north, w for west ...)
67                           theses 8 points are the 4 corners + the 4 middles of graph
68                           so a gradient "nn" will go from "nn" to "ss"
69                           a gradient "nw" will go from "nw" to "se"
70
71
72 v1.0 (31 Oct. 2010) original release
73
74 --      This program is free software; you can redistribute it and/or modify
75 --      it under the terms of the GNU General Public License as published by
76 --      the Free Software Foundation version 3 (GPLv3)
77 --     
78 --      This program is distributed in the hope that it will be useful,
79 --      but WITHOUT ANY WARRANTY; without even the implied warranty of
80 --      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
81 --      GNU General Public License for more details.
82 --     
83 --      You should have received a copy of the GNU General Public License
84 --      along with this program; if not, write to the Free Software
85 --      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
86 --      MA 02110-1301, USA.
87
88 ]]
89
90 require 'cairo'
91
92 function set_settings()
93         graph_settings={
94             {
95                         name="cpu",
96                         arg="",
97                         max=100,
98                         y=87,
99                         x=5,
100                         autoscale=false,
101                         width=215,
102                         height=29,
103                         nb_values=120,
104                         fg_bd_size=2,
105                         bg_colour = {{0,0x007AF6,0},{1,0x000000,0}},
106                         --fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFFA209,1},{1,0xFFFF00,1}},
107                         fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFF3300,1},{1,0xFFFFFF,1}},
108                         --fg_colour = { {0,0xFF0000,1},{0.5,0xFFA209,1},{1,0xFFFF00,1}},
109                         fg_colour = { {0,0x00CCFF,0.7},{0.5,0x0000FF,0.7},{1,0x000000,0.1}},
110                         foreground=true,
111                         fg_orientation="ne",
112                         bg_bd_orientation="nn",
113                         bg_orientation="ne",
114                 },
115             {
116                         name="upspeedf",
117                         arg="wlan0",
118                         max=9999,
119                         y=233,
120                         x=5,
121                         autoscale=true,
122                         width=275,
123                         height=35,
124                         nb_values=120,
125                         fg_bd_size=1,
126                         bg_colour = {{0,0x007AF6,0},{1,0x000000,0}},
127                         fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFFA209,0.8},{1,0xFFFF00,0.6}},
128                         fg_colour = { {0,0xFF0000,0.65},{0.5,0xFFA209,0.5},{1,0xFFFF00,0.3}},
129                         foreground=true,
130                         inverse=true,
131                         DrawMe="${if_up wlan0}1${else}0$endif",
132                         bg_orientation="ww",
133             },
134             {
135                         name="downspeedf",
136                         arg="wlan0",
137                         max=9999,
138                         y=233,
139                         x=575,
140                         autoscale=true,
141                         width=275,
142                         height=35,
143                         nb_values=120,
144                         fg_bd_size=1,
145                         bg_colour = {{0,0x007AF6,0},{1,0x000000,0}},
146                         fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFFA209,0.8},{1,0xFFFF00,0.6}},
147                         fg_colour = { {0,0xFF0000,0.65},{0.5,0xFFA209,0.5},{1,0xFFFF00,0.3}},
148                         foreground=true,
149                         DrawMe="${if_up wlan0}1${else}0$endif",
150                         bg_orientation="ee",
151             },
152             {
153                         name="upspeedf",
154                         arg="gprs0",
155                         max=9999,
156                         y=233,
157                         x=5,
158                         autoscale=true,
159                         width=275,
160                         height=35,
161                         nb_values=120,
162                         fg_bd_size=1,
163                         bg_colour = {{0,0x007AF6,0},{1,0x000000,0}},
164                         fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFFA209,0.8},{1,0xFFFF00,0.6}},
165                         fg_colour = { {0,0xFF0000,0.65},{0.5,0xFFA209,0.5},{1,0xFFFF00,0.3}},
166                         foreground=true,
167                         inverse=true,
168                         DrawMe="${if_up gprs0}1${else}0$endif",
169                         bg_orientation="ww",
170             },
171             {
172                         name="downspeedf",
173                         arg="gprs0",
174                         max=9999,
175                         y=233,
176                         x=525,
177                         autoscale=true,
178                         width=275,
179                         height=35,
180                         nb_values=120,
181                         fg_bd_size=1,
182                         bg_colour = {{0,0x007AF6,0},{1,0x000000,0}},
183                         fg_bd_colour = { {0,0xFF0000,1},{0.5,0xFFA209,0.8},{1,0xFFFF00,0.6}},
184                         fg_colour = { {0,0xFF0000,0.65},{0.5,0xFFA209,0.5},{1,0xFFFF00,0.3}},
185                         foreground=true,
186                         DrawMe="${if_up gprs0}1${else}0$endif",
187                         bg_orientation="ee",
188             },
189             {
190                         name="loadavg",
191                         arg="1",
192                         max=20,
193                         y=95,
194                         x=367,
195                         autoscale=true,
196                         width=140,
197                         height=36,
198                         nb_values=100,
199                         fg_bd_size=0,
200                         bg_colour = {{0,0x667AF6,0},{1,0x000000,0}},
201                         fg_bd_colour =  { {0,0xdd0000,0.7},{0.5,0xdddd00,0.7},{1,0xFFFF00,0.7}},
202                         fg_colour = { {0,0x000000,0.7},{0.5,0xdd0000,0.7},{1,0xff0000,0.7}},
203                         foreground=true,
204             },
205             {
206                         name="to_bytes ${diskio",
207                         arg="mmcblk0}",
208                         max=2500000,
209                         y=183,
210                         x=349,
211                         autoscale=true,
212                         width=250,
213                         height=89,
214                         nb_values=125,
215                         fg_bd_size=0,
216                         bg_colour = {{0,0x667AF6,0},{1,0x000000,0}},
217                         fg_bd_colour =  { {0,0x111111,0.3},{0.5,0xcc6600,0.6},{1,0xcc6600,0.8}},
218                         fg_colour = { {1,0x111111,0.3},{0.5,0xcc6600,0.6},{0,0xcc6600,0.8}},
219                         foreground=true,
220                         bg_orientation="ne",
221                         fg_orientation="ee",
222             },
223             {
224                         name="battery_rate", --displays when discharging
225                         arg="",
226                         max=500,
227                         y=183,
228                         x=593,
229                         autoscale=true,
230                         width=250,
231                         height=85,
232                         nb_values=125,
233                         fg_bd_size=0,
234                         bg_colour = {{0,0x667AF6,0},{1,0x000000,0}},
235                         fg_bd_colour =  { {0,0xFF0000,1},{0.5,0xFF0000,1},{1,0xFF0000,1}},
236                         fg_colour = { {1,0xFF0000,0.3},{0.5,0xFF0000,0.6},{0,0xFF0000,0.8}},
237                         foreground=true,
238                         bg_orientation="ne",
239                         fg_orientation="ee",
240                         DrawMe="${if_match ${battery_rate} > 0}1${else}0$endif",
241             },
242             {
243                         name="battery_rate", --displays when charging
244                         arg="",
245                         max=500,
246                         y=183,
247                         x=593,
248                         autoscale=true,
249                         width=250,
250                         height=85,
251                         nb_values=125,
252                         fg_bd_size=0,
253                         bg_colour = {{0,0x667AF6,0},{1,0x000000,0}},
254                         fg_bd_colour =  { {0,0x00FF00,1},{0.5,0x00FF00,1},{1,0x00FF00,1}},
255                         fg_colour = { {1,0x00FF00,0.3},{0.5,0x00FF00,0.6},{0,0x00FF00,0.8}},
256                         foreground=true,
257                         bg_orientation="ne",
258                         fg_orientation="ee",
259                         DrawMe="${if_match ${battery_rate} < 0}1${else}0$endif",
260             },
261     }
262 end
263
264 function check_settings(t)
265     --tables are check only when conky start
266         if t.name==nil and t.arg==nil then 
267                 print ("No input values ... use parameters 'name'" .. 
268                         " with 'arg' or only parameter 'arg' ") 
269                 return 1
270         end
271         if t.max==nil then
272                 print ("No maximum value defined, use 'max'")
273                 print ("for name=" .. t.name .. " with arg=" .. t.arg)
274                 return 1
275         end
276         if t.name==nil then t.name="" end
277         if t.arg==nil then t.arg="" end
278         return 0
279 end
280
281 function conky_main_graph()
282     if conky_window == nil then return end
283     local w=conky_window.width
284     local h=conky_window.height
285     local cs=cairo_xlib_surface_create(conky_window.display, 
286                                         conky_window.drawable, conky_window.visual, w, h)
287     cr=cairo_create(cs)
288     updates=tonumber(conky_parse('${updates}'))
289     --start drawing after "updates_gap" updates
290     --prevent segmentation error for cpu
291     updates_gap=5
292     if updates==1 then    
293         set_settings()
294             flagOK=0
295                         for i in pairs(graph_settings) do
296                                 if graph_settings[i].width==nil then graph_settings[i].width=100 end
297         if graph_settings[i].nb_values==nil then  
298                 graph_settings[i].nb_values= graph_settings[i].width
299         end
300                         --create an empty table to store values
301                         graph_settings[i]["values"]={}
302                         --beginning point
303                         graph_settings[i].beg = graph_settings[i].nb_values
304                         --graph_settings[i].beg = 0
305                         for j=1, graph_settings[i].nb_values do
306                             graph_settings[i].values[j]=0
307                         end
308                   graph_settings[i].flag_init=true
309                   flagOK=flagOK + check_settings(graph_settings[i])
310                   end
311     end
312     if flagOK>0 then 
313         --abort script if error in one of the tables
314         print ("ERROR : Check the graph_setting table")
315         return
316     end
317     --drawing process
318     if updates > updates_gap then
319                 for i in pairs(graph_settings) do
320                         if (graph_settings[i].DrawMe==nil or conky_parse(graph_settings[i].DrawMe) == "1") then 
321                                 --print(graph_settings[i].DrawMe.."= '"..conky_parse(graph_settings[i].DrawMe).."'")
322                                 --return
323                          --cancel fetch value from conky if interface down or whatever
324                         local nb_values=graph_settings[i].nb_values
325                         graph_settings[i].automax=0
326                         for j =1, nb_values do
327                                 if graph_settings[i].values[j+1]==nil then 
328                                     graph_settings[i].values[j+1]=0 
329                                 end
330                                 graph_settings[i].values[j]=graph_settings[i].values[j+1]
331                                 if j==nb_values then
332                                         --store value
333                                         if graph_settings[i].name=="" then
334                                             value=graph_settings[i].arg
335                                         else
336                                         value=tonumber(conky_parse('${' .. 
337                                             graph_settings[i].name .. " " ..
338                                             graph_settings[i].arg ..'}'))
339                                         value=math.abs(value)
340                         end
341                                         graph_settings[i].values[nb_values]=value
342                                 end
343                                 graph_settings[i].automax=math.max(graph_settings[i].automax,
344                                                                    graph_settings[i].values[j])
345                                 if graph_settings[i].automax == 0 then graph_settings[i].automax = 1 end --should stop weird glitches at beginning when no values reported yet for upspeed or diskio
346                         end
347                         draw_graph(graph_settings[i])
348                         end
349                 end
350     end
351     cairo_destroy(cr)
352     cairo_surface_destroy(cs)
353         updates=nil
354         updates_gap=nil
355 end
356
357 function draw_graph(t)
358     --drawing function
359     local function rgb_to_r_g_b(colour)
360         return ((colour[2] / 0x10000) % 0x100) / 255., ((colour[2] / 0x100) % 0x100) / 255., (colour[2] % 0x100) / 255., colour[3]
361     end
362  
363         local function linear_orientation(o,w,h)
364             --set gradient for bg and bg border
365                 local p
366                 if o=="nn" then
367                         p={w/2,h,w/2,0}
368                 elseif o=="ne" then
369                         p={w,h,0,0}
370                 elseif o=="ww" then
371                         p={0,h/2,w,h/2}
372                 elseif o=="se" then
373                         p={w,0,0,h}
374                 elseif o=="ss" then
375                         p={w/2,0,w/2,h}
376                 elseif o=="ee" then
377                         p={w,h/2,0,h/2}         
378                 elseif o=="sw" then
379                         p={0,0,w,h}
380                 elseif o=="nw" then
381                         p={0,h,w,0}
382                 end
383                 return p
384         end
385
386         local function linear_orientation_inv(o,w,h)
387             --set gradient for fg and fg border
388                 local p
389                 if o=="ss" then
390                         p={w/2,h,w/2,0}
391                 elseif o=="sw" then
392                         p={w,h,0,0}
393                 elseif o=="ee" then
394                         p={0,h/2,w,h/2}
395                 elseif o=="nw" then
396                         p={w,0,0,h}
397                 elseif o=="nn" then
398                         p={w/2,0,w/2,h}
399                 elseif o=="ww" then
400                         p={w,h/2,0,h/2}         
401                 elseif o=="ne" then
402                         p={0,0,w,h}
403                 elseif o=="se" then
404                         p={0,h,w,0}
405                 end
406                 return p
407         end
408         if t.DrawMe~=nil and conky_parse(t.DrawMe) ~= "1" then 
409                 --print(t.DrawMe.."= '"..conky_parse(t.DrawMe).."'")
410                 return
411         --else
412         --      print(t.name)
413         end --cancel drawing for same reason as earlier 
414         --set default values
415         if t.height==nil        then t.height=20 end
416         --checked in previous part : width and nb_values
417         if t.background==nil    then t.background=true end
418         if t.bg_bd_size==nil    then t.bg_bd_size=0 end
419         if t.x==nil                 then t.x=t.bg_bd_size end
420         if t.y==nil                 then t.y=conky_window.height -t.bg_bd_size end
421         if t.bg_colour==nil     then t.bg_colour={{0,0x000000,.5},{1,0xFFFFFF,.5}} end
422         if t.bg_bd_colour==nil  then t.bg_bd_colour={{1,0xFFFFFF,1}} end
423         if t.foreground==nil    then t.foreground=true end
424         if t.fg_colour==nil     then t.fg_colour={{0,0x00FFFF,1},{1,0x0000FF,1}} end
425         if t.fg_bd_size==nil    then t.fg_bd_size=0 end 
426         if t.fg_bd_colour==nil  then t.fg_bd_colour={{1,0xFFFF00,1}} end
427         if t.autoscale==nil     then t.autoscale=false end
428         if t.inverse==nil       then t.inverse=false end
429         if t.angle==nil         then t.angle=0 end
430         if t.bg_bd_orientation==nil then t.bg_bd_orientation="nn" end
431         if t.bg_orientation==nil then t.bg_orientation="nn" end
432         if t.fg_bd_orientation==nil then t.fg_bd_orientation="nn" end
433         if t.fg_orientation==nil then t.fg_orientation="nn" end
434     --check colours tables
435         for i=1, #t.fg_colour do    
436         if #t.fg_colour[i]~=3 then 
437                 print ("error in fg_colour table")
438                 t.fg_colour[i]={1,0x0000FF,1} 
439         end
440     end
441         for i=1, #t.fg_bd_colour do    
442         if #t.fg_bd_colour[i]~=3 then 
443                 print ("error in fg_bd_colour table")
444                 t.fg_bd_colour[i]={1,0x00FF00,1} 
445         end
446     end
447         for i=1, #t.bg_colour do    
448         if #t.bg_colour[i]~=3 then 
449                 print ("error in background color table")
450                 t.bg_colour[i]={1,0xFFFFFF,0.5} 
451         end
452     end    
453         for i=1, #t.bg_bd_colour do    
454         if #t.bg_bd_colour[i]~=3 then 
455                 print ("error in background border color table")
456                 t.bg_bd_colour[i]={1,0xFFFFFF,1} 
457         end
458     end    
459     --calculate skew parameters if needed
460     if t.flag_init then
461             if t.skew_x == nil then 
462                     t.skew_x=0 
463             else
464                     t.skew_x = math.pi*t.skew_x/180     
465             end
466             if t.skew_y == nil then 
467                     t.skew_y=0
468             else
469                     t.skew_y = math.pi*t.skew_y/180     
470             end
471             t.flag_init=false
472         end
473     cairo_set_line_cap(cr,CAIRO_LINE_CAP_ROUND)
474     cairo_set_line_join(cr,CAIRO_LINE_JOIN_ROUND)
475     local matrix0 = cairo_matrix_t:create()
476         tolua.takeownership(matrix0)
477     cairo_save(cr)
478     cairo_matrix_init (matrix0, 1,t.skew_y,t.skew_x,1,0,0)
479     cairo_transform(cr,matrix0)
480         local ratio=t.width/t.nb_values
481         cairo_translate(cr,t.x,t.y)
482         cairo_rotate(cr,t.angle*math.pi/180)
483         cairo_scale(cr,1,-1)
484         --background
485         if t.background then
486             local pts=linear_orientation(t.bg_orientation,t.width,t.height)
487                 local pat = cairo_pattern_create_linear (pts[1],pts[2],pts[3],pts[4])
488                 for i=1, #t.bg_colour do
489                         --print ("i",i,t.colour[i][1], rgb_to_r_g_b(t.colour[i]))
490                     cairo_pattern_add_color_stop_rgba (pat, t.bg_colour[i][1], rgb_to_r_g_b(t.bg_colour[i]))
491                 end
492                 cairo_set_source (cr, pat)
493                 cairo_rectangle(cr,0,0,t.width,t.height)        
494                 cairo_fill(cr)
495                 cairo_pattern_destroy(pat)
496         end
497     --autoscale
498     cairo_save(cr)
499         if t.autoscale then
500                 t.max= t.automax*1.1
501         end
502     local scale_x = t.width/(t.nb_values-1)
503         local scale_y = t.height/t.max
504     --define first point of the graph
505         if updates-updates_gap <t.nb_values then 
506                 t.beg = t.beg - 1
507         --next line prevent segmentation error when conky window is redraw 
508                 --quicly when another window "fly" over it
509                 if t.beg<0 then t.beg=0 end
510         else
511                 t.beg=0
512         end
513     if t.inverse then cairo_scale(cr,-1,1)
514     cairo_translate(cr,-t.width,0) end
515         --graph foreground
516         if t.foreground then
517             local pts_fg=linear_orientation_inv(t.fg_orientation,t.width,t.height)
518             local pat = cairo_pattern_create_linear (pts_fg[1],pts_fg[2],pts_fg[3],pts_fg[4])
519                 for i=1,#t.fg_colour,1 do
520                         cairo_pattern_add_color_stop_rgba (pat, 1-t.fg_colour[i][1], rgb_to_r_g_b(t.fg_colour[i]))
521                 end
522                 cairo_set_source (cr, pat)
523                 cairo_move_to(cr,t.beg*scale_x,0)
524                 cairo_line_to(cr,t.beg*scale_x,t.values[t.beg+1]*scale_y)
525                 for i=t.beg, t.nb_values-1 do
526                         cairo_line_to(cr,i*scale_x,t.values[i+1]*scale_y)               
527                 end
528                 cairo_line_to(cr,(t.nb_values-1)*scale_x,0)
529                 cairo_close_path(cr)
530                 cairo_fill(cr)
531                 cairo_pattern_destroy(pat)
532         end
533         --graph_border
534         if t.fg_bd_size>0 then
535         local pts=linear_orientation_inv(t.fg_bd_orientation,t.width,t.height)
536                 local pat = cairo_pattern_create_linear (pts[1],pts[2],pts[3],pts[4])
537                 for i=1,#t.fg_bd_colour,1 do
538                         cairo_pattern_add_color_stop_rgba (pat, 1-t.fg_bd_colour[i][1], rgb_to_r_g_b(t.fg_bd_colour[i]))
539                 end
540                 cairo_set_source (cr, pat)
541                 cairo_move_to(cr,t.beg*scale_x,t.values[t.beg+1]*scale_y)
542                 for i=t.beg, t.nb_values-1 do
543                         cairo_line_to(cr,i*scale_x,t.values[i+1]*scale_y)               
544                 end
545                 cairo_set_line_width(cr,t.fg_bd_size)
546                 cairo_stroke(cr)
547                 cairo_pattern_destroy(pat)
548         end
549         cairo_restore(cr)
550         --background border
551         if t.bg_bd_size>0 then
552         local pts=linear_orientation(t.bg_bd_orientation,t.width,t.height)
553                 local pat = cairo_pattern_create_linear (pts[1],pts[2],pts[3],pts[4])
554                 for i=1, #t.bg_bd_colour do
555                         --print ("i",i,t.colour[i][1], rgb_to_r_g_b(t.colour[i]))
556                     cairo_pattern_add_color_stop_rgba (pat, t.bg_bd_colour[i][1], rgb_to_r_g_b(t.bg_bd_colour[i]))
557                 end
558                 cairo_set_source (cr, pat)
559                 cairo_rectangle(cr,0,0,t.width,t.height)        
560                 cairo_set_line_width(cr,t.bg_bd_size)
561                 cairo_stroke(cr)
562                 cairo_pattern_destroy(pat)
563         end     
564
565         cairo_restore(cr)
566
567 end
568