fixed fonts, wchar support, window mapping
[xlua.git] / xcb.lua
blob943b5dce2af0c9048841f78bae33716babcd1312
1 --
2 -- this short utility attempts to make educated guess
3 -- about how to talk the x11 protocol based on the input
4 -- xproto.xml file. i've tried proper XSLT for lua first
5 -- and ended up like this ... its far more flexible anyway.
6 --
8 ---------------------------------------------------------
9 -- misc util
10 ---------------------------------------------------------
12 local tconcat = table.concat
13 -- insert to table, if not nil
14 local tinsert = function(t,v)
15 if (v) then table.insert(t,v) end
16 end
17 -- quote string
18 local function quote(s)
19 return '"'..s..'"'
20 end
22 ---------------------------------------------------------
23 -- naive, but trivial xml parser
24 ---------------------------------------------------------
26 -- transcribe &escapes;, add more if necessary
27 local htmltab = {
28 amp = "&"
31 local function striphtml(s)
32 return s:gsub("(%&[a-zA-Z]*%;)", function(s)
33 return htmltab[s:sub(2,#s-1):lower()]
34 end)
35 end
37 -- parse label attributes
38 local function parseargs(s)
39 local arg = {}
40 string.gsub(s, "([%w-]+)[ \t]*=[ \t]*([\"'])(.-)%2", function (w, _, a)
41 arg[striphtml(w)] = striphtml(a)
42 end)
43 return arg
44 end
46 -- build tree
47 local function xml_parse(s)
48 local stack = {}
49 local top = {}
50 tinsert(stack, top)
51 local ni,c,label,xarg, empty
52 local i, j = 1, 1
53 while true do
54 ni,j,c,label,xarg, empty = string.find(s, "<(%/?)(%w+)(.-)(%/?)>", i)
55 if not ni then break end
56 local text = string.sub(s, i, ni-1)
57 if not string.find(text, "^%s*$") then
58 tinsert(top, text)
59 end
60 if empty == "/" then -- empty element tag
61 tinsert(top, {label=label, xarg=parseargs(xarg), empty=1})
62 elseif c == "" then -- start tag
63 top = {label=label, xarg=parseargs(xarg)}
64 tinsert(stack, top) -- new level
65 else -- end tag
66 local toclose = table.remove(stack) -- remove top
67 top = stack[#stack]
68 if #stack < 1 then
69 error("nothing to close with "..label)
70 end
71 if toclose.label ~= label then
72 error("trying to close "..toclose.label.." with "..label)
73 end
74 tinsert(top, toclose)
75 end
76 i = j+1
77 end
78 local text = string.sub(s, i)
79 if not string.find(text, "^%s*$") then
80 tinsert(stack[stack.n], striphtml(text))
81 end
82 if #stack > 1 then
83 error("unclosed "..stack[stack.n].label)
84 end
85 return stack[1]
86 end
89 ---------------------------------------------------------
90 -- X11/XML madness begins here
91 ---------------------------------------------------------
93 local typfmt = {
94 BOOL = "1",
95 BYTE = "B",
96 CARD8 = "B",
97 CARD16 = "H",
98 CARD32 = "I",
99 INT16 = "h",
100 INT32 = "I",
101 INT8 = "b",
102 void = "c",
103 char = "c",
106 local enums = {}
107 local typfun = {}
108 local errornum = {}
109 local errorfun = {}
110 local eventnum = {}
111 local eventfun = {}
112 local reqfun = {}
113 local reqnum = {}
115 -- convert values with fieldrefs, operators etc to lua
116 local function process_value(tlist,...)
117 assert(tlist)
118 local optab
119 local level=1
120 local lstack = {{}}
121 local expr
122 optab = {
123 op = function(oa)
125 level=$+1
126 lstack[level] = {}
127 -- force integer division XXX todo
128 --if (oa.op == "/") then oa.op = "//" end
129 lstack[level].op = oa.op
130 return optab
131 end,
132 fieldref = function() return {
133 _data = function(d)
134 tinsert(lstack[level], "v."..d)
135 end,
136 } end,
137 value = function() return {
138 _data = function(d)
139 tinsert(lstack[level], d)
140 end,
141 } end,
142 bit = function() return {
143 _data = function(d)
144 tinsert(lstack[level], tostring((1<<tonumber(d))))
145 end,
146 } end,
147 _exit = function()
148 if (level==1) then
149 local e= tconcat(lstack[1])
150 if (tonumber(e)) then
151 -- avoid function if its just constant
152 tinsert(tlist,tonumber(e))
153 else
154 tinsert(tlist,"function(v) return (".. e..") end")
156 -- append args
157 for i=1,#arg do
158 tinsert(tlist,arg[i])
160 --tinsert(tlist,term)
161 else
162 -- we're deep in expression, reverse the polish to natural notation
163 assert(lstack[level].op)
164 local expr = tconcat(lstack[level], lstack[level].op)
165 level=$-1
166 tinsert(lstack[level], '('..expr..')')
168 end,
170 return optab
174 -- generate struct/union/request/response etc field parsers
175 local fields
176 fields = function(finishfunc)
177 local ofmt = {}
178 local onames = {narg=0}
179 local prepend = {}
180 local rfmt = ""
181 local rnames = {}
182 return {
183 field = function(fa)
184 tinsert(ofmt,typfmt[fa.type])
185 tinsert(onames,quote(fa.name))
186 onames.narg=$+1
187 end,
188 pad = function(fa)
189 tinsert(ofmt,tonumber(fa.bytes).."x")
190 end,
191 list = function(fa,empty)
192 local tf = typfmt[fa.type]
193 tinsert(ofmt, "#"..tf)
194 onames.narg=$+1
195 if empty then
196 tinsert(onames, "#arg-"..onames.narg)
197 if (tf == "$") then
198 tinsert(onames, "typfun."..fa.type)
200 tinsert(onames, quote(fa.name))
201 return
203 return process_value(onames, (tf=="$" and "typfun."..fa.type), quote(fa.name))
204 end,
205 valueparam = function(vpa)
206 local vptype = vpa['value-mask-type']
207 local vpname = vpa['value-mask-name']
208 local vplname = vpa['value-list-name']
210 tinsert(ofmt, typfmt[vptype])
211 tinsert(onames, quote(vpname))
212 onames.narg=$+1
214 tinsert(ofmt, '#I')
215 tinsert(onames, 'function() return arg[#arg] end')
216 tinsert(onames, quote(vplname))
217 onames.narg=$+1
219 tinsert(prepend, 'chkvlist(arg,"'..vpname..'")')
220 end,
221 exprfield = function(exa)
222 tinsert(prepend, 'exprfield(arg,"'..exa.name..'",'..onames.narg..",")
223 tinsert(ofmt, typfmt[exa.type])
224 tinsert(onames, quote(exa.name))
225 onames.narg=$+1
226 return process_value(prepend, ")")
227 end,
228 reply = function(rplya)
229 return fields(function(ofmt,onames)
230 rfmt = ofmt
231 rnames = onames
232 end)
233 end,
234 _exit = function()
235 finishfunc(ofmt,onames,prepend,rfmt,rnames)
240 local function copy_type(a,b)
241 typfmt[a] = typfmt[b]
242 typfun[a] = typfun[b]
245 local parenthooks
248 -- the most interesting stuff
249 parenthooks = { xcb = function() return {
250 xidtype = function(a) typfmt[a.name] = "I" end,
251 xidunion = function(a)
252 return {
253 type = function(ta)
254 return {
255 _data = function(d)
256 copy_type(a.name, d)
259 end,
261 end,
262 typedef = function(a)
263 copy_type(a.newname,a.oldname)
264 end,
265 enum = function(a)
266 local nlist = {}
267 local elist = {}
268 enums[a.name] = {}
269 return {
270 item = function(ea)
271 tinsert(nlist,ea.name)
272 return process_value(elist)
273 end,
274 _exit = function()
275 for i,nam in ipairs(nlist) do
276 enums[a.name][nam] = elist[i]
280 end,
281 struct = function(a)
282 --local ofmt = ""
283 --local onames = {}
284 typfmt[a.name] = "$"
285 return fields(
286 function(ofmt,onames)
287 typfun[a.name] =
288 'function(a,s,arg) return s:format("'..tconcat(ofmt)..
289 '",arg,'..tconcat(onames,",")..') end'
290 end)
291 end,
292 union = function(a)
293 typfmt[a.name] = "$"
294 return fields(
295 function(ofmt,onames)
296 typfun[a.name] = 'function(a,s,arg) return s:format("|'..tconcat(ofmt,"|")..
297 '",arg,'..tconcat(onames,",")..')end'
298 end)
299 end,
300 event = function(eva)
301 eventnum[eva.name] = eva.number
302 return fields(
303 function(ofmt,onames)
304 eventfun[eva.name] = 'function(s,arg) return s:format("'..tconcat(ofmt)..
305 '",arg,'..tconcat(onames,",")..')end'
306 end)
307 end,
308 eventcopy = function(eva)
309 eventnum[eva.name] = eva.number
310 eventfun[eva.name] = "eventfun."..eva.ref -- eventfun[eva.ref]
311 end,
312 error = function(era)
313 errornum[era.name] = era.number
314 return fields(function(ofmt,onames)
315 errorfun[era.name] = 'function(s,arg) return s:format("'..tconcat(ofmt)..
316 '",arg,'..tconcat(onames,",")..')end'
317 end)
318 end,
319 errorcopy = function(era)
320 errornum[era.name] = era.number
321 errorfun[era.name] = "errorfun."..era.ref -- eventfun[eva.ref]
322 end,
323 request = function(ra)
324 --reqnum[ra.name] = ra.opcode
325 return fields(
326 function(fmt,names,prepend,rfmt,rnames)
327 reqfun[ra.name] = 'function(s,arg) '..tconcat(prepend,"\n")..
328 'xrequest('..ra.opcode..',s:format("'..tconcat(fmt)..'",arg'..
329 (names[1] and ',' or "")..tconcat(names,",").."))"..
330 (rfmt[1] and ('return s:format("'..tconcat(rfmt)..
331 '", xreply(), '..tconcat(rnames,",")..') end') or "end")
332 end)
334 } end }
336 -- here comes xml diving
337 local xml_dive
338 xml_dive = function(tree,hooks)
339 for idx,branch in ipairs(tree) do
340 if type(branch) =="string" then
341 if (hooks._data) then hooks._data(branch) end
342 continue
344 local lab = branch.label
345 local args = branch.xarg
346 -- no valid label, ignore!
347 if not lab then continue end
348 if not hooks or not hooks[lab] then
349 print("WARNING: unhandled label "..lab)
350 continue
352 -- recurse deeper
353 xml_dive(branch, hooks[lab](args, branch.empty))
355 -- exit hook while leaving the branch
356 if hooks and hooks._exit then hooks._exit() end
360 ---------------------------------------------------------
361 -- dumpers, define your own out() function
362 ---------------------------------------------------------
364 local function dumpconst(t,tn)
365 out(tn.."={")
366 for k,v in pairs(t) do
367 for a,b in pairs(v) do
368 out(k..a.."="..b..",\n")
371 out("}\n")
374 local function dumpfun(t,tn)
375 local save={}
376 out(tn.."={")
377 for k,v in pairs(t) do
378 if v:sub(1,#tn) != tn then
379 out(k.."="..v..",\n")
380 else
381 tinsert(save, tn.."."..k.."="..v)
384 out("}\n")
385 out(tconcat(save,"\n").."\n")
388 local function dumpval(t,tn)
389 out(tn.."={")
390 for k,v in pairs(t) do
391 if (tonumber(v)) then
392 v = tonumber(v)
393 elseif (type(v) == "string") then
394 v = '"'..v..'"'
396 out(k.."="..v..",\n")
398 out("}\n")
401 ---------------------------------------------------------
402 -- main()
403 ---------------------------------------------------------
404 function out(a)
405 io.stderr:write(a)
408 local a = io.open("xproto.xml"):read("*a")
409 r=xml_parse(a)
410 xml_dive(r,parenthooks)
412 dumpconst(enums,"const")
414 dumpval(typfmt,"typfmt")
415 dumpfun(typfun,"typfun")
417 dumpval(errornum,"errornum")
418 dumpfun(errorfun,"errorfun")
420 dumpval(eventnum,"eventnum")
421 dumpfun(eventfun,"eventfun")
423 dumpval(reqnum,"reqnum")
424 dumpfun(reqfun,"reqfun")