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