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