1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2018. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%%-------------------------------------------------------------------
21%%% File    : gl_gen_erl.erl
22%%% Author  : Dan Gudmundsson <dgud@erix.ericsson.se>
23%%% Description :
24%%%
25%%% Created : 18 Apr 2007 by Dan Gudmundsson <dgud@erix.ericsson.se>
26%%%-------------------------------------------------------------------
27-module(gl_gen_erl).
28
29-include("gl_gen.hrl").
30
31-compile(export_all).
32
33-import(lists, [foldl/3,foldr/3,reverse/1, keysearch/3, map/2, filter/2, max/1]).
34-import(gen_util, [lowercase/1, lowercase_all/1, uppercase/1, uppercase_all/1,
35		   open_write/1, open_write/2, close/0, erl_copyright/0, w/2,
36		   args/3, args/4, strip_name/2]).
37
38
39-define(HTTP_TOP, "https://www.khronos.org/registry/OpenGL-Refpages/").
40
41gl_defines(Defs) ->
42    open_write("../include/gl.hrl"),
43    erl_copyright(),
44    w("~n%% OPENGL DEFINITIONS~n~n", []),
45    w("%% This file is generated DO NOT EDIT~n~n", []),
46    [gen_define(Def) || Def=#def{} <- Defs],
47    close(),
48    ok.
49
50glu_defines(Defs) ->
51    open_write("../include/glu.hrl"),
52    erl_copyright(),
53    w("~n%% GLU DEFINITIONS~n~n", []),
54    w("%% This file is generated DO NOT EDIT~n~n", []),
55    [gen_define(Def) || Def=#def{} <- Defs],
56    close(),
57    ok.
58
59gen_define(#def{name=N, val=Val, type=int}) ->
60    w("-define(~s, ~p).~n", [N,Val]);
61gen_define(#def{name=N, val=Val, type=float_str}) ->
62    w("-define(~s, ~s).~n", [N,Val]);
63gen_define(#def{name=N, val=Val, type=hex}) ->
64    w("-define(~s, 16#~s).~n", [N,Val]);
65gen_define(#def{name=N, val=Val, type=string}) ->
66    w("-define(~s, ?~s).~n", [N,Val]);
67gen_define(#def{name="GLEXT_64_TYPES"++_, val=undefined, type=undefined}) ->
68    ok.
69
70types() ->
71    [{"GLenum",    "32/native-unsigned"},
72     {"GLboolean", "8/native-unsigned"},
73     {"GLbitfield","32/native-unsigned"}, %
74     %%{"GLvoid",":void		"},%
75     {"GLbyte",    "8/native-signed"},	  % 1-byte signed
76     {"GLshort",   "16/native-signed"},   % 2-byte signed
77     {"GLint",	   "32/native-signed"},   % 4-byte signed
78     {"GLubyte",   "8/native-unsigned"},  % 1-byte unsigned
79     {"GLushort",  "16/native-unsigned"}, % 2-byte unsigned
80     {"GLuint",	   "32/native-unsigned"}, % 4-byte unsigned
81     {"GLsizei",   "32/native-signed"},   % 4-byte signed
82     {"GLfloat",   "32/native-float"},    % single precision float
83     {"GLclampf",  "32/native-float"},    % single precision float in [0,1]
84     {"GLdouble",  "64/native-float"},    % double precision float
85     {"GLclampd",  "64/native-float"},    % double precision float in [0,1]
86     {"GLsizeiptr","64/native-unsigned"}, % 64 bits int, convert on c-side
87     {"GLintptr",  "64/native-unsigned"}, % 64 bits int, convert on c-sidew
88     {"GLUquadric", "64/native-unsigned"},% Handle 32bits aargh 64bits on mac64
89     {"GLhandleARB","64/native-unsigned"},% Handle 32bits aargh 64bits on mac64
90
91     {"GLsync",     "64/native-unsigned"}, % Pointer to record
92     {"GLuint64",     "64/native-unsigned"},
93     {"GLint64",     "64/native-signed"}
94    ].
95
96gl_api(Fs) ->
97    open_write("../src/gen/gl.erl", [{encoding,utf8}]),
98    erl_copyright(),
99    w("~n%% OPENGL API~n~n", []),
100    w("%% This file is generated DO NOT EDIT~n~n", []),
101    w("%% @doc  Standard OpenGL api.~n", []),
102    w("%% See <a href=\""++ ?HTTP_TOP ++ "\">www.khronos.org</a>~n",[]),
103    w("%%~n", []),
104    w("%% Booleans are represented by integers 0 and 1.~n~n", []),
105
106    w("-module(gl).~n~n",[]),
107    w("-compile(inline).~n", []),
108%%    w("-include(\"wxe.hrl\").~n", []),
109    [w("-define(~s,~s).~n", [GL,Erl]) || {GL,Erl} <- types()],
110
111    gen_types(gl),
112
113    Exp = fun(F) -> gen_export(F) end,
114    ExportList = lists:map(Exp,Fs),
115
116    w("~n-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
117    w("-export([call/2, cast/2, send_bin/1]).~n",[]),
118    w("%% @hidden~n", []),
119    w("call(Op, Args) ->~n", []),
120    w("    Port = get(opengl_port), ~n", []),
121    w("    _ = erlang:port_control(Port,Op,Args),~n", []),
122    w("    rec(Op).~n", []),
123    w("    ~n", []),
124    w("%% @hidden~n", []),
125    w("cast(Op, Args) ->~n", []),
126    w("    Port = get(opengl_port), ~n", []),
127    w("    _ = erlang:port_control(Port,Op,Args),~n", []),
128    w("    ok.~n", []),
129    w("    ~n", []),
130    w("%% @hidden~n", []),
131    w("rec(Op) ->~n", []),
132    w("    receive~n", []),
133    w("        {'_egl_result_', Res} -> Res;~n", []),
134    w("        {'_egl_error_',  Op, Res} -> error({error,Res,Op});~n", []),
135    w("        {'_egl_error_', Other, Res} ->~n ", []),
136    w("               Err = io_lib:format(\"~~p in op: ~~p\", [Res, Other]),~n", []),
137    w("               error_logger:error_report([{gl, error}, {message, lists:flatten(Err)}]),~n", []),
138    w("               rec(Op)~n", []),
139    w("    end.~n", []),
140    w("~n", []),
141    w("%% @hidden~n", []),
142    w("send_bin(Bin) when is_binary(Bin) ->~n", []),
143    w("    Port = get(opengl_port), ~n", []),
144    w("    erlang:port_command(Port,Bin);~n", []),
145    w("send_bin(Tuple) when is_tuple(Tuple) ->~n", []),
146    w("    Port = get(opengl_port), ~n", []),
147    w("    case element(2, Tuple) of~n", []),
148    w("        Bin when is_binary(Bin) ->~n", []),
149    w("            erlang:port_command(Port,Bin)~n", []),
150    w("    end.~n", []),
151    w("~n", []),
152
153    w("~n%% API~n~n", []),
154    [gen_funcs(F) || F <- Fs],
155    close(),
156    ok.
157
158glu_api(Fs) ->
159    open_write("../src/gen/glu.erl", [{encoding,utf8}]),
160    erl_copyright(),
161    w("~n%% OPENGL UTILITY API~n~n", []),
162    w("%% This file is generated DO NOT EDIT~n~n", []),
163    w("%% @doc  A part of the standard OpenGL Utility api.~n", []),
164    w("%% See <a href=\""++ ?HTTP_TOP ++ "\">www.khronos.org</a>~n",[]),
165    w("%%~n", []),
166    w("%% Booleans are represented by integers 0 and 1.~n~n", []),
167
168    w("-module(glu).~n",[]),
169    w("-compile(inline).~n", []),
170    %%w("-include(\"wxe.hrl\").~n", []),
171    [w("-define(~s,~s).~n", [GL,Erl]) || {GL,Erl} <- types()],
172
173    gen_types(glu),
174
175    Exp = fun(F) -> gen_export(F) end,
176    ExportList = ["tesselate/2" | lists:map(Exp,Fs)],
177    w("~n-export([~s]).~n~n", [args(fun(EF) -> EF end, ",", ExportList, 60)]),
178    w("-import(gl, [call/2,cast/2,send_bin/1]).", []),
179    w("~n%% API~n~n", []),
180
181    %% w("%% @spec (Vec3, [Vec3]) -> {Triangles, VertexPos}~n",[]),
182    %% w("%%  Vec3 = {float(),float(),float()}~n",[]),
183    %% w("%%  Triangles = [VertexIndex::integer()]~n",[]),
184    %% w("%%  VertexPos  = binary()~n",[]),
185    w("%% @doc General purpose polygon triangulation.~n",[]),
186    w("%% The first argument is the normal and the second a list of~n"
187      "%% vertex positions. Returned is a list of indecies of the vertices~n"
188      "%% and a binary (64bit native float) containing an array of~n"
189      "%% vertex positions, it starts with the vertices in Vs and~n"
190      "%% may contain newly created vertices in the end.~n", []),
191
192    w("-spec tesselate(Normal, [Vs]) -> {Triangles, VertexPos}~n", []),
193    w("                  when Normal :: vertex(), Vs :: vertex(),~n", []),
194    w("                  Triangles :: [integer()], VertexPos :: binary().~n", []),
195    w("tesselate({Nx,Ny,Nz}, Vs) ->~n",[]),
196    w("  call(5000, <<(length(Vs)):32/native,0:32,~n"
197      "    Nx:?GLdouble,Ny:?GLdouble,Nz:?GLdouble,~n"
198      "    (<< <<Vx:?GLdouble,Vy:?GLdouble,Vz:?GLdouble >>~n"
199      "        || {Vx,Vy,Vz} <- Vs>>)/binary >>).~n~n", []),
200
201    [gen_funcs(F) || F <- Fs],
202    close(),
203    ok.
204
205gen_funcs([F]) when is_list(F) ->
206    put(current_func,F),
207    gen_func(get(F)),
208    erase(current_func),
209    w(".~n~n",[]);
210gen_funcs(All=[F|Fs]) when is_list(F) ->
211    put(current_func,F),
212    gen_doc([get(A) || A <- All]),
213    gen_func(get(F)),
214    erase(current_func),
215    w(";~n",[]),
216    gen_funcs(Fs);
217gen_funcs([]) ->
218    w(".~n~n",[]);
219gen_funcs(F) ->
220    put(current_func,F),
221    gen_doc([get(F)]),
222    gen_func(get(F)),
223    erase(current_func),
224    w(".~n~n",[]).
225
226gen_types(Where) ->
227    case Where of
228	glu ->
229	    w("-type vertex() :: {float(), float(), float()}.~n", []),
230	    w("-type enum() :: non_neg_integer().   %% See wx/include/gl.hrl or glu.hrl~n", []);
231	gl ->
232	    w("-type enum() :: non_neg_integer().   %% See wx/include/gl.hrl~n", []),
233	    w("-type clamp() :: float().    %% 0.0..1.0~n", []),
234	    w("-type offset() :: non_neg_integer(). %% Offset in memory block~n", [])
235    end,
236    w("-type matrix12() :: {float(),float(),float(),float(),~n", []),
237    w("                   float(),float(),float(),float(),~n", []),
238    w("                   float(),float(),float(),float()}.~n", []),
239    w("-type matrix16() :: {float(),float(),float(),float(),~n", []),
240    w("                   float(),float(),float(),float(),~n", []),
241    w("                   float(),float(),float(),float(),~n", []),
242    w("                   float(),float(),float(),float()}.~n", []),
243    w("-type matrix() :: matrix12() | matrix16().~n", []),
244    w("-type mem() :: binary() | tuple().   %% Memory block~n", []),
245    ok.
246
247gen_export(F) ->
248    try gen_export_1(F)
249    catch E:R:S ->
250	    io:format("Crash ~p:~p in ~p ~n",[E,R,S]),
251	    io:format("Func = ~p~n  ~p", [F, get(F)])
252    end.
253
254gen_export_1([F|_]) when is_list(F) ->
255    gen_export2(get(F));
256gen_export_1(F) when is_list(F) ->
257    gen_export2(get(F)).
258
259gen_export2(#func{name=Name,alt=Alt={vector,VecPos,Vec}}) ->
260    #func{params=As0} = get(Vec),
261    {As1,_As2} = lists:split(VecPos, As0),
262    Args = lists:filter(fun(Arg) -> func_arg(Arg) =/= skip end, As1),
263    Export = erl_func_name(Name) ++ "/" ++ integer_to_list(length(Args) +1),
264    DocN = doc_name(Name,Alt),
265    (get({export_arg,DocN}) == undefined) andalso put({export_arg, DocN}, Export),
266    Export;
267gen_export2(#func{name=Name,params=As0, alt=Alt}) ->
268    Args = lists:filter(fun(Arg) -> func_arg(Arg) =/= skip end, As0),
269    Export = erl_func_name(Name) ++ "/" ++ integer_to_list(length(Args)),
270    DocN = doc_name(Name,Alt),
271    (get({export_arg,DocN}) == undefined) andalso put({export_arg, DocN}, Export),
272    Export.
273
274gen_doc([#func{name=Name, params=Orig, alt={vector,VecPos,Vec}}]) ->
275    #func{type=T,params=As} = get(Vec),
276    {As1,As2} = lists:split(VecPos, As),
277    #arg{name=OrigName} = lists:last(Orig),
278    Args1 = case args(fun func_arg/1, ",", As1) of [] -> []; Else -> Else++"," end,
279    Args2 = args(fun func_arg/1, ",", As2),
280    w("%% @equiv ~s(~s)~n",[erl_func_name(Vec), Args1++Args2]),
281    SA1 = case doc_arg_types(As1) of [] -> []; E -> E++"," end,
282    SA2 = doc_arg_types(As2),
283    w("-spec ~s(~s~s) -> ~s when ~s :: {~s}.~n",
284      [erl_func_name(Name), SA1, erl_arg_name(OrigName),
285       doc_return_types(T,As), erl_arg_name(OrigName), SA2]);
286
287gen_doc([F=#func{name=Name,type=T,params=As, alt=Alt}|_]) ->
288    gen_doc(Name, Alt, gen_export2(F)),
289    Ps = [Arg || #arg{name=Arg, in=In, where=Where} <- As,
290		 In =/= false, Where =/= c],
291    Args = args(fun erl_arg_name/1, ", ", Ps),
292    case Args of
293	[] ->
294	    w("-spec ~s(~s) -> ~s.~n",
295	      [erl_func_name(Name), Args, doc_return_types(T,As)]);
296	_  -> w("-spec ~s(~s) -> ~s when ~s.~n",
297		[erl_func_name(Name), Args, doc_return_types(T,As), doc_arg_types(As)])
298    end.
299
300-define(LINE_LEN, 90).
301
302gen_doc(Name0, Alt, Export) ->
303    Name = doc_name(Name0, Alt),
304    case get({doc, Name}) of
305	undefined ->
306	    case parse_doc(Name, Dir1 ="gl_man4", Dir2="gl_man2") of
307		{error, _} ->
308		    case reverse(Name) of
309			"BRA" ++ _ -> ok;
310			"TXE" ++ _ -> ok;
311			_ ->
312			    %% io:format("Missing doc: no ~s.xml (~s) found in ~s or ~s~n",
313			    %% 	      [Name, Name0, Dir1, Dir2]),
314			    ok
315		    end,
316		    w("%% @doc ~s~n%%~n"
317                      "%% See <a href=\"~s\">external</a> documentation.~n",
318		      [Name, ?HTTP_TOP]);
319		{Found, Doc} ->
320                    {Dir,Ext} = case Found of
321                                    Dir1 -> {"gl4/html", "xhtml"};
322                                    Dir2 -> {"gl2.1/xhtml", "xml"}
323                                end,
324		    put({doc, Name}, Export),
325		    format_doc(Doc, ?LINE_LEN),
326		    w("~n%%~n%% See <a href=\"~s~s/~s.~s\">external</a> documentation.~n",
327		      [?HTTP_TOP, Dir, Name, Ext])
328	    end;
329	Where ->
330	    w("%% @doc ~n", []),
331	    w("%% See {@link ~s}~n", [Where])
332    end.
333
334parse_doc(Name, Dir1, Dir2) ->
335    case gl_scan_doc:file(filename:join(Dir1, Name++".xml"), []) of
336	{error, {_, "no such" ++ _}} ->
337	    case gl_scan_doc:file(filename:join(Dir2, Name++".xml"), []) of
338                {error, _} = Err -> Err;
339                Doc -> {Dir2, Doc}
340            end;
341	Doc ->
342	    {Dir1, Doc}
343    end.
344
345format_doc(Strs, Count) when Count < 0 ->
346    w("~n%% ", []),
347    format_doc(Strs, ?LINE_LEN);
348format_doc([{constant, Const}|Rest], Count) ->
349    w("`?~s'", [Const]),
350    format_doc(Rest, Count-length(Const)-8);
351format_doc([{emphasis, Const}|Rest], Count) ->
352    w("`~ts'", [Const]),
353    format_doc(Rest, Count-length(Const)-7);
354format_doc([{function, Func}|Rest], Count) ->
355    case Func of
356	"glu" ++ _ ->
357	    w("``glu:~s''", [erl_func_name(Func)]);
358	"gl" ++ _ ->
359	    w("``gl:~s''", [erl_func_name(Func)]);
360	_ ->
361	    w("`~s'", [Func])
362    end,
363    format_doc(Rest, Count-length(Func)-7);
364format_doc([{reffunc, Func}|Rest], Count) ->
365    Out = fun(Export) ->
366		  case Func of
367		      "glu" ++ _ -> w(" {@link glu:~s} ", [Export]);
368		      "gl" ++ _ ->  w(" {@link gl:~s} ", [Export])
369		  end
370	  end,
371    case get({export_arg, Func}) of
372	undefined ->
373	    case get({export_arg, doc_name(Func, undefined)}) of
374		undefined ->
375		    %% io:format("Func ~p undefined (~p) ~n",
376		    %% 	      [Func, doc_name(Func, undef)]),
377		    w("see `~s'", [Func]);
378		Export -> Out(Export)
379	    end;
380	Export ->
381	    Out(Export)
382    end,
383    format_doc(Rest, Count-length(Func)-10);
384format_doc([{parameter, Param}|Rest], Count) ->
385    w(" `~s' ", [erl_arg_name(Param)]),
386    format_doc(Rest, Count-length(Param)-7);
387format_doc([{equation, Eq}|Rest], Count) ->
388%%    w("```", []),
389    format_doc([Eq], Count),
390%%    w("'''", []),
391    format_doc(Rest, Count);
392format_doc([{fenced, Open, Close, Eq}|Rest], Count) ->
393    w(Open, []),
394    format_doc(Eq, Count),
395    w(Close, []),
396    format_doc(Rest, Count);
397
398format_doc([{code, Code}|Rest], Count) ->
399    w("``~ts''", [Code]),
400    format_doc(Rest, Count-length(Code)-7);
401
402format_doc([para|Rest], _Count) ->
403    w("~n%%~n%% ", []),
404    format_doc(Rest, ?LINE_LEN);
405format_doc([break|Rest], _Count) ->
406    w("<br />~n%% ", []),
407    format_doc(Rest, ?LINE_LEN);
408format_doc([{purpose, Purpose}, para | Doc], _Count) ->
409    w("%% @doc ~ts~n%%~n%% ", [uppercase(Purpose)]),
410    format_doc(Doc, ?LINE_LEN);
411format_doc([{purpose, Purpose} | Doc], _Count) ->
412    w("%% @doc ~ts~n%%~n%% ", [Purpose]),
413    format_doc(Doc, ?LINE_LEN);
414format_doc([listentry|Rest], _Count) ->
415    w("~n%%~n%% ", []),
416    format_doc(Rest, ?LINE_LEN);
417format_doc([Str|Rest], Count) ->
418    case length(Str) of
419	Len when Len < Count ->
420	    w("~ts", [Str]),
421	    format_doc(Rest, Count-Len);
422	_ ->
423	    {Str1, Str2} = split(Str, Count, []),
424	    w("~ts~n%% ", [Str1]),
425	    format_doc([Str2|Rest], ?LINE_LEN)
426    end;
427format_doc([], _) -> ok.
428
429split([$  |Str], Count, Acc) when Count =< 5 ->
430    {reverse(Acc), Str};
431split([Chr|Str], Count, Acc) ->
432    split(Str, Count-1, [Chr|Acc]);
433split([], _, Acc) ->
434    {reverse(Acc), []}.
435
436gen_func(#func{name=Name,alt={vector,VecPos,Vec}}) ->
437    #func{params=As} = get(Vec),
438    {As1,As2} = lists:split(VecPos, As),
439    Args1 = case args(fun func_arg/1, ",", As1) of [] -> []; Else -> Else++"," end,
440    Args2 = args(fun func_arg/1, ",", As2),
441
442    w("~s(~s{~s}) ->", [erl_func_name(Name),Args1,Args2]),
443    w("  ~s(~s)",      [erl_func_name(Vec), Args1++Args2]);
444gen_func(_F=#func{name=Name,type=T,params=As,id=MId}) ->
445    Args = args(fun func_arg/1, ",", As),
446    w("~s(~s)~s ", [erl_func_name(Name), Args, guard_test(As)]),
447    w("->~n", []),
448    PreAs  = pre_marshal(As),
449    {StrArgs,_} = marshal_args(PreAs),
450    case have_return_vals(T,As) of
451	true ->
452	    w("  call(~p, <<~s>>)", [MId, StrArgs]);
453	false ->
454	    w("  cast(~p, <<~s>>)", [MId, StrArgs])
455    end.
456
457func_arg(#arg{in=In,where=W,name=Name,type=Type})
458  when In =/= false, W =/= c ->
459    case Type of
460	#type{single={tuple,TSz0}} when TSz0 =/= undefined ->
461	    TSz = if is_integer(TSz0) -> TSz0;
462		     TSz0 =:= matrix12 -> 12
463		  end,
464	    [NameId|_] = erl_arg_name(Name),
465	    Names = [[NameId|integer_to_list(Id)] || Id <- lists:seq(1,TSz)],
466	    "{" ++ args(fun(ElName) -> ElName end, ",", Names) ++ "}";
467	_ ->
468	    erl_arg_name(Name)
469    end;
470func_arg(_) -> skip.
471
472doc_arg_types(Ps0) ->
473    Ps = [P || P=#arg{in=In, where=Where} <- Ps0,In =/= false, Where =/= c],
474    args(fun(Arg) -> doc_arg_type(Arg) end, ",", Ps).
475
476doc_return_types(T, Ps0) ->
477    Ps = [P || P=#arg{in=In, where=Where} <- Ps0,In =/= true, Where =/= c],
478    doc_return_types2(T, Ps).
479
480doc_return_types2(void, []) ->    "'ok'";
481doc_return_types2(void, [#arg{type=T}]) ->  doc_arg_type2(T);
482doc_return_types2(T, []) ->              doc_arg_type2(T);
483doc_return_types2(void, Ps) ->
484    "{" ++ args(fun(Arg) -> doc_arg_type(Arg) end,",",Ps) ++ "}";
485doc_return_types2(T, Ps) ->
486    "{" ++ doc_arg_type2(T) ++ "," ++
487	args(fun(Arg) -> doc_arg_type(Arg) end,",",Ps) ++ "}".
488
489doc_arg_type(#arg{name=Name,type=T}) ->
490    try
491	erl_arg_name(Name) ++ " :: " ++ doc_arg_type2(T)
492    catch _:Error:Stacktrace ->
493	    io:format("Error spec: ~p ~p~n~p~n",[Name, Error, Stacktrace]),
494	    exit(error)
495    end.
496
497doc_arg_type2(T=#type{single=true}) ->
498    doc_arg_type3(T);
499doc_arg_type2(T=#type{single=undefined}) ->
500    doc_arg_type3(T);
501doc_arg_type2(_T=#type{single={tuple,undefined}}) ->
502    "tuple()";
503doc_arg_type2(#type{base=float, single={tuple,16}}) ->
504    "matrix()";
505doc_arg_type2(#type{base=string, single=list}) ->
506    "iolist()";
507doc_arg_type2(T=#type{single={tuple,Sz}}) ->
508    "{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}";
509doc_arg_type2(#type{base=guard_int, single=list}) ->
510    "[integer()]|mem()";
511doc_arg_type2(T=#type{single=list}) ->
512    "[" ++ doc_arg_type3(T) ++ "]";
513doc_arg_type2(T=#type{single={list, _Max}}) ->
514    "[" ++ doc_arg_type3(T) ++ "]";
515doc_arg_type2(T=#type{single={list,_,_}}) ->
516    "[" ++ doc_arg_type3(T) ++ "]";
517doc_arg_type2(T=#type{single={tuple_list,Sz}}) ->
518    "[{" ++ args(fun doc_arg_type3/1, ",", lists:duplicate(Sz,T)) ++ "}]".
519
520doc_arg_type3(#type{name="GLenum"}) ->  "enum()";
521doc_arg_type3(#type{name="GLclamp"++_}) ->  "clamp()";
522doc_arg_type3(#type{base=int}) ->       "integer()";
523doc_arg_type3(#type{base=float}) ->     "float()";
524doc_arg_type3(#type{base=guard_int}) -> "offset()|mem()";
525doc_arg_type3(#type{base=string}) ->    "string()";
526doc_arg_type3(#type{base=bool}) ->      "0|1";
527doc_arg_type3(#type{base=binary}) ->    "binary()";
528doc_arg_type3(#type{base=memory}) ->    "mem()".
529
530guard_test(As) ->
531    Str = args(fun(#arg{name=N,type=#type{base=guard_int, single=list}}) ->
532		       " is_list("++erl_arg_name(N)++")";
533                   (#arg{name=N,type=#type{base=guard_int}}) ->
534		       " is_integer("++erl_arg_name(N)++")";
535		  (_) ->
536		       skip
537	       end, ",", As),
538    case Str of
539	[] -> [];
540	Other -> " when " ++ Other
541    end.
542
543pre_marshal([#arg{name=N,in=true, type=#type{base=binary, single=list}=T, alt=list_binary}=A|R]) ->
544    w("  send_bin(~s),~n", [erl_arg_name(N)]),
545    w("  ~sLen = byte_size(if is_binary(~s) -> ~s; is_tuple(~s) -> element(2, ~s) end) div 4,~n",
546      [erl_arg_name(N),erl_arg_name(N), erl_arg_name(N), erl_arg_name(N), erl_arg_name(N)]),
547    Type = T#type{base=int, by_val=true, single=true, ref=undefined},
548    Arg=A#arg{name=N++"Len", where=both, type=Type},
549    [Arg|pre_marshal(R)];
550pre_marshal([#arg{name=N,in=true,type=#type{base=binary}}|R]) ->
551    w("  send_bin(~s),~n", [erl_arg_name(N)]),
552    pre_marshal(R);
553pre_marshal([#arg{name=N,type=#type{base=memory}}|R]) ->
554    w("  send_bin(~s),~n", [erl_arg_name(N)]),
555    pre_marshal(R);
556pre_marshal([A=#arg{name=N,type=#type{base=string,single=list}}|R]) ->
557    %% With null terminations
558    w("  ~sTemp = list_to_binary([[Str|[0]] || Str <- ~s ]),~n",
559      [erl_arg_name(N), erl_arg_name(N)]),
560    w("  ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
561    [A|pre_marshal(R)];
562pre_marshal([A=#arg{name=N,type=#type{base=string,single=true,ref={pointer,1}}}|R]) ->
563    w("  ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
564    [A|pre_marshal(R)];
565pre_marshal([A=#arg{name=N,type=#type{single=list}}|R]) ->
566    w("  ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
567    [A|pre_marshal(R)];
568pre_marshal([A=#arg{name=N,type=#type{single={tuple_list,_}}}|R]) ->
569    w("  ~sLen = length(~s),~n",[erl_arg_name(N), erl_arg_name(N)]),
570    [A|pre_marshal(R)];
571pre_marshal([A|R]) ->
572    [A|pre_marshal(R)];
573pre_marshal([]) -> [].
574
575marshal_args(As) ->
576    marshal_args(As, [], 0).
577
578marshal_args([#arg{where=erl}|Ps], Margs, Align) ->
579    marshal_args(Ps, Margs, Align);
580marshal_args([#arg{where=c}|Ps], Margs, Align) ->
581    marshal_args(Ps, Margs, Align);
582marshal_args([#arg{in=false}|Ps], Margs, Align) ->
583    marshal_args(Ps, Margs, Align);
584marshal_args([#arg{name=Name, type=Type}|Ps], Margs, Align0) ->
585    {Arg,Align} = marshal_arg(Type,erl_arg_name(Name),Align0),
586    marshal_args(Ps, [Arg|Margs], Align);
587marshal_args([],Margs, Align) ->
588    {args(fun(Str) -> Str end, ",", reverse(Margs)), Align}.
589
590marshal_arg(#type{size=Sz,name=Type,single={tuple,undefined}},Name,A0) ->
591    KeepA = case Sz of 8 -> "0:32,"; _ -> "" end,
592    Str0 = "(size("++Name++")):?GLuint,"++KeepA++"\n"
593	"      (<< <<C:?"++Type++ ">> ||"
594	"C <- tuple_to_list("++Name++")>>)/binary",
595    case Sz of
596	4 ->
597	    {Str,Align} = align(4,A0,Str0),
598	    {Str++",0:((("++integer_to_list(Align div 4)++
599	     "+size("++Name++")) rem 2)*32)",0};
600	8 ->
601	    align(8,A0,Str0)
602    end;
603marshal_arg(#type{size=BSz,name=Type,single={tuple,TSz}},Name,A0)
604  when is_integer(TSz) ->
605    NameId = hd(Name),
606    Names = [[NameId|integer_to_list(Id)] || Id <- lists:seq(1,TSz)],
607    All = args(fun(ElName) -> ElName ++ ":?" ++ Type end, ",", Names),
608    align(BSz,TSz,A0,All);
609
610marshal_arg(#type{size=BSz,name=Type,single={tuple,matrix12}},Name,A0) ->
611    NameId = hd(Name),
612    Ns0 = [[NameId|integer_to_list(Id)] || Id <- lists:seq(1,3)],
613    Ns1 = [[NameId|integer_to_list(Id)] || Id <- lists:seq(4,6)],
614    Ns2 = [[NameId|integer_to_list(Id)] || Id <- lists:seq(7,9)],
615    Ns3 = [[NameId|integer_to_list(Id)] || Id <- lists:seq(10,12)],
616    All = args(fun(ElName) -> ElName ++ ":?" ++ Type end, ",",
617	       Ns0 ++ ["0"] ++ Ns1 ++ ["0"] ++ Ns2 ++ ["0"] ++ Ns3 ++ ["1"]),
618    align(BSz,16,A0,All);
619
620marshal_arg(#type{size=Sz,name=Type,base=Base,single=list},Name,A0)
621  when Base =:= float; Base =:= int; Base =:= guard_int ->
622    KeepA = case Sz of 8 -> "0:32,"; _ -> "" end,
623    Str0 = Name++"Len:?GLuint,"++KeepA++"\n"
624	"        (<< <<C:?"++Type++">> || C <- "++Name++">>)/binary",
625    {Str,Align} = align(max([Sz,4]),A0,Str0),
626    align_after(Sz,Align,0,1,Name,Str);
627
628marshal_arg(#type{base=guard_int},Name,A0) ->
629    align(4,A0,Name ++ ":?GLuint");
630
631marshal_arg(#type{size=Sz,name=Type,single=true,
632		  by_val=true,ref=undefined},Name,A0) ->
633    align(Sz,A0,Name ++ ":?" ++ Type);
634
635marshal_arg(#type{size=8,name="GLUquadric"=Type},Name,A0) ->
636    align(8,A0,Name ++ ":?" ++ Type);
637
638marshal_arg(#type{base=string,single=true,ref={pointer,1}},Name,A0) ->
639    Str = "(list_to_binary(["++Name++"|[0]]))/binary", % Null terminate
640    align_after(1,A0,1,1,Name,Str);
641
642marshal_arg(#type{base=string,single=list,ref={pointer,2}},Name,A0) ->
643    Str0 =
644	Name++"Len:?GLuint,"
645        "(size("++Name ++ "Temp)):?GLuint,"
646	"(" ++ Name ++ "Temp)/binary",
647    {Str,A} = align(4,A0,Str0),
648    {Str ++ ",0:((8-((size("++Name++"Temp)+"++
649     integer_to_list(A) ++") rem 8)) rem 8)", 0};
650
651marshal_arg(#type{size=Sz,name=Type,single={tuple_list,TSz}},Name,A0) ->
652    NameId = hd(Name),
653    Names = [[NameId|integer_to_list(Id)] || Id <- lists:seq(1,TSz)],
654    TTup = args(fun(ElName) -> ElName end, ",", Names),
655    TBin = args(fun(ElName) -> ElName ++ ":?" ++ Type end, ",", Names),
656
657    KeepA = case Sz of 8 -> "0:32,"; 4 -> "" end,
658    Str0 = Name++"Len:?GLuint,"++KeepA++"\n"
659	"        (<< <<"++TBin++">> || {"++TTup++"} <- "++Name++">>)/binary",
660    align(Sz,A0,Str0);
661
662marshal_arg(T=#type{}, Name, Align) ->
663    io:format("{\"~s\", {\"~s\", }}.~n", [get(current_func),lowercase(Name)]),
664    %%?error({unhandled_type, {Name,T}}).
665    w(" Don't know how to marshal this type ~p ~p~n", [T,Name]),
666    align(8,Align,"").
667
668% Make sure that it is aligned before adding it, and update alignment
669align(Size, PreAlign, Str) ->
670    align(Size,1,PreAlign,Str).
671
672align(1,N,A,Str) ->                      {Str,          (A+1*N+0) rem 8};
673
674align(2,N,A,Str) when (A rem 2) =:= 0 -> {Str,          (A+2*N+0) rem 8};
675align(2,N,A,Str) when (A rem 2) =:= 1 -> {"0:8," ++Str, (A+2*N+1) rem 8};
676
677align(4,N,A,Str) when (A rem 4) =:= 0 -> {Str,          (A+4*N+0) rem 8};
678align(4,N,A,Str) when (A rem 4) =:= 1 -> {"0:24,"++Str, (A+4*N+3) rem 8};
679align(4,N,A,Str) when (A rem 4) =:= 2 -> {"0:16,"++Str, (A+4*N+2) rem 8};
680align(4,N,A,Str) when (A rem 4) =:= 3 -> {"0:8," ++Str, (A+4*N+1) rem 8};
681
682align(8,_,0,Str) -> {Str,          0};
683align(8,_,1,Str) -> {"0:56,"++Str, 0};
684align(8,_,2,Str) -> {"0:48,"++Str, 0};
685align(8,_,3,Str) -> {"0:40,"++Str, 0};
686align(8,_,4,Str) -> {"0:32,"++Str, 0};
687align(8,_,5,Str) -> {"0:24,"++Str, 0};
688align(8,_,6,Str) -> {"0:16,"++Str, 0};
689align(8,_,7,Str) -> {"0:8," ++Str, 0}.
690
691align_after(8,0,_Add,_Multiplier,_Name,Str) -> {Str,0};
692align_after(4,0,Add,Mult,Name,Str) ->
693    Extra = extra_align(Add,Mult),
694    Align = ",0:((("++Name++"Len"++Extra++") rem 2)*32)",
695    {Str ++ Align,0};
696align_after(4,4,Add,Mult,Name,Str) ->
697    Extra = extra_align(Add,Mult),
698    Align = ",0:(((1+"++Name++"Len"++Extra++") rem 2)*32)",
699    {Str ++ Align,0};
700align_after(2,A,Add,Mult,Name,Str) when (A rem 2) =:= 0 ->
701    Extra = extra_align(A+Add*2,Mult),
702    Align = ",0:((8-(("++Name++"Len*2"++Extra++") rem 8)) rem 8)",
703    {Str ++ Align,0};
704align_after(1,A,Add,Mult,Name,Str) ->
705    Extra = extra_align(A+Add,Mult),
706    Align = ",0:((8-(("++Name++"Len"++Extra++") rem 8)) rem 8)",
707    {Str ++ Align,0};
708align_after(Sz,A,Add,Mult,Name,Str) ->
709    io:format("~p ~p with ~p ~p ~s~n, ~p", [Sz,A,Add,Mult,Name,Str]),
710    ?error(align_error).
711
712extra_align(0,1) -> "";
713extra_align(0,M) when M > 1 -> "* " ++ integer_to_list(M);
714extra_align(A,1) when A > 0 -> "+ " ++ integer_to_list(A);
715extra_align(A,M) when A > 0,M>1 ->
716    "* " ++ integer_to_list(M) ++ "+ " ++ integer_to_list(A).
717
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719
720have_return_vals(void, Ps) ->
721    lists:any(fun(#arg{in=In, type=#type{base=B}}) ->
722		      In =/= true orelse B =:= memory
723	      end, Ps);
724have_return_vals(#type{}, _) -> true.
725
726erl_func_name("glu" ++ Name) ->   check_name(lowercase(Name));
727erl_func_name("gl" ++ Name) ->   check_name(lowercase(Name)).
728
729erl_arg_name(Name) ->    uppercase(Name).
730
731check_name("begin") -> "'begin'";
732check_name("end") -> "'end'";
733check_name(Other) -> Other.
734
735doc_name(N="glGetBufferParameteriv", _) -> N;
736doc_name("glEnd"++What, _) -> "glBegin"++What;
737doc_name("glDisable" ++ What, _) -> "glEnable" ++ What;
738doc_name("glPop" ++ What, _) -> "glPush" ++ What;
739doc_name("glGetBooleanv", _) -> "glGet";
740doc_name("glGetBooleani_v", _) -> "glGet";
741doc_name("glGetIntegerv", _) -> "glGet";
742doc_name("glGetIntegeri_v", _) -> "glGet";
743doc_name("glGetInteger64v", _) -> "glGet";
744doc_name("glGetInteger64i_v", _) -> "glGet";
745doc_name("glGetFloatv", _) -> "glGet";
746doc_name("glGetDoublev", _) -> "glGet";
747doc_name("glGetFloati_v", _) -> "glGet";
748doc_name("glGetDoublei_v", _) -> "glGet";
749doc_name("glUniformMatr" ++ _, _) -> "glUniform";
750doc_name("glTexSubImage" ++ _, _) -> "glTexSubImage";
751doc_name("glFramebufferText" ++ _, _) -> "glFramebufferTexture";
752doc_name("glProgramUniformMatr" ++ _, _) -> "glProgramUniform";
753doc_name(Name, {has_vector,_,_}) ->
754    strip_hard(reverse(Name));
755doc_name(Name, _) ->
756    reverse(strip(reverse(Name))).
757
758strip_hard(Rev) ->
759    case strip(Rev) of
760	Rev ->   reverse(strip2(Rev));
761	Other -> reverse(Other)
762    end.
763
764strip("BRA"++R) -> "BRA"++strip(R);
765strip([$v,$b,$u,$N,N|R]) when N > 47, N < 58 ->R;
766strip([$v,$i,$u,$N,N|R]) when N > 47, N < 58 ->R;
767strip([$v,$s,$u,$N,N|R]) when N > 47, N < 58 ->R;
768strip([$v,$b,$N,N|R]) when N > 47, N < 58 -> R;
769strip([$v,$i,$N,N|R]) when N > 47, N < 58 -> R;
770strip([$v,$s,$N,N|R]) when N > 47, N < 58 -> R;
771strip([$v,$d,$N,N|R]) when N > 47, N < 58 -> R;
772strip([$v,$f,$N,N|R]) when N > 47, N < 58 -> R;
773strip([$b,$u,$N,N|R]) when N > 47, N < 58 -> R;
774strip([$v,$b,$I,N|R]) when N > 47, N < 58 -> R;
775strip([$v,$i,$I,N|R]) when N > 47, N < 58 -> R;
776strip([$v,$s,$I,N|R]) when N > 47, N < 58 -> R;
777strip([$v,$d,$I,N|R]) when N > 47, N < 58 -> R;
778strip([$v,$f,$I,N|R]) when N > 47, N < 58 -> R;
779strip([$b,$u,$I,N|R]) when N > 47, N < 58 -> R;
780
781strip([$v,$b,$u,N,$I|R]) when N > 47, N < 58 ->R;
782strip([$v,$i,$u,N,$I|R]) when N > 47, N < 58 ->R;
783strip([$v,$s,$u,N,$I|R]) when N > 47, N < 58 ->R;
784strip([$v,$b,N,$I|R]) when N > 47, N < 58 -> R;
785strip([$v,$i,N,$I|R]) when N > 47, N < 58 -> R;
786strip([$v,$s,N,$I|R]) when N > 47, N < 58 -> R;
787strip([$v,$d,N,$I|R]) when N > 47, N < 58 -> R;
788strip([$v,$f,N,$I|R]) when N > 47, N < 58 -> R;
789
790strip([$v,$b,$u,N|R]) when N > 47, N < 58 ->R;
791strip([$v,$i,$u,N|R]) when N > 47, N < 58 ->R;
792strip([$v,$s,$u,N|R]) when N > 47, N < 58 ->R;
793strip([$v,$b,N|R]) when N > 47, N < 58 -> R;
794strip([$v,$i,N|R]) when N > 47, N < 58 -> R;
795strip([$v,$s,N|R]) when N > 47, N < 58 -> R;
796strip([$v,$d,N|R]) when N > 47, N < 58 -> R;
797strip([$v,$f,N|R]) when N > 47, N < 58 -> R;
798
799strip([$b,$u,N,$I|R]) when N > 47, N < 58 ->R;
800strip([$i,$u,N,$I|R]) when N > 47, N < 58 ->R;
801strip([$s,$u,N,$I|R]) when N > 47, N < 58 ->R;
802strip([$b,N,$I|R]) when N > 47, N < 58 -> R;
803strip([$i,N,$I|R]) when N > 47, N < 58 -> R;
804strip([$s,N,$I|R]) when N > 47, N < 58 -> R;
805strip([$d,N,$I|R]) when N > 47, N < 58 -> R;
806strip([$f,N,$I|R]) when N > 47, N < 58 -> R;
807
808strip([$b,$u,N|R]) when N > 47, N < 58 ->R;
809strip([$i,$u,N|R]) when N > 47, N < 58 ->R;
810strip([$s,$u,N|R]) when N > 47, N < 58 ->R;
811strip([$b,N|R]) when N > 47, N < 58 -> R;
812strip([$i,N|R]) when N > 47, N < 58 -> R;
813strip([$s,N|R]) when N > 47, N < 58 -> R;
814strip([$d,N|R]) when N > 47, N < 58 -> R;
815strip([$f,N|R]) when N > 47, N < 58 -> R;
816
817strip([$v,$b,$u|R])  -> R;
818strip([$v,$i,$u|R])  -> R;
819strip([$v,$s,$u|R])  -> R;
820strip([$v,$b|R])     -> R;
821strip([$v,$i,$I|R])     -> R;
822strip([$v,$i|R])     -> R;
823strip([$v,$s|R])     -> R;
824strip([$v,$d|R])     -> R;
825strip([$v,$f|R])     -> R;
826
827strip(R = "delban" ++ _) -> R;
828strip([$d,$e|R]) -> [$e|R];
829strip([$f,$e|R]) -> [$e|R];
830strip([$i,$e|R]) -> [$e|R];
831strip([$d,$x|R]) -> [$x|R];
832strip([$f,$x|R]) -> [$x|R];
833strip([$d,$d|R]) -> [$d|R];
834strip([$f,$d|R]) -> [$d|R];
835strip([$i,$l|R]) -> [$l|R];
836strip([$f,$l|R]) -> [$l|R];
837strip([$i,$r|R]) -> [$r|R];
838strip([$f,$r|R]) -> [$r|R];
839strip([$i,$g|R]) -> [$g|R];
840strip([$f,$g|R]) -> [$g|R];
841strip([$i,$n|R]) -> [$n|R];
842strip([$f,$n|R]) -> [$n|R];
843strip([$d,$n|R]) -> [$n|R];
844
845%% strip([$D,$3|R]) -> R;
846%% strip([$D,$2|R]) -> R;
847%% strip([$D,$1|R]) -> R;
848
849strip([$I|R]) -> R;
850strip([$L|R]) -> R;
851strip([$v,R]) -> R;
852strip([N|R]) when N > 47, N < 58 -> R;
853strip([_|R="tceRlg"]) -> R;
854strip([_|R="thgiLlg"]) -> R;
855strip(R) -> R.
856
857strip2([$b,$u|R])  -> R;
858strip2([$i,$u|R])  -> R;
859strip2([$s,$u|R])  -> R;
860strip2([$b|R])     -> R;
861strip2([$i|R])     -> R;
862strip2([$s|R])     -> R;
863strip2([$d|R])     -> R;
864strip2([$f|R])     -> R;
865strip2(R) ->          R.
866
867gen_debug(GL, GLU) ->
868    open_write("../src/gen/gl_debug.hrl"),
869    erl_copyright(),
870    w("%% This file is generated DO NOT EDIT~n~n", []),
871    w("gldebug_table() ->~n[~n", []),
872    [printd(F,gl)  || F <- GL],
873    [printd(F,glu) || F <- GLU],
874    w(" {-1, {mod, func, -1}}~n",[]),
875    w("].~n~n", []),
876    close().
877
878printd([F|R],Mod) when is_list(F) ->
879    printd(F,Mod),
880    printd(R,Mod);
881printd([],_) -> ok;
882printd(F,Mod) ->
883    case get(F) of
884	#func{alt={vector,_VecPos,_Vec}} -> ok;
885	#func{where=erl} -> ok;
886	#func{id=Id, name=Method} ->
887	    w(" {~p, {~s, ~s, 0}},~n", [Id, Mod, erl_func_name(Method)]);
888	_Other ->
889	    io:format("F= ~p => ~p~n", [F, _Other])
890    end.
891