4
%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
19
%%%-------------------------------------------------------------------
21
%%% Author : Dan Gudmundsson <dgud@erix.ericsson.se>
24
%%% Created : 16 Apr 2007 by Dan Gudmundsson <dgud@erix.ericsson.se>
25
%%%-------------------------------------------------------------------
30
-include_lib("xmerl/include/xmerl.hrl").
31
-include("gl_gen.hrl").
33
-import(lists, [foldl/3,foldr/3,reverse/1,reverse/2,keysearch/3,map/2,filter/2]).
34
-import(proplists, [get_value/2,get_value/3]).
36
-import(gen_util,[uppercase_all/1]).
40
code() -> safe(fun gen_code/0,true).
42
devcode() -> spawn(fun() -> safe(fun gen_code/0,false) end).
44
safe(What, QuitOnErr) ->
47
io:format("Completed succesfully~n~n", []),
48
QuitOnErr andalso gen_util:halt(0)
50
catch gen_util:close(),
51
io:format("Error ~p: ~p:~p~n ~p~n",
52
[get(current_func),Err,Reason,erlang:get_stacktrace()]),
54
QuitOnErr andalso gen_util:halt(1)
58
{ok, Opts0} = file:consult("glapi.conf"),
60
Opts = init_defs(Opts0),
61
GLUDefs = parse_glu_defs(Opts),
62
GLDefs = parse_gl_defs(Opts),
63
{GLUDefines,GLUFuncs} = setup(GLUDefs, Opts),
64
{GLDefines,GLFuncs} = setup(GLDefs, Opts),
65
gl_gen_erl:glu_defines(GLUDefines),
66
gl_gen_erl:glu_api(GLUFuncs),
68
gl_gen_erl:gl_defines(GLDefines),
69
gl_gen_erl:gl_api(GLFuncs),
70
gl_gen_erl:gen_debug(GLFuncs,GLUFuncs),
71
gl_gen_c:gen(GLFuncs,GLUFuncs),
77
parse_glu_defs(Opts0) ->
78
All = foldl(fun(File, Acc) -> load_file(File,Opts0,Acc) end, [], ["glu"]),
81
parse_gl_defs(Opts0) ->
82
All = foldl(fun(File, Acc) -> load_file(File,Opts0,Acc) end, [], ["gl","glext"]),
85
load_file(FileName, Opts, Acc) ->
86
File = filename:join(["gl_xml",FileName ++ "_8h.xml"]),
87
put({loaded, FileName}, true),
88
case xmerl_scan:file(File, [{space, normalize}]) of
90
io:format("Skipped File not found ~p ~n", [File]),
93
io:format("Scanning ~p ~n", [File]),
94
%% It's duplicated in xml get sectiondef only once.
95
Content = find_data(Doc#xmlElement.content),
96
lists:foreach(fun(D) -> extract_argnames(D) end, Content),
97
foldl(fun(Data,Acc0) -> parse_file(Data, Opts, Acc0) end,
101
extract_argnames(#xmlElement{name=memberdef,attributes=Attr,content=C}) ->
102
case keysearch(kind, #xmlAttribute.name, Attr) of
103
{value, #xmlAttribute{value = "typedef"}} ->
104
parse_typedef(C,undefined);
108
extract_argnames(_) -> ok.
110
parse_typedef([#xmlElement{name=argsstring,content=[#xmlText{value=AS}]}|R],_) ->
112
parse_typedef([#xmlElement{name=name}|_],undefined) ->
114
parse_typedef([#xmlElement{name=name,content=[#xmlText{value=N}]}|_],AS) ->
115
Args0 = string:tokens(AS," ,()*[]"),
117
Args = get_arg_names(Args0),
118
put({typedef,string:strip(N)},Args)
120
io:format("Error ~p: ~p ~p~n", [N,Args0,Where]),
123
parse_typedef([_|R],AS) ->
125
parse_typedef([],_) -> skip.
127
get_arg_names(As0) ->
128
Args = lists:filter(fun("const") -> false; (_) -> true end, As0),
129
get_arg_names(Args, []).
131
get_arg_names([_Type,Name|R],Acc) ->
132
get_arg_names(R, [Name|Acc]);
133
get_arg_names([],Acc) -> reverse(Acc);
134
get_arg_names(["void"],[]) -> [];
135
get_arg_names(Error,_Acc) -> exit(Error).
137
%% Avoid bugs in (old) doxygen..the new one doesn't have 'em
138
find_data([#xmlElement{name=compounddef, content=C}|_]) -> find_data(C);
139
find_data([#xmlElement{name=sectiondef, attributes=Attr, content=C}|R]) ->
140
case keysearch(kind, #xmlAttribute.name, Attr) of
141
{value, _} -> %% The new one have func define typedef
146
find_data([_Hmm|R]) ->
149
%% io:format("0 ~p ~n",[_Hmm#xmlElement.name]);
156
parse_file(#xmlElement{name=memberdef,attributes=Attr, content=C}, Opts, Acc) ->
157
case keysearch(kind, #xmlAttribute.name, Attr) of
158
{value, #xmlAttribute{value = "function"}} ->
160
Def = parse_func(C, Opts),
162
catch throw:skip -> Acc
163
after erase(current_func)
165
{value, #xmlAttribute{value = "define"}} ->
167
Def = parse_define(C, #def{}, Opts),
169
catch throw:skip -> Acc
171
{value, #xmlAttribute{value = "typedef"}} ->
174
io:format("Hmm ~p~n",[_W]),
177
parse_file(_Hmm,_,Acc) ->
180
parse_define([#xmlElement{name=name,content=[#xmlText{value="API" ++ _}]}|_],_Def,_Os) ->
182
parse_define([#xmlElement{name=name,content=[#xmlText{value="GLAPI"++_}]}|_],_Def,_Os) ->
184
parse_define([#xmlElement{name=name,content=[#xmlText{value="WINGDIAPI"++_}]}|_],_Def,_Os) ->
186
parse_define([#xmlElement{name=name,content=[#xmlText{value=Name}]}|R], Def, Os) ->
187
parse_define(R, Def#def{name=Name}, Os);
188
parse_define([#xmlElement{name=initializer,content=[#xmlText{value=V}]}|_],Def,_Os) ->
189
Val0 = string:strip(V),
193
Val = http_util:hexlist_to_integer(Val1),
194
Def#def{val=Val, type=hex};
196
Val = list_to_integer(Val0),
197
Def#def{val=Val, type=int}
200
Def#def{val=Val0, type=string}
202
parse_define([_|R], D, Opts) ->
203
parse_define(R, D, Opts);
204
parse_define([], D, _Opts) ->
207
parse_func(Xml, Opts) ->
208
{Func,_} = foldl(fun(X,Acc) -> parse_func(X,Acc,Opts) end, {#func{},1}, Xml),
209
#func{params=Args0,type=Type0} = Func,
210
Args = filter(fun(#arg{type=void}) -> false; (_) -> true end, Args0),
212
patch_param(Func#func.name,#arg{name="result",type=Type0},Opts),
213
Func#func{params=reverse(Args), type=Type}.
215
parse_func(#xmlElement{name=type, content=C}, {F,AC}, Os) ->
216
Type = parse_type(drop_empty(C), Os),
217
{F#func{type=Type},AC};
218
parse_func(#xmlElement{name=name, content=[#xmlText{value=C}]},{F,AC},Os) ->
219
Func = string:strip(C),
220
put(current_func, Func),
221
{F#func{name=name(Func,Os)},AC};
222
parse_func(#xmlElement{name=param, content=C},{F,AC},Os) ->
223
Parse = fun(Con, Ac) -> parse_param(Con, Ac, Os) end,
224
Param0 = foldl(Parse, #arg{}, drop_empty(C)),
225
Param = fix_param_name(Param0, F, AC),
226
{add_param(Param, Os, F),AC+1};
227
parse_func(_, F,_) ->
230
fix_param_name(A=#arg{name=undefined,type=T},#func{name=Func},Count) ->
231
TDName = "PFN" ++ uppercase_all(Func) ++ "PROC",
232
case get({typedef,TDName}) of
233
undefined when T == void ->
236
io:format("Didn't find typedef for: ~s~n", [TDName]),
239
try A#arg{name = lists:nth(Count, AS)}
243
fix_param_name(A,_,_) -> A.
245
parse_param(#xmlElement{name=type,content=C}, Arg, Os) ->
246
Arg#arg{type=parse_type(drop_empty(C),Os)};
247
parse_param(#xmlElement{name=declname,content=[C]},Arg,_Os) ->
248
#xmlText{value=Name} = C,
250
parse_param(#xmlElement{name=array,content=[#xmlText{value=C}]},
251
Arg=#arg{type=Type0},_Os) ->
253
[Int] = string:tokens(C, "[] "),
254
Val = list_to_integer(Int),
255
Arg#arg{type=Type0#type{single={tuple,Val}, by_val=true}}
257
?warning("Undefined Array size ~p in ~p ~p~n",
258
[Arg, get(current_func), C]),
259
Arg#arg{type=Type0#type{single={tuple,undefined}, by_val=true}}
263
parse_param(#xmlElement{name=definition}, Arg, _) -> Arg;
264
parse_param(#xmlElement{name=argsstring}, Arg,_) -> Arg;
265
parse_param(#xmlElement{name=briefdescription}, Arg,_) -> Arg;
266
parse_param(#xmlElement{name=detaileddescription}, Arg,_) -> Arg;
267
parse_param(#xmlElement{name=inbodydescription}, Arg,_) -> Arg;
268
parse_param(#xmlElement{name=location}, Arg,_) -> Arg;
269
parse_param(Other, Arg,_) ->
270
io:format("Unhandled Param ~p ~n in ~p~n", [Other,Arg]),
271
?error(unhandled_param).
273
add_param(Arg0=#arg{type=T0}, Opts, F=#func{name=Name,params=Args}) ->
275
%% #type{mod=[const],ref={pointer,1},name="GLubyte"} ->
276
%% Arg0#arg{type=T0#type{base=binary}};
277
#type{mod=[const]} -> Arg0; %% In is true default
278
#type{ref={pointer,_}} -> Arg0#arg{in=false,
279
type=T0#type{single=undefined}};
282
Patched = patch_param(Name,Arg,Opts),
283
F#func{params=[Patched|Args]}.
285
patch_param(Method,P = #arg{name=ArgName},AllOpts) ->
286
case lookup(Method,AllOpts,undefined) of
289
%%io:format("~p ~p => ~p", [Method, ArgName, What]),
291
{ArgName,Fopt} when is_list(Fopt) ->
292
foldl(fun handle_arg_opt/2,P,Fopt);
294
handle_arg_opt(Fopt,P);
296
Opts when is_list(Opts) ->
297
case get_value(ArgName, Opts, undefined) of
299
List when is_list(List) ->
300
foldl(fun handle_arg_opt/2,P,List);
302
handle_arg_opt(Val,P)
307
handle_arg_opt(skip, P) -> P#arg{where=c};
308
%%handle_arg_opt(nowhere, P) -> P#arg{where=nowhere};
309
%%handle_arg_opt(skip_member, _P) -> throw(skip_member);
310
handle_arg_opt(in, P) -> P#arg{in=true};
311
handle_arg_opt(out, P) -> P#arg{in=false};
312
handle_arg_opt(both, P) -> P#arg{in=both};
313
handle_arg_opt(binary, P=#arg{type=T}) ->
314
P#arg{type=T#type{size=undefined,base=binary}};
315
handle_arg_opt({binary,Sz}, P=#arg{type=T}) ->
316
P#arg{type=T#type{size=Sz,base=binary}};
317
handle_arg_opt({type,Type}, P=#arg{type=T}) -> P#arg{type=T#type{name=Type}};
318
handle_arg_opt({single,Opt},P=#arg{type=T}) -> P#arg{type=T#type{single=Opt}};
319
handle_arg_opt({base,Opt}, P=#arg{type=T}) -> P#arg{type=T#type{base=Opt}};
320
handle_arg_opt({c_only,Opt},P) -> P#arg{where=c, alt=Opt}.
322
parse_type([], _Os) -> void;
324
{Type,_Info} = foldl(fun extract_type_info/2,{[],undefined},C),
326
case parse_type2(reverse(Type),Empty,Os) of
327
Empty -> ?error({strange_type, Type});
331
extract_type_info(#xmlText{value=Value}, {Acc, Info}) ->
332
{reverse(foldl(fun extract_type_info2/2, [],
333
string:tokens(Value, " "))) ++ Acc, Info};
334
extract_type_info(#xmlElement{name=ref,attributes=As,
335
content=[#xmlText{value=V}]},
337
{value, #xmlAttribute{value = Refid}} =
338
keysearch(refid,#xmlAttribute.name,As),
339
{value, #xmlAttribute{value = Kind}} =
340
keysearch(kindref,#xmlAttribute.name,As),
341
{reverse(foldl(fun extract_type_info2/2, [],
342
string:tokens(V, " "))) ++ Acc,
344
extract_type_info(What,Acc) ->
345
?error({parse_error,What,Acc}).
347
extract_type_info2("const",Acc) -> [const|Acc];
348
extract_type_info2("*", [{by_ref,{pointer,N}}|Acc]) ->
349
[{by_ref,{pointer,N+1}}|Acc];
350
extract_type_info2("*", Acc) -> [{by_ref,{pointer,1}}|Acc];
351
extract_type_info2("**", Acc) -> [{by_ref,{pointer,2}}|Acc];
352
extract_type_info2(Type, Acc) -> [Type|Acc].
354
parse_type2(["void"], _T, _Opts) -> void;
355
parse_type2([N="void"|R], T, Opts) ->
356
parse_type2(R,T#type{name=N},Opts);
357
parse_type2([const|R],T=#type{mod=Mod},Opts) ->
358
parse_type2(R,T#type{mod=[const|Mod]},Opts);
359
parse_type2(["unsigned"|R],T=#type{mod=Mod},Opts) ->
360
parse_type2(R,T#type{mod=[unsigned|Mod]},Opts);
361
parse_type2([N="GLenum"|R],T,Opts) ->
362
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
363
parse_type2([N="GLboolean"|R],T,Opts) ->
364
parse_type2(R,T#type{name=N, size=1, base=bool},Opts);
365
parse_type2([N="GLbitfield"|R],T,Opts) ->
366
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
367
parse_type2([N="GLvoid"|R],T,Opts) ->
368
parse_type2(R,T#type{name=N, base=idx_binary},Opts);
370
parse_type2([N="GLbyte"|R],T,Opts) ->
371
parse_type2(R,T#type{name=N, size=1, base=int},Opts);
372
parse_type2([N="GLubyte"|R],T,Opts) ->
373
parse_type2(R,T#type{name=N, size=1, base=int},Opts);
374
parse_type2([N="GLshort"|R],T,Opts) ->
375
parse_type2(R,T#type{name=N, size=2, base=int},Opts);
376
parse_type2([N="GLushort"|R],T,Opts) ->
377
parse_type2(R,T#type{name=N, size=2, base=int},Opts);
378
parse_type2([N="GLint"|R],T,Opts) ->
379
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
380
parse_type2([N="GLuint"|R],T,Opts) ->
381
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
382
parse_type2([N="GLsizei"|R],T,Opts) ->
383
parse_type2(R,T#type{name=N, size=4, base=int},Opts);
385
parse_type2([N="GLfloat"|R],T,Opts) ->
386
parse_type2(R,T#type{name=N, size=4,base=float},Opts);
387
parse_type2([N="GLdouble"|R],T,Opts) ->
388
parse_type2(R,T#type{name=N, size=8,base=float},Opts);
389
parse_type2([N="GLclampf"|R],T,Opts) ->
390
parse_type2(R,T#type{name=N, size=4,base=float},Opts);
391
parse_type2([N="GLclampd"|R],T,Opts) ->
392
parse_type2(R,T#type{name=N, size=8,base=float},Opts);
393
parse_type2([N="GLhandleARB"|R],T,Opts) -> %% unsigned int normally (in glext.h) but
394
parse_type2(R,T#type{name=N, size=4,base=int},Opts); %% is void * on Mac!! FIXME
395
parse_type2(["GLchar" ++ _ARB|R],T,Opts) ->
396
parse_type2(R,T#type{name="GLchar",size=1,base=string},Opts);
397
parse_type2(["GLintptr" ++ _ARB|R],T,Opts) ->
398
parse_type2(R,T#type{name="GLintptr",size=8,base=int},Opts);
399
parse_type2(["GLsizeiptr" ++ _ARB|R],T,Opts) ->
400
parse_type2(R,T#type{name="GLsizeiptr",size=8,base=int},Opts);
402
parse_type2([{by_ref,Ref}|R],T,Opts) ->
403
parse_type2(R,T#type{ref=Ref,by_val=false},Opts);
405
%% Let type errors be seen later because we don't know if these unhandled types
407
parse_type2(_A = [Name|R],T,Opts) ->
408
%% io:format("unhandled ~p ~p ~n",[_A,T]),
409
New = T#type{name={unhandled,Name,get(current_func)}},
410
parse_type2(R,New,Opts);
411
parse_type2([], T, _) -> T.
413
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414
%% Function mangling %%
418
foldr(fun(F=#func{name=N},{Fs,Ds}) ->
425
(D=#def{}, {Fs,Ds}) ->
428
Fs = setup_functions(Fs0,Opts,[]),
430
%% Remove duplicates but keep order
431
{Ds,_} = foldl(fun(D=#def{name=Name},{Keep,Defined}) ->
432
case gb_sets:is_member(Name,Defined) of
433
true -> {Keep,Defined};
434
false -> {[D|Keep],gb_sets:add(Name,Defined)}
436
end, {[],gb_sets:empty()}, Ds0),
439
setup_functions([F0|Defs],Opts,Acc) ->
440
put(current_func, F0),
441
{Name, Ext} = get_extension(F0,Opts),
442
%%io:format("~p = ~p + ~p~n", [F0, Name, Ext]),
443
Skip = (not keep(F0,Opts)) andalso (skip(F0,Opts) orelse skip(Ext,Opts)),
446
setup_functions(Defs,Opts,Acc);
448
case setup_extension(Name,Ext,Opts) of
450
setup_functions(Defs,Opts,Acc);
452
setup_functions(Defs,Opts,[New|Acc])
455
setup_functions([],_, Acc) -> reverse(Acc).
457
setup_extension(Name,"",Opts) ->
458
setup_vector_variant(Name,"",Opts);
459
setup_extension(Name,Ext,Opts) ->
462
setup_vector_variant(Name,Ext,Opts);
465
case is_equal(F,OrigF) of
467
put(Name, OrigF#func{ext={ext,Ext}}),
470
setup_vector_variant(Name,"",Opts)
474
setup_vector_variant(Name,Ext,Opts) ->
475
case reverse(Name) of
476
[$v|NoVec] -> %% Hmm might be a vector version
477
RealName = reverse(NoVec,Ext),
478
case get(RealName) of
480
setup_idx_binary(Name,Ext,Opts);
482
verify_args(Name,Ext,Real,RealName,Opts)
485
setup_idx_binary(Name,Ext,Opts)
488
verify_args(Name,Ext,Real = #func{params=RAs},RealName,Opts) ->
489
FuncName = Name ++ Ext,
490
Vector = #func{params=Args} = get(FuncName),
491
case is_vector(Name,Opts) of
493
Check = fun(#arg{type=#type{name=Type}},Acc) ->
494
if Type =:= Acc -> Acc;
495
Acc =:= undefined -> Type;
499
case foldl(Check,undefined,RAs) of
501
setup_idx_binary(Name,Ext,Opts);
503
setup_idx_binary(Name,Ext,Opts);
504
_ when length(Args) =/= 1 ->
505
setup_idx_binary(Name,Ext,Opts);
507
put(FuncName,Vector#func{where=erl,alt={vector,0,RealName}}),
508
put(RealName,Real#func{alt={has_vector,0,FuncName}}),
512
put(FuncName,Vector#func{where=erl,alt={vector,VecPos,RealName}}),
513
put(RealName,Real#func{alt={has_vector,VecPos,FuncName}}),
517
is_vector(Name, Opts) ->
518
Vecs = get_value(vector, Opts, []),
519
lookup(Name, Vecs, false).
521
lookup(Name,[{Vector, VecPos}|R],Def) when is_list(Vector) ->
522
case lists:prefix(Vector,Name) of
524
%%io:format("~s ~s => ~p ~n", [Vector,Name,VecPos]),
526
false -> lookup(Name,R, Def)
528
lookup(Name,[_|R],Def) ->
530
lookup(_,[], Def) -> Def.
532
setup_idx_binary(Name,Ext,_Opts) ->
533
FuncName = Name ++ Ext,
534
Func = #func{params=Args} = get(FuncName),
535
Id = next_id(function),
537
%% Ok warn if single is undefined
538
lists:foreach(fun(#arg{type=#type{base=memory}}) -> ok;
539
(#arg{type=#type{base=idx_binary}}) -> ok;
540
(A=#arg{type=#type{single=undefined}}) ->
541
?warning("~p Unknown size of~n ~p~n",
542
[get(current_func),A]),
543
io:format("{~p, {~p, }}.~n",
544
[get(current_func),A#arg.name]),
549
case setup_idx_binary(Args, []) of
551
put(FuncName, Func#func{id=Id}),
554
put(FuncName, Func#func{id=Id,params=A1}),
555
Extra = FuncName++"Bin",
556
put(Extra, Func#func{params=A2, id=next_id(function)}),
560
setup_idx_binary([A=#arg{in=true,type=T=#type{base=idx_binary}}|R], Acc) ->
561
A1 = A#arg{type=T#type{base=guard_int,size=4}},
562
A2 = A#arg{type=T#type{base=binary}},
564
case setup_idx_binary(R, []) of
566
{Head ++ [A1|R], Head ++ [A2|R]};
568
{Head ++ [A1|R1], Head ++ [A2|R2]}
570
setup_idx_binary([H|R],Acc) ->
571
setup_idx_binary(R,[H|Acc]);
572
setup_idx_binary([],_) -> ignore.
574
is_equal(F1=#func{type=T1,params=A1},F2=#func{type=T2,params=A2}) ->
575
Equal = is_equal_type(T1,T2) andalso is_equal_args(A1,A2),
579
?warning("Skipped Ext Not Equal ~p ~p~n",
580
[F1#func.name,F2#func.name])
584
is_equal_args([],[]) -> true;
585
is_equal_args([_A1=#arg{type=T1}|A1s],[_A2=#arg{type=T2}|A2s]) ->
586
case is_equal_type(T1,T2) of
587
true -> is_equal_args(A1s,A2s);
589
io:format("Diff~n ~p~n ~p ~n~n", [_A1,_A2]),
593
is_equal_type(T,T) -> true;
594
is_equal_type(#type{name="GLcharARB"},#type{name="GLchar"}) -> true;
595
is_equal_type(#type{name="GLhandleARB"},#type{name="GLuint"}) -> true;
596
is_equal_type(#type{name="GLenum"},#type{name="GLuint"}) -> true;
597
is_equal_type(#type{name="GLenum"},#type{name="GLint"}) -> true;
598
is_equal_type(#type{base=idx_binary},#type{base=guard_int}) -> true;
599
is_equal_type(#type{base=idx_binary},#type{base=memory}) -> true;
600
is_equal_type(#type{base=B,single=S,name=N,size=Sz},
601
#type{base=B,single=S,name=N,size=Sz}) -> true;
602
is_equal_type(_,_) -> false.
605
Skip = get_value(skip, Opts, []),
606
lists:any(fun(Prefix) -> lists:prefix(Prefix,Name) end, Skip).
609
Skip = get_value(keep, Opts, []),
610
lists:any(fun(Prefix) -> lists:prefix(Prefix,Name) end, Skip).
612
get_extension(ExtName,_Opts) ->
613
case reverse(ExtName) of
614
"BRA" ++ Name -> {reverse(Name),"ARB"};
615
"TXE" ++ Name -> {reverse(Name),"EXT"};
616
"ASEM" ++ Name -> {reverse(Name),"MESA"};
617
"ITA" ++ Name -> {reverse(Name),"ATI"};
618
"VN" ++ Name -> {reverse(Name),"NV"}; %Nvidia
619
"ELPPA"++ Name -> {reverse(Name),"APPLE"};
620
"LETNI"++ Name -> {reverse(Name),"INTEL"};
621
"NUS" ++ Name -> {reverse(Name),"SUN"};
622
"XNUS" ++ Name -> {reverse(Name),"SUNX"};
623
"IGS" ++ Name -> {reverse(Name),"SGI"};
624
"SIGS" ++ Name -> {reverse(Name),"SGIS"};
625
"XIGS" ++ Name -> {reverse(Name),"SGIX"};
626
"XFD3" ++ Name -> {reverse(Name),"3DFX"};
627
"MBI" ++ Name -> {reverse(Name),"IBM"};
628
"RGNI" ++ Name -> {reverse(Name),"INGR"};
629
"IGP" ++ Name -> {reverse(Name),"PGI"};
630
"PH" ++ Name -> {reverse(Name),"HP"};
631
"YDEMERG" ++ Name -> {reverse(Name),"GREMEDY"};
632
%%["" ++ Name] -> {Name; %%
639
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642
filter(fun(#xmlText { value = Text}) ->
643
string:strip(Text) =/= "";
648
name(Name, _Opts) -> Name.
651
Next = case get(What) of
652
undefined -> 5001; %% Opengl