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