From http://www.jwz.org/xscreensaver/xscreensaver-5.30.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
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                           0);
1297     }
1298
1299     if (! mine->fast_p)
1300       *next_actiontime += 4.0;
1301     *stepno = 10;
1302     break;
1303
1304   case 10:
1305     {
1306       unsigned char buf[1024];
1307       int nr,nwant;
1308       double elapsed;
1309
1310       elapsed=sim->curtime - mine->last_emit_time;
1311       mine->last_emit_time=sim->curtime;
1312       nwant=elapsed*25.0;
1313       if (elapsed>1.0) nwant=1;
1314       if (nwant<1) nwant=1;
1315       if (nwant>4) nwant=4;
1316
1317       if (mine->fast_p)
1318         nwant = sizeof(buf)-1;
1319
1320       nr=terminal_read(mine, buf, nwant);
1321       for (i=0; i<nr; i++) {
1322         c=buf[i];
1323
1324         if (mine->tc)
1325           a2_vt100_printc (sim, mine, c);
1326         else
1327           a2_ascii_printc (st, c, False, False, False, True);
1328       }
1329     }
1330     break;
1331
1332   case A2CONTROLLER_FREE:
1333     terminal_closegen(mine);
1334     free(mine);
1335     mine = 0;
1336     return;
1337   }
1338 }
1339
1340 struct basic_controller_data {
1341   int prog_line;
1342   int x,y,k;
1343   const char * const * progtext;
1344   int progstep;
1345   char *rep_str;
1346   int rep_pos;
1347   double prog_start_time;
1348   char error_buf[256];
1349 };
1350
1351 /*
1352   Adding more programs is easy. Just add a listing here and to all_programs,
1353   then add the state machine to actually execute it to basic_controller.
1354  */
1355 static const char * const moire_program[]={
1356   "10 HGR2\n",
1357   "20 FOR Y = 0 TO 190 STEP 2\n",
1358   "30 HCOLOR=4 : REM BLACK\n",
1359   "40 HPLOT 0,191-Y TO 279,Y\n",
1360   "60 HCOLOR=7 : REM WHITE\n",
1361   "80 HPLOT 0,190-Y TO 279,Y+1\n",
1362   "90 NEXT Y\n",
1363   "100 FOR X = 0 TO 278 STEP 3\n",
1364   "110 HCOLOR=4\n",
1365   "120 HPLOT 279-X,0 TO X,191\n",
1366   "140 HCOLOR=7\n",
1367   "150 HPLOT 278-X,0 TO X+1,191\n",
1368   "160 NEXT X\n",
1369   NULL
1370 };
1371
1372 static const char * const sinewave_program[] = {
1373   "10 HGR\n",
1374   "25 K=0\n",
1375   "30 FOR X = 0 TO 279\n",
1376   "32 HCOLOR= 0\n",
1377   "35 HPLOT X,0 TO X,159\n",
1378   "38 HCOLOR= 3\n",
1379   "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1380   "50 HPLOT X,Y\n",
1381   "60 NEXT X\n",
1382   "70 K=K+4\n",
1383   "80 GOTO 30\n",
1384   NULL
1385 };
1386
1387 #if 0
1388 static const char * const dumb_program[]={
1389   "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1390   "20 GOTO 10\n",
1391   NULL
1392 };
1393 #endif
1394
1395 static const char * const random_lores_program[]={
1396   "1 REM APPLE ][ SCREEN SAVER\n",
1397   "10 GR\n",
1398   "100 COLOR= RND(1)*16\n",
1399
1400   "110 X=RND(1)*40\n",
1401   "120 Y1=RND(1)*40\n",
1402   "130 Y2=RND(1)*40\n",
1403   "140 FOR Y = Y1 TO Y2\n",
1404   "150 PLOT X,Y\n",
1405   "160 NEXT Y\n",
1406
1407   "210 Y=RND(1)*40\n",
1408   "220 X1=RND(1)*40\n",
1409   "230 X2=RND(1)*40\n",
1410   "240 FOR X = X1 TO X2\n",
1411   "250 PLOT X,Y\n",
1412   "260 NEXT X\n",
1413   "300 GOTO 100\n",
1414
1415   NULL
1416 };
1417
1418 static char typo_map[256];
1419
1420 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1421 {
1422   int i,j;
1423   int errc;
1424   int success=0;
1425   err_buf[0]=0;
1426
1427   typo_map['A']='Q';
1428   typo_map['S']='A';
1429   typo_map['D']='S';
1430   typo_map['F']='G';
1431   typo_map['G']='H';
1432   typo_map['H']='J';
1433   typo_map['J']='H';
1434   typo_map['K']='L';
1435   typo_map['L']=';';
1436
1437   typo_map['Q']='1';
1438   typo_map['W']='Q';
1439   typo_map['E']='3';
1440   typo_map['R']='T';
1441   typo_map['T']='Y';
1442   typo_map['Y']='U';
1443   typo_map['U']='Y';
1444   typo_map['I']='O';
1445   typo_map['O']='P';
1446   typo_map['P']='[';
1447
1448   typo_map['Z']='X';
1449   typo_map['X']='C';
1450   typo_map['C']='V';
1451   typo_map['V']='C';
1452   typo_map['B']='N';
1453   typo_map['N']='B';
1454   typo_map['M']='N';
1455   typo_map[',']='.';
1456   typo_map['.']=',';
1457
1458   typo_map['!']='1';
1459   typo_map['@']='2';
1460   typo_map['#']='3';
1461   typo_map['$']='4';
1462   typo_map['%']='5';
1463   typo_map['^']='6';
1464   typo_map['&']='7';
1465   typo_map['*']='8';
1466   typo_map['(']='9';
1467   typo_map[')']='0';
1468
1469   typo_map['1']='Q';
1470   typo_map['2']='W';
1471   typo_map['3']='E';
1472   typo_map['4']='R';
1473   typo_map['5']='T';
1474   typo_map['6']='Y';
1475   typo_map['7']='U';
1476   typo_map['8']='I';
1477   typo_map['9']='O';
1478   typo_map['0']='-';
1479
1480   strcpy(out_buf, orig);
1481   for (i=0; out_buf[i]; i++) {
1482     char *p = out_buf+i;
1483
1484     if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1485       break;
1486
1487     if (isalpha(p[0]) &&
1488         isalpha(p[1]) &&
1489         p[0] != p[1] &&
1490         random()%15==0)
1491       {
1492         int tmp=p[1];
1493         p[1]=p[0];
1494         p[0]=tmp;
1495         success=1;
1496         sprintf(err_buf,"?SYNTAX ERROR\n");
1497         break;
1498       }
1499
1500     if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1501       int remain=strlen(p);
1502       int past=random()%(remain-2)+1;
1503       memmove(p+past+past, p, remain+1);
1504       p[0]=errc;
1505       for (j=0; j<past; j++) {
1506         p[past+j]=010;
1507       }
1508       break;
1509     }
1510   }
1511   return success;
1512 }
1513
1514 static const struct {
1515   const char * const * progtext;
1516   int progstep;
1517 } all_programs[]={
1518   {moire_program, 100},
1519   /*{dumb_program, 200}, */
1520   {sinewave_program, 400},
1521   {random_lores_program, 500},
1522 };
1523
1524 static void
1525 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1526 {
1527   apple2_state_t *st=sim->st;
1528   int i;
1529
1530   struct basic_controller_data *mine;
1531   if (!sim->controller_data)
1532     sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1533   mine=(struct basic_controller_data *) sim->controller_data;
1534
1535   switch (*stepno) {
1536   case 0:
1537     st->gr_mode=0;
1538     a2_cls(st);
1539     a2_goto(st,0,16);
1540     a2_prints(st, "APPLE ][");
1541     a2_goto(st,23,0);
1542     a2_printc(st,']');
1543     sim->typing_rate=0.2;
1544
1545     i=random()%countof(all_programs);
1546     mine->progtext=all_programs[i].progtext;
1547     mine->progstep=all_programs[i].progstep;
1548     mine->prog_line=0;
1549
1550     *next_actiontime += 1.0;
1551     *stepno=10;
1552     break;
1553
1554   case 10:
1555     if (st->cursx==0) a2_printc(st,']');
1556     if (mine->progtext[mine->prog_line]) {
1557       if (random()%4==0) {
1558         int err=make_typo(sim->typing_buf,
1559                           mine->progtext[mine->prog_line],
1560                           mine->error_buf);
1561         sim->typing=sim->typing_buf;
1562         if (err) {
1563           *stepno=11;
1564         } else {
1565           mine->prog_line++;
1566         }
1567       } else {
1568         sim->typing=mine->progtext[mine->prog_line++];
1569       }
1570     } else {
1571       *stepno=15;
1572     }
1573     break;
1574
1575   case 11:
1576     sim->printing=mine->error_buf;
1577     *stepno=12;
1578     break;
1579
1580   case 12:
1581     if (st->cursx==0) a2_printc(st,']');
1582     *next_actiontime+=1.0;
1583     *stepno=10;
1584     break;
1585
1586   case 15:
1587     sim->typing="RUN\n";
1588     mine->y=0;
1589     mine->x=0;
1590     mine->k=0;
1591     mine->prog_start_time=*next_actiontime;
1592     *stepno=mine->progstep;
1593     break;
1594
1595     /* moire_program */
1596   case 100:
1597     st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1598     for (i=0; i<24 && mine->y<192; i++)
1599       {
1600         a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1601         a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1602         mine->y += 2;
1603       }
1604     if (mine->y>=192) {
1605       mine->x = 0;
1606       *stepno = 110;
1607     }
1608     break;
1609
1610   case 110:
1611     for (i=0; i<24 && mine->x<280; i++)
1612       {
1613         a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1614         a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1615         mine->x+=3;
1616       }
1617     if (mine->x >= 280) *stepno=120;
1618     break;
1619
1620   case 120:
1621     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1622     break;
1623
1624     /* dumb_program */
1625   case 200:
1626     mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1627     for (i=0; i<30; i++) {
1628       a2_prints(st, mine->rep_str);
1629     }
1630     *stepno=210;
1631     break;
1632
1633   case 210:
1634     i=random()%strlen(mine->rep_str);
1635     while (mine->rep_pos != i) {
1636       a2_printc(st, mine->rep_str[mine->rep_pos]);
1637       mine->rep_pos++;
1638       if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1639     }
1640     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1641     break;
1642
1643     /* sinewave_program */
1644   case 400:
1645     st->gr_mode=A2_GR_HIRES;
1646     *stepno=410;
1647     break;
1648
1649   case 410:
1650     for (i=0; i<48; i++) {
1651       int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1652       a2_hline(st, 0, mine->x, 0, mine->x, 159);
1653       a2_hplot(st, 3, mine->x, y);
1654       mine->x += 1;
1655       if (mine->x>=279) {
1656         mine->x=0;
1657         mine->k+=4;
1658       }
1659     }
1660     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1661     break;
1662
1663   case 420:
1664     a2_prints(st, "]");
1665     *stepno=999;
1666     break;
1667
1668     /* random_lores_program */
1669   case 500:
1670     st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1671     a2_clear_gr(st);
1672     *stepno=510;
1673
1674   case 510:
1675     for (i=0; i<10; i++) {
1676       int color,x,y,x1,x2,y1,y2;
1677
1678       color=random()%15;
1679       x=random()%40;
1680       y1=random()%48;
1681       y2=random()%48;
1682       for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1683
1684       x1=random()%40;
1685       x2=random()%40;
1686       y=random()%48;
1687       for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1688     }
1689     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1690     break;
1691
1692   case 999:
1693     *stepno=0;
1694     break;
1695
1696   case A2CONTROLLER_FREE:
1697     free(mine);
1698     mine = 0;
1699     break;
1700   }
1701
1702 }
1703
1704 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1705                                      double *next_actiontime) = {
1706   slideshow_controller,
1707   terminal_controller,
1708   basic_controller
1709 };
1710
1711 struct state {
1712   int duration;
1713   Bool random_p;
1714   apple2_sim_t *sim;
1715   void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1716 };
1717
1718
1719 static void *
1720 apple2_init (Display *dpy, Window window)
1721 {
1722   struct state *st = (struct state *) calloc (1, sizeof(*st));
1723   char *s;
1724
1725   st->duration = get_integer_resource (dpy, "duration", "Integer");
1726
1727   st->controller = 0;
1728   if (st->duration < 1) st->duration = 1;
1729
1730   s = get_string_resource (dpy, "mode", "Mode");
1731   if (!s || !*s || !strcasecmp(s, "random"))
1732     st->random_p = True;
1733   else if (!strcasecmp(s, "text"))
1734      st->controller = terminal_controller;
1735   else if (!strcasecmp(s, "slideshow"))
1736      st->controller = slideshow_controller;
1737   else if (!strcasecmp(s, "basic"))
1738      st->controller = basic_controller;
1739   else
1740     {
1741       fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1742                progname, s);
1743       exit (1);
1744     }
1745   if (s) free (s);
1746
1747   global_program = get_string_resource (dpy, "program", "Program");
1748   global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
1749
1750
1751   /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
1752   {
1753     const char *s = getenv ("XSCREENSAVER_STANDALONE");
1754     if (s && *s && strcmp(s, "0"))
1755       {
1756         st->controller = terminal_controller;
1757         st->random_p   = False;
1758         global_program = getenv ("SHELL");
1759         global_fast_p  = True;
1760       }
1761   }
1762
1763
1764   if (! st->random_p) {
1765     if (st->controller == terminal_controller ||
1766         st->controller == slideshow_controller)
1767       st->duration = 999999;  /* these run "forever" */
1768   }
1769
1770   return st;
1771 }
1772
1773 static unsigned long
1774 apple2_draw (Display *dpy, Window window, void *closure)
1775 {
1776   struct state *st = (struct state *) closure;
1777
1778   if (! st->sim) {
1779     if (st->random_p)
1780       st->controller = controllers[random() % (countof(controllers))];
1781     st->sim = apple2_start (dpy, window, st->duration, st->controller);
1782   }
1783
1784   if (! apple2_one_frame (st->sim)) {
1785     st->sim = 0;
1786   }
1787
1788 #ifdef USE_IPHONE
1789   return 0;
1790 #else
1791   return 5000;
1792 #endif
1793 }
1794
1795 static void
1796 apple2_reshape (Display *dpy, Window window, void *closure, 
1797                  unsigned int w, unsigned int h)
1798 {
1799   struct state *st = (struct state *) closure;
1800   if (st->sim)
1801     analogtv_reconfigure (st->sim->dec);
1802 }
1803
1804 static Bool
1805 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1806 {
1807   struct state *st = (struct state *) closure;
1808
1809   if (st->sim &&
1810       st->controller == terminal_controller &&
1811       event->xany.type == KeyPress) {
1812     terminal_keypress_handler (dpy, event, st->sim->controller_data);
1813     return True;
1814   }
1815
1816   return False;
1817 }
1818
1819 static void
1820 apple2_free (Display *dpy, Window window, void *closure)
1821 {
1822   struct state *st = (struct state *) closure;
1823   if (st->sim) {
1824     st->sim->stepno = A2CONTROLLER_DONE;
1825     if (apple2_one_frame (st->sim))
1826       abort();  /* should have freed! */
1827   }
1828   free (st);
1829 }
1830
1831
1832 XSCREENSAVER_MODULE ("Apple2", apple2)