record widgets in a table, remove from /rbar entries that we don't have widget object...
[wmiirc-lua.git] / core / wmii.lua
blob422d5921b688c23512246c6d3fd953c291595217
1 --
2 -- Copyright (c) 2007, Bart Trojanowski <bart@jukie.net>
3 --
4 -- WMII event loop, in lua
5 --
6 -- git://www.jukie.net/wmiirc-lua.git/
7 --
9 -- ========================================================================
10 -- DOCUMENTATION
11 -- ========================================================================
12 --[[
13 =pod
15 =head1 NAME
17 wmii.lua - WMII event-loop methods in lua
19 =head1 SYNOPSIS
21 require "wmii"
23 -- Write something to the wmii filesystem, in this case a key event.
24 wmii.write ("/event", "Key Mod1-j")
26 -- Set your wmii /ctl parameters
27 wmii.set_ctl({
28 font = '....'
31 -- Configure wmii.lua parameters
32 wmii.set_conf ({
33 xterm = 'x-terminal-emulator'
36 -- Now start the event loop
37 wmii.run_event_loop()
39 =head1 DESCRIPTION
41 wmii.lua provides methods for replacing the stock sh-based wmiirc shipped with
42 wmii 3.6 and newer with a lua-based event loop.
44 It should be used by your wmiirc
46 =head1 METHODS
48 =over 4
50 =cut
51 --]]
53 -- ========================================================================
54 -- MODULE SETUP
55 -- ========================================================================
57 local wmiirc = os.getenv("HOME") .. "/.wmii-3.5/wmiirc"
59 package.path = package.path
60 .. ";" .. os.getenv("HOME") .. "/.wmii-3.5/plugins/?.lua"
61 package.cpath = package.cpath
62 .. ";" .. os.getenv("HOME") .. "/.wmii-3.5/core/?.so"
63 .. ";" .. os.getenv("HOME") .. "/.wmii-3.5/plugins/?.so"
65 local ixp =require "ixp"
66 local eventloop = require "eventloop"
68 local base = _G
69 local io = require("io")
70 local os = require("os")
71 local posix = require("posix")
72 local string = require("string")
73 local table = require("table")
74 local math = require("math")
75 local type = type
76 local error = error
77 local print = print
78 local pairs = pairs
79 local tostring = tostring
80 local tonumber = tonumber
81 local setmetatable = setmetatable
83 module("wmii")
85 -- get the process id
86 local mypid = posix.getprocessid("pid")
88 -- ========================================================================
89 -- MODULE VARIABLES
90 -- ========================================================================
92 -- wmiir points to the wmiir executable
93 -- TODO: need to make sure that wmiir is in path, and if not find it
94 local wmiir = "wmiir"
96 -- wmii_adr is the address we use when connecting using ixp
97 local wmii_adr = os.getenv("WMII_ADDRESS")
98 or ("unix!/tmp/ns." .. os.getenv("USER") .. "."
99 .. os.getenv("DISPLAY"):match("(:%d+)") .. "/wmii")
101 -- wmixp is the ixp context we use to talk to wmii
102 local wmixp = ixp.new(wmii_adr)
104 -- history of previous views, view_hist[#view_hist] is the last one
105 local view_hist = {} -- sorted with 1 being the oldest
106 local view_hist_max = 10 -- max number to keep track of
108 -- allow for a client to be forced to a tag
109 local next_client_goes_to_tag = nil
111 -- ========================================================================
112 -- LOCAL HELPERS
113 -- ========================================================================
115 --[[
116 =pod
118 =item log ( str )
120 Log the message provided in C<str>
122 Currently just writes to io.stderr
124 =cut
125 --]]
126 function log (str)
127 io.stderr:write (str .. "\n")
130 -- ========================================================================
131 -- MAIN ACCESS FUNCTIONS
132 -- ========================================================================
134 --[[
135 =pod
137 =item ls ( dir, fmt )
139 List the wmii filesystem directory provided in C<dir>, in the format specified
140 by C<fmt>.
142 Returns an iterator of TODO
144 =cut
145 --]]
146 function ls (dir, fmt)
147 local verbose = fmt and fmt:match("l")
149 local s = wmixp:stat(dir)
150 if not s then
151 return function () return nil end
153 if s.modestr:match("^[^d]") then
154 return function ()
155 return stat2str(verbose, s)
159 local itr = wmixp:idir (dir)
160 if not itr then
161 --return function ()
162 return nil
163 --end
167 return function ()
168 local s = itr()
169 if s then
170 return stat2str(verbose, s)
172 return nil
176 local function stat2str(verbose, stat)
177 if verbose then
178 return string.format("%s %s %s %5d %s %s", stat.modestr, stat.uid, stat.gid, stat.length, stat.timestr, stat.name)
179 else
180 if stat.modestr:match("^d") then
181 return stat.name .. "/"
182 else
183 return stat.name
188 -- ------------------------------------------------------------------------
189 -- read all contents of a wmii virtual file
190 function read (file)
191 return wmixp:read (file)
194 -- ------------------------------------------------------------------------
195 -- return an iterator which walks all the lines in the file
197 -- example:
198 -- for event in wmii.iread("/event")
199 -- ...
200 -- end
201 function iread (file)
202 return wmixp:iread(file)
205 -- ------------------------------------------------------------------------
206 -- create a wmii file, optionally write data to it
207 function create (file, data)
208 wmixp:create(file, data)
211 -- ------------------------------------------------------------------------
212 -- remove a wmii file
213 function remove (file)
214 wmixp:remove(file)
217 -- ------------------------------------------------------------------------
218 -- write a value to a wmii virtual file system
219 function write (file, value)
220 wmixp:write (file, value)
223 -- ------------------------------------------------------------------------
224 -- setup a table describing dmenu command
225 local function dmenu_cmd (prompt)
226 local cmdt = { "dmenu", "-b" }
227 local fn = get_ctl("font")
228 if fn then
229 cmdt[#cmdt+1] = "-fn"
230 cmdt[#cmdt+1] = fn
232 local normcolors = get_ctl("normcolors")
233 if normcolors then
234 local nf, nb = normcolors:match("(#%x+)%s+(#%x+)%s#%x+")
235 if nf then
236 cmdt[#cmdt+1] = "-nf"
237 cmdt[#cmdt+1] = "'" .. nf .. "'"
239 if nb then
240 cmdt[#cmdt+1] = "-nb"
241 cmdt[#cmdt+1] = "'" .. nb .. "'"
244 local focuscolors = get_ctl("focuscolors")
245 if focuscolors then
246 local sf, sb = focuscolors:match("(#%x+)%s+(#%x+)%s#%x+")
247 if sf then
248 cmdt[#cmdt+1] = "-sf"
249 cmdt[#cmdt+1] = "'" .. sf .. "'"
251 if sb then
252 cmdt[#cmdt+1] = "-sb"
253 cmdt[#cmdt+1] = "'" .. sb .. "'"
256 if prompt then
257 cmdt[#cmdt+1] = "-p"
258 cmdt[#cmdt+1] = "'" .. prompt .. "'"
261 return cmdt
264 -- ------------------------------------------------------------------------
265 -- displays the menu given an table of entires, returns selected text
266 function menu (tbl, prompt)
267 local dmenu = dmenu_cmd(prompt)
269 local infile = os.tmpname()
270 local fh = io.open (infile, "w+")
272 local i,v
273 for i,v in pairs(tbl) do
274 if type(i) == 'number' and type(v) == 'string' then
275 fh:write (v)
276 else
277 fh:write (i)
279 fh:write ("\n")
281 fh:close()
283 local outfile = os.tmpname()
285 dmenu[#dmenu+1] = "<"
286 dmenu[#dmenu+1] = infile
287 dmenu[#dmenu+1] = ">"
288 dmenu[#dmenu+1] = outfile
290 local cmd = table.concat(dmenu," ")
291 os.execute (cmd)
293 fh = io.open (outfile, "r")
294 os.remove (outfile)
296 local sel = fh:read("*l")
297 fh:close()
299 return sel
302 -- ------------------------------------------------------------------------
303 -- displays the a tag selection menu, returns selected tag
304 function tag_menu ()
305 local tags = get_tags()
307 return menu(tags, "tag:")
310 -- ------------------------------------------------------------------------
311 -- displays the a program menu, returns selected program
312 function prog_menu ()
313 local dmenu = dmenu_cmd("cmd:")
315 local outfile = os.tmpname()
317 dmenu[#dmenu+1] = ">"
318 dmenu[#dmenu+1] = outfile
320 local cmd = "dmenu_path |" .. table.concat(dmenu," ")
321 os.execute (cmd)
323 local fh = io.open (outfile, "rb")
324 os.remove (outfile)
326 local prog = fh:read("*l")
327 io.close (fh)
329 return prog
332 -- ------------------------------------------------------------------------
333 -- displays the a program menu, returns selected program
334 function get_tags()
335 local t = {}
336 local s
337 for s in wmixp:idir ("/tag") do
338 if s.name and not (s.name == "sel") then
339 t[#t + 1] = s.name
342 table.sort(t)
343 return t
346 -- ------------------------------------------------------------------------
347 -- displays the a program menu, returns selected program
348 function get_view()
349 local v = wmixp:read("/ctl") or ""
350 return v:match("view%s+(%S+)")
353 -- ------------------------------------------------------------------------
354 -- changes the current view to the name given
355 function set_view(sel)
356 local cur = get_view()
357 local all = get_tags()
359 if #all < 2 or sel == cur then
360 -- nothing to do if we have less then 2 tags
361 return
364 if not (type(sel) == "string") then
365 error ("string argument expected")
368 -- set new view
369 write ("/ctl", "view " .. sel)
372 -- ------------------------------------------------------------------------
373 -- changes the current view to the index given
374 function set_view_index(sel)
375 local cur = get_view()
376 local all = get_tags()
378 if #all < 2 then
379 -- nothing to do if we have less then 2 tags
380 return
383 local num = tonumber (sel)
384 if not num then
385 error ("number argument expected")
388 local name = all[sel]
389 if not name or name == cur then
390 return
393 -- set new view
394 write ("/ctl", "view " .. name)
397 -- ------------------------------------------------------------------------
398 -- chnages to current view by offset given
399 function set_view_ofs(jump)
400 local cur = get_view()
401 local all = get_tags()
403 if #all < 2 then
404 -- nothing to do if we have less then 2 tags
405 return
408 -- range check
409 if (jump < - #all) or (jump > #all) then
410 error ("view selector is out of range")
413 -- find the one that's selected index
414 local curi = nil
415 local i,v
416 for i,v in pairs (all) do
417 if v == cur then curi = i end
420 -- adjust by index
421 local newi = math.fmod(#all + curi + jump - 1, #all) + 1
422 if (newi < - #all) or (newi > #all) then
423 error ("error computng new view")
426 write ("/ctl", "view " .. all[newi])
429 -- ------------------------------------------------------------------------
430 -- toggle between last view and current view
431 function toggle_view()
432 local last = view_hist[#view_hist]
433 if last then
434 set_view(last)
438 -- ========================================================================
439 -- ACTION HANDLERS
440 -- ========================================================================
442 local action_handlers = {
443 quit = function ()
444 write ("/ctl", "quit")
445 end,
447 exec = function (act, args)
448 local what = args or wmiirc
449 write ("/ctl", "exec " .. what)
450 end,
452 wmiirc = function ()
453 posix.exec ("lua", wmiirc)
454 end,
456 rehash = function ()
457 -- TODO: consider storing list of executables around, and
458 -- this will then reinitialize that list
459 log (" TODO: rehash")
460 end,
462 status = function ()
463 -- TODO: this should eventually update something on the /rbar
464 log (" TODO: status")
468 -- ========================================================================
469 -- KEY HANDLERS
470 -- ========================================================================
472 local key_handlers = {
473 ["*"] = function (key)
474 log ("*: " .. key)
475 end,
477 -- execution and actions
478 ["Mod1-Return"] = function (key)
479 local xterm = get_conf("xterm") or "xterm"
480 log (" executing: " .. xterm)
481 os.execute (xterm .. " &")
482 end,
483 ["Mod1-Shift-Return"] = function (key)
484 local tag = tag_menu()
485 if tag then
486 local xterm = get_conf("xterm") or "xterm"
487 log (" executing: " .. xterm .. " on: " .. tag)
488 next_client_goes_to_tag = tag
489 os.execute (xterm .. " &")
491 end,
492 ["Mod1-a"] = function (key)
493 local text = menu(action_handlers, "action:")
494 if text then
495 local act = text
496 local args = nil
497 local si = text:find("%s")
498 if si then
499 act,args = string.match(text .. " ", "(%w+)%s(.+)")
501 if act then
502 local fn = action_handlers[act]
503 if fn then
504 fn (act,args)
508 end,
509 ["Mod1-p"] = function (key)
510 local prog = prog_menu()
511 if prog then
512 log (" executing: " .. prog)
513 os.execute (prog .. " &")
515 end,
516 ["Mod1-Shift-p"] = function (key)
517 local tag = tag_menu()
518 if tag then
519 local prog = prog_menu()
520 if prog then
521 log (" executing: " .. prog .. " on: " .. tag)
522 next_client_goes_to_tag = tag
523 os.execute (prog .. " &")
526 end,
527 ["Mod1-Shift-c"] = function (key)
528 write ("/client/sel/ctl", "kill")
529 end,
531 -- HJKL active selection
532 ["Mod1-h"] = function (key)
533 write ("/tag/sel/ctl", "select left")
534 end,
535 ["Mod1-l"] = function (key)
536 write ("/tag/sel/ctl", "select right")
537 end,
538 ["Mod1-j"] = function (key)
539 write ("/tag/sel/ctl", "select down")
540 end,
541 ["Mod1-k"] = function (key)
542 write ("/tag/sel/ctl", "select up")
543 end,
545 -- HJKL movement
546 ["Mod1-Shift-h"] = function (key)
547 write ("/tag/sel/ctl", "send sel left")
548 end,
549 ["Mod1-Shift-l"] = function (key)
550 write ("/tag/sel/ctl", "send sel right")
551 end,
552 ["Mod1-Shift-j"] = function (key)
553 write ("/tag/sel/ctl", "send sel down")
554 end,
555 ["Mod1-Shift-k"] = function (key)
556 write ("/tag/sel/ctl", "send sel up")
557 end,
559 -- floating vs tiled
560 ["Mod1-space"] = function (key)
561 write ("/tag/sel/ctl", "select toggle")
562 end,
563 ["Mod1-Shift-space"] = function (key)
564 write ("/tag/sel/ctl", "send sel toggle")
565 end,
567 -- work spaces
568 ["Mod4-#"] = function (key, num)
569 set_view_index (num)
570 end,
571 ["Mod4-Shift-#"] = function (key, num)
572 write ("/client/sel/tags", tostring(num))
573 end,
574 ["Mod1-comma"] = function (key)
575 set_view_ofs (-1)
576 end,
577 ["Mod1-period"] = function (key)
578 set_view_ofs (1)
579 end,
580 ["Mod1-r"] = function (key)
581 -- got to the last view
582 toggle_view()
583 end,
585 -- switching views and retagging
586 ["Mod1-t"] = function (key)
587 -- got to a view
588 local tag = tag_menu()
589 if tag then
590 set_view (tag)
592 end,
593 ["Mod1-Shift-t"] = function (key)
594 -- move selected client to a tag
595 local tag = tag_menu()
596 if tag then
597 write ("/client/sel/tags", tag)
599 end,
600 ["Mod1-Shift-r"] = function (key)
601 -- move selected client to a tag, and follow
602 local tag = tag_menu()
603 if tag then
604 write ("/client/sel/tags", tag)
605 set_view(tag)
607 end,
608 ["Mod1-Control-t"] = function (key)
609 log (" TODO: Mod1-Control-t: " .. key)
610 end,
612 -- column modes
613 ["Mod1-d"] = function (key)
614 write("/tag/sel/ctl", "colmode sel default")
615 end,
616 ["Mod1-s"] = function (key)
617 write("/tag/sel/ctl", "colmode sel stack")
618 end,
619 ["Mod1-m"] = function (key)
620 write("/tag/sel/ctl", "colmode sel max")
624 -- ------------------------------------------------------------------------
625 -- update the /keys wmii file with the list of all handlers
626 function update_active_keys ()
627 local t = {}
628 local x, y
629 for x,y in pairs(key_handlers) do
630 if x:find("%w") then
631 local i = x:find("#")
632 if i then
633 local j
634 for j=0,9 do
635 t[#t + 1]
636 = x:sub(1,i-1) .. j
638 else
639 t[#t + 1]
640 = tostring(x)
644 local all_keys = table.concat(t, "\n")
645 --log ("setting /keys to...\n" .. all_keys .. "\n");
646 write ("/keys", all_keys)
649 -- ------------------------------------------------------------------------
650 -- update the /lbar wmii file with the current tags
651 function update_displayed_tags ()
652 -- colours for /lbar
653 local fc = get_ctl("focuscolors") or ""
654 local nc = get_ctl("normcolors") or ""
656 -- build up a table of existing tags in the /lbar
657 local old = {}
658 local s
659 for s in wmixp:idir ("/lbar") do
660 old[s.name] = 1
663 -- for all actual tags in use create any entries in /lbar we don't have
664 -- clear the old table entries if we have them
665 local cur = get_view()
666 local all = get_tags()
667 local i,v
668 for i,v in pairs(all) do
669 local color = nc
670 if cur == v then
671 color = fc
673 if not old[v] then
674 create ("/lbar/" .. v, color .. " " .. v)
676 write ("/lbar/" .. v, color .. " " .. v)
677 old[v] = nil
680 -- anything left in the old table should be removed now
681 for i,v in pairs(old) do
682 if v then
683 remove("/lbar/"..i)
688 -- ========================================================================
689 -- EVENT HANDLERS
690 -- ========================================================================
692 local ev_handlers = {
693 ["*"] = function (ev, arg)
694 log ("ev: " .. tostring(ev) .. " - " .. tostring(arg))
695 end,
697 -- process timer events
698 ProcessTimerEvents = function (ev, arg)
699 process_timers()
700 end,
702 -- exit if another wmiirc started up
703 Start = function (ev, arg)
704 if arg then
705 if arg == "wmiirc" then
706 -- backwards compatibility with bash version
707 os.exit (0)
708 else
709 -- ignore if it came from us
710 local pid = string.match(arg, "wmiirc (%d+)")
711 if pid then
712 local pid = tonumber (pid)
713 if not (pid == mypid) then
714 os.exit (0)
719 end,
721 -- tag management
722 CreateTag = function (ev, arg)
723 local nc = get_ctl("normcolors") or ""
724 create ("/lbar/" .. arg, nc .. " " .. arg)
725 end,
726 DestroyTag = function (ev, arg)
727 remove ("/lbar/" .. arg)
728 end,
730 FocusTag = function (ev, arg)
731 local fc = get_ctl("focuscolors") or ""
732 create ("/lbar/" .. arg, fc .. " " .. arg)
733 write ("/lbar/" .. arg, fc .. " " .. arg)
734 end,
735 UnfocusTag = function (ev, arg)
736 local nc = get_ctl("normcolors") or ""
737 create ("/lbar/" .. arg, nc .. " " .. arg)
738 write ("/lbar/" .. arg, nc .. " " .. arg)
740 -- don't duplicate the last entry
741 if not (arg == view_hist[#view_hist]) then
742 view_hist[#view_hist+1] = arg
744 -- limit to view_hist_max
745 if #view_hist > view_hist_max then
746 table.remove(view_hist, 1)
749 end,
751 -- key event handling
752 Key = function (ev, arg)
753 log ("Key: " .. arg)
754 local num = nil
755 -- can we find an exact match?
756 local fn = key_handlers[arg]
757 if not fn then
758 local key = arg:gsub("-%d+", "-#")
759 -- can we find a match with a # wild card for the number
760 fn = key_handlers[key]
761 if fn then
762 -- convert the trailing number to a number
763 num = tonumber(arg:match("-(%d+)"))
764 else
765 -- everything else failed, try default match
766 fn = key_handlers["*"]
769 if fn then
770 fn (arg, num)
772 end,
774 -- mouse handling on the lbar
775 LeftBarClick = function (ev, arg)
776 local button,tag = string.match(arg, "(%w+)%s+(%w+)")
777 set_view (tag)
778 end,
780 -- focus updates
781 ClientFocus = function (ev, arg)
782 log ("ClientFocus: " .. arg)
783 end,
784 ColumnFocus = function (ev, arg)
785 log ("ColumnFocus: " .. arg)
786 end,
788 -- client handling
789 CreateClient = function (ev, arg)
790 if next_client_goes_to_tag then
791 local tag = next_client_goes_to_tag
792 local cli = arg
793 next_client_goes_to_tag = nil
794 write ("/client/" .. cli .. "/tags", tag)
795 set_view(tag)
797 end,
799 -- urgent tag?
800 UrgentTag = function (ev, arg)
801 log ("UrgentTag: " .. arg)
802 -- wmiir xwrite "/lbar/$@" "*$@"
803 end,
804 NotUrgentTag = function (ev, arg)
805 log ("NotUrgentTag: " .. arg)
806 -- wmiir xwrite "/lbar/$@" "$@"
811 -- ========================================================================
812 -- MAIN INTERFACE FUNCTIONS
813 -- ========================================================================
815 local config = {
816 xterm = 'x-terminal-emulator'
819 -- ------------------------------------------------------------------------
820 -- write configuration to /ctl wmii file
821 -- wmii.set_ctl({ "var" = "val", ...})
822 -- wmii.set_ctl("var, "val")
823 function set_ctl (first,second)
824 if type(first) == "table" and second == nil then
825 local x, y
826 for x, y in pairs(first) do
827 write ("/ctl", x .. " " .. y)
830 elseif type(first) == "string" and type(second) == "string" then
831 write ("/ctl", first .. " " .. second)
833 else
834 error ("expecting a table or two string arguments")
838 -- ------------------------------------------------------------------------
839 -- read a value from /ctl wmii file
840 function get_ctl (name)
841 local s
842 for s in iread("/ctl") do
843 local var,val = s:match("(%w+)%s+(.+)")
844 if var == name then
845 return val
848 return nil
851 -- ------------------------------------------------------------------------
852 -- set an internal wmiirc.lua variable
853 -- wmii.set_conf({ "var" = "val", ...})
854 -- wmii.set_conf("var, "val")
855 function set_conf (first,second)
856 if type(first) == "table" and second == nil then
857 local x, y
858 for x, y in pairs(first) do
859 config[x] = y
862 elseif type(first) == "string"
863 and (type(second) == "string"
864 or type(second) == "number") then
865 config[first] = second
867 else
868 error ("expecting a table, or string and string/number as arguments")
872 -- ------------------------------------------------------------------------
873 -- read an internal wmiirc.lua variable
874 function get_conf (name)
875 return config[name]
878 -- ========================================================================
879 -- THE EVENT LOOP
880 -- ========================================================================
882 -- the event loop instance
883 local el = eventloop.new()
885 -- add the core event handler for events
886 el:add_exec (wmiir .. " read /event",
887 function (line)
888 local line = line or "nil"
890 -- try to split off the argument(s)
891 local ev,arg = string.match(line, "(%S+)%s+(.+)")
892 if not ev then
893 ev = line
896 -- now locate the handler function and call it
897 local fn = ev_handlers[ev] or ev_handlers["*"]
898 if fn then
899 fn (ev, arg)
901 end)
903 -- ------------------------------------------------------------------------
904 -- run the event loop and process events, this function does not exit
905 function run_event_loop ()
906 -- stop any other instance of wmiirc
907 wmixp:write ("/event", "Start wmiirc " .. tostring(mypid))
909 log("wmii: updating lbar")
911 update_displayed_tags ()
913 log("wmii: updating rbar")
915 update_displayed_widgets ()
917 log("wmii: updating active keys")
919 update_active_keys ()
921 log("wmii: starting event loop")
922 while true do
923 local sleep_for = process_timers()
924 el:run_loop(sleep_for)
928 -- ========================================================================
929 -- PLUGINS API
930 -- ========================================================================
932 -- ------------------------------------------------------------------------
933 -- widget template
934 widget = {}
935 widgets = {}
937 -- ------------------------------------------------------------------------
938 -- create a widget object and add it to the wmii /rbar
940 -- examples:
941 -- widget = wmii.widget:new ("999_clock")
942 -- widget = wmii.widget:new ("999_clock", clock_event_handler)
943 function widget:new (name, fn)
944 o = {}
946 if type(name) == "string" then
947 o.name = name
948 if type(fn) == "function" then
949 o.fn = fn
951 else
952 error ("expected name followed by an optional function as arguments")
955 setmetatable (o,self)
956 self.__index = self
957 self.__gc = function (o) o:hide() end
959 widgets[name] = o
961 o:show()
962 return o
965 -- ------------------------------------------------------------------------
966 -- stop and destroy the timer
967 function widget:delete ()
968 widgets[self.name] = nil
969 self:hide()
972 -- ------------------------------------------------------------------------
973 -- displays or updates the widget text
974 function widget:show (txt)
975 local txt = txt or ""
976 local color = get_ctl("normcolors") or ""
977 if not self.txt then
978 create ("/rbar/" .. self.name, color .. " " .. txt)
979 else
980 write ("/rbar/" .. self.name, color .. " " .. txt)
982 self.txt = txt
985 -- ------------------------------------------------------------------------
986 -- hides a widget and removes it from the bar
987 function widget:hide ()
988 if self.txt then
989 remove ("/lbar/" .. self.name)
990 self.txt = nil
994 -- ------------------------------------------------------------------------
995 -- remove all /rbar entries that we don't have widget objects for
996 function update_displayed_widgets ()
997 -- colours for /rbar
998 local nc = get_ctl("normcolors") or ""
1000 -- build up a table of existing tags in the /lbar
1001 local old = {}
1002 local s
1003 for s in wmixp:idir ("/rbar") do
1004 old[s.name] = 1
1007 -- for all actual widgets in use we want to remove them from the old list
1008 local i,v
1009 for i,v in pairs(widgets) do
1010 old[v.name] = nil
1013 -- anything left in the old table should be removed now
1014 for i,v in pairs(old) do
1015 if v then
1016 remove("/rbar/"..i)
1021 -- ------------------------------------------------------------------------
1022 -- create a new program and for each line it generates call the callback function
1023 -- returns fd which can be passed to kill_exec()
1024 function add_exec (command, callback)
1025 return el:add_exec (command, callback)
1028 -- ------------------------------------------------------------------------
1029 -- terminates a program spawned off by add_exec()
1030 function kill_exec (fd)
1031 return el:kill_exec (fd)
1034 -- ------------------------------------------------------------------------
1035 -- timer template
1036 timer = {}
1037 local timers = {}
1039 -- ------------------------------------------------------------------------
1040 -- create a timer object and add it to the event loop
1042 -- examples:
1043 -- timer:new (my_timer_fn)
1044 -- timer:new (my_timer_fn, 15)
1045 function timer:new (fn, seconds)
1046 o = {}
1048 if type(fn) == "function" then
1049 o.fn = fn
1050 else
1051 error ("expected function followed by an optional number as arguments")
1054 setmetatable (o,self)
1055 self.__index = self
1056 self.__gc = function (o) o:stop() end
1058 -- add the timer
1059 timers[#timers+1] = o
1061 if seconds then
1062 o:resched(seconds)
1064 return o
1067 -- ------------------------------------------------------------------------
1068 -- stop and destroy the timer
1069 function timer:delete ()
1070 self:stop()
1071 local i,t
1072 for i,t in pairs(timers) do
1073 if t == timer then
1074 table.remove (timers,i)
1075 return
1080 -- ------------------------------------------------------------------------
1081 -- run the timer given new interval
1082 function timer:resched (seconds)
1083 local seconds = seconds or self.interval
1084 if not (type(seconds) == "number") then
1085 error ("expected number as argument")
1088 local now = tonumber(os.date("%s"))
1090 self.interval = seconds
1091 self.next_time = now + seconds
1093 -- resort the timer list
1094 table.sort (timers, timer.is_less_then)
1097 function timer:is_less_then(another)
1098 if not self.next_time then
1099 return false -- another is smaller, nil means infinity
1101 elseif not another.next_time then
1102 return true -- self is smaller, nil means infinity
1104 elseif self.next_time < another.next_time then
1105 return true -- self is smaller than another
1108 return false -- another is smaller then self
1111 -- ------------------------------------------------------------------------
1112 -- stop the timer
1113 function timer:stop ()
1114 self.next_time = nil
1116 -- resort the timer list
1117 table.sort (timers, timer.is_less_then)
1120 -- ------------------------------------------------------------------------
1121 -- figure out how long before the next event
1122 function time_before_next_timer_event()
1123 local timer = timers[1]
1124 if timer and timer.next_time then
1125 local now = tonumber(os.date("%s"))
1126 local seconds = timer.next_time - now
1127 if seconds > 0 then
1128 return seconds
1131 return 0 -- sleep for ever
1134 -- ------------------------------------------------------------------------
1135 -- handle outstanding events
1136 function process_timers ()
1137 local now = tonumber(os.date("%s"))
1138 local torun = {}
1139 local i,timer
1141 for i,timer in pairs (timers) do
1142 if (not timer) or (not timer.next_time) then
1143 table.remove(timers,i)
1144 return 1
1147 if timer.next_time > now then
1148 return timer.next_time - now
1151 torun[#torun+1] = timer
1154 for i,timer in pairs (torun) do
1155 timer:stop()
1156 local new_interval = timer:fn()
1157 if not (new_interval == -1) then
1158 timer:resched(rc)
1162 local sleep_for = time_before_next_timer_event()
1163 return sleep_for
1167 -- ========================================================================
1168 -- DOCUMENTATION
1169 -- ========================================================================
1171 --[[
1172 =pod
1174 =back
1176 =head1 ENVIRONMENT
1178 =over 4
1180 =item WMII_ADDRESS
1182 Used to determine location of wmii's listen socket.
1184 =back
1186 =head1 SEE ALSO
1188 L<wmii(1)>, L<lua(1)>
1190 =head1 AUTHOR
1192 Bart Trojanowski B<< <bart@jukie.net> >>
1194 =head1 COPYRIGHT AND LICENSE
1196 Copyright (c) 2007, Bart Trojanowski <bart@jukie.net>
1198 This is free software. You may redistribute copies of it under the terms of
1199 the GNU General Public License L<http://www.gnu.org/licenses/gpl.html>. There
1200 is NO WARRANTY, to the extent permitted by law.
1202 =cut
1203 --]]