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