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