-- -*- coding: utf-8 -*- -------------------------------------------------------------------------------- -- @author Nicolas Berthier <nberthier@gmail.com> -- @copyright 2010 Nicolas Berthier -------------------------------------------------------------------------------- -- -- Bowls are kind of helpers that can be drawn (at the bottom --- for now) of an -- area, and displaying the current key prefix. It is inspired by emacs' -- behavior, that prints prefix keys in the minibuffer after a certain time. -- -- I call it `bowl' as a reference to the bowl that one might have at home, -- where one puts its actual keys... A more serious name would be `hint' or -- `tooltip' (but they do not fit well for this usage). -- -- Example usage: see `rc.lua' file. -- -------------------------------------------------------------------------------- --{{{ Grab environment (mostly aliases) local setmetatable = setmetatable local ipairs = ipairs local type = type local pairs = pairs local string = string local print = print local error = error local capi = capi local client = client local awesome = awesome local root = root local timer = timer local infoline = require ("infoline") --}}} module ("bowl") -- Privata data: we use weak keys in order to allow collection of private data -- if keys (clients) are collected (i.e., no longer used, after having been -- killed for instance) local data = setmetatable ({}, { __mode = 'k' }) --{{{ Default values --- Default modifier filter local modfilter = { ["Mod1"] = "M", ["Mod4"] = "S", ["Control"] = "C", ["Shift"] = string.upper, } -- Timers configuration local use_timers = true local timeout = 2.0 --}}} --{{{ Keychain pretty-printing function mod_to_string (mods, k) local ret, k = "", k for _, mod in ipairs (mods) do if modfilter[mod] then local t = type (modfilter[mod]) if t == "function" then k = modfilter[mod](k) elseif t == "string" then ret = ret .. modfilter[mod] .. "-" else error ("Invalid modifier key filter: got a " .. t) end else ret = ret .. mod .. "-" end end return ret, k end function ks_to_string (m, k) local m, k = mod_to_string (m, k) return m .. k end --}}} --{{{ Timer management local function delete_timer_maybe (d) if d.timer then -- stop and remove the timer d.timer:remove_signal ("timeout", d.timer_function) d.timer:stop () d.timer = nil d.timer_expired = true end end local function delayed_call_maybe (d, f) if use_timers then if not d.timer_expired and not d.timer then -- create and start the timer d.timer = timer ({ timeout = timeout }) d.timer_function = function () f (); delete_timer_maybe (d) end d.timer:add_signal ("timeout", d.timer_function) d.timer:start () d.timer_expired = false elseif not d.timer_expired then -- restart the timer... -- XXX: What is the actual semantics of the call to `start' (ie, -- does it restart the timer with the initial timeout)? d.timer:stop () d.timer.timeout = timeout -- reset timeout d.timer:start () end else -- timers disabled f () -- call the given function directly end end --}}} --{{{ Infoline management function dispose (w) local d = data[w] if d.bowl then -- if bowl was enabled... (should always be true...) infoline.dispose (d.bowl) d.bowl = nil end delete_timer_maybe (d) data[w] = nil end function append (w, m, k) local d = data[w] local pretty_ks = ks_to_string (m, k) .. " " infoline.set_text (d.bowl, infoline.get_text (d.bowl) .. pretty_ks) local function enable_bowl () -- XXX: is there a possible bad interleaving that could make -- this function execute while the bowl has already been -- disposed of? in which case the condition should be checked -- first... -- if d.bowl then infoline.attach (d.bowl, w) -- end end delayed_call_maybe (d, enable_bowl) end function create (w) -- XXX: Note the prefix text could be customizable... data[w] = { bowl = infoline.new (" ") } end --}}} --- Initializes the bowl module, with given properties; should be called before --- ANY other function of this module. -- Configurations fields include: -- -- `use_timers', `timeout': A boolean defining whether bowls drawing should be -- delayed, along with a number being this time shift, in seconds (Default -- values are `true' and `2'). -- -- `modfilter': A table associating modifiers (Mod1, Mod4, Control, Shift, etc.) -- with either a string (in this case it will replace the modifier when printed -- in heplers) or functions (in this case the key string will be repaced by a -- call to this function with the key string as parameter). Default value is: -- { ["Mod1"] = "M", ["Mod4"] = "S", ["Control"] = "C", ["Shift"] = -- string.upper } -- -- @param c The table of properties. function init (c) local c = c or { } modfilter = c.modfilter and c.modfilter or modfilter if c.use_timers ~= nil then use_timers = c.use_timers end if use_timers then timeout = c.timeout ~= nil and c.timeout or timeout end end --- Setup signal listeners, that trigger appropriate functions for a default --- behavior. function default_setup () local function to_root (f) return function (...) f (root, ...) end end client.add_signal ("keychain::enter", create) client.add_signal ("keychain::append", append) client.add_signal ("keychain::leave", dispose) awesome.add_signal ("keychain::enter", to_root (create)) awesome.add_signal ("keychain::append", to_root (append)) awesome.add_signal ("keychain::leave", to_root (dispose)) end -- Local variables: -- indent-tabs-mode: nil -- fill-column: 80 -- lua-indent-level: 4 -- End: -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80