0 | |
------------------------------------------------------------------------
|
1 | |
-- Bashets - use your shellscript's output in Awesome3 widgets
|
2 | |
--
|
3 | |
-- @author Anton Lobov <ahmad200512@yandex.ru>
|
4 | |
-- @copyright 2010 Anton Lobov
|
5 | |
-- @license GPLv2
|
6 | |
-- @release 0.3.1 for Awesome 3.4
|
7 | |
-- @todo Implement better timer scheduling if possible.
|
8 | |
-----------------------------------------------------------------------
|
9 | |
|
10 | |
-- Grab only needed enviroment
|
11 | |
local awful = require("awful")
|
12 | |
local string = string
|
13 | |
local io = io
|
14 | |
local table = table
|
15 | |
local pairs = pairs
|
16 | |
local timer = timer
|
17 | |
local type = type
|
18 | |
|
19 | |
--- Bashets module
|
20 | |
module("bashets")
|
21 | |
|
22 | |
-- Default paths
|
23 | |
local script_path = "/usr/share/awesome/bashets/"
|
24 | |
local tmp_folder = "/tmp/"
|
25 | |
|
26 | |
-- Utility functions table
|
27 | |
local util = {}
|
28 | |
|
29 | |
-- Timer data
|
30 | |
local timerdata = {}
|
31 | |
local timers = {}
|
32 | |
|
33 | |
-- Some default values
|
34 | |
local defaults = {}
|
35 | |
defaults.update_time = 1
|
36 | |
defaults.file_update_time = 2
|
37 | |
defaults.format_string = "$1"
|
38 | |
defaults.separator = " "
|
39 | |
defaults.field = "text"
|
40 | |
|
41 | |
widget_field = defaults.field
|
42 | |
|
43 | |
-- State variable
|
44 | |
local is_running = false
|
45 | |
|
46 | |
-- # Utility functions
|
47 | |
|
48 | |
--- Split string by separator into table
|
49 | |
-- @param str String to split
|
50 | |
-- @param sep Separator to use
|
51 | |
function util.split(str, sep)
|
52 | |
local parts = {} --parts array
|
53 | |
local first = 1
|
54 | |
local ostart, oend = string.find(str, sep, first, true) --regexp disabled search
|
55 | |
|
56 | |
while ostart do
|
57 | |
local part = string.sub(str, first, ostart - 1)
|
58 | |
table.insert(parts, part)
|
59 | |
first = oend + 1
|
60 | |
ostart, oend = string.find(str, sep, first, true)
|
61 | |
end
|
62 | |
|
63 | |
local part = string.sub(str, first)
|
64 | |
table.insert(parts, part)
|
65 | |
|
66 | |
return parts
|
67 | |
end
|
68 | |
|
69 | |
function util.tmpname(script)
|
70 | |
-- Replace all slashes with empty string so that /home/user1/script.sh
|
71 | |
-- and /home/user2/script.sh will have different temporary files
|
72 | |
local tmpname = string.gsub(script, '/', '')
|
73 | |
|
74 | |
-- Replace all spaces with dots so that "script.sh arg1"
|
75 | |
-- and "script.sh arg2" will have different temporary files
|
76 | |
tmpname = string.gsub(tmpname, '%s+', '.')
|
77 | |
|
78 | |
-- Generated script-parameter unique temporary file path
|
79 | |
local file = tmp_folder .. tmpname .. '.bashets.out'
|
80 | |
|
81 | |
return file
|
82 | |
end
|
83 | |
|
84 | |
function util.fullpath(script)
|
85 | |
if string.find(script, '^/') == nil then
|
86 | |
script = script_path .. script
|
87 | |
end
|
88 | |
|
89 | |
return script
|
90 | |
end
|
91 | |
|
92 | |
--- Execute a command and write it's output to temporary file
|
93 | |
-- @param script Script to execute
|
94 | |
-- @param file File for script output
|
95 | |
function util.execfile(script, file)
|
96 | |
-- Spawn command and redirect it's output to file
|
97 | |
awful.util.spawn_with_shell(script .. " > " .. file)
|
98 | |
end
|
99 | |
|
100 | |
--- Read temporary file to a table or string
|
101 | |
-- @param file File to be read
|
102 | |
-- @param israw If true, return raw string, not table
|
103 | |
function util.readfile(file, sep)
|
104 | |
local fh = io.input(file)
|
105 | |
local str = fh:read("*all");
|
106 | |
io.close(fh)
|
107 | |
|
108 | |
if sep == nil then
|
109 | |
return str
|
110 | |
else
|
111 | |
parts = util.split(str, sep);
|
112 | |
return parts
|
113 | |
end
|
114 | |
end
|
115 | |
|
116 | |
--- Read script output to a table or string
|
117 | |
-- @param script Script to execute
|
118 | |
-- @param israw If true, return raw string, not table
|
119 | |
function util.readshell(script, sep)
|
120 | |
local str = awful.util.pread(script)
|
121 | |
|
122 | |
if sep == nil then
|
123 | |
return str
|
124 | |
else
|
125 | |
parts = util.split(str, sep);
|
126 | |
return parts
|
127 | |
end
|
128 | |
end
|
129 | |
|
130 | |
--- Format script output with user defined format string
|
131 | |
-- @param output sep-separated string of values
|
132 | |
-- @param format Format string
|
133 | |
-- @param sep Separator of values in string
|
134 | |
function util.format(parts, format, sep)
|
135 | |
-- For each part with number "k" replace corresponding "$k" variable in format string
|
136 | |
for k,part in pairs(parts) do
|
137 | |
local part = string.gsub(part, "%%", "%1%1") --percent fix for next gsub (bug found in Wicked)
|
138 | |
part = awful.util.escape(part) --escape XML entities for correct Pango markup
|
139 | |
format = string.gsub(format, "$" .. k, part)
|
140 | |
end
|
141 | |
|
142 | |
return format
|
143 | |
end
|
144 | |
|
145 | |
--- Add function to corresponding timer object (Awesome >= 3.4 timer API)
|
146 | |
-- @param updtime Update time for widget, also dispatch time for timer
|
147 | |
-- @param func Function to dispatch
|
148 | |
function util.add_to_timings(updtime, func, force_new)
|
149 | |
local found = false
|
150 | |
|
151 | |
-- Search for an existing timer at the same period
|
152 | |
for k,tmr in pairs(timerdata) do
|
153 | |
if tmr[1] == updtime and (force_new == nil or force_new == false) then
|
154 | |
table.insert(timerdata[k][2], func)
|
155 | |
found = true
|
156 | |
end
|
157 | |
end
|
158 | |
|
159 | |
-- Add a new timer for period if not found
|
160 | |
if not found then
|
161 | |
table.insert(timerdata, {updtime, {func}})
|
162 | |
end
|
163 | |
end
|
164 | |
|
165 | |
--- Create timer table to define timers for multiple widget updates
|
166 | |
function util.create_timers_table()
|
167 | |
-- Parse table with timer data
|
168 | |
for _,tmr in pairs(timerdata) do
|
169 | |
-- Create timer for the period
|
170 | |
local t = timer {timeout = tmr[1]}
|
171 | |
-- Function to call all dispatched functions
|
172 | |
local f = function()
|
173 | |
for _, func in pairs(tmr[2]) do
|
174 | |
func()
|
175 | |
end
|
176 | |
end
|
177 | |
t:add_signal("timeout", f)
|
178 | |
table.insert(timers, t)
|
179 | |
end
|
180 | |
end
|
181 | |
|
182 | |
--- Update widget from values
|
183 | |
function util.update_widget(widget, values, format, field)
|
184 | |
field = field or "text"
|
185 | |
|
186 | |
if type(widget) == "table" or type(format) == "table" then
|
187 | |
if #widget ~= #format then
|
188 | |
io.stderr:write("E: bashets: widget table must have the same length with format string table")
|
189 | |
return
|
190 | |
end
|
191 | |
|
192 | |
for k, widg in pairs(widget) do
|
193 | |
if widg ~= nil then
|
194 | |
if type(values) == "table" then
|
195 | |
pvalues = util.format(values, format[k])
|
196 | |
else
|
197 | |
pvalues = values
|
198 | |
end
|
199 | |
|
200 | |
if field == "image" then
|
201 | |
pvalues = image(pvalues)
|
202 | |
end
|
203 | |
|
204 | |
if type(field) == "table" then
|
205 | |
if #widget ~= #field then
|
206 | |
io.stderr:write("E: bashets: fields table must have the same length with widget table");
|
207 | |
return
|
208 | |
end
|
209 | |
widg[field[k]] = pvalues
|
210 | |
else
|
211 | |
widg[field] = pvalues
|
212 | |
end
|
213 | |
end
|
214 | |
end
|
215 | |
else
|
216 | |
if widget ~= nil then
|
217 | |
if type(values) == "table" then
|
218 | |
pvalues = util.format(values, format)
|
219 | |
else
|
220 | |
pvalues = values
|
221 | |
end
|
222 | |
|
223 | |
if field == "image" then
|
224 | |
pvalues = image(util.format(values, format))
|
225 | |
end
|
226 | |
|
227 | |
widget[field] = pvalues
|
228 | |
end
|
229 | |
end
|
230 | |
end
|
231 | |
|
232 | |
-- # Setter functions
|
233 | |
|
234 | |
--- Set path for scripts
|
235 | |
-- @param path Path to set
|
236 | |
function set_script_path(path)
|
237 | |
script_path = path
|
238 | |
end
|
239 | |
|
240 | |
--- Set path for temporary files
|
241 | |
-- @param path Path to set
|
242 | |
function set_temporary_path(path)
|
243 | |
tmp_folder = path
|
244 | |
end
|
245 | |
|
246 | |
--- Set default values
|
247 | |
-- @param defs Table with defaults
|
248 | |
function set_defaults(defs)
|
249 | |
if type(defs) == "table" then
|
250 | |
if defs.update_time ~= nil then
|
251 | |
defaults.update_time = defs.update_time
|
252 | |
end
|
253 | |
if defs.file_update_time ~= nil then
|
254 | |
defaults.file_update_time = defs.file_update_time
|
255 | |
end
|
256 | |
if defs.format_string ~= nil then
|
257 | |
defaults.format_string = defs.format_string
|
258 | |
end
|
259 | |
|
260 | |
defaults.separator = defs.separator --now could be nil
|
261 | |
|
262 | |
if defs.widget_field ~= nil then
|
263 | |
defaults.field = defs.widget_field
|
264 | |
end
|
265 | |
end
|
266 | |
end
|
267 | |
|
268 | |
--- Set widget field for updates
|
269 | |
-- @param fieldstr String representing widget field
|
270 | |
function set_widget_field(fieldstr)
|
271 | |
widget_field = fieldstr
|
272 | |
end
|
273 | |
|
274 | |
|
275 | |
-- # Acting functions
|
276 | |
|
277 | |
--- Start widget updates
|
278 | |
function start()
|
279 | |
-- Create timers table if not initialized or empty
|
280 | |
if (not timers) or table.maxn(timers) == 0 then
|
281 | |
util.create_timers_table()
|
282 | |
end
|
283 | |
-- Start all timers
|
284 | |
for _, tmr in pairs(timers) do
|
285 | |
tmr:start()
|
286 | |
end
|
287 | |
is_running = true
|
288 | |
end
|
289 | |
|
290 | |
--- Stop widget updates
|
291 | |
function stop()
|
292 | |
-- Stop all timers
|
293 | |
for _, tmr in pairs(timers) do
|
294 | |
tmr:stop()
|
295 | |
end
|
296 | |
is_running = false
|
297 | |
end
|
298 | |
|
299 | |
--- Check whether updates are running
|
300 | |
function get_running()
|
301 | |
return is_running
|
302 | |
end
|
303 | |
|
304 | |
--- Toggle updates
|
305 | |
function toggle()
|
306 | |
if is_running then
|
307 | |
start()
|
308 | |
else
|
309 | |
stop()
|
310 | |
end
|
311 | |
end
|
312 | |
|
313 | |
--- Shedule function for timed execution
|
314 | |
-- @param func Function to run
|
315 | |
-- @param updatime Update time (optional)
|
316 | |
function schedule(func, updtime)
|
317 | |
updtime = updtime or defaults.update_time
|
318 | |
if func ~= nil then
|
319 | |
util.add_to_timings(updtime, func)
|
320 | |
end
|
321 | |
end
|
322 | |
|
323 | |
-- # Widget registration functions
|
324 | |
|
325 | |
--- Register script for text widget
|
326 | |
-- @param widget Widget to update
|
327 | |
-- @param script Script to use it's output
|
328 | |
-- @param format User-defined format string (optional)
|
329 | |
-- @param updtime Update time in seconds (optional)
|
330 | |
-- @param sep Output separator (optional)
|
331 | |
function register(widget, script, format, updtime, sep, field)
|
332 | |
-- Set optional variables
|
333 | |
updtime = updtime or defaults.update_time
|
334 | |
format = format or defaults.format_string
|
335 | |
sep = sep or defaults.separator
|
336 | |
|
337 | |
script = util.fullpath(script)
|
338 | |
|
339 | |
-- Do it first time
|
340 | |
local data = util.readshell(script)
|
341 | |
util.update_widget(widget, data, format, field)
|
342 | |
|
343 | |
-- Schedule it for timed execution
|
344 | |
schedule(function()
|
345 | |
local data = util.readshell(script, sep)
|
346 | |
util.update_widget(widget, data, format, field)
|
347 | |
end, updtime)
|
348 | |
end
|
349 | |
|
350 | |
--- Register script for widget's widget_field throughout the temporary file
|
351 | |
-- @param widget Widget to update
|
352 | |
-- @param script Script to use it's output
|
353 | |
-- @param format User-defined format string (optional)
|
354 | |
-- @param time1 File update time in seconds (optional)
|
355 | |
-- @param time2 Widget update time in seconds (optional)
|
356 | |
-- @param sep Output separator (optional)
|
357 | |
function register_async(widget, script, format, time1, time2, sep, field)
|
358 | |
-- Set optional variables
|
359 | |
time1 = time1 or defaults.file_update_time
|
360 | |
time2 = time2 or defaults.update_time
|
361 | |
format = format or defaults.format_string
|
362 | |
sep = sep or defaults.separator
|
363 | |
|
364 | |
script = util.fullpath(script)
|
365 | |
local tmpfile = util.tmpname(script)
|
366 | |
|
367 | |
-- Create temporary file if not exists
|
368 | |
fl = io.open(tmpfile, "w")
|
369 | |
io.close(fl)
|
370 | |
|
371 | |
-- Do it first time
|
372 | |
util.execfile(script, tmpfile)
|
373 | |
local data = util.readfile(tmpfile)
|
374 | |
util.update_widget(widget, data, format, field)
|
375 | |
|
376 | |
-- Schedule it for timed execution
|
377 | |
schedule(function() util.execfile(script, tmpfile) end, time1)
|
378 | |
schedule(function()
|
379 | |
local data = util.readfile(tmpfile, sep)
|
380 | |
util.update_widget(widget, data, format, field)
|
381 | |
end, time2)
|
382 | |
end
|
383 | |
|
384 | |
--- Register text file for text widget
|
385 | |
-- @param widget Widget to update
|
386 | |
-- @param file File to use as data source
|
387 | |
-- @param format Format string (optional)
|
388 | |
-- @param time Update time (optional)
|
389 | |
-- @param sep Separator (optional)
|
390 | |
function register_file(widget, file, format, time, sep, field)
|
391 | |
-- Set optional variables
|
392 | |
time = time or defaults.update_time
|
393 | |
format = format or defaults.format_string
|
394 | |
sep = sep or defaults.separator
|
395 | |
|
396 | |
-- Do it first time
|
397 | |
local data = util.readfile(file, sep)
|
398 | |
util.update_widget(widget, data, format, field)
|
399 | |
|
400 | |
-- Schedule it for timed execution
|
401 | |
schedule(function()
|
402 | |
local data = util.readfile(file, sep)
|
403 | |
util.update_widget(widget, data, format, field)
|
404 | |
end, time)
|
405 | |
end
|
406 | |
|
407 | |
--- Register Lua function for text widget
|
408 | |
-- @param widget Widget to update
|
409 | |
-- @param func Function to return variables table
|
410 | |
-- @param format Format string (optional)
|
411 | |
-- @param time Update time (optional)
|
412 | |
function register_lua(widget, func, format, time)
|
413 | |
-- Set optional variables
|
414 | |
time = time or defaults.update_time
|
415 | |
format = format or defaults.format_string
|
416 | |
sep = sep or defaults.separator
|
417 | |
|
418 | |
-- Do it first time
|
419 | |
local data = func()
|
420 | |
util.update_widget(widget, data, format, field)
|
421 | |
|
422 | |
-- Schedule it for timed execution
|
423 | |
schedule(function()
|
424 | |
local data = func()
|
425 | |
util.update_widget(widget, data, format, field)
|
426 | |
end, time)
|
427 | |
end
|