0e1508ff1ad514e1a0ca064ab9f58df78634df81
[xscreensaver] / hacks / apple2-main.c
1 /* xscreensaver, Copyright (c) 1998-2012 Jamie Zawinski <jwz@jwz.org>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
12  * with additional work by Jamie Zawinski <jwz@jwz.org>
13  * Pty and vt100 emulation by Fredrik Tolf <fredrik@dolda2000.com>
14  */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif /* HAVE_CONFIG_H */
19
20 #include <math.h>
21 #include <ctype.h>
22
23 #ifdef HAVE_UNISTD_H
24 # include <unistd.h>
25 #endif
26
27 #include "screenhack.h"
28 #include "apple2.h"
29 #include "textclient.h"
30
31 #undef countof
32 #define countof(x) (sizeof((x))/sizeof((*x)))
33
34 #define SCREEN_COLS 40
35 #define SCREEN_ROWS 24
36
37 #define DEBUG
38
39
40 /* Given a bitmask, returns the position and width of the field.
41  */
42 static void
43 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
44 {
45   int i;
46   for (i = 0; i < 32; i++)
47     if (mask & (1L << i))
48       {
49         int j = 0;
50         *pos_ret = i;
51         for (; i < 32; i++, j++)
52           if (! (mask & (1L << i)))
53             break;
54         *size_ret = j;
55         return;
56       }
57 }
58
59
60 /* Given a value and a field-width, expands the field to fill out 8 bits.
61  */
62 static unsigned char
63 spread_bits (unsigned char value, unsigned char width)
64 {
65   switch (width)
66     {
67     case 8: return value;
68     case 7: return (value << 1) | (value >> 6);
69     case 6: return (value << 2) | (value >> 4);
70     case 5: return (value << 3) | (value >> 2);
71     case 4: return (value << 4) | (value);
72     case 3: return (value << 5) | (value << 2) | (value >> 2);
73     case 2: return (value << 6) | (value << 4) | (value);
74     default: abort(); break;
75     }
76 }
77
78
79 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
80    Scales it (without dithering) to WxH.
81  */
82 static void
83 scale_image (Display *dpy, Window window, XImage *in,
84              int fromx, int fromy, int fromw, int fromh,
85              unsigned int *out, int w, int h)
86 {
87   float scale;
88   int x, y, i;
89   unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
90   unsigned int rsiz=0, gsiz=0, bsiz=0;
91   unsigned int rmsk=0, gmsk=0, bmsk=0;
92   unsigned char spread_map[3][256];
93   XWindowAttributes xgwa;
94   XColor *colors = 0;
95
96   if (fromx + fromw > in->width ||
97       fromy + fromh > in->height)
98     abort();
99
100   XGetWindowAttributes (dpy, window, &xgwa);
101
102   /* Compute the field offsets for RGB decoding in the XImage,
103      when in TrueColor mode.  Otherwise we use the colormap.
104    */
105   if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
106       visual_class (xgwa.screen, xgwa.visual) == GrayScale)
107     {
108       int ncolors = visual_cells (xgwa.screen, xgwa.visual);
109       colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
110       for (i = 0; i < ncolors; i++)
111         colors[i].pixel = i;
112       XQueryColors (dpy, xgwa.colormap, colors, ncolors);
113     }
114   else
115     {
116       rmsk = xgwa.visual->red_mask;
117       gmsk = xgwa.visual->green_mask;
118       bmsk = xgwa.visual->blue_mask;
119       decode_mask (rmsk, &rpos, &rsiz);
120       decode_mask (gmsk, &gpos, &gsiz);
121       decode_mask (bmsk, &bpos, &bsiz);
122
123       for (i = 0; i < 256; i++)
124         {
125           spread_map[0][i] = spread_bits (i, rsiz);
126           spread_map[1][i] = spread_bits (i, gsiz);
127           spread_map[2][i] = spread_bits (i, bsiz);
128         }
129     }
130
131   scale = (fromw > fromh
132            ? (float) fromw / w
133            : (float) fromh / h);
134
135   /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
136    */
137   for (y = 0; y < h-1; y++)     /* iterate over dest pixels */
138     for (x = 0; x < w-1; x++)
139       {
140         int xx, yy;
141         unsigned int r=0, g=0, b=0;
142
143         int xx1 = x * scale + fromx;
144         int yy1 = y * scale + fromy;
145         int xx2 = (x+1) * scale + fromx;
146         int yy2 = (y+1) * scale + fromy;
147
148         /* Iterate over the source pixels contributing to this one, and sum. */
149         for (xx = xx1; xx < xx2; xx++)
150           for (yy = yy1; yy < yy2; yy++)
151             {
152               unsigned char rr, gg, bb;
153               unsigned long sp = ((xx > in->width || yy > in->height)
154                                   ? 0 : XGetPixel (in, xx, yy));
155               if (colors)
156                 {
157                   rr = colors[sp].red   & 0xFF;
158                   gg = colors[sp].green & 0xFF;
159                   bb = colors[sp].blue  & 0xFF;
160                 }
161               else
162                 {
163                   rr = (sp & rmsk) >> rpos;
164                   gg = (sp & gmsk) >> gpos;
165                   bb = (sp & bmsk) >> bpos;
166                   rr = spread_map[0][rr];
167                   gg = spread_map[1][gg];
168                   bb = spread_map[2][bb];
169                 }
170               r += rr;
171               g += gg;
172               b += bb;
173             }
174
175         /* Scale summed pixel values down to 8/8/8 range */
176         i = (xx2 - xx1) * (yy2 - yy1);
177         if (i < 1) i = 1;
178         r /= i;
179         g /= i;
180         b /= i;
181
182         out[y * w + x] = (r << 16) | (g << 8) | b;
183       }
184 }
185
186
187 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
188    Picks a random sub-image out of the source image, and scales it to WxH.
189  */
190 static void
191 pick_a2_subimage (Display *dpy, Window window, XImage *in,
192                   unsigned int *out, int w, int h)
193 {
194   int fromx, fromy, fromw, fromh;
195   if (in->width <= w || in->height <= h)
196     {
197       fromx = 0;
198       fromy = 0;
199       fromw = in->width;
200       fromh = in->height;
201     }
202   else
203     {
204       int dw, dh;
205       do {
206         double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
207         fromw = w * scale;
208         fromh = h * scale;
209       } while (fromw > in->width ||
210                fromh > in->height);
211
212       dw = (in->width  - fromw) / 2;   /* near the center! */
213       dh = (in->height - fromh) / 2;
214
215       fromx = (dw <= 0 ? 0 : (random() % dw) + (dw/2));
216       fromy = (dh <= 0 ? 0 : (random() % dh) + (dh/2));
217     }
218
219   scale_image (dpy, window, in,
220                fromx, fromy, fromw, fromh,
221                out, w, h);
222 }
223
224
225 /* Floyd-Steinberg dither.  Derived from ppmquant.c,
226    Copyright (c) 1989, 1991 by Jef Poskanzer.
227  */
228 static void
229 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
230 {
231   /*
232     Apple ][ color map. Each pixel can only be 1 or 0, but what that
233     means depends on whether it's an odd or even pixel, and whether
234     the high bit in the byte is set or not. If it's 0, it's always
235     black.
236    */
237   static const int a2_cmap[2][2][3] = {
238     {
239       /* hibit=0 */
240       {/* odd pixels = blue */    0x00, 0x80, 0xff},
241       {/* even pixels = red */    0xff, 0x80, 0x00}
242     },
243     {
244       /* hibit=1 */
245       {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
246       {/* odd pixels = green */   0x40, 0xff, 0x40}
247     }
248   };
249
250   int x, y;
251   unsigned int **pixels;
252   unsigned int *pP;
253   int maxval = 255;
254   long *this_rerr;
255   long *next_rerr;
256   long *this_gerr;
257   long *next_gerr;
258   long *this_berr;
259   long *next_berr;
260   long *temp_err;
261   int fs_scale = 1024;
262   int brightness = 75;
263
264 #if 0
265   {
266     FILE *pipe = popen ("xv -", "w");
267     fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
268     for (y = 0; y < h; y++)
269       for (x = 0; x < w; x++)
270         {
271           unsigned int p = in[y * w + x];
272           unsigned int r = (p >> 16) & 0xFF;
273           unsigned int g = (p >>  8) & 0xFF;
274           unsigned int b = (p      ) & 0xFF;
275           fprintf(pipe, "%c%c%c", r, g, b);
276         }
277     fclose (pipe);
278   }
279 #endif
280
281   /* Initialize Floyd-Steinberg error vectors. */
282   this_rerr = (long *) calloc (w + 2, sizeof(long));
283   next_rerr = (long *) calloc (w + 2, sizeof(long));
284   this_gerr = (long *) calloc (w + 2, sizeof(long));
285   next_gerr = (long *) calloc (w + 2, sizeof(long));
286   this_berr = (long *) calloc (w + 2, sizeof(long));
287   next_berr = (long *) calloc (w + 2, sizeof(long));
288
289
290   /* #### do we really need more than one element of "pixels" at once?
291    */
292   pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
293   for (y = 0; y < h; y++)
294     pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
295
296   for (x = 0; x < w + 2; ++x)
297     {
298       this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
299       this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
300       this_berr[x] = random() % (fs_scale * 2) - fs_scale;
301       /* (random errors in [-1 .. 1]) */
302     }
303
304   for (y = 0; y < h; y++)
305     for (x = 0; x < w; x++)
306       pixels[y][x] = in[y * w + x];
307
308   for (y = 0; y < h; y++)
309     {
310       int xbyte;
311       int err;
312       int prev_byte=0;
313
314       for (x = 0; x < w + 2; x++)
315         next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
316
317       /* It's too complicated to go back and forth on alternate rows,
318          so we always go left-right here. It doesn't change the result
319          very much.
320
321          For each group of 7 pixels, we have to try it both with the
322          high bit=0 and =1. For each high bit value, we add up the
323          total error and pick the best one.
324
325          Because we have to go through each group of bits twice, we
326          don't propagate the error values through this_[rgb]err since
327          it would add them twice. So we keep seperate local_[rgb]err
328          variables for propagating error within the 7-pixel group.
329       */
330
331       pP = pixels[y];
332       for (xbyte=0; xbyte<280; xbyte+=7)
333         {
334           int best_byte=0;
335           int best_error=2000000000;
336           int hibit;
337           int sr, sg, sb;
338           int r2, g2, b2;
339           int local_rerr=0, local_gerr=0, local_berr=0;
340
341           for (hibit=0; hibit<2; hibit++)
342             {
343               int byte = hibit<<7;
344               int tot_error=0;
345
346               for (x=xbyte; x<xbyte+7; x++)
347                 {
348                   int dist0, dist1;
349
350                   /* Use Floyd-Steinberg errors to adjust actual color. */
351                   sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
352                   sg = ((pP[x] >>  8) & 0xFF) * brightness/256;
353                   sb = ((pP[x]      ) & 0xFF) * brightness/256;
354                   sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
355                   sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
356                   sb += (this_berr[x + 1] + local_berr) / fs_scale;
357
358                   if  (sr < 0) sr = 0;
359                   else if  (sr > maxval) sr = maxval;
360                   if  (sg < 0) sg = 0;
361                   else if  (sg > maxval) sg = maxval;
362                   if  (sb < 0) sb = 0;
363                   else if  (sb > maxval) sb = maxval;
364
365                   /* This is the color we'd get if we set the bit 1. For 0,
366                      we get black */
367                   r2=a2_cmap[hibit][x&1][0];
368                   g2=a2_cmap[hibit][x&1][1];
369                   b2=a2_cmap[hibit][x&1][2];
370
371                   /*
372                      dist0 and dist1 are the error (Minkowski 2-metric
373                      distances in the color space) for choosing 0 and
374                      1 respectively. 0 is black, 1 is the color r2,g2,b2.
375                   */
376                   dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
377                   dist0= sr*sr + sg*sg + sb*sb;
378
379                   if (dist1<dist0)
380                     {
381                       byte |= 1 << (x-xbyte);
382                       tot_error += dist1;
383
384                       /* Wanted sr but got r2, so propagate sr-r2 */
385                       local_rerr =  (sr - r2) * fs_scale * 7/16;
386                       local_gerr =  (sg - g2) * fs_scale * 7/16;
387                       local_berr =  (sb - b2) * fs_scale * 7/16;
388                     }
389                   else
390                     {
391                       tot_error += dist0;
392
393                       /* Wanted sr but got 0, so propagate sr */
394                       local_rerr =  sr * fs_scale * 7/16;
395                       local_gerr =  sg * fs_scale * 7/16;
396                       local_berr =  sb * fs_scale * 7/16;
397                     }
398                 }
399
400               if (tot_error < best_error)
401                 {
402                   best_byte = byte;
403                   best_error = tot_error;
404                 }
405             }
406
407           /* Avoid alternating 7f and ff in all-white areas, because it makes
408              regular pink vertical lines */
409           if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
410             best_byte=prev_byte;
411           prev_byte=best_byte;
412
413         /*
414           Now that we've chosen values for all 8 bits of the byte, we
415           have to fill in the real pixel values into pP and propagate
416           all the error terms. We end up repeating a lot of the code
417           above.
418          */
419
420         for (x=xbyte; x<xbyte+7; x++)
421           {
422             int bit=(best_byte>>(x-xbyte))&1;
423             hibit=(best_byte>>7)&1;
424
425             sr = (pP[x] >> 16) & 0xFF;
426             sg = (pP[x] >>  8) & 0xFF;
427             sb = (pP[x]      ) & 0xFF;
428             sr += this_rerr[x + 1] / fs_scale;
429             sg += this_gerr[x + 1] / fs_scale;
430             sb += this_berr[x + 1] / fs_scale;
431
432             if  (sr < 0) sr = 0;
433             else if  (sr > maxval) sr = maxval;
434             if  (sg < 0) sg = 0;
435             else if  (sg > maxval) sg = maxval;
436             if  (sb < 0) sb = 0;
437             else if  (sb > maxval) sb = maxval;
438
439             r2=a2_cmap[hibit][x&1][0] * bit;
440             g2=a2_cmap[hibit][x&1][1] * bit;
441             b2=a2_cmap[hibit][x&1][2] * bit;
442
443             pP[x] = (r2<<16) | (g2<<8) | (b2);
444
445             /* Propagate Floyd-Steinberg error terms. */
446             err =  (sr - r2) * fs_scale;
447             this_rerr[x + 2] +=  (err * 7) / 16;
448             next_rerr[x    ] +=  (err * 3) / 16;
449             next_rerr[x + 1] +=  (err * 5) / 16;
450             next_rerr[x + 2] +=  (err    ) / 16;
451             err =  (sg - g2) * fs_scale;
452             this_gerr[x + 2] +=  (err * 7) / 16;
453             next_gerr[x    ] +=  (err * 3) / 16;
454             next_gerr[x + 1] +=  (err * 5) / 16;
455             next_gerr[x + 2] +=  (err    ) / 16;
456             err =  (sb - b2) * fs_scale;
457             this_berr[x + 2] +=  (err * 7) / 16;
458             next_berr[x    ] +=  (err * 3) / 16;
459             next_berr[x + 1] +=  (err * 5) / 16;
460             next_berr[x + 2] +=  (err    ) / 16;
461           }
462
463         /*
464           And put the actual byte into out.
465         */
466
467         out[y*(w/7) + xbyte/7] = best_byte;
468
469         }
470
471       temp_err  = this_rerr;
472       this_rerr = next_rerr;
473       next_rerr = temp_err;
474       temp_err  = this_gerr;
475       this_gerr = next_gerr;
476       next_gerr = temp_err;
477       temp_err  = this_berr;
478       this_berr = next_berr;
479       next_berr = temp_err;
480     }
481
482   free (this_rerr);
483   free (next_rerr);
484   free (this_gerr);
485   free (next_gerr);
486   free (this_berr);
487   free (next_berr);
488
489   for (y=0; y<h; y++)
490     free (pixels[y]);
491   free (pixels);
492
493 #if 0
494   {
495     /* let's see what we got... */
496     FILE *pipe = popen ("xv -", "w");
497     fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
498     for (y = 0; y < h; y++)
499       for (x = 0; x < w; x++)
500         {
501           unsigned int r = (pixels[y][x]>>16)&0xff;
502           unsigned int g = (pixels[y][x]>>8)&0xff;
503           unsigned int b = (pixels[y][x]>>0)&0xff;
504           fprintf(pipe, "%c%c%c", r, g, b);
505         }
506     fclose (pipe);
507   }
508 #endif
509 }
510
511 typedef struct slideshow_data_s {
512   int slideno;
513   int render_img_lineno;
514   unsigned char *render_img;
515   char *img_filename;
516   Bool image_loading_p;
517 } slideshow_data;
518
519
520 static void
521 image_loaded_cb (Screen *screen, Window window, Drawable p,
522                  const char *name, XRectangle *geometry,
523                  void *closure)
524 {
525   Display *dpy = DisplayOfScreen (screen);
526   apple2_sim_t *sim = (apple2_sim_t *) closure;
527   slideshow_data *mine = (slideshow_data *) sim->controller_data;
528   XWindowAttributes xgwa;
529   int w = 280;
530   int h = 192;
531   XImage *image;
532   unsigned int  *buf32 = (unsigned int  *) calloc (w, h * 4);
533   unsigned char *buf8  = (unsigned char *) calloc (w/7, h);
534
535   if (!buf32 || !buf8)
536     {
537       fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
538       exit (1);
539     }
540
541   XGetWindowAttributes (dpy, window, &xgwa);
542
543   image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
544   XFreePixmap (dpy, p);
545   p = 0;
546
547   /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
548      image (regardless of whether it started as TrueColor/PseudoColor.)
549    */
550   pick_a2_subimage (dpy, window, image, buf32, w, h);
551   free(image->data);
552   image->data = 0;
553   XDestroyImage(image);
554
555   /* Then dither the 32bpp image to a 6-color Apple][ colormap.
556    */
557   a2_dither (buf32, buf8, w, h);
558
559   free (buf32);
560
561   mine->image_loading_p = False;
562   mine->img_filename = (name ? strdup (name) : 0);
563   mine->render_img = buf8;
564 }
565
566 \f
567
568 static const char *apple2_defaults [] = {
569   ".background:            black",
570   ".foreground:            white",
571   "*mode:                  random",
572   "*duration:              60",
573   "*program:               xscreensaver-text --cols 40",
574   "*metaSendsESC:          True",
575   "*swapBSDEL:             True",
576   "*fast:                  False",
577 # ifdef HAVE_FORKPTY
578   "*usePty:                True",
579 #else
580   "*usePty:                False",
581 # endif /* !HAVE_FORKPTY */
582
583   ANALOGTV_DEFAULTS
584   0
585 };
586
587 static XrmOptionDescRec apple2_options [] = {
588   { "-mode",            ".mode",                XrmoptionSepArg, 0 },
589   { "-slideshow",       ".mode",                XrmoptionNoArg,  "slideshow" },
590   { "-basic",           ".mode",                XrmoptionNoArg,  "basic" },
591   { "-text",            ".mode",                XrmoptionNoArg,  "text" },
592   { "-program",         ".program",             XrmoptionSepArg, 0 },
593   { "-duration",        ".duration",            XrmoptionSepArg, 0 },
594   { "-pty",             ".usePty",              XrmoptionNoArg,  "True"  },
595   { "-pipe",            ".usePty",              XrmoptionNoArg,  "False" },
596   { "-meta",            ".metaSendsESC",        XrmoptionNoArg,  "False" },
597   { "-esc",             ".metaSendsESC",        XrmoptionNoArg,  "True"  },
598   { "-bs",              ".swapBSDEL",           XrmoptionNoArg,  "False" },
599   { "-del",             ".swapBSDEL",           XrmoptionNoArg,  "True"  },
600   { "-fast",            ".fast",                XrmoptionNoArg,  "True"  },
601   ANALOGTV_OPTIONS
602   { 0, 0, 0, 0 }
603 };
604
605 /*
606   TODO: this should load 10 images at startup time, then cycle through them
607   to avoid the pause while it loads.
608  */
609
610 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
611                                  double *next_actiontime)
612 {
613   apple2_state_t *st=sim->st;
614   int i;
615   slideshow_data *mine;
616
617   if (!sim->controller_data)
618     sim->controller_data = calloc (1, sizeof(*mine));
619   mine = (slideshow_data *) sim->controller_data;
620
621   switch(*stepno) {
622
623   case 0:
624     a2_invalidate(st);
625     a2_clear_hgr(st);
626     a2_cls(st);
627     sim->typing_rate = 0.3;
628     sim->dec->powerup=0.0;
629
630     a2_goto(st, 0, 16);
631     a2_prints(st, "APPLE ][");
632     a2_goto(st,23,0);
633     a2_printc(st,']');
634
635     *stepno=10;
636     break;
637
638   case 10:
639     {
640       XWindowAttributes xgwa;
641       Pixmap p;
642       XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
643       p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height, 
644                          xgwa.depth);
645       mine->image_loading_p = True;
646       load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
647
648       /* pause with a blank screen for a bit, while the image loads in the
649          background. */
650       *next_actiontime += 2.0;
651       *stepno=11;
652     }
653     break;
654
655   case 11:
656     if (! mine->image_loading_p) {  /* image is finally loaded */
657       if (st->gr_mode) {
658         *stepno=30;
659       } else {
660         *stepno=20;
661       }
662       *next_actiontime += 3.0;
663     }
664     break;
665
666   case 20:
667     sim->typing="HGR\n";
668     *stepno=29;
669     break;
670
671   case 29:
672     sim->printing="]";
673     *stepno=30;
674     break;
675
676   case 30:
677     st->gr_mode=A2_GR_HIRES;
678     if (mine->img_filename) {
679       char *basename, *tmp;
680       char *s;
681
682       basename = tmp = strdup (mine->img_filename);
683       while (1)
684         {
685           char *slash = strchr(basename, '/');
686           if (!slash || !slash[1]) break;
687           basename = slash+1;
688         }
689       {
690         char *dot=strrchr(basename,'.');
691         if (dot) *dot=0;
692       }
693       if (strlen(basename)>20) basename[20]=0;
694       for (s=basename; *s; s++) {
695         *s = toupper (*s);
696         if (*s <= ' ') *s = '_';
697       }
698       sprintf(sim->typing_buf, "BLOAD %s\n", basename);
699       sim->typing = sim->typing_buf;
700
701       free(tmp);
702     } else {
703       sim->typing = "BLOAD IMAGE\n";
704     }
705     mine->render_img_lineno=0;
706
707     *stepno=35;
708     break;
709
710   case 35:
711     *next_actiontime += 0.7;
712     *stepno=40;
713     break;
714
715   case 40:
716     if (mine->render_img_lineno>=192) {
717       sim->printing="]";
718       sim->typing="POKE 49234,0\n";
719       *stepno=50;
720       return;
721     }
722
723     for (i=0; i<6 && mine->render_img_lineno<192; i++) {
724       a2_display_image_loading(st, mine->render_img,
725                                mine->render_img_lineno++);
726     }
727
728     /* The disk would have to seek every 13 sectors == 78 lines.
729        (This ain't no newfangled 16-sector operating system) */
730     if ((mine->render_img_lineno%78)==0) {
731       *next_actiontime += 0.5;
732     } else {
733       *next_actiontime += 0.08;
734     }
735     break;
736
737   case 50:
738     st->gr_mode |= A2_GR_FULL;
739     *stepno=60;
740     /* Note that sim->delay is sometimes "infinite" in this controller.
741        These images are kinda dull anyway, so don't leave it on too long. */
742     *next_actiontime += 2;
743     break;
744
745   case 60:
746     sim->printing="]";
747     sim->typing="POKE 49235,0\n";
748     *stepno=70;
749     break;
750
751   case 70:
752     sim->printing="]";
753     st->gr_mode &= ~A2_GR_FULL;
754     if (mine->render_img) {
755       free(mine->render_img);
756       mine->render_img=NULL;
757     }
758     if (mine->img_filename) {
759       free(mine->img_filename);
760       mine->img_filename=NULL;
761     }
762     *stepno=10;
763     break;
764
765   case A2CONTROLLER_FREE:
766     free(mine->render_img);
767     free(mine->img_filename);
768     free(mine);
769     mine = 0;
770     return;
771
772   }
773 }
774
775 #define NPAR 16
776
777 struct terminal_controller_data {
778   Display *dpy;
779   char curword[256];
780   unsigned char lastc;
781   double last_emit_time;
782   text_data *tc;
783
784   int escstate;
785   int csiparam[NPAR];
786   int curparam;
787   int cursor_x, cursor_y;
788   int saved_x,  saved_y;
789   union {
790     struct {
791       unsigned int bold : 1;
792       unsigned int blink : 1;
793       unsigned int rev : 1;
794     } bf;
795     int w;
796   } termattrib;
797   Bool fast_p;
798
799 };
800
801
802 /* The structure of closure linkage throughout this code is so amazingly
803    baroque that I can't get to the 'struct state' from where I need it. */
804 static const char *global_program;
805 static Bool global_fast_p;
806
807
808 static void
809 terminal_closegen(struct terminal_controller_data *mine)
810 {
811   if (mine->tc) {
812     textclient_close (mine->tc);
813     mine->tc = 0;
814   }
815 }
816
817 static int
818 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
819 {
820   if (!mine || !mine->tc) {
821     return 0;
822   } else {
823     int i, count = 0;
824     for (i = 0; i < n; i++) {
825       int c = textclient_getc (mine->tc);
826       if (c <= 0) break;
827       buf[i] = c;
828       mine->lastc = c;
829       count++;
830     }
831     return count;
832   }
833 }
834
835
836 static int
837 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
838 {
839   struct terminal_controller_data *mine =
840     (struct terminal_controller_data *) data;
841   mine->dpy = dpy;
842   if (event->xany.type == KeyPress && mine->tc)
843     return textclient_putc (mine->tc, &event->xkey);
844   return 0;
845 }
846
847
848 static void
849 a2_ascii_printc (apple2_state_t *st, unsigned char c,
850                  Bool bold_p, Bool blink_p, Bool rev_p,
851                  Bool scroll_p)
852 {
853   if (c >= 'a' && c <= 'z')            /* upcase lower-case chars */
854     {
855       c &= 0xDF;
856     }
857   else if ((c >= 'A'+128) ||                    /* upcase and blink */
858            (c < ' ' && c != 014 &&              /* high-bit & ctl chrs */
859             c != '\r' && c != '\n' && c!='\t'))
860     {
861       c = (c & 0x1F) | 0x80;
862     }
863   else if (c >= 'A' && c <= 'Z')            /* invert upper-case chars */
864     {
865       c |= 0x80;
866     }
867
868   if (bold_p)  c |= 0xc0;
869   if (blink_p) c = (c & ~0x40) | 0x80;
870   if (rev_p)   c |= 0xc0;
871
872   if (scroll_p)
873     a2_printc(st, c);
874   else
875     a2_printc_noscroll(st, c);
876 }
877
878
879 static void
880 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
881                  unsigned char c)
882 {
883   apple2_state_t *st=sim->st;
884   int cols = SCREEN_COLS;
885   int rows = SCREEN_ROWS;
886
887   int i;
888   int start, end;
889
890   switch (state->escstate)
891     {
892     case 0:
893       switch (c)
894         {
895         case 7: /* BEL */
896           /* Dummy case - we don't want the screensaver to beep */
897           /* #### But maybe this should flash the screen? */
898           break;
899         case 8: /* BS */
900           if (state->cursor_x > 0)
901             state->cursor_x--;
902           break;
903         case 9: /* HT */
904           if (state->cursor_x < cols - 8)
905             {
906               state->cursor_x = (state->cursor_x & ~7) + 8;
907             }
908           else
909             {
910               state->cursor_x = 0;
911               if (state->cursor_y < rows - 1)
912                 state->cursor_y++;
913               else
914                 a2_scroll (st);
915             }
916           break;
917         case 10: /* LF */
918 # ifndef HAVE_FORKPTY
919           state->cursor_x = 0;  /* No ptys on iPhone; assume CRLF. */
920 # endif
921         case 11: /* VT */
922         case 12: /* FF */
923           if (state->cursor_y < rows - 1)
924             state->cursor_y++;
925           else
926             a2_scroll (st);
927           break;
928         case 13: /* CR */
929           state->cursor_x = 0;
930           break;
931         case 14: /* SO */
932         case 15: /* SI */
933           /* Dummy case - there is one and only one font. */
934           break;
935         case 24: /* CAN */
936         case 26: /* SUB */
937           /* Dummy case - these interrupt escape sequences, so
938              they don't do anything in this state */
939           break;
940         case 27: /* ESC */
941           state->escstate = 1;
942           break;
943         case 127: /* DEL */
944           /* Dummy case - this is supposed to be ignored */
945           break;
946         case 155: /* CSI */
947           state->escstate = 2;
948           for(i = 0; i < NPAR; i++)
949             state->csiparam[i] = 0;
950           state->curparam = 0;
951           break;
952         default:
953           /* If the cursor is in column 39 and we print a character, then
954              that character shows up in column 39, and the cursor is no longer
955              visible on the screen (it's in "column 40".)  If another character
956              is printed, then that character shows up in column 0, and the
957              cursor moves to column 1.
958
959              This is empirically what xterm and gnome-terminal do, so that must
960              be the right thing.  (In xterm, the cursor vanishes, whereas; in
961              gnome-terminal, the cursor overprints the character in col 39.)
962            */
963           if (state->cursor_x >= cols)
964             {
965               state->cursor_x = 0;
966               if (state->cursor_y >= rows - 1)
967                 a2_scroll (st);
968               else
969                 state->cursor_y++;
970             }
971
972           a2_goto(st, state->cursor_y, state->cursor_x);  /* clips range */
973           a2_ascii_printc (st, c,
974                            state->termattrib.bf.bold,
975                            state->termattrib.bf.blink,
976                            state->termattrib.bf.rev,
977                            False);
978           state->cursor_x++;
979
980           break;
981         }
982       break;
983     case 1:
984       switch (c)
985         {
986         case 24: /* CAN */
987         case 26: /* SUB */
988           state->escstate = 0;
989           break;
990         case 'c': /* Reset */
991           a2_cls(st);
992           state->escstate = 0;
993           break;
994         case 'D': /* Linefeed */
995           if (state->cursor_y < rows - 1)
996             state->cursor_y++;
997           else
998             a2_scroll (st);
999           state->escstate = 0;
1000           break;
1001         case 'E': /* Newline */
1002           state->cursor_x = 0;
1003           state->escstate = 0;
1004           break;
1005         case 'M': /* Reverse newline */
1006           if (state->cursor_y > 0)
1007             state->cursor_y--;
1008           state->escstate = 0;
1009           break;
1010         case '7': /* Save state */
1011           state->saved_x = state->cursor_x;
1012           state->saved_y = state->cursor_y;
1013           state->escstate = 0;
1014           break;
1015         case '8': /* Restore state */
1016           state->cursor_x = state->saved_x;
1017           state->cursor_y = state->saved_y;
1018           state->escstate = 0;
1019           break;
1020         case '[': /* CSI */
1021           state->escstate = 2;
1022           for(i = 0; i < NPAR; i++)
1023             state->csiparam[i] = 0;
1024           state->curparam = 0;
1025           break;
1026         case '%': /* Select charset */
1027           /* No, I don't support UTF-8, since the apple2 font
1028              isn't even Unicode anyway. We must still catch the
1029              last byte, though. */
1030         case '(':
1031         case ')':
1032           /* I don't support different fonts either - see above
1033              for SO and SI */
1034           state->escstate = 3;
1035           break;
1036         default:
1037           /* Escape sequences not supported:
1038            * 
1039            * H - Set tab stop
1040            * Z - Terminal identification
1041            * > - Keypad change
1042            * = - Other keypad change
1043            * ] - OS command
1044            */
1045           state->escstate = 0;
1046           break;
1047         }
1048       break;
1049     case 2:
1050       switch (c)
1051         {
1052         case 24: /* CAN */
1053         case 26: /* SUB */
1054           state->escstate = 0;
1055           break;
1056         case '0': case '1': case '2': case '3': case '4':
1057         case '5': case '6': case '7': case '8': case '9':
1058           if (state->curparam < NPAR)
1059             state->csiparam[state->curparam] =
1060               (state->csiparam[state->curparam] * 10) + (c - '0');
1061           break;
1062         case ';':
1063           state->csiparam[++state->curparam] = 0;
1064           break;
1065         case '[':
1066           state->escstate = 3;
1067           break;
1068         case '@':
1069           for (i = 0; i < state->csiparam[0]; i++)
1070             {
1071               if(++state->cursor_x > cols)
1072                 {
1073                   state->cursor_x = 0;
1074                   if (state->cursor_y < rows - 1)
1075                     state->cursor_y++;
1076                   else
1077                     a2_scroll (st);
1078                 }
1079             }
1080           state->escstate = 0;
1081           break;
1082         case 'F':
1083           state->cursor_x = 0;
1084         case 'A':
1085           if (state->csiparam[0] == 0)
1086             state->csiparam[0] = 1;
1087           if ((state->cursor_y -= state->csiparam[0]) < 0)
1088             state->cursor_y = 0;
1089           state->escstate = 0;
1090           break;
1091         case 'E':
1092           state->cursor_x = 0;
1093         case 'e':
1094         case 'B':
1095           if (state->csiparam[0] == 0)
1096             state->csiparam[0] = 1;
1097           if ((state->cursor_y += state->csiparam[0]) >= rows)
1098             state->cursor_y = rows - 1;
1099           state->escstate = 0;
1100           break;
1101         case 'a':
1102         case 'C':
1103           if (state->csiparam[0] == 0)
1104             state->csiparam[0] = 1;
1105           if ((state->cursor_x += state->csiparam[0]) >= cols)
1106             state->cursor_x = cols - 1;
1107           state->escstate = 0;
1108           break;
1109         case 'D':
1110           if (state->csiparam[0] == 0)
1111             state->csiparam[0] = 1;
1112           if ((state->cursor_x -= state->csiparam[0]) < 0)
1113             state->cursor_x = 0;
1114           state->escstate = 0;
1115           break;
1116         case 'd':
1117           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1118             state->cursor_y = rows - 1;
1119           state->escstate = 0;
1120           break;
1121         case '`':
1122         case 'G':
1123           if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1124             state->cursor_x = cols - 1;
1125           state->escstate = 0;
1126           break;
1127         case 'f':
1128         case 'H':
1129           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1130             state->cursor_y = rows - 1;
1131           if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1132             state->cursor_x = cols - 1;
1133           if(state->cursor_y < 0)
1134             state->cursor_y = 0;
1135           if(state->cursor_x < 0)
1136             state->cursor_x = 0;
1137           state->escstate = 0;
1138           break;
1139         case 'J':
1140           start = 0;
1141           end = rows * cols;
1142           if (state->csiparam[0] == 0)
1143             start = cols * state->cursor_y + state->cursor_x;
1144           if (state->csiparam[0] == 1)
1145             end = cols * state->cursor_y + state->cursor_x;
1146
1147           a2_goto(st, state->cursor_y, state->cursor_x);
1148           for (i = start; i < end; i++)
1149             {
1150               a2_ascii_printc(st, ' ', False, False, False, False);
1151             }
1152           state->escstate = 0;
1153           break;
1154         case 'K':
1155           start = 0;
1156           end = cols;
1157           if (state->csiparam[0] == 0)
1158             start = state->cursor_x;
1159           if (state->csiparam[1] == 1)
1160             end = state->cursor_x;
1161
1162           a2_goto(st, state->cursor_y, state->cursor_x);
1163           for (i = start; i < end; i++)
1164             {
1165               a2_ascii_printc(st, ' ', False, False, False, False);
1166             }
1167           state->escstate = 0;
1168           break;
1169         case 'm': /* Set attributes */
1170           for (i = 0; i <= state->curparam; i++)
1171             {
1172               switch(state->csiparam[i])
1173                 {
1174                 case 0:
1175                   state->termattrib.w = 0;
1176                   break;
1177                 case 1:
1178                   state->termattrib.bf.bold = 1;
1179                   break;
1180                 case 5:
1181                   state->termattrib.bf.blink = 1;
1182                   break;
1183                 case 7:
1184                   state->termattrib.bf.rev = 1;
1185                   break;
1186                 case 21:
1187                 case 22:
1188                   state->termattrib.bf.bold = 0;
1189                   break;
1190                 case 25:
1191                   state->termattrib.bf.blink = 0;
1192                   break;
1193                 case 27:
1194                   state->termattrib.bf.rev = 0;
1195                   break;
1196                 }
1197             }
1198           state->escstate = 0;
1199           break;
1200         case 's': /* Save position */
1201           state->saved_x = state->cursor_x;
1202           state->saved_y = state->cursor_y;
1203           state->escstate = 0;
1204           break;
1205         case 'u': /* Restore position */
1206           state->cursor_x = state->saved_x;
1207           state->cursor_y = state->saved_y;
1208           state->escstate = 0;
1209           break;
1210         case '?': /* DEC Private modes */
1211           if ((state->curparam != 0) || (state->csiparam[0] != 0))
1212             state->escstate = 0;
1213           break;
1214         default:
1215           /* Known unsupported CSIs:
1216            *
1217            * L - Insert blank lines
1218            * M - Delete lines (I don't know what this means...)
1219            * P - Delete characters
1220            * X - Erase characters (difference with P being...?)
1221            * c - Terminal identification
1222            * g - Clear tab stop(s)
1223            * h - Set mode (Mainly due to its complexity and lack of good
1224            docs)
1225            * l - Clear mode
1226            * m - Set mode (Phosphor is, per defenition, green on black)
1227            * n - Status report
1228            * q - Set keyboard LEDs
1229            * r - Set scrolling region (too exhausting - noone uses this,
1230            right?)
1231           */
1232           state->escstate = 0;
1233           break;
1234         }
1235       break;
1236     case 3:
1237       state->escstate = 0;
1238       break;
1239     }
1240   a2_goto(st, state->cursor_y, state->cursor_x);
1241 }
1242
1243
1244 /*
1245   It's fun to put things like "gdb" as the command. For one, it's
1246   amusing how the standard mumble (version, no warranty, it's
1247   GNU/Linux dammit) occupies an entire screen on the Apple ][.
1248 */
1249
1250 static void
1251 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1252 {
1253   apple2_state_t *st=sim->st;
1254   int c;
1255   int i;
1256
1257   struct terminal_controller_data *mine;
1258   if (!sim->controller_data)
1259     sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1260   mine=(struct terminal_controller_data *) sim->controller_data;
1261   mine->dpy = sim->dpy;
1262
1263   mine->fast_p           = global_fast_p;
1264
1265   switch(*stepno) {
1266
1267   case 0:
1268     if (random()%2)
1269       st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1270                                     showing text */
1271     a2_cls(st);
1272     a2_goto(st,0,16);
1273     a2_prints(st, "APPLE ][");
1274     a2_goto(st,2,0);
1275     mine->cursor_y = 2;
1276
1277     if (! mine->tc) {
1278       mine->tc = textclient_open (mine->dpy);
1279       textclient_reshape (mine->tc,
1280                           SCREEN_COLS, SCREEN_ROWS,
1281                           SCREEN_COLS, SCREEN_ROWS);
1282     }
1283
1284     if (! mine->fast_p)
1285       *next_actiontime += 4.0;
1286     *stepno = 10;
1287     break;
1288
1289   case 10:
1290     {
1291       unsigned char buf[1024];
1292       int nr,nwant;
1293       double elapsed;
1294
1295       elapsed=sim->curtime - mine->last_emit_time;
1296       mine->last_emit_time=sim->curtime;
1297       nwant=elapsed*25.0;
1298       if (elapsed>1.0) nwant=1;
1299       if (nwant<1) nwant=1;
1300       if (nwant>4) nwant=4;
1301
1302       if (mine->fast_p)
1303         nwant = sizeof(buf)-1;
1304
1305       nr=terminal_read(mine, buf, nwant);
1306       for (i=0; i<nr; i++) {
1307         c=buf[i];
1308
1309         if (mine->tc)
1310           a2_vt100_printc (sim, mine, c);
1311         else
1312           a2_ascii_printc (st, c, False, False, False, True);
1313       }
1314     }
1315     break;
1316
1317   case A2CONTROLLER_FREE:
1318     terminal_closegen(mine);
1319     free(mine);
1320     mine = 0;
1321     return;
1322   }
1323 }
1324
1325 struct basic_controller_data {
1326   int prog_line;
1327   int x,y,k;
1328   const char * const * progtext;
1329   int progstep;
1330   char *rep_str;
1331   int rep_pos;
1332   double prog_start_time;
1333   char error_buf[256];
1334 };
1335
1336 /*
1337   Adding more programs is easy. Just add a listing here and to all_programs,
1338   then add the state machine to actually execute it to basic_controller.
1339  */
1340 static const char * const moire_program[]={
1341   "10 HGR2\n",
1342   "20 FOR Y = 0 TO 190 STEP 2\n",
1343   "30 HCOLOR=4 : REM BLACK\n",
1344   "40 HPLOT 0,191-Y TO 279,Y\n",
1345   "60 HCOLOR=7 : REM WHITE\n",
1346   "80 HPLOT 0,190-Y TO 279,Y+1\n",
1347   "90 NEXT Y\n",
1348   "100 FOR X = 0 TO 278 STEP 3\n",
1349   "110 HCOLOR=4\n",
1350   "120 HPLOT 279-X,0 TO X,191\n",
1351   "140 HCOLOR=7\n",
1352   "150 HPLOT 278-X,0 TO X+1,191\n",
1353   "160 NEXT X\n",
1354   NULL
1355 };
1356
1357 static const char * const sinewave_program[] = {
1358   "10 HGR\n",
1359   "25 K=0\n",
1360   "30 FOR X = 0 TO 279\n",
1361   "32 HCOLOR= 0\n",
1362   "35 HPLOT X,0 TO X,159\n",
1363   "38 HCOLOR= 3\n",
1364   "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1365   "50 HPLOT X,Y\n",
1366   "60 NEXT X\n",
1367   "70 K=K+4\n",
1368   "80 GOTO 30\n",
1369   NULL
1370 };
1371
1372 #if 0
1373 static const char * const dumb_program[]={
1374   "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1375   "20 GOTO 10\n",
1376   NULL
1377 };
1378 #endif
1379
1380 static const char * const random_lores_program[]={
1381   "1 REM APPLE ][ SCREEN SAVER\n",
1382   "10 GR\n",
1383   "100 COLOR= RND(1)*16\n",
1384
1385   "110 X=RND(1)*40\n",
1386   "120 Y1=RND(1)*40\n",
1387   "130 Y2=RND(1)*40\n",
1388   "140 FOR Y = Y1 TO Y2\n",
1389   "150 PLOT X,Y\n",
1390   "160 NEXT Y\n",
1391
1392   "210 Y=RND(1)*40\n",
1393   "220 X1=RND(1)*40\n",
1394   "230 X2=RND(1)*40\n",
1395   "240 FOR X = X1 TO X2\n",
1396   "250 PLOT X,Y\n",
1397   "260 NEXT X\n",
1398   "300 GOTO 100\n",
1399
1400   NULL
1401 };
1402
1403 static char typo_map[256];
1404
1405 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1406 {
1407   int i,j;
1408   int errc;
1409   int success=0;
1410   err_buf[0]=0;
1411
1412   typo_map['A']='Q';
1413   typo_map['S']='A';
1414   typo_map['D']='S';
1415   typo_map['F']='G';
1416   typo_map['G']='H';
1417   typo_map['H']='J';
1418   typo_map['J']='H';
1419   typo_map['K']='L';
1420   typo_map['L']=';';
1421
1422   typo_map['Q']='1';
1423   typo_map['W']='Q';
1424   typo_map['E']='3';
1425   typo_map['R']='T';
1426   typo_map['T']='Y';
1427   typo_map['Y']='U';
1428   typo_map['U']='Y';
1429   typo_map['I']='O';
1430   typo_map['O']='P';
1431   typo_map['P']='[';
1432
1433   typo_map['Z']='X';
1434   typo_map['X']='C';
1435   typo_map['C']='V';
1436   typo_map['V']='C';
1437   typo_map['B']='N';
1438   typo_map['N']='B';
1439   typo_map['M']='N';
1440   typo_map[',']='.';
1441   typo_map['.']=',';
1442
1443   typo_map['!']='1';
1444   typo_map['@']='2';
1445   typo_map['#']='3';
1446   typo_map['$']='4';
1447   typo_map['%']='5';
1448   typo_map['^']='6';
1449   typo_map['&']='7';
1450   typo_map['*']='8';
1451   typo_map['(']='9';
1452   typo_map[')']='0';
1453
1454   typo_map['1']='Q';
1455   typo_map['2']='W';
1456   typo_map['3']='E';
1457   typo_map['4']='R';
1458   typo_map['5']='T';
1459   typo_map['6']='Y';
1460   typo_map['7']='U';
1461   typo_map['8']='I';
1462   typo_map['9']='O';
1463   typo_map['0']='-';
1464
1465   strcpy(out_buf, orig);
1466   for (i=0; out_buf[i]; i++) {
1467     char *p = out_buf+i;
1468
1469     if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1470       break;
1471
1472     if (isalpha(p[0]) &&
1473         isalpha(p[1]) &&
1474         p[0] != p[1] &&
1475         random()%15==0)
1476       {
1477         int tmp=p[1];
1478         p[1]=p[0];
1479         p[0]=tmp;
1480         success=1;
1481         sprintf(err_buf,"?SYNTAX ERROR\n");
1482         break;
1483       }
1484
1485     if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1486       int remain=strlen(p);
1487       int past=random()%(remain-2)+1;
1488       memmove(p+past+past, p, remain+1);
1489       p[0]=errc;
1490       for (j=0; j<past; j++) {
1491         p[past+j]=010;
1492       }
1493       break;
1494     }
1495   }
1496   return success;
1497 }
1498
1499 static const struct {
1500   const char * const * progtext;
1501   int progstep;
1502 } all_programs[]={
1503   {moire_program, 100},
1504   /*{dumb_program, 200}, */
1505   {sinewave_program, 400},
1506   {random_lores_program, 500},
1507 };
1508
1509 static void
1510 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1511 {
1512   apple2_state_t *st=sim->st;
1513   int i;
1514
1515   struct basic_controller_data *mine;
1516   if (!sim->controller_data)
1517     sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1518   mine=(struct basic_controller_data *) sim->controller_data;
1519
1520   switch (*stepno) {
1521   case 0:
1522     st->gr_mode=0;
1523     a2_cls(st);
1524     a2_goto(st,0,16);
1525     a2_prints(st, "APPLE ][");
1526     a2_goto(st,23,0);
1527     a2_printc(st,']');
1528     sim->typing_rate=0.2;
1529
1530     i=random()%countof(all_programs);
1531     mine->progtext=all_programs[i].progtext;
1532     mine->progstep=all_programs[i].progstep;
1533     mine->prog_line=0;
1534
1535     *next_actiontime += 1.0;
1536     *stepno=10;
1537     break;
1538
1539   case 10:
1540     if (st->cursx==0) a2_printc(st,']');
1541     if (mine->progtext[mine->prog_line]) {
1542       if (random()%4==0) {
1543         int err=make_typo(sim->typing_buf,
1544                           mine->progtext[mine->prog_line],
1545                           mine->error_buf);
1546         sim->typing=sim->typing_buf;
1547         if (err) {
1548           *stepno=11;
1549         } else {
1550           mine->prog_line++;
1551         }
1552       } else {
1553         sim->typing=mine->progtext[mine->prog_line++];
1554       }
1555     } else {
1556       *stepno=15;
1557     }
1558     break;
1559
1560   case 11:
1561     sim->printing=mine->error_buf;
1562     *stepno=12;
1563     break;
1564
1565   case 12:
1566     if (st->cursx==0) a2_printc(st,']');
1567     *next_actiontime+=1.0;
1568     *stepno=10;
1569     break;
1570
1571   case 15:
1572     sim->typing="RUN\n";
1573     mine->y=0;
1574     mine->x=0;
1575     mine->k=0;
1576     mine->prog_start_time=*next_actiontime;
1577     *stepno=mine->progstep;
1578     break;
1579
1580     /* moire_program */
1581   case 100:
1582     st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1583     for (i=0; i<24 && mine->y<192; i++)
1584       {
1585         a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1586         a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1587         mine->y += 2;
1588       }
1589     if (mine->y>=192) {
1590       mine->x = 0;
1591       *stepno = 110;
1592     }
1593     break;
1594
1595   case 110:
1596     for (i=0; i<24 && mine->x<280; i++)
1597       {
1598         a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1599         a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1600         mine->x+=3;
1601       }
1602     if (mine->x >= 280) *stepno=120;
1603     break;
1604
1605   case 120:
1606     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1607     break;
1608
1609     /* dumb_program */
1610   case 200:
1611     mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1612     for (i=0; i<30; i++) {
1613       a2_prints(st, mine->rep_str);
1614     }
1615     *stepno=210;
1616     break;
1617
1618   case 210:
1619     i=random()%strlen(mine->rep_str);
1620     while (mine->rep_pos != i) {
1621       a2_printc(st, mine->rep_str[mine->rep_pos]);
1622       mine->rep_pos++;
1623       if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1624     }
1625     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1626     break;
1627
1628     /* sinewave_program */
1629   case 400:
1630     st->gr_mode=A2_GR_HIRES;
1631     *stepno=410;
1632     break;
1633
1634   case 410:
1635     for (i=0; i<48; i++) {
1636       int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1637       a2_hline(st, 0, mine->x, 0, mine->x, 159);
1638       a2_hplot(st, 3, mine->x, y);
1639       mine->x += 1;
1640       if (mine->x>=279) {
1641         mine->x=0;
1642         mine->k+=4;
1643       }
1644     }
1645     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1646     break;
1647
1648   case 420:
1649     a2_prints(st, "]");
1650     *stepno=999;
1651     break;
1652
1653     /* random_lores_program */
1654   case 500:
1655     st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1656     a2_clear_gr(st);
1657     *stepno=510;
1658
1659   case 510:
1660     for (i=0; i<10; i++) {
1661       int color,x,y,x1,x2,y1,y2;
1662
1663       color=random()%15;
1664       x=random()%40;
1665       y1=random()%48;
1666       y2=random()%48;
1667       for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1668
1669       x1=random()%40;
1670       x2=random()%40;
1671       y=random()%48;
1672       for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1673     }
1674     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1675     break;
1676
1677   case 999:
1678     *stepno=0;
1679     break;
1680
1681   case A2CONTROLLER_FREE:
1682     free(mine);
1683     mine = 0;
1684     break;
1685   }
1686
1687 }
1688
1689 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1690                                      double *next_actiontime) = {
1691   slideshow_controller,
1692   terminal_controller,
1693   basic_controller
1694 };
1695
1696 struct state {
1697   int duration;
1698   Bool random_p;
1699   apple2_sim_t *sim;
1700   void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1701 };
1702
1703
1704 static void *
1705 apple2_init (Display *dpy, Window window)
1706 {
1707   struct state *st = (struct state *) calloc (1, sizeof(*st));
1708   char *s;
1709
1710   st->duration = get_integer_resource (dpy, "duration", "Integer");
1711
1712   st->controller = 0;
1713   if (st->duration < 1) st->duration = 1;
1714
1715   s = get_string_resource (dpy, "mode", "Mode");
1716   if (!s || !*s || !strcasecmp(s, "random"))
1717     st->random_p = True;
1718   else if (!strcasecmp(s, "text"))
1719      st->controller = terminal_controller;
1720   else if (!strcasecmp(s, "slideshow"))
1721      st->controller = slideshow_controller;
1722   else if (!strcasecmp(s, "basic"))
1723      st->controller = basic_controller;
1724   else
1725     {
1726       fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1727                progname, s);
1728       exit (1);
1729     }
1730   if (s) free (s);
1731
1732   global_program = get_string_resource (dpy, "program", "Program");
1733   global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
1734
1735
1736   /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
1737   {
1738     const char *s = getenv ("XSCREENSAVER_STANDALONE");
1739     if (s && *s && strcmp(s, "0"))
1740       {
1741         st->controller = terminal_controller;
1742         st->random_p   = False;
1743         global_program = getenv ("SHELL");
1744         global_fast_p  = True;
1745       }
1746   }
1747
1748
1749   if (! st->random_p) {
1750     if (st->controller == terminal_controller ||
1751         st->controller == slideshow_controller)
1752       st->duration = 999999;  /* these run "forever" */
1753   }
1754
1755   return st;
1756 }
1757
1758 static unsigned long
1759 apple2_draw (Display *dpy, Window window, void *closure)
1760 {
1761   struct state *st = (struct state *) closure;
1762
1763   if (! st->sim) {
1764     if (st->random_p)
1765       st->controller = controllers[random() % (countof(controllers))];
1766     st->sim = apple2_start (dpy, window, st->duration, st->controller);
1767   }
1768
1769   if (! apple2_one_frame (st->sim)) {
1770     st->sim = 0;
1771   }
1772
1773   return 10000;
1774 }
1775
1776 static void
1777 apple2_reshape (Display *dpy, Window window, void *closure, 
1778                  unsigned int w, unsigned int h)
1779 {
1780   struct state *st = (struct state *) closure;
1781   analogtv_reconfigure (st->sim->dec);
1782 }
1783
1784 static Bool
1785 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1786 {
1787   struct state *st = (struct state *) closure;
1788
1789   if (st->controller == terminal_controller &&
1790       event->xany.type == KeyPress) {
1791     terminal_keypress_handler (dpy, event, st->sim->controller_data);
1792     return True;
1793   }
1794
1795   return False;
1796 }
1797
1798 static void
1799 apple2_free (Display *dpy, Window window, void *closure)
1800 {
1801   struct state *st = (struct state *) closure;
1802   if (st->sim) {
1803     st->sim->stepno = A2CONTROLLER_DONE;
1804     if (apple2_one_frame (st->sim))
1805       abort();  /* should have freed! */
1806   }
1807   free (st);
1808 }
1809
1810
1811 XSCREENSAVER_MODULE ("Apple2", apple2)