1local function apply_scheme(palette, wnd) 2 if (palette and valid_vid(wnd.external, TYPE_FRAMESERVER)) then 3-- used to convey alpha, color scheme, etc. primarily for TUIs 4 for k,v in ipairs(palette) do 5 local ind = key_to_graphmode(k); 6 if (ind and type(v) == "table" and #v == 3) then 7 target_graphmode(wnd.external, ind, v[1], v[2], v[3]); 8 else 9 warning("apply_scheme(), broken key " .. k); 10 end 11 end 12-- commit 13 target_graphmode(wnd.external, 0); 14 end 15end 16 17local function run_group(group, prefix, wnd) 18 local ad = active_display(); 19 local as = ad.selected; 20 21-- hack around the problem with many menu paths written using the dumb 22-- active_display().selected 23 if (wnd) then 24 ad.selected = wnd; 25 end 26 27-- this doesn't check / account for reattachments/migrations etc. 28 if (group and type(group) == "table") then 29 for k,v in ipairs(group) do 30 if (type(v) == "string" and string.starts_with(v, prefix)) then 31 dispatch_symbol(v); 32 end 33 end 34 end 35 36-- and restore if we didn't destroy something, but this method isn't safe 37-- from modification, and that's mentioned in the documentation 38 if (as and as.canvas) then 39 ad.selected = as; 40 end 41end 42 43local function run_domain(group, pal, set) 44 run_group(group, "/global/", nil); 45 if (set) then 46-- need a copy to survive UAF- self modification 47 local lst = {}; 48 for k,v in ipairs(set) do 49 table.insert(lst, v); 50 end 51 for i,wnd in ipairs(lst) do 52 if (wnd.canvas) then 53 run_group(group, "/target/", wnd); 54 end 55 apply_scheme(pal, wnd); 56 end 57 end 58end 59 60local function tryload_scheme(v) 61 local res = system_load(v, 0); 62 if (not res) then 63 warning(string.format("devmaps/schemes, system_load on %s failed", v)); 64 return; 65 end 66 67 local okstate, tbl = pcall(res); 68 if (not okstate) then 69 warning(string.format("devmaps/schemes, couldn't parse/extract %s", v)); 70 return; 71 end 72 73-- FIXME: [a_Z,0-9 on name] 74 if (type(tbl) ~= "table" or not tbl.name or not tbl.label) then 75 warning(string.format("devmaps/schemes, no name/label field for %s", v)); 76 return; 77 end 78 79-- pretty much all fields are optional as it stands 80 return tbl; 81end 82 83local schemes; 84local function scan_schemes() 85 schemes = {}; 86 local list = glob_resource("devmaps/schemes/*.lua", APPL_RESOURCE); 87 for i,v in ipairs(list) do 88 local res = tryload_scheme("devmaps/schemes/" .. v); 89 if (res) then 90 table.insert(schemes, res); 91 end 92 end 93end 94 95function ui_scheme_menu(scope, tgt) 96 local res = {}; 97 98-- load / parse on demand 99 if (not schemes) then 100 scan_schemes(); 101 if (not schemes) then 102 return; 103 end 104 end 105 106 for k,v in ipairs(schemes) do 107 table.insert(res, { 108 name = v.name, 109 label = v.label, 110 kind = "action", 111 scheme = v, 112 handler = function() 113 if (scope == "global") then 114 local lst = {}; 115 for wnd in all_windows(true) do 116 table.insert(lst, wnd); 117 end 118 run_domain(v.actions, v.palette, lst); 119 elseif (scope == "display") then 120 local lst = {}; 121 for i, wnd in ipairs(tgt.windows) do 122 table.insert(lst, wnd); 123 end 124 run_domain(v.actions, nil, lst); 125 run_domain(v.display, v.palette, lst); 126 elseif (scope == "workspace") then 127 local lst = {}; 128 for i,v in ipairs(tgt.children) do 129 table.insert(lst, v); 130 end 131 run_domain(v.actions, nil, lst); 132 run_domain(v.workspace, v.palette, lst); 133 elseif (scope == "window") then 134 run_domain(v.actions, nil, {tgt}); 135 run_domain(v.window, v.palette, {tgt}); 136 end 137 end 138 }); 139 end 140 141 return res; 142end 143