1-- Copyright 2015-2019, Björn Ståhl 2-- License: 3-Clause BSD 3-- Reference: http://durden.arcan-fe.com 4-- 5-- Description: Global / Persistent configuration management. 6-- Deals with key lookup, update hooks and so on. For actual default 7-- configuration values, see config.lua 8-- 9 10local log, fmt = suppl_add_logfn("config"); 11 12-- here for the time being, will move with internationalization 13LBL_YES = "yes"; 14LBL_NO = "no"; 15LBL_FLIP = "toggle"; 16LBL_BIND_COMBINATION = "Press and hold the desired combination, %s to Cancel"; 17LBL_BIND_KEYSYM = "Press and hold single key to bind keysym %s, %s to Cancel"; 18LBL_BIND_COMBINATION_REP = "Press and hold or repeat- press, %s to Cancel"; 19LBL_UNBIND_COMBINATION = "Press and hold the combination to unbind, %s to Cancel"; 20LBL_METAGUARD = "Query Rebind in %d keypresses"; 21LBL_METAGUARD_META = "Rebind (meta keys) in %.2f seconds, %s to Cancel"; 22LBL_METAGUARD_BASIC = "Rebind (basic keys) in %.2f seconds, %s to Cancel"; 23LBL_METAGUARD_MENU = "Rebind (global menu) in %.2f seconds, %s to Cancel"; 24LBL_METAGUARD_TMENU = "Rebind (target menu) in %.2f seconds, %s to Cancel"; 25 26HC_PALETTE = { 27 "\\#efd469", 28 "\\#43abc9", 29 "\\#cd594a", 30 "\\#b5c689", 31 "\\#f58b4c", 32 "\\#ed6785", 33 "\\#d0d0d0", 34}; 35 36local defaults = system_load("config.lua")(); 37local listeners = {}; 38 39function gconfig_listen(key, id, fun) 40 if (listeners[key] == nil) then 41 listeners[key] = {}; 42 end 43 listeners[key][id] = fun; 44end 45 46-- for tools and other plugins to enable their own values 47function gconfig_register(key, val) 48 if (not defaults[key]) then 49 local v = get_key(key); 50 if (v ~= nil) then 51 if (type(val) == "number") then 52 v = tonumber(v); 53 elseif (type(val) == "boolean") then 54 v = v == "true"; 55 end 56 defaults[key] = v; 57 else 58 defaults[key] = val; 59 end 60 end 61end 62 63function gconfig_set(key, val, force) 64 if (type(val) ~= type(defaults[key])) then 65 log(fmt( 66 "key=%s:kind=error:type_in=%s:type_out=%s:value=%s", 67 key, type(val), type(defaults[key]), val 68 )); 69 return; 70 end 71 72-- lua5.1 timebomb, 5.2 adds to_lstring on args, 5.1 does not, might 73-- need to go with a fixup where .format walks ... and tostrings boolean 74 log(fmt("key=%s:kind=set:new_value=%s", key, tostring(val))); 75 defaults[key] = val; 76 77 if (force) then 78 store_key(key, tostring(val)); 79 end 80 81 if (listeners[key]) then 82 for k,v in pairs(listeners[key]) do 83 v(key, val); 84 end 85 end 86end 87 88function gconfig_get(key) 89 return defaults[key]; 90end 91 92-- 93-- these need special consideration, packing and unpacking so treat 94-- them separately 95-- 96 97gconfig_buttons = { 98 all = {}, 99 float = { 100 }, 101 tile = { 102 }, 103}; 104 105gconfig_statusbar_buttons = { 106}; 107 108-- for the sake of convenience, : is blocked from being a valid vsym as 109-- it is used as a separator elsewhere (suppl_valid_vsymbol) 110local function btn_str(v) 111 return string.format("%s:%s:%s", v.direction, v.label, v.command); 112end 113 114local function str_to_btn(dst, v) 115 local str, rest = string.split_first(v, "="); 116 local dir, rest = string.split_first(rest, ":"); 117 local key, rest = string.split_first(rest, ":"); 118 local base, cmd = string.split_first(rest, ":"); 119 120 if (#dir > 0 and #rest > 0 and #key > 0) then 121 local ind = string.sub(str, 10); 122 123 table.insert(dst, { 124 label = key, 125 command = cmd, 126 direction = dir, 127 ind = tonumber(ind) 128 }); 129 return true; 130 end 131end 132 133function gconfig_statusbar_rebuild(nosynch) 134--double negative, but oh well - save the current state as config 135 if (not nosynch) then 136 drop_keys("sbar_btn_%"); 137 drop_keys("sbar_btn_alt_%"); 138 drop_keys("sbar_btn_drag_%"); 139 local keys_out = {}; 140 141 for i,v in ipairs(gconfig_statusbar_buttons) do 142 keys_out["sbar_btn_" .. tostring(i)] = btn_str(v); 143 if (v.alt_command) then 144 keys_out["sbar_btn_alt_" .. tostring(i)] = v.alt_command; 145 end 146 if (v.drag_command) then 147 keys_out["sbar_btn_drag_" .. tostring(i)] = v.drag_command; 148 end 149 end 150 store_key(keys_out); 151 end 152 153 gconfig_statusbar_buttons = {}; 154 local ofs = 0; 155 for _,v in ipairs(match_keys("sbar_btn_%")) do 156 if (str_to_btn(gconfig_statusbar_buttons, v)) then 157 local ent = gconfig_statusbar_buttons[#gconfig_statusbar_buttons]; 158 if (ent.ind) then 159 ent.alt_command = get_key("sbar_btn_alt_" .. tostring(ent.ind)); 160 ent.drag_command = get_key("sbar_btn_drag_" .. tostring(ent.ind)); 161 end 162 end 163 end 164 165-- will take care of synching against gconfig_statusbar, 166-- but only if the tiler itself expose that method (i.e. 167-- gconf can be loaded before) 168 if all_tilers_iter then 169 for tiler in all_tilers_iter() do 170 if (tiler.rebuild_statusbar_custom) then 171 tiler:rebuild_statusbar_custom(gconfig_statusbar_buttons); 172 end 173 end 174 end 175end 176 177function gconfig_buttons_rebuild(nosynch) 178 local keys = {}; 179 180-- delete the keys, then rebuild buttons so we use the same code for both 181-- update dynamically and for initial load 182 if (not nosynch) then 183 drop_keys("tbar_btn_all_%"); 184 drop_keys("tbar_btn_float_%"); 185 drop_keys("tbar_btn_tile_%"); 186 187 local keys_out = {}; 188 for _, group in ipairs({"all", "float", "tile"}) do 189 for i,v in ipairs(gconfig_buttons[group]) do 190 keys_out["tbar_btn_" .. group .. "_" .. tostring(i)] = btn_str(v); 191 end 192 end 193 store_key(keys_out); 194 end 195 196 for _, group in ipairs({"all", "float", "tile"}) do 197 gconfig_buttons[group] = {}; 198 for _,v in ipairs(match_keys("tbar_btn_" .. group .. "_%")) do 199 str_to_btn(gconfig_buttons[group], v); 200 end 201 end 202end 203 204local function gconfig_setup() 205 for k,vl in pairs(defaults) do 206 local v = get_key(k); 207 if (v) then 208 if (type(vl) == "number") then 209 defaults[k] = tonumber(v); 210-- naive packing for tables (only used with colors currently), just 211-- use : as delimiter and split/concat to manage - just sanity check/ 212-- ignore on count and assume same type. 213 elseif (type(vl) == "table") then 214 local lst = string.split(v, ':'); 215 local ok = true; 216 for i=1,#lst do 217 if (not vl[i]) then 218 ok = false; 219 break; 220 end 221 if (type(vl[i]) == "number") then 222 lst[i] = tonumber(lst[i]); 223 if (not lst[i]) then 224 ok = false; 225 break; 226 end 227 elseif (type(vl[i]) == "boolean") then 228 lst[i] = lst[i] == "true"; 229 end 230 end 231 if (ok) then 232 defaults[k] = lst; 233 end 234 elseif (type(vl) == "boolean") then 235 defaults[k] = v == "true"; 236 else 237 defaults[k] = v; 238 end 239 end 240 end 241 242-- separate handling for mouse 243 local ms = mouse_state(); 244 mouse_acceleration(defaults.mouse_factor, defaults.mouse_factor); 245 ms.autohide = defaults.mouse_autohide; 246 ms.hover_ticks = defaults.mouse_hovertime; 247 ms.drag_delta = defaults.mouse_dragdelta; 248 ms.hide_base = defaults.mouse_hidetime; 249 for i=1,8 do 250 ms.btns_bounce[i] = defaults["mouse_debounce_" .. tostring(i)]; 251 end 252 253-- and for the high-contrast palette used for widgets, ... 254 for _,v in ipairs(match_keys("hc_palette_%")) do 255 local cl = string.split(v, "="); 256 local ind = tonumber(string.sub(cl[1], 12)); 257 if ind then 258 HC_PALETTE[ind] = cl[2]; 259 end 260 end 261 262-- and for global state of titlebar and statusbar 263 gconfig_buttons_rebuild(true); 264 gconfig_statusbar_rebuild(true); 265end 266 267local mask_state = false; 268function gconfig_mask_temp(state) 269 mask_state = state; 270end 271 272-- shouldn't store all of default overrides in database, just from a 273-- filtered subset 274function gconfig_shutdown() 275 local ktbl = {}; 276 for k,v in pairs(defaults) do 277 if (type(v) ~= "table") then 278 ktbl[k] = tostring(v); 279 else 280 ktbl[k] = table.concat(v, ':'); 281 end 282 end 283 284 if not mask_state then 285 for i,v in ipairs(match_keys("durden_temp_%")) do 286 local k = string.split(v, "=")[1]; 287 ktbl[k] = ""; 288 end 289 end 290 291 for i,v in ipairs(HC_PALETTE) do 292 ktbl["hc_palette_" .. tostring(i)] = v 293 end 294 295 store_key(ktbl); 296end 297 298gconfig_setup(); 299