From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / android / generate_files.pl
1 #!/usr/bin/perl -w
2 # Copyright © 2008-2015 Jamie Zawinski <jwz@jwz.org>
3 # Copyright © 2015 Dennis Sheil <dennis@panaceasupplies.com>
4 #
5 # Permission to use, copy, modify, distribute, and sell this software and its
6 # documentation for any purpose is hereby granted without fee, provided that
7 # the above copyright notice appear in all copies and that both that
8 # copyright notice and this permission notice appear in supporting
9 # documentation.  No representations are made about the suitability of this
10 # software for any purpose.  It is provided "as is" without express or 
11 # implied warranty.
12 #
13 # This parses the .c and .xml files and makes sure they are in sync: that
14 # options are spelled the same, and that all the numbers are in sync.
15 # Some of that functionality may be removed in the future.
16 #
17 # This also generates necessary Android files based on the information in
18 # those source and XML files.
19 #
20 # For the moment, the get_keys_and_values() subroutine is where support for
21 # previously unsupported Android live wallpapers is added.
22 #
23 # Created:  13-May-2015.
24
25 require 5;
26 use diagnostics;
27 use strict;
28
29 my $progname = $0; $progname =~ s@.*/@@g;
30 my ($version) = ('$Revision: 1.1 $' =~ m/\s(\d[.\d]+)\s/s);
31
32 my $verbose = 0;
33
34
35 my $xlockmore_default_opts = '';
36 foreach (qw(count cycles delay ncolors size font)) {
37   $xlockmore_default_opts .= "{\"-$_\", \".$_\", XrmoptionSepArg, 0},\n";
38 }
39 $xlockmore_default_opts .= 
40  "{\"-wireframe\", \".wireframe\", XrmoptionNoArg, \"true\"},\n" .
41  "{\"-3d\", \".use3d\", XrmoptionNoArg, \"true\"},\n" .
42  "{\"-no-3d\", \".use3d\", XrmoptionNoArg, \"false\"},\n";
43
44 my $thread_default_opts = 
45   "{\"-threads\",    \".useThreads\", XrmoptionNoArg, \"True\"},\n" .
46   "{\"-no-threads\", \".useThreads\", XrmoptionNoArg, \"False\"},\n";
47
48 my $analogtv_default_opts = '';
49 foreach (qw(color tint brightness contrast)) {
50   $analogtv_default_opts .= "{\"-tv-$_\", \".TV$_\", XrmoptionSepArg, 0},\n";
51 }
52
53 $analogtv_default_opts .= $thread_default_opts;
54
55
56 sub parse_settings_xml($) {
57
58   my ($saver) = @_;
59
60   my $file = "project/xscreensaver/res/values/settings.xml";
61   my $body = '';
62
63   local *IN;
64
65   if (-e $file) {
66       open (IN, '<', $file) || error ("$file: $!");
67   }
68   else {
69       my @short;
70       return @short;
71   }
72
73   while (<IN>) { $body .= $_; }
74   close IN;
75   $file =~ s@^.*/@@;
76   $body =~ s/<!--.*?-->/ /gsi;
77   $body =~ s/\s+/ /gs;
78   $body =~ s/</\001</gs;
79
80   my (@all);
81   my $loop;
82
83   foreach (split (m/\001/, $body)) {
84     next if (m/^\s*$/s);
85     my ($type, $args) = m@^<([?/]?[-_a-z]+)\b\s*(.*)$@si;
86     error ("$progname: $file: unparsable: $_") unless $type;
87     next if ($type =~ m@^/@);
88
89     if ($type =~ m/^(\?xml|resources)/s) {
90
91     } elsif ($type eq 'string-array') {
92       my ($name) = ($args =~ m/\bname\s*=\s*\"([^\"]+)\"/);
93       my ($value) = ($args =~ m/>([^\"]+)/);
94       $loop = $name;
95
96       if ($name =~ /^$saver/) {
97         error ("$saver: $saver already in $file");
98       }
99
100     } elsif ($type eq 'item') {
101
102       my ($item_value) = ($args =~ m/>(.+)/);
103       my $item = $loop . " = " . $item_value;
104       push @all, $item;
105
106     } else {
107       error ("$file: unknown type \"$type\" for no arg");
108     }
109   }
110
111   return @all;
112
113 }
114
115
116 sub parse_items_xml($) {
117
118   my ($saver) = @_;
119
120   my $file = "project/xscreensaver/res/values/items.xml";
121   my $body = '';
122   my (%pixkeys) ;
123
124   local *IN;
125
126   if (-e $file) {
127       open (IN, '<', $file) || error ("$file: $!");
128   }
129   else {
130       my %short;
131       return %short;
132   }
133
134   while (<IN>) { $body .= $_; }
135   close IN;
136   $file =~ s@^.*/@@;
137   $body =~ s/<!--.*?-->/ /gsi;
138
139   $body =~ s/\s+/ /gs;
140   $body =~ s/</\001</gs;
141
142   foreach (split (m/\001/, $body)) {
143     next if (m/^\s*$/s);
144     my ($type, $args) = m@^<([?/]?[-_a-z]+)\b\s*(.*)$@si;
145     error ("$progname: $file: unparsable: $_") unless $type;
146     next if ($type =~ m@^/@);
147
148     if ($type =~ m/^(\?xml|resources)/s) {
149
150     } elsif ($type eq 'item') {
151       my ($name) = ($args =~ m/\bname\s*=\s*\"([^\"]+)\"/);
152       my ($value) = ($args =~ m/>([^\"]+)/);
153
154       if ($name =~ /^$saver/) {
155         error ("$saver: $saver already in $file");
156       }
157
158       $pixkeys{$name} = $value;
159
160     } else {
161       error ("$file: unknown type \"$type\" for no arg");
162     }
163   }
164
165   return (%pixkeys);
166 }
167
168
169 sub parse_glue($) {
170   my ($saver) = @_;
171   my $file = "gen/glue.c";
172   my $in;
173
174   if (-e $file) {
175       open ($in, '<', $file) || error ("$file: $!");
176   }
177   else {
178       my @short;
179       return @short;
180   }
181
182   my $body = '';
183   while (<$in>) { $body .= $_; }
184   close $in;
185   $file =~ s@^.*/@@;
186   $body =~ s@^#\s*(if|ifdef|ifndef|elif|else|endif).*$@@gm;
187
188   my (@hacks);
189   if ($body =~ m/table\s*\*([a-z,\s\*_]+)xscreensaver_function_table;/s) {
190     foreach (split (/,\s*\n/, $1)) {
191       s/^\s*//s;
192       s/\*//s;
193       my @ftables = split (/_/, $_);
194       my $ftable = $ftables[0];
195       if ($ftable eq $saver) {
196          error("$saver is already in glue");
197       }
198       push @hacks, $ftable;
199     }
200   }
201   return @hacks;
202 }
203
204 # Returns two tables:
205 # - A table of the default resource values.
206 # - A table of "-switch" => "resource: value", or "-switch" => "resource: %"
207 #
208 sub parse_src($) {
209   my ($saver) = @_;
210   my $ffile = lc($saver) . ".c";
211
212   # kludge...
213   $ffile = 'apple2-main.c' if ($ffile eq 'apple2.c');
214   $ffile = 'sproingiewrap.c' if ($ffile eq 'sproingies.c');
215   $ffile = 'b_lockglue.c' if ($ffile eq 'bubble3d.c');
216   $ffile = 'polyhedra-gl.c' if ($ffile eq 'polyhedra.c');
217   $ffile = 'companion.c' if ($ffile eq 'companioncube.c');
218
219   my $file = "../hacks/" . $ffile;
220
221   $file = "../hacks/glx/$ffile" unless (-f $file);
222   my $body = '';
223   open (my $in, '<', $file) || error ("$file: $!");
224   while (<$in>) { $body .= $_; }
225   close $in;
226   $file =~ s@^.*/@@;
227
228   my $xlockmore_p = 0;
229   my $thread_p = ($body =~ m/THREAD_DEFAULTS/);
230   my $analogtv_p = ($body =~ m/ANALOGTV_DEFAULTS/);
231
232   $body =~ s@/\*.*?\*/@@gs;
233   $body =~ s@^#\s*(if|ifdef|ifndef|elif|else|endif).*$@@gm;
234   $body =~ s/(THREAD|ANALOGTV)_(DEFAULTS|OPTIONS)(_XLOCK)?//gs;
235
236   print STDERR "$progname: $file: defaults:\n" if ($verbose > 2);
237   my %res_to_val;
238   if ($body =~ m/_defaults\s*\[\]\s*=\s*{(.*?)}\s*;/s) {
239     foreach (split (/,\s*\n/, $1)) {
240       s/^\s*//s;
241       s/\s*$//s;
242       next if m/^0?$/s;
243       my ($key, $val) = m@^\"([^:\s]+)\s*:\s*(.*?)\s*\"$@;
244       print STDERR "$progname: $file: unparsable: $_\n" unless $key;
245       $key =~ s/^[.*]//s;
246       $res_to_val{$key} = $val;
247       print STDERR "$progname: $file:   $key = $val\n" if ($verbose > 2);
248     }
249   } elsif ($body =~ m/\#\s*define\s*DEFAULTS\s*\\?\s*(.*?)\n[\n#]/s) {
250     $xlockmore_p = 1;
251     my $str = $1;
252     $str =~ s/\"\s*\\\n\s*\"//gs;
253     $str =~ m/^\s*\"(.*?)\"\s*\\?\s*$/ || 
254       error ("$file: unparsable defaults: $str");
255     $str = $1;
256     $str =~ s/\s*\\n\s*/\n/gs;
257     foreach (split (/\n/, $str)) {
258       my ($key, $val) = m@^([^:\s]+)\s*:\s*(.*?)\s*$@;
259       print STDERR "$progname: $file: unparsable: $_\n" unless $key;
260       $key =~ s/^[.*]//s;
261       $res_to_val{$key} = $val;
262       print STDERR "$progname: $file:   $key = $val\n" if ($verbose > 2);
263     }
264
265     while ($body =~ s/^#\s*define\s+(DEF_([A-Z\d_]+))\s+\"([^\"]+)\"//m) {
266       my ($key1, $key2, $val) = ($1, lc($2), $3);
267       $key2 =~ s/_(.)/\U$1/gs;  # "foo_bar" -> "fooBar"
268       $key2 =~ s/Rpm/RPM/;      # kludge
269       $res_to_val{$key2} = $val;
270       print STDERR "$progname: $file:   $key1 ($key2) = $val\n" 
271         if ($verbose > 2);
272     }
273
274   } else {
275     error ("$file: no defaults");
276   }
277
278   $body =~ m/XSCREENSAVER_MODULE(_2)?\s*\(\s*\"([^\"]+)\"/ ||
279     error ("$file: no module name");
280   $res_to_val{progclass} = $2;
281   $res_to_val{doFPS} = 'false';
282   print STDERR "$progname: $file:   progclass = $2\n" if ($verbose > 2);
283
284   print STDERR "$progname: $file: switches to resources:\n"
285     if ($verbose > 2);
286   my %switch_to_res;
287   $switch_to_res{-fps} = 'doFPS: true';
288   $switch_to_res{-fg}  = 'foreground: %';
289   $switch_to_res{-bg}  = 'background: %';
290
291   my ($ign, $opts) = ($body =~ m/(_options|\bopts)\s*\[\]\s*=\s*{(.*?)}\s*;/s);
292   if  ($xlockmore_p || $thread_p || $analogtv_p || $opts) {
293     $opts = '' unless $opts;
294     $opts .= ",\n$xlockmore_default_opts" if ($xlockmore_p);
295     $opts .= ",\n$thread_default_opts" if ($thread_p);
296     $opts .= ",\n$analogtv_default_opts" if ($analogtv_p);
297
298     foreach (split (/,\s*\n/, $opts)) {
299       s/^\s*//s;
300       s/\s*$//s;
301       next if m/^$/s;
302       next if m/^{\s*0\s*,/s;
303       my ($switch, $res, $type, $v0, $v1, $v2) =
304         m@^ \s* { \s * \"([^\"]+)\" \s* ,
305                   \s * \"([^\"]+)\" \s* ,
306                   \s * ([^\s]+)     \s* ,
307                   \s * (\"([^\"]*)\"|([a-zA-Z\d_]+)) \s* }@xi;
308       print STDERR "$progname: $file: unparsable: $_\n" unless $switch;
309       my $val = defined($v1) ? $v1 : $v2;
310       $val = '%' if ($type eq 'XrmoptionSepArg');
311       $res =~ s/^[.*]//s;
312       $res =~ s/^[a-z\d]+\.//si;
313       $switch =~ s/^\+/-no-/s;
314
315       $val = "$res: $val";
316       if (defined ($switch_to_res{$switch})) {
317         print STDERR "$progname: $file:   DUP! $switch = \"$val\"\n" 
318           if ($verbose > 2);
319       } else {
320         $switch_to_res{$switch} = $val;
321         print STDERR "$progname: $file:   $switch = \"$val\"\n" 
322           if ($verbose > 2);
323       }
324     }
325   } else {
326     error ("$file: no options");
327   }
328
329   return (\%res_to_val, \%switch_to_res);
330 }
331
332 # Returns a list of:
333 #    "resource = default value"
334 # or "resource != non-default value"
335 #
336 sub parse_manifest_xml($$) {
337   my @result = ();
338   my ($saver, $switch_to_res) = @_;
339   my $file = "project/xscreensaver/AndroidManifest.xml";
340   my $body = '';
341   local *IN;
342
343   if (-e $file) {
344       open (IN, "<$file") || error ("$file: $!");
345   }
346   else {
347       return @result;
348   }
349
350   while (<IN>) { $body .= $_; }
351   close IN;
352   $file =~ s@^.*/@@;
353
354   $body =~ s/<!--.*?-->/ /gsi;
355
356   $body =~ s/\s+/ /gs;
357   $body =~ s/</\001</gs;
358   $body =~ s/\001(<option)/$1/gs;
359
360   print STDERR "$progname: $file: options:\n" if ($verbose > 2);
361
362   foreach (split (m/\001/, $body)) {
363     next if (m/^\s*$/s);
364     my ($type, $args) = m@^<([?/]?[-_a-z]+)\b\s*(.*)$@si;
365     error ("$progname: $file: unparsable: $_") unless $type;
366     next if ($type =~ m@^/@);
367     if ($type eq 'meta-data') {
368         my ($value) = ($args =~ m/\@xml\/([^\"]+)\"/);
369         push @result, $value;
370     }
371   }
372   return @result;
373 }
374
375 # Returns a list of:
376 #    "resource = default value"
377 # or "resource != non-default value"
378 #
379 sub parse_strings_xml($$) {
380   my @result = ();
381   my ($saver, $switch_to_res) = @_;
382   my $file = "project/xscreensaver/res/values/strings.xml";
383   my $body = '';
384   local *IN;
385
386   if (-e $file) {
387       open (IN, "<$file") || error ("$file: $!");
388   }
389   else {
390       return @result;
391   }
392
393   while (<IN>) { $body .= $_; }
394   close IN;
395   $file =~ s@^.*/@@;
396
397   $body =~ s/<!--.*?-->/ /gsi;
398
399   $body =~ s/\s+/ /gs;
400   $body =~ s/</\001</gs;
401   $body =~ s/\001(<option)/$1/gs;
402
403   print STDERR "$progname: $file: options:\n" if ($verbose > 2);
404
405   my $saver_name = $saver . "_name";
406
407   foreach (split (m/\001/, $body)) {
408     next if (m/^\s*$/s);
409     my ($type, $args) = m@^<([?/]?[-_a-z]+)\b\s*(.*)$@si;
410     error ("$progname: $file: unparsable: $_") unless $type;
411     next if ($type =~ m@^/@);
412
413     if ($type =~ m/^([hv]group|\?xml|resources|xscreensaver-(image|text|updater))/s) {
414
415     } elsif ($type eq 'string') {
416       my ($name) = ($args =~ m/\bname\s*=\s*\"([^\"]+)\"/);
417       my ($value) = ($args =~ m/>([^\"]+)/);
418       my ($val) = "$name = $value";
419       if ($saver_name eq $name) {
420         error ("$saver: $saver already in $file");
421       }
422       push @result, $val;
423     } elsif ($type eq 'item')  {
424       # ignore
425     } else {
426       error ("$file: unknown type \"$type\" for no arg");
427     }
428   }
429
430   return @result;
431 }
432
433
434
435 # Returns a list of:
436 #    "resource = default value"
437 # or "resource != non-default value"
438 #
439 sub parse_xml($$) {
440   my ($saver, $switch_to_res) = @_;
441   my $file = "../hacks/config/" . lc($saver) . ".xml";
442   my $body = '';
443   local *IN;
444   open (IN, "<$file") || error ("$file: $!");
445   while (<IN>) { $body .= $_; }
446   close IN;
447   $file =~ s@^.*/@@;
448
449   my @result = ();
450
451   $body =~ s/<!--.*?-->/ /gsi;
452
453   $body =~ s/\s+/ /gs;
454   $body =~ s/</\001</gs;
455   $body =~ s/\001(<option)/$1/gs;
456
457   my $video = undef;
458
459   print STDERR "$progname: $file: options:\n" if ($verbose > 2);
460   foreach (split (m/\001/, $body)) {
461     next if (m/^\s*$/s);
462     my ($type, $args) = m@^<([?/]?[-_a-z]+)\b\s*(.*)$@si;
463
464     my $type_val;
465     error ("$progname: $file: unparsable: $_") unless $type;
466     next if ($type =~ m@^/@);
467
468     if ($type =~ m/^([hv]group|\?xml|command|string|file|_description|xscreensaver-(image|text|updater))/s) {
469
470     } elsif ($type eq 'screensaver') {
471       my ($name) = ($args =~ m/\b_label\s*=\s*\"([^\"]+)\"/);
472       my $val = "progclass = $name";
473       push @result, $val;
474       print STDERR "$progname: $file:   name:    $name\n" if ($verbose > 2);
475
476     } elsif ($type eq 'video') {
477       error ("$file: multiple videos") if $video;
478       ($video) = ($args =~ m/\bhref="(.*?)"/);
479       error ("$file: unparsable video") unless $video;
480       error ("$file: unparsable video URL")
481         unless ($video =~ m@^https?://www\.youtube\.com/watch\?v=[^?&]+$@s);
482
483     } elsif ($type eq 'number') {
484       my ($arg) = ($args =~ m/\barg\s*=\s*\"([^\"]+)\"/);
485       my ($val) = ($args =~ m/\bdefault\s*=\s*\"([^\"]+)\"/);
486       $val = '' unless defined($val);
487
488       my ($low) = ($args =~ m/\blow\s*=\s*\"([^\"]+)\"/);
489       my ($high) = ($args =~ m/\bhigh\s*=\s*\"([^\"]+)\"/);
490
491       my ($ll) = ($args =~ m/\b_low-label\s*=\s*\"([^\"]+)\"/);
492       my ($hl) = ($args =~ m/\b_high-label\s*=\s*\"([^\"]+)\"/);
493
494       my $switch = $arg;
495       $switch =~ s/\s+.*$//;
496       my ($res) = $switch_to_res->{$switch};
497       error ("$file: no resource for $type switch \"$arg\"") unless $res;
498       $res =~ s/: \%$//;
499       error ("$file: unparsable value: $res") if ($res =~ m/:/);
500
501       $type_val = "$res" . "_type = $type";
502       push @result, $type_val;
503       $val = "$res = $val";
504       push @result, $val;
505       $val = "$res" . "_low = $low";
506       push @result, $val;
507       $val = "$res" . "_high = $high";
508       push @result, $val;
509       $val = "$res" . "_low-label = $ll";
510       push @result, $val;
511       $val = "$res" . "_high-label = $hl";
512       push @result, $val;
513
514       print STDERR "$progname: $file:   number:  $val\n" if ($verbose > 2);
515
516     } elsif ($type eq 'boolean') {
517       my ($set)   = ($args =~ m/\barg-set\s*=\s*\"([^\"]+)\"/);
518       my ($unset) = ($args =~ m/\barg-unset\s*=\s*\"([^\"]+)\"/);
519       my ($arg) = $set || $unset || error ("$file: unparsable: $args");
520       my ($res) = $switch_to_res->{$arg};
521         error ("$file: no resource for boolean switch \"$arg\"") unless $res;
522       my ($res2, $val) = ($res =~ m/^(.*?): (.*)$/s);
523       error ("$file: unparsable boolean resource: $res") unless $res2;
524       $res = $res2;
525       $type_val = "$res" . "_type = $type";
526       push @result, $type_val;
527 #      $val = ($set ? "$res != $val" : "$res = $val");
528       $val = "$res != $val";
529       push @result, $val;
530
531       print STDERR "$progname: $file:   boolean: $val\n" if ($verbose > 2);
532
533     } elsif ($type eq 'select') {
534       $args =~ s/</\001</gs;
535       my @opts = split (/\001/, $args);
536       shift @opts;
537       my $unset_p = 0;
538       my $this_res = undef;
539       foreach (@opts) {
540         error ("$file: unparsable: $_") unless (m/^<option\s/);
541         my ($set) = m/\barg-set\s*=\s*\"([^\"]+)\"/;
542         if ($set) {
543           my ($set2, $val) = ($set =~ m/^(.*?) (.*)$/s);
544           $set = $set2 if ($set2);
545           my ($res) = $switch_to_res->{$set};
546           error ("$file: no resource for select switch \"$set\"") unless $res;
547
548           my ($res2, $val2) = ($res =~ m/^(.*?): (.*)$/s);
549           error ("$file: unparsable select resource: $res") unless $res2;
550           $res = $res2;
551           $type_val = "$res" . "_type = $type";
552           push @result, $type_val;
553           $val = $val2 unless ($val2 eq '%');
554
555           error ("$file: mismatched resources: $res vs $this_res")
556             if (defined($this_res) && $this_res ne $res);
557           $this_res = $res;
558
559           $val = "$res != $val";
560           push @result, $val;
561           $val = "$res" . "_type = $type";
562           push @result, $val;
563
564           print STDERR "$progname: $file:   select:  $val\n" if ($verbose > 2);
565
566         } else {
567           error ("$file: multiple default options: $set") if ($unset_p);
568           $unset_p++;
569         }
570       }
571
572     } else {
573       error ("$file: unknown type \"$type\" for no arg");
574     }
575   }
576
577 #  error ("$file: no video") unless $video;
578   print STDERR "\n$file: WARNING: no video\n\n" unless $video;
579
580   return @result;
581 }
582
583
584 sub parse_then_make($) {
585   my ($saver) = @_;
586
587   # kludge
588   return 0 if ($saver =~ m/(-helper)$/);
589
590   my ($src_opts, $switchmap) = parse_src ($saver);
591   my (@xml_opts) = parse_xml ($saver, $switchmap);
592
593   # tests if hack is supported yet
594   my (@test) = get_keys_and_values($saver, @xml_opts);
595   my (@strings_xml_opts) = parse_strings_xml ($saver, $switchmap);
596   my (%pixkeys) =  parse_items_xml($saver);
597   my (@manifest_xml_opts) = parse_manifest_xml ($saver, $switchmap);
598   my (@glue_hacks) = parse_glue($saver);
599   my (@settings_xml_opts) = parse_settings_xml($saver);
600
601   my (@all_settings) = get_settings($saver, $switchmap, \@xml_opts);
602
603   make_settings($saver);
604   make_service($saver);
605   make_wallpaper($saver, @xml_opts);
606
607   make_manifest($saver, @manifest_xml_opts);
608
609   make_hack_xml($saver);
610   make_hack_settings_xml($saver, @xml_opts);
611   make_strings_xml($saver, \@xml_opts, \@strings_xml_opts);
612   make_items_xml($saver, \@xml_opts, \%pixkeys);
613   make_settings_xml($saver, \@all_settings, \@settings_xml_opts);
614
615   make_glue($saver, @glue_hacks);
616
617   return 0;
618 }
619
620
621 sub make_manifest($$) {
622   my ($saver, @manifest_opts) = @_;
623   push @manifest_opts, $saver unless grep{$_ eq $saver} @manifest_opts;
624   my $hack = ucfirst($saver);
625   my $file = "project/xscreensaver/AndroidManifest.xml";
626   open (my $in, '>', $file) || error ("$file: $!");
627
628   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
629               "<manifest " .
630               "xmlns:android=\"http://schemas.android.com/apk/res/android\" " .
631               "package=\"org.jwz.xscreensaver\"\n" .
632               "android:versionCode=\"1\"\n" .
633               "android:versionName=\"1.0\">\n" .
634               "<uses-sdk android:minSdkVersion=\"14\" " .
635               "android:targetSdkVersion=\"19\" />\n" .
636               "<application android:icon=\"\@drawable/thumbnail\" " .
637               "android:label=\"\@string/app_name\" " .
638               "android:name=\".XscreensaverApp\">\n\n");
639
640   foreach my $save (@manifest_opts) {
641       my $hac = ucfirst($save);
642       $body = $body . ("<service android:label=\"\@string/" . $save . 
643               "_name\" android:name=\".gen." . $hac . "Service\" " .
644               "android:permission=\"android.permission.BIND_WALLPAPER\">\n" .
645               " <intent-filter>\n" .
646               "   <action " .
647               "android:name=\"android.service.wallpaper.WallpaperService\" " .
648               "/>\n" .
649               " </intent-filter>\n" .
650               " <meta-data android:name=\"android.service.wallpaper\" " .
651               "android:resource=\"\@xml/" . $save . "\" />\n" .
652               "</service>\n" .
653               "<activity " .
654               "android:label=\"\@string/" . $save . "_settings\" " .
655               "android:name=\"org.jwz.xscreensaver.gen." . $hac . 
656               "Settings\" " .
657               "android:theme=\"\@android:style/Theme.Light.WallpaperSettings\" " .
658               "android:exported=\"true\">\n" .
659               "</activity>\n\n");
660
661   }
662
663   $body = $body . ("</application>\n\n" .
664               "<uses-sdk android:minSdkVersion=\"14\" />\n" .
665               "<uses-feature " .
666               "android:name=\"android.software.live_wallpaper\" " .
667               "android:required=\"true\" />\n" .
668               "</manifest>\n");
669
670   print $in $body;
671   close $in;
672 }
673
674
675 sub make_hack_settings_xml($$) {
676
677   my ($saver, @xml_opts) = @_;
678   my $hack = ucfirst($saver);
679   my $file = "project/xscreensaver/res/xml/" . $saver . "_settings.xml";
680   my (%saver_keys) = get_keys_and_values($saver, @xml_opts);
681
682   open (my $in, '>', $file) || error ("$file: $!");
683
684   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
685               "<PreferenceScreen xmlns:android=" .
686               "\"http://schemas.android.com/apk/res/android\">\n" .
687               "    <PreferenceCategory\n" .
688               "        android:title=\"\@string/" . $saver .
689               "_settings\"\n" .
690               "        android:key=\"" . $saver .
691               "wallpaper_settings\">\n");
692
693   my @keyarray = keys %saver_keys;
694
695
696   foreach my $sgkey (@keyarray) {
697
698     my $type = get_type($sgkey, @xml_opts);
699
700
701     if ($type eq "number") {
702         $body = $body . "    <org.jwz.xscreensaver.SliderPreference\n" .
703               "        android:defaultValue=\"\@string/" . $saver .
704               "_" . $sgkey .
705               "_float\"\n" .
706               "        android:dialogMessage=\"\@string/" . $saver .
707               "_" . $sgkey .
708               "_settings_summary\"\n" .
709               "        android:key=\"" . $saver . "_" . $sgkey .
710               "\"\n" .
711               "        android:summary=\"\@array/" . $saver . "_" . $sgkey .
712               "_prefix\"\n" .
713               "        android:title=\"\@string/" . $saver . "_" . $sgkey .
714               "_settings_title\" />\n";
715      } else {
716          $body = $body .  "    <ListPreference\n" .
717               "            android:key=\"" . $saver . "_" . $sgkey .
718               "\"\n" .
719               "            android:title=\"\@string/" . $saver . "_" . $sgkey .
720               "_settings_title\"\n" .
721               "            android:summary=\"\@string/$saver" . "_" . $sgkey .
722               "_settings_summary\"\n" .
723               "            android:entries=\"\@array/$saver" . "_$sgkey" .
724               "_names\"\n" .
725               "            android:defaultValue=\"\@string/" . $saver . 
726               "_" . $sgkey . "_default" . "\"\n" .
727               "            android:entryValues=\"\@array/$saver" .
728               "_$sgkey" .
729               "_prefix\" />\n";
730      }
731   }
732
733   $body = $body .   "    </PreferenceCategory>\n" .
734               "</PreferenceScreen>\n";
735
736   print $in $body;
737   close $in;
738 }
739
740
741 sub make_items_xml($\@\%) {
742   my $saver = $_[0];
743   my @xml_opts = @{$_[1]};
744   my %pixkeys = %{$_[2]};
745
746   my $file = "project/xscreensaver/res/values/items.xml";
747
748   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
749               "<resources>\n");
750
751   while(my($key, $value) = each %pixkeys) {
752       $body = $body . "    <item name=\"" . $key .
753              "\" format=\"float\" type=\"string\">". $value . "</item>\n";
754
755   }
756
757   my (%saver_keys) = get_keys_and_values($saver, @xml_opts);
758   my @keyarray = keys %saver_keys;
759
760   foreach my $item_key (@keyarray) {
761
762     my $type = get_type($item_key, @xml_opts);
763
764     if ($type eq "number") {
765
766       my ($low, $high, $default) = get_low_high_def($item_key, @xml_opts);
767       my $float = ($default - $low) / ($high - $low);
768
769       $body = ($body .
770               "    <item name=\"" . $saver . "_" . $item_key .
771               "_float\" format=\"float\" type=\"string\">$float</item>\n");
772     }
773   }
774
775   $body =    ($body .
776               "</resources>\n");
777   open (my $in, '>', $file) || error ("$file: $!");
778   print $in $body;
779   close $in;
780 }
781
782
783 sub get_type($@) {
784
785     my($type_key, @xml_opts) = @_;
786     my $type='';
787
788     foreach my $claim (@xml_opts) {
789
790         my ($res, $compare, $xval) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
791         if ($res eq $type_key . "_type") {
792             $type = $xval;
793         }
794
795     }
796     return $type;
797
798 }
799
800
801 sub get_low_high_def($@) {
802
803     my($sgkey, @xml_opts) = @_;
804
805     my $low;
806     my $high;
807     my $default;
808
809     foreach my $claim (@xml_opts) {
810         my ($res, $compare, $xval) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
811         if ($res eq $sgkey . "_low") {
812             $low = $xval;
813         }
814         elsif ($res eq $sgkey . "_high") {
815             $high = $xval;
816         }
817         elsif ($res eq $sgkey) {
818             $default = $xval;
819         }
820     }
821
822     return ($low, $high, $default);
823
824 }
825
826
827 sub make_settings_xml($\@\@) {
828
829   my $saver = $_[0];
830   my @xml_opts = @{$_[1]};
831   my @old_settings_xml = @{$_[2]};
832   my $file = "project/xscreensaver/res/values/settings.xml";
833
834   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
835               "<resources " .
836               "xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n");
837
838   my $arrays_from_old_settings = old_settings_string_arrays(@old_settings_xml);
839
840   $body = $body . $arrays_from_old_settings;
841
842   my (%saver_keys) = get_keys_and_values($saver, @xml_opts);
843   my @key_array = keys %saver_keys;
844
845   my ($low, $high, $low_label, $high_label, $type);
846   my @selects;
847
848   # for each setting of the hack which we chose to add
849   foreach my $selected_setting_key (@key_array) {
850       # see what values were in the relevant xml in hacks/config for this hack
851       foreach my $claim (@xml_opts) {
852             my ($xres, $xcompare, $xval) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
853             if ($xres =~ /^$selected_setting_key/) {
854                 my $one, my $two;
855                 if ($xres =~ /_/ ) {
856                     ($one, $two) = ($xres =~ m/^(.*)_(.*)$/s);
857                     if ($two eq "type") {
858                         $type = $xval;
859                     } elsif ($two eq "low-label") {
860                         $low_label = $xval;
861                     } elsif ($two eq "high-label") {
862                         $high_label = $xval;
863                     } elsif ($two eq "low") {
864                         $low = $xval;
865                     } elsif ($two eq "high") {
866                         $high = $xval;
867                     }
868                 } else {
869                     $one = $xres;
870                     if ($type eq "select") {
871                         push @selects, $xval;
872                     }
873                 }
874             }
875        }
876
877        # add setting values based on the setting type (boolean, number, select)
878        if ($type eq "boolean") {
879            $body = $body . "    <string-array name=\"" . $saver .
880            "_" . $selected_setting_key . "_names" . "\">\n" .
881            "        <item>\"True\"</item>\n" .
882            "        <item>\"False\"</item>\n" .
883            "    </string-array>\n" .
884            "    <string-array name=\"" . $saver . "_" . $selected_setting_key .
885            "_prefix" . "\">\n" .
886            "        <item>\@string/t</item>\n" .
887            "        <item>\@string/f</item>\n" .
888            "    </string-array>\n";
889        } elsif ($type eq "number") {
890            $body = $body . "    <string-array name=\"" . $saver .
891            "_" . $selected_setting_key . "_names" . "\">\n" .
892            "        <item>\"" . $low_label . "\"</item>\n" .
893            "        <item>\"" . $high_label . "\"</item>\n" .
894            "    </string-array>\n" .
895            "    <string-array name=\"" . $saver . "_" . $selected_setting_key .
896            "_prefix" . "\">\n" .
897            "        <item>\"" . $low . "\"</item>\n" .
898            "        <item>\"" . $high . "\"</item>\n" .
899            "    </string-array>\n";
900        } elsif ($type eq "select") {
901            $body = $body . "    <string-array name=\"" . $saver .
902            "_" . $selected_setting_key . "_names" . "\">\n";
903
904            foreach my $item (@selects) {
905                $body = $body . "        <item>\"" . $item . "\"</item>\n" ;
906            }
907
908            $body = $body . "    </string-array>\n" .
909            "    <string-array name=\"" . $saver .
910            "_" . $selected_setting_key . "_prefix" . "\">\n";
911
912            foreach my $item (@selects) {
913                $body = $body . "        <item>\"" . $item . "\"</item>\n" ;
914            }
915
916            $body = $body . "    </string-array>\n";
917        }
918
919        @selects=();
920   }
921
922   $body =    ($body .
923               "</resources>\n");
924
925   open (my $in, '>', $file) || error ("$file: $!");
926   print $in $body;
927   close $in;
928
929 }
930
931
932 sub old_settings_string_arrays(@) {
933
934   my (@old_settings_file) = @_;
935
936   my $body = '';
937   my $current_string_array='';
938
939
940   foreach my $claim (@old_settings_file) {
941     my ($res, $compare, $xval) = ($claim =~ m/^(.*) (=) (.*)$/s);
942     error ("unparsable xml claim: $_") unless $compare;
943
944     if ($current_string_array ne $res) {
945         if (length($current_string_array) > 0) {
946            $body = $body . "    </string-array>\n";
947         }
948
949         $current_string_array = $res;
950         $body = $body .  "    <string-array name=\"" . $current_string_array .
951                          "\">\n";
952     }
953
954     $body = $body . "        <item>" . $xval . "</item>\n";
955   }
956
957   if ($#old_settings_file > -1) {
958       $body = $body . "    </string-array>\n";
959   }
960
961
962   return $body;
963
964 }
965
966
967 # TODO: This adds the proper parameters to settings such as hilbert's, but it
968 # does not remove the improper parameters from hacks such as Hilbert yet.
969 #
970 sub get_settings($$\@) {
971   my $saver = $_[0];
972   my $switchmap = $_[1];
973   my @xml_opts = @{$_[2]};
974
975   my @keys = keys % { $switchmap};
976
977   my $res_seen = 0;
978   my $val_seen = 0;
979   my @also;
980   foreach my $sgkey (@keys) {
981       my ($k, $v) = ($switchmap->{$sgkey} =~ m/^(.*): (.*)$/);
982
983       if ($v ne '%') {
984           foreach my $claim (@xml_opts) {
985                my ($res, $compare, $val) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
986                if ($res eq $k && $val eq $v) {
987                    $val_seen = $val_seen + 1;
988                }
989                elsif ($res eq $k) {
990                    $res_seen = $res_seen + 1;
991                }
992           }
993
994           if ($val_seen eq 0 && $res_seen > 0) {
995               my $so = "$k != $v";
996               push @also, $so;
997           }
998
999           $val_seen = 0;
1000           $res_seen = 0;
1001       }
1002   }
1003
1004   my @all = (@xml_opts, @also);
1005   return @all;
1006
1007 }
1008
1009
1010 sub make_strings_xml($\@\@) {
1011
1012   my $saver = $_[0];
1013   my @xml_opts = @{$_[1]};
1014   my @strings_xml_opts = @{$_[2]};
1015
1016   my $saver_name = $saver . "_name";
1017   my $hack = ucfirst($saver);
1018   my $file = "project/xscreensaver/res/values/strings.xml";
1019   my (%saver_keys) = get_keys_and_values($saver, @xml_opts);
1020
1021   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
1022               "<resources>\n" .
1023               "    <string name=\"hello\">Hello World!</string>\n" .
1024               "    <string name=\"service_label\">Xscreensaver</string>\n" .
1025               "    <string name=\"description\">A live wallpaper</string>\n\n" .
1026               "    <string name=\"app_name\">Xscreensaver</string>\n" .
1027               "    <string name=\"author\">jwz and helpers</string>\n" .
1028               "    <string name=\"t\">True</string>\n" .
1029               "    <string name=\"f\">False</string>\n");
1030
1031   foreach my $claim (@strings_xml_opts) {
1032     my ($res, $compare, $xval) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
1033     error ("$saver: unparsable xml claim: $_") unless $compare;
1034     if ($res eq 'hello' ||
1035         $res eq 'service_label' ||
1036         $res eq 'description' ||
1037         $res eq 'app_name' ||
1038         $res eq 'author' ||
1039         $res eq 't' ||
1040         $res eq 'f') {
1041     }
1042     elsif ($res eq $saver_name) {
1043         error ("$saver: $saver already in $file");
1044     }
1045     else {
1046         $body = ($body .
1047                  "    <string name=\"" . $res . "\">" . $xval . "</string>\n");
1048     }
1049   }
1050
1051   $body =    ($body .
1052               "    <string name=\"" . $saver . "_name\">" . $hack .  
1053               "</string>\n" .
1054               "    <string name=\"" . $saver . 
1055               "_settings\">Settings</string>\n" .
1056               "    <string name=\"" . $saver . "_description\">" . $hack .  
1057
1058               "</string>\n");
1059
1060   my @keyarray = keys %saver_keys;
1061
1062   foreach my $sgkey (@keyarray) {
1063
1064     my $type = get_type($sgkey, @xml_opts);
1065
1066     if ($type eq "number") {
1067
1068           my ($low, $high, $default) = get_low_high_def($sgkey, @xml_opts);
1069           my $float = ($default - $low) / ($high - $low);
1070
1071           $body = ($body . "    <string name=\"" . $saver . "_" . $sgkey .
1072               "_settings_title\">" . "Set " . $sgkey . "</string>\n" .
1073               "    <string name=\"" . $saver . "_" . $sgkey .
1074               "_settings_summary\">" . "Choose " . $sgkey . "</string>\n" .
1075               "    <string name=\"" . $saver . "_" . $sgkey .
1076               "_low\">" . $low . "</string>\n" .
1077               "    <string name=\"" . $saver . "_" . $sgkey .
1078               "_high\">" . $high . "</string>\n" .
1079               "    <string name=\"" . $saver . "_" . $sgkey .
1080               "_default\">" . $saver_keys{$sgkey} . "</string>\n");
1081     }
1082       else {
1083
1084               $body = ($body . "    <string name=\"" . $saver . "_" . $sgkey .
1085               "_settings_title\">" . "Set " . $sgkey . "</string>\n" .
1086               "    <string name=\"" . $saver . "_" . $sgkey .  
1087
1088               "_settings_summary\">" . "Choose " . $sgkey . "</string>\n" .
1089               "    <string name=\"" . $saver . "_" . $sgkey .  
1090               "_default\">" . $saver_keys{$sgkey} . "</string>\n");
1091       }
1092   }
1093
1094   $body =    ($body .
1095               "</resources>\n");
1096
1097   open (my $in, '>', $file) || error ("$file: $!");
1098   print $in $body;
1099   close $in;
1100 }
1101
1102
1103 sub make_hack_xml($) {
1104   my ($saver) = @_;
1105   my $hack = ucfirst($saver);
1106
1107   my $dir = "project/xscreensaver/res/xml/";
1108   my $file = $dir . $saver . ".xml";
1109   my $in;
1110
1111   if (-d $dir) {
1112       open ($in, '>', $file) || error ("$file: $!");
1113   }
1114   else {
1115       mkdir $dir;
1116       open ($in, '>', $file) || error ("$file: $!");
1117   }
1118
1119   my $body = ("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" .
1120               "<wallpaper xmlns:android=" .
1121               "\"http://schemas.android.com/apk/res/android\"\n" .
1122               "   android:description=\"\@string/" . $saver .
1123               "_description\"\n" .
1124               "   android:settingsActivity=\"org.jwz.xscreensaver.gen.$hack" .
1125               "Settings\"\n" .
1126               "   android:thumbnail=\"\@drawable/" . $saver .
1127               "\" />\n");
1128
1129   print $in $body;
1130   close $in;
1131 }
1132
1133
1134 sub make_glue($$) {
1135   my ($saver, @glue_hacks) = @_;
1136   my (@hacks) = @glue_hacks;
1137
1138   push @hacks, $saver;
1139
1140   my $dir = "gen/";
1141   my $file = $dir . "glue.c";
1142   my $in;
1143
1144   if (-d $dir) {
1145       open ($in, '>', $file) || error ("$file: $!");
1146   }
1147   else {
1148       mkdir $dir;
1149       open ($in, '>', $file) || error ("$file: $!");
1150   }
1151
1152
1153   my $body = ("#include <jni.h>\n" .
1154               "#include <math.h>\n" .
1155               "#include <stdlib.h>\n" .
1156               "#include <stdio.h>\n" .
1157               "#include <time.h>\n" .
1158               "#include <pthread.h>\n" .
1159               "#include <GLES/gl.h>\n\n" .
1160               "#include \"screenhackI.h\"\n" .
1161               "#include \"jwzglesI.h\"\n" .
1162               "#include \"version.h\"\n\n" .
1163               "void drawXscreensaver();\n\n" .
1164               "int sWindowWidth = 0;\n" .
1165               "int sWindowHeight = 0;\n" .
1166               "int initTried = 0;\n" .
1167               "int renderTried = 0;\n" .
1168               "int resetTried = 0;\n" .
1169               "int currentFlip = 0;\n\n" .
1170               "pthread_mutex_t mutg = PTHREAD_MUTEX_INITIALIZER;\n\n" .
1171               "extern struct xscreensaver_function_table " .
1172               "*xscreensaver_function_table;\n\n" .
1173               "// if adding a table here, increase the magic number\n" .
1174               "struct xscreensaver_function_table\n");
1175
1176               for my $i (0 .. $#hacks) {
1177                 $body = $body . "    *" . $hacks[$i] ;
1178                 $body = $body . "_xscreensaver_function_table";
1179                 if ($i eq $#hacks  ) {
1180                   $body = $body . ";\n\n";
1181                 }
1182                 else {
1183                   $body = $body . ",\n";
1184                 }
1185               }
1186
1187   $body = $body . "struct running_hack {\n" .
1188               "    struct xscreensaver_function_table *xsft;\n" .
1189               "    Display *dpy;\n" .
1190               "    Window window;\n" .
1191               "    void *closure;\n" .
1192               "};\n\n" .
1193               "const char *progname;\n" .
1194               "const char *progclass;\n\n" .
1195               "struct running_hack rh[";
1196   $body = $body . scalar(@hacks);
1197   $body = $body . "];\n" .
1198               "// ^ magic number of hacks - TODO: remove magic number\n\n\n" .
1199               "int chosen;\n" .
1200               "JNIEXPORT void JNICALL\n" .
1201               "    Java_org_jwz_xscreensaver_CallNative_nativeInit\n" .
1202               "    (JNIEnv * env);\n" .
1203               "JNIEXPORT void JNICALL\n" .
1204               "    Java_org_jwz_xscreensaver_CallNative_nativeResize\n" .
1205               "    (JNIEnv * env, jobject thiz, jint w, jint h);\n" .
1206               "JNIEXPORT void JNICALL\n" .
1207               "    Java_org_jwz_xscreensaver_CallNative_nativeRender\n" .
1208               "    (JNIEnv * env);\n" .
1209               "JNIEXPORT void JNICALL\n" .
1210               "    Java_org_jwz_xscreensaver_CallNative_nativeDone\n" .
1211               "    (JNIEnv * env);\n";
1212
1213   foreach my $bighack (@hacks) {
1214       my $bh = ucfirst($bighack);
1215       $body = $body . "JNIEXPORT void JNICALL\n" .
1216               "    Java_org_jwz_xscreensaver_gen_" . $bh . 
1217               "Wallpaper_allnativeSettings\n" .
1218               "    (JNIEnv * env, jobject thiz, jstring jhack," .
1219               " jstring hackPref,\n" .
1220               "     jint draw, jstring key);\n";
1221
1222   }
1223
1224   $body = $body . "\n\n\nvoid doinit()\n{\n\n" ;
1225
1226   for my $j (0 .. $#hacks) {
1227     if ($j == 0) {
1228       $body = $body . "    if (chosen == " . $j . ") {\n" ;
1229     } elsif ($j == $#hacks) {
1230       $body = $body .         "    } else {\n" ;
1231     } else {
1232       $body = $body . "    } else if (chosen == " . $j . ") {\n";
1233     }
1234       $body = $body .  "        progname = \"" . $hacks[$j] . "\";\n" .
1235               " rh[chosen].xsft = &" . $hacks[$j] . 
1236               "_xscreensaver_function_table;\n" ;
1237     }
1238
1239   $body = $body . "    }\n\n" ;
1240   $body = $body . "    rh[chosen].dpy = jwxyz_make_display(0, 0);\n" .
1241               "    rh[chosen].window = XRootWindow(rh[chosen].dpy, 0);\n" .
1242               "// TODO: Zero looks right, " .
1243               "but double-check that is the right number\n\n" .
1244               "    progclass = rh[chosen].xsft->progclass;\n\n" .
1245               "    if (rh[chosen].xsft->setup_cb)\n" .
1246               " rh[chosen].xsft->setup_cb(rh[chosen].xsft,\n" .
1247               "                           rh[chosen].xsft->setup_arg);\n\n" .
1248               "    if (resetTried < 1) {\n" .
1249               " resetTried++;\n" .
1250               "        jwzgles_reset();\n" .
1251               "    }\n\n" .
1252               "    void *(*init_cb) (Display *, Window, void *) =\n" .
1253               " (void *(*)(Display *, Window, void *)) " .
1254               "rh[chosen].xsft->init_cb;\n\n" .
1255               "    rh[chosen].closure =\n" .
1256               " init_cb(rh[chosen].dpy, rh[chosen].window,\n" .
1257               "         rh[chosen].xsft->setup_arg);\n\n}\n\n\n" .
1258               "void drawXscreensaver()\n{\n" .
1259               "    pthread_mutex_lock(&mutg);\n" .
1260               "    rh[chosen].xsft->draw_cb(rh[chosen].dpy, " .
1261               "rh[chosen].window,\n" .
1262               "                      rh[chosen].closure);\n" .
1263               "    pthread_mutex_unlock(&mutg);\n\n}\n\n\n" .
1264               "JNIEXPORT void JNICALL\n" .
1265               "    Java_org_jwz_xscreensaver_CallNative_nativeInit\n" .
1266               "    (JNIEnv * env) {\n\n" .
1267               "    if (initTried < 1) {\n" .
1268               " initTried++;\n" .
1269               "    } else {\n" .
1270               " if (!rh[chosen].dpy) {\n" .
1271               "     doinit();\n" .
1272               " } else {\n" .
1273               "     rh[chosen].xsft->free_cb(rh[chosen].dpy, " .
1274               "rh[chosen].window,\n" .
1275               "                              rh[chosen].closure);\n" .
1276               "     jwxyz_free_display(rh[chosen].dpy);\n" .
1277               "     rh[chosen].dpy = NULL;\n" .
1278               "     rh[chosen].window = NULL;\n" .
1279               "     if (!rh[chosen].dpy) {\n" .
1280               "         doinit();\n" .
1281               "     }\n\n        }\n" .
1282               "    }\n\n}\n\n\n" .
1283               "JNIEXPORT void JNICALL\n" .
1284               "    Java_org_jwz_xscreensaver_CallNative_nativeResize\n" .
1285               "    (JNIEnv * env, jobject thiz, jint w, jint h) {\n\n" .
1286               "    sWindowWidth = w;\n" .
1287               "    sWindowHeight = h;\n\n" .
1288               "    if (!rh[chosen].dpy) {\n" .
1289               " doinit();\n" .
1290               "    }\n\n" .
1291               "    jwxyz_window_resized(rh[chosen].dpy, " .
1292               "rh[chosen].window, 0, 0, w, h, 0);\n\n" .
1293               "    rh[chosen].xsft->reshape_cb(rh[chosen].dpy, " .
1294               "rh[chosen].window,\n" .
1295               "                         rh[chosen].closure, w, h);\n}\n\n" .
1296               "JNIEXPORT void JNICALL\n" .
1297               "    Java_org_jwz_xscreensaver_CallNative_nativeRender\n" .
1298               "    (JNIEnv * env) {\n" .
1299               "    if (renderTried < 1) {\n" .
1300               " renderTried++;\n" .
1301               "    } else {\n" .
1302               " drawXscreensaver();\n" .
1303               "    }\n}\n\n" .
1304               "// TODO: Check Java side is calling this properly\n" .
1305               "JNIEXPORT void JNICALL\n" .
1306               "    Java_org_jwz_xscreensaver_CallNative_nativeDone\n" .
1307               "    (JNIEnv * env) {\n\n" .
1308               "    rh[chosen].xsft->free_cb(rh[chosen].dpy, " .
1309               "rh[chosen].window,\n" .
1310               "                      rh[chosen].closure);\n" .
1311               "    jwxyz_free_display(rh[chosen].dpy);\n" .
1312               "    rh[chosen].dpy = NULL;\n" .
1313               "    rh[chosen].window = NULL;\n\n}\n\n" ;
1314
1315   for my $j (0 .. $#hacks) {
1316     my $jhack =  ucfirst($hacks[$j]);
1317
1318     $body = $body . "JNIEXPORT void JNICALL\n" .
1319               "    Java_org_jwz_xscreensaver_gen_" . $jhack . 
1320               "Wallpaper_allnativeSettings\n" .
1321               "    (JNIEnv * env, jobject thiz, jstring jhack," .
1322               " jstring hackPref,\n" .
1323               "     jint draw, jstring key) {\n\n" .
1324               "    const char *chack = " .
1325               "(*env)->GetStringUTFChars(env, hackPref, NULL);\n" .
1326               "    char *hck = (char *) chack;\n" .
1327               "    const char *kchack = " .
1328               "(*env)->GetStringUTFChars(env, key, NULL);\n" .
1329               "    char *khck = (char *) kchack;\n\n" .
1330               "    if (draw == 2) {\n" .
1331               "        set" . $jhack . "Settings(hck, khck);\n" .
1332               "    }\n\n" .
1333               "    chosen = " . $j . ";\n}\n\n";
1334
1335   }
1336
1337
1338   print $in $body;
1339   close $in;
1340 }
1341
1342 sub make_wallpaper($$) {
1343   my ($saver, @xml_opts) = @_;
1344   my $hack = ucfirst($saver);
1345   my $file = "project/xscreensaver/src/org/jwz/xscreensaver/gen/";
1346   $file = $file . $hack . "Wallpaper.java";
1347   my (%saver_keys) = get_keys_and_values($saver, @xml_opts);
1348
1349   open (my $in, '>', $file) || error ("$file: $!");
1350
1351   my $body = ("package org.jwz.xscreensaver.gen;\n" .
1352               "import javax.microedition.khronos.egl.EGLConfig;\n" .
1353               "import javax.microedition.khronos.opengles.GL10;\n" .
1354               "import net.rbgrn.android.glwallpaperservice.*;\n" .
1355               "import android.opengl.GLU;\n" .
1356               "import android.content.Context;\n" .
1357               "import android.content.SharedPreferences;\n" .
1358               "import org.jwz.xscreensaver.*;\n" .
1359               "public class " . $hack .
1360               "Wallpaper extends ARenderer {\n" .
1361               "    private static native void allnativeSettings(" .
1362               "String hack, String hackPref, int draw, String key);\n" .
1363               "    public static final String SHARED_PREFS_NAME=\"" . $saver .
1364               "settings\";\n" .
1365               "    CallNative cn;\n" .
1366               "    public void onSurfaceCreated(" .
1367               "GL10 gl, EGLConfig config) {\n" .
1368               "        super.onSurfaceCreated(gl, config);\n" .
1369               "        cn = new CallNative();\n" .
1370               "        NonSurfaceCreated();\n" .
1371               "    }\n" .
1372               "    public void onDrawFrame(GL10 gl) {\n" .
1373               "        super.onDrawFrame(gl);\n" .
1374               "        allnativeSettings(\"bogus\", \"bogus\", 1, \"bogus\");\n" .
1375               "        NonDrawFrame();\n" .
1376               "    }\n" .
1377               "    void NonDrawFrame() {\n" .
1378               "        cn.nativeRender();\n" .
1379               "    }\n" .
1380               "    void doSP(SharedPreferences sspp) {\n" .
1381
1382
1383               "        String hack = \"" . $saver . "\";\n");
1384
1385   my @keyarray = keys %saver_keys;
1386   foreach my $sgkey (@keyarray) {          
1387
1388     my $type = get_type($sgkey, @xml_opts);
1389
1390     if ($type eq "number") {
1391
1392               my ($low, $high, $default) = get_low_high_def($sgkey, @xml_opts);
1393               my $float = ($default - $low) / ($high - $low);
1394
1395               $body = $body .
1396               "        String " . $sgkey .
1397               "_low = sspp.getString(\"" . $saver .
1398               "_" . $sgkey . "_low\", \"". $low . "\");\n" .
1399               "        String " . $sgkey .
1400               "_high = sspp.getString(\"" . $saver .
1401               "_" . $sgkey . "_high\", \"" . $high . "\");\n" .
1402               "        Float " . $sgkey . "PrefF = sspp.getFloat(\"" . $saver .
1403               "_" . $sgkey . "\", " . $float . "f);\n" .
1404               "        String " . $sgkey . "Pref = getNumber(" . $sgkey .
1405               "_low, " . $sgkey . "_high, " . $sgkey . "PrefF);\n" .
1406               "        allnativeSettings(hack, " . $sgkey .
1407               "Pref, 2, \"" . $saver .  "_" . $sgkey . "\");\n";
1408     }
1409       elsif ($type eq "boolean") {
1410
1411               $body = $body . "        String " . $sgkey .
1412               "Pref = sspp.getString(\"" . $saver .  "_" . $sgkey . 
1413               "\", \"" . $saver_keys{$sgkey} . "\");\n" .
1414               "        allnativeSettings(hack, " . $sgkey .
1415               "Pref, 2, \"" . $saver .  "_" . $sgkey . "\");\n";
1416
1417       }
1418       elsif ($type eq "select") {
1419
1420               $body = $body . "        String " . $sgkey .
1421               "Pref = sspp.getString(\"" . $saver .  "_" . $sgkey .
1422               "\", \"" . $saver_keys{$sgkey} . "\");\n" .
1423               "        allnativeSettings(hack, " . $sgkey .
1424               "Pref, 2, \"" . $saver .  "_" . $sgkey . "\");\n";
1425
1426       }
1427       else {
1428           print STDERR "$progname: type $type not yet implemented \n";
1429       }
1430
1431   }
1432
1433   $body = $body . "    }\n" .
1434               "    String getNumber(String low, String high, Float pref) {\n" .
1435               "        Float lowF = Float.parseFloat(low);\n" .
1436               "        Float lowH = Float.parseFloat(high);\n" .
1437               "        Float diff = lowH - lowF;\n" .
1438               "        Float mult = pref * diff;\n" .
1439               "        Float add = mult + lowF;\n" .
1440               "        int i;\n" .
1441               "        String s;\n" .
1442               "        if (diff > 2.0) {\n" .
1443               "            i = (Integer) Math.round(add);\n" .
1444               "            s = Integer.toString(i);\n}\n" .
1445               "        else {\n" .
1446               "            s = Float.toString(add);\n}\n" .
1447               "        return s;\n" .
1448               "    }\n\n" .
1449               "    static\n" .
1450               "    {\n" .
1451               "        System.loadLibrary (\"xscreensaver\");\n" .
1452               "    }\n" .
1453               "}\n";
1454
1455   print $in $body;
1456   close $in;
1457
1458 }
1459
1460 sub get_keys_and_values($$) {
1461
1462   my ($saver, @xml_opts) = @_;
1463   my (%saver_keys) ;
1464
1465   foreach my $claim (@xml_opts) {
1466     my ($res, $compare, $xval) = ($claim =~ m/^(.*) (=|!=) (.*)$/s);
1467     error ("$saver: unparsable xml claim: $_") unless $compare;
1468
1469     if ($saver eq "sproingies") {
1470         if ($res eq "count") {
1471             $saver_keys{$res} = $xval;
1472         }
1473         elsif ($res eq "wireframe") {
1474             #$saver_keys{$res} = $xval;
1475             $saver_keys{$res} = "False";
1476         }
1477
1478     }
1479     elsif ($saver eq "hilbert") {
1480         if ($res eq "mode") {
1481             $saver_keys{$res} = $xval;
1482         }
1483     }
1484     elsif ($saver eq "stonerview") {
1485         if ($res eq "transparent") {
1486             #$saver_keys{$res} = $xval;
1487             $saver_keys{$res} = "False";
1488         }
1489     }
1490     elsif ($saver eq "superquadrics") {
1491         # spinspeed/speed.  float/int
1492         if ($res eq "spinspeed") {
1493             $saver_keys{$res} = $xval;
1494         }
1495     }
1496     elsif ($saver eq "bouncingcow") {
1497         if ($res eq "count") {
1498             $saver_keys{$res} = "3";
1499         }
1500         elsif ($res eq "speed") {
1501             $saver_keys{$res} = "0.1";
1502         }
1503     }
1504     elsif ($saver eq "unknownpleasures") {
1505         if ($res eq "wireframe") {
1506             $saver_keys{$res} = "True";
1507         }
1508         elsif ($res eq "speed") {
1509             $saver_keys{$res} = "3.0";
1510         }
1511         #elsif ($res eq "count") {
1512         #    $saver_keys{$res} = $xval;
1513         #}
1514         #elsif ($res eq "resolution") {
1515         #    $saver_keys{$res} = $xval;
1516         #}
1517         #elsif ($res eq "ortho") {
1518         #    $saver_keys{$res} = $xval;
1519         #}
1520
1521     }
1522     elsif ($saver eq "hypertorus") {
1523         if ($res =~ /^(displayMode|appearance|colors|projection3d|projection4d|speedwx|speedwy|speedwz|speedxy|speedxz|speedyz)$/) {
1524             $saver_keys{$res} = $xval;
1525         }
1526     }
1527     elsif ($saver eq "glhanoi") {
1528         if ($res =~ /^(light|fog|trails|poles|speed)$/) {
1529             # TODO: check in xval for true/false should be higher up in logic
1530             if ($xval =~ /^(true|false)$/) {
1531                 $saver_keys{$res} = ucfirst($xval);
1532             }
1533             else {
1534                 $saver_keys{$res} = $xval;
1535             }
1536         }
1537     }
1538     else {
1539         error ("$saver: not yet supported for Android");
1540     }
1541
1542   }
1543
1544   return (%saver_keys);
1545 }
1546
1547
1548 sub make_service($) {
1549   my ($saver) = @_;
1550   my $hack = ucfirst($saver);
1551   my $file = "project/xscreensaver/src/org/jwz/xscreensaver/gen/";
1552   $file = $file . $hack . "Service.java";
1553   open (my $in, '>', $file) || error ("$file: $!");
1554
1555   my $body = ("package org.jwz.xscreensaver.gen;\n\n" .
1556               "import net.rbgrn.android.glwallpaperservice.*;\n" .
1557               "import android.content.SharedPreferences;\n" .
1558               "import org.jwz.xscreensaver.*;\n\n" .
1559               "// Original code provided by Robert Green\n" .
1560               "// http://www.rbgrn.net/content/354-glsurfaceview-adapted-3d-live-wallpapers\n" .
1561               "public class " . $hack .
1562               "Service extends GLWallpaperService {\n\n" .
1563               "    SharedPreferences sp;\n\n" .
1564               "    public " . $hack .
1565               "Service() {\n" .
1566               "        super();\n" .
1567               "    }\n\n" .
1568               "    \@Override\n" .
1569               "    public void onCreate() {\n" .
1570               "        sp = ((XscreensaverApp)getApplication())." .
1571               "getThePrefs($hack" . "Wallpaper.SHARED_PREFS_NAME);\n" .
1572               "    }\n\n" .
1573               "    public Engine onCreateEngine() {\n" .
1574               "        MyEngine engine = new MyEngine();\n" .
1575               "        return engine;\n" .
1576               "    }\n\n" .
1577               "    class MyEngine extends GLEngine {\n" .
1578               "        " . $hack .
1579               "Wallpaper renderer;\n" .
1580               "        public MyEngine() {\n" .
1581               "            super();\n" .
1582               "            // handle prefs, other initialization\n" .
1583               "            renderer = new " . $hack .
1584               "Wallpaper();\n" .
1585               "            setEGLConfigChooser(8, 8, 8, 8, 16, 0);\n" .
1586               "            setRenderer(renderer);\n" .
1587               "            setRenderMode(RENDERMODE_CONTINUOUSLY);\n" .
1588               "        }\n\n" .
1589               "        public void onDestroy() {\n" .
1590               "            super.onDestroy();\n" .
1591               "            if (renderer != null) {\n" .
1592               "                renderer.release(); " .
1593               "// assuming yours has this method - it should!\n" .
1594               "            }\n" .
1595               "            renderer = null;\n" .
1596               "        }\n\n" .
1597               "        \@Override\n" .
1598               "        public void onVisibilityChanged(boolean visible) {\n" .
1599               "            super.onVisibilityChanged(visible);\n" .
1600               "            if (visible) {\n" .
1601               "                renderer.doSP(sp);\n" .
1602               "            }\n" .
1603               "        }\n\n" .
1604               "    }\n" .
1605               "    static\n" .
1606               "    {\n" .
1607               "        System.loadLibrary (\"xscreensaver\");\n" .
1608               "    }\n\n\n" .
1609               "}\n");
1610
1611   print $in $body;
1612   close $in;
1613
1614 }
1615
1616 sub make_settings($) {
1617   my ($saver) = @_;
1618   my $hack = ucfirst($saver);
1619   my $dir = "project/xscreensaver/src/org/jwz/xscreensaver/gen/";
1620   my $file = $dir . $hack . "Settings.java";
1621   my $in;
1622
1623   if (-d $dir) {
1624       open ($in, '>', $file) || error ("$file: $!");
1625   }
1626   else {
1627       mkdir $dir;
1628       open ($in, '>', $file) || error ("$file: $!");
1629   }
1630
1631   my $body = ("/*\n" .
1632               " * Copyright (C) 2009 Google Inc.\n" .
1633               " *\n" .
1634               " * Licensed under the Apache License, Version 2.0 " .
1635               "(the \"License\"); you may not\n" .
1636               " * use this file except in compliance with the License. " .
1637               "You may obtain a copy of\n" .
1638               " * the License at\n" .
1639               " *\n" .
1640               " * http://www.apache.org/licenses/LICENSE-2.0\n" .
1641               " *\n" .
1642               " * Unless required by applicable law or agreed to in writing," .
1643               " software\n" .
1644               " * distributed under the License is distributed" .
1645               " on an \"AS IS\" BASIS, WITHOUT\n" .
1646               " * WARRANTIES OR CONDITIONS OF ANY KIND," .
1647               " either express or implied. See the\n" .
1648               " * License for the specific language governing" .
1649               "permissions and limitations under\n" .
1650               " * the License.\n" .
1651               " */\n\n" .
1652               "package org.jwz.xscreensaver.gen;\n\n" .
1653               "import org.jwz.xscreensaver.R;\n\n" .
1654               "import android.content.SharedPreferences;\n" .
1655               "import android.os.Bundle;\n" .
1656               "import android.preference.PreferenceActivity;\n\n" .
1657               "public class " . $hack .
1658               "Settings extends PreferenceActivity\n" .
1659               "    implements " .
1660               "SharedPreferences.OnSharedPreferenceChangeListener {\n\n" .
1661               "    \@Override\n" .
1662               "    protected void onCreate(Bundle icicle) {\n" .
1663               "        super.onCreate(icicle);\n" .
1664               "        getPreferenceManager().setSharedPreferencesName(\n" .
1665               "            " . $hack .
1666               "Wallpaper.SHARED_PREFS_NAME);\n" .
1667               "        addPreferencesFromResource(R.xml." . $saver .
1668               "_settings);\n" .
1669               "        getPreferenceManager().getSharedPreferences()." .
1670               "registerOnSharedPreferenceChangeListener(\n" .
1671               "            this);\n" .
1672               "    }\n\n" .
1673               "    \@Override\n" .
1674               "    protected void onResume() {\n" .
1675               "        super.onResume();\n" .
1676               "    }\n\n" .
1677               "    \@Override\n" .
1678               "    protected void onDestroy() {\n" .
1679               "        getPreferenceManager().getSharedPreferences()." .
1680               "unregisterOnSharedPreferenceChangeListener(\n" .
1681               "            this);\n" .
1682               "        super.onDestroy();\n" .
1683               "    }\n\n" .
1684               "    public void onSharedPreferenceChanged(" .
1685               "SharedPreferences sharedPreferences,\n" .
1686               "                                          String key) {\n" .
1687               "    }\n" .
1688               "}\n");
1689
1690   print $in $body;
1691   close $in;
1692 }
1693
1694
1695 sub error($) {
1696   my ($err) = @_;
1697   print STDERR "$progname: $err\n";
1698   exit 1;
1699 }
1700
1701 sub usage() {
1702   print STDERR "usage: $progname [--verbose] files ...\n";
1703   exit 1;
1704 }
1705
1706 sub main() {
1707   my @files = ();
1708   while ($#ARGV >= 0) {
1709     $_ = shift @ARGV;
1710     if (m/^--?verbose$/) { $verbose++; }
1711     elsif (m/^-v+$/) { $verbose += length($_)-1; }
1712     elsif (m/^-./) { usage; }
1713     else { push @files, $_; }
1714 #    else { usage; }
1715   }
1716
1717   usage unless ($#files >= 0);
1718   my $failures = 0;
1719   foreach (@files) { $failures += parse_then_make($_); }
1720   exit ($failures);
1721 }
1722
1723 main();