From http://www.jwz.org/xscreensaver/xscreensaver-5.16.tar.gz
[xscreensaver] / hacks / apple2-main.c
1 /* xscreensaver, Copyright (c) 1998-2012 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 A2CONTROLLER_FREE:
766     free(mine->render_img);
767     free(mine->img_filename);
768     free(mine);
769     mine = 0;
770     return;
771
772   }
773 }
774
775 #define NPAR 16
776
777 struct terminal_controller_data {
778   Display *dpy;
779   char curword[256];
780   unsigned char lastc;
781   double last_emit_time;
782   text_data *tc;
783
784   int escstate;
785   int csiparam[NPAR];
786   int curparam;
787   int cursor_x, cursor_y;
788   int saved_x,  saved_y;
789   union {
790     struct {
791       unsigned int bold : 1;
792       unsigned int blink : 1;
793       unsigned int rev : 1;
794     } bf;
795     int w;
796   } termattrib;
797   Bool fast_p;
798
799 };
800
801
802 /* The structure of closure linkage throughout this code is so amazingly
803    baroque that I can't get to the 'struct state' from where I need it. */
804 static const char *global_program;
805 static Bool global_fast_p;
806
807
808 static void
809 terminal_closegen(struct terminal_controller_data *mine)
810 {
811   if (mine->tc) {
812     textclient_close (mine->tc);
813     mine->tc = 0;
814   }
815 }
816
817 static int
818 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
819 {
820   if (!mine || !mine->tc) {
821     return 0;
822   } else {
823     int i, count = 0;
824     for (i = 0; i < n; i++) {
825       int c = textclient_getc (mine->tc);
826       if (c <= 0) break;
827       buf[i] = c;
828       mine->lastc = c;
829       count++;
830     }
831     return count;
832   }
833 }
834
835
836 static int
837 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
838 {
839   struct terminal_controller_data *mine =
840     (struct terminal_controller_data *) data;
841   mine->dpy = dpy;
842   if (event->xany.type == KeyPress && mine->tc)
843     return textclient_putc (mine->tc, &event->xkey);
844   return 0;
845 }
846
847
848 static void
849 a2_ascii_printc (apple2_state_t *st, unsigned char c,
850                  Bool bold_p, Bool blink_p, Bool rev_p,
851                  Bool scroll_p)
852 {
853   if (c >= 'a' && c <= 'z')            /* upcase lower-case chars */
854     {
855       c &= 0xDF;
856     }
857   else if ((c >= 'A'+128) ||                    /* upcase and blink */
858            (c < ' ' && c != 014 &&              /* high-bit & ctl chrs */
859             c != '\r' && c != '\n' && c!='\t'))
860     {
861       c = (c & 0x1F) | 0x80;
862     }
863   else if (c >= 'A' && c <= 'Z')            /* invert upper-case chars */
864     {
865       c |= 0x80;
866     }
867
868   if (bold_p)  c |= 0xc0;
869   if (blink_p) c = (c & ~0x40) | 0x80;
870   if (rev_p)   c |= 0xc0;
871
872   if (scroll_p)
873     a2_printc(st, c);
874   else
875     a2_printc_noscroll(st, c);
876 }
877
878
879 static void
880 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
881                  unsigned char c)
882 {
883   apple2_state_t *st=sim->st;
884   int cols = SCREEN_COLS;
885   int rows = SCREEN_ROWS;
886
887   int i;
888   int start, end;
889
890   switch (state->escstate)
891     {
892     case 0:
893       switch (c)
894         {
895         case 7: /* BEL */
896           /* Dummy case - we don't want the screensaver to beep */
897           /* #### But maybe this should flash the screen? */
898           break;
899         case 8: /* BS */
900           if (state->cursor_x > 0)
901             state->cursor_x--;
902           break;
903         case 9: /* HT */
904           if (state->cursor_x < cols - 8)
905             {
906               state->cursor_x = (state->cursor_x & ~7) + 8;
907             }
908           else
909             {
910               state->cursor_x = 0;
911               if (state->cursor_y < rows - 1)
912                 state->cursor_y++;
913               else
914                 a2_scroll (st);
915             }
916           break;
917         case 10: /* LF */
918         case 11: /* VT */
919         case 12: /* FF */
920           if (state->cursor_y < rows - 1)
921             state->cursor_y++;
922           else
923             a2_scroll (st);
924           break;
925         case 13: /* CR */
926           state->cursor_x = 0;
927           break;
928         case 14: /* SO */
929         case 15: /* SI */
930           /* Dummy case - there is one and only one font. */
931           break;
932         case 24: /* CAN */
933         case 26: /* SUB */
934           /* Dummy case - these interrupt escape sequences, so
935              they don't do anything in this state */
936           break;
937         case 27: /* ESC */
938           state->escstate = 1;
939           break;
940         case 127: /* DEL */
941           /* Dummy case - this is supposed to be ignored */
942           break;
943         case 155: /* CSI */
944           state->escstate = 2;
945           for(i = 0; i < NPAR; i++)
946             state->csiparam[i] = 0;
947           state->curparam = 0;
948           break;
949         default:
950           /* If the cursor is in column 39 and we print a character, then
951              that character shows up in column 39, and the cursor is no longer
952              visible on the screen (it's in "column 40".)  If another character
953              is printed, then that character shows up in column 0, and the
954              cursor moves to column 1.
955
956              This is empirically what xterm and gnome-terminal do, so that must
957              be the right thing.  (In xterm, the cursor vanishes, whereas; in
958              gnome-terminal, the cursor overprints the character in col 39.)
959            */
960           if (state->cursor_x >= cols)
961             {
962               state->cursor_x = 0;
963               if (state->cursor_y >= rows - 1)
964                 a2_scroll (st);
965               else
966                 state->cursor_y++;
967             }
968
969           a2_goto(st, state->cursor_y, state->cursor_x);  /* clips range */
970           a2_ascii_printc (st, c,
971                            state->termattrib.bf.bold,
972                            state->termattrib.bf.blink,
973                            state->termattrib.bf.rev,
974                            False);
975           state->cursor_x++;
976
977           break;
978         }
979       break;
980     case 1:
981       switch (c)
982         {
983         case 24: /* CAN */
984         case 26: /* SUB */
985           state->escstate = 0;
986           break;
987         case 'c': /* Reset */
988           a2_cls(st);
989           state->escstate = 0;
990           break;
991         case 'D': /* Linefeed */
992           if (state->cursor_y < rows - 1)
993             state->cursor_y++;
994           else
995             a2_scroll (st);
996           state->escstate = 0;
997           break;
998         case 'E': /* Newline */
999           state->cursor_x = 0;
1000           state->escstate = 0;
1001           break;
1002         case 'M': /* Reverse newline */
1003           if (state->cursor_y > 0)
1004             state->cursor_y--;
1005           state->escstate = 0;
1006           break;
1007         case '7': /* Save state */
1008           state->saved_x = state->cursor_x;
1009           state->saved_y = state->cursor_y;
1010           state->escstate = 0;
1011           break;
1012         case '8': /* Restore state */
1013           state->cursor_x = state->saved_x;
1014           state->cursor_y = state->saved_y;
1015           state->escstate = 0;
1016           break;
1017         case '[': /* CSI */
1018           state->escstate = 2;
1019           for(i = 0; i < NPAR; i++)
1020             state->csiparam[i] = 0;
1021           state->curparam = 0;
1022           break;
1023         case '%': /* Select charset */
1024           /* No, I don't support UTF-8, since the apple2 font
1025              isn't even Unicode anyway. We must still catch the
1026              last byte, though. */
1027         case '(':
1028         case ')':
1029           /* I don't support different fonts either - see above
1030              for SO and SI */
1031           state->escstate = 3;
1032           break;
1033         default:
1034           /* Escape sequences not supported:
1035            * 
1036            * H - Set tab stop
1037            * Z - Terminal identification
1038            * > - Keypad change
1039            * = - Other keypad change
1040            * ] - OS command
1041            */
1042           state->escstate = 0;
1043           break;
1044         }
1045       break;
1046     case 2:
1047       switch (c)
1048         {
1049         case 24: /* CAN */
1050         case 26: /* SUB */
1051           state->escstate = 0;
1052           break;
1053         case '0': case '1': case '2': case '3': case '4':
1054         case '5': case '6': case '7': case '8': case '9':
1055           if (state->curparam < NPAR)
1056             state->csiparam[state->curparam] =
1057               (state->csiparam[state->curparam] * 10) + (c - '0');
1058           break;
1059         case ';':
1060           state->csiparam[++state->curparam] = 0;
1061           break;
1062         case '[':
1063           state->escstate = 3;
1064           break;
1065         case '@':
1066           for (i = 0; i < state->csiparam[0]; i++)
1067             {
1068               if(++state->cursor_x > cols)
1069                 {
1070                   state->cursor_x = 0;
1071                   if (state->cursor_y < rows - 1)
1072                     state->cursor_y++;
1073                   else
1074                     a2_scroll (st);
1075                 }
1076             }
1077           state->escstate = 0;
1078           break;
1079         case 'F':
1080           state->cursor_x = 0;
1081         case 'A':
1082           if (state->csiparam[0] == 0)
1083             state->csiparam[0] = 1;
1084           if ((state->cursor_y -= state->csiparam[0]) < 0)
1085             state->cursor_y = 0;
1086           state->escstate = 0;
1087           break;
1088         case 'E':
1089           state->cursor_x = 0;
1090         case 'e':
1091         case 'B':
1092           if (state->csiparam[0] == 0)
1093             state->csiparam[0] = 1;
1094           if ((state->cursor_y += state->csiparam[0]) >= rows)
1095             state->cursor_y = rows - 1;
1096           state->escstate = 0;
1097           break;
1098         case 'a':
1099         case 'C':
1100           if (state->csiparam[0] == 0)
1101             state->csiparam[0] = 1;
1102           if ((state->cursor_x += state->csiparam[0]) >= cols)
1103             state->cursor_x = cols - 1;
1104           state->escstate = 0;
1105           break;
1106         case 'D':
1107           if (state->csiparam[0] == 0)
1108             state->csiparam[0] = 1;
1109           if ((state->cursor_x -= state->csiparam[0]) < 0)
1110             state->cursor_x = 0;
1111           state->escstate = 0;
1112           break;
1113         case 'd':
1114           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1115             state->cursor_y = rows - 1;
1116           state->escstate = 0;
1117           break;
1118         case '`':
1119         case 'G':
1120           if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1121             state->cursor_x = cols - 1;
1122           state->escstate = 0;
1123           break;
1124         case 'f':
1125         case 'H':
1126           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1127             state->cursor_y = rows - 1;
1128           if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1129             state->cursor_x = cols - 1;
1130           if(state->cursor_y < 0)
1131             state->cursor_y = 0;
1132           if(state->cursor_x < 0)
1133             state->cursor_x = 0;
1134           state->escstate = 0;
1135           break;
1136         case 'J':
1137           start = 0;
1138           end = rows * cols;
1139           if (state->csiparam[0] == 0)
1140             start = cols * state->cursor_y + state->cursor_x;
1141           if (state->csiparam[0] == 1)
1142             end = cols * state->cursor_y + state->cursor_x;
1143
1144           a2_goto(st, state->cursor_y, state->cursor_x);
1145           for (i = start; i < end; i++)
1146             {
1147               a2_ascii_printc(st, ' ', False, False, False, False);
1148             }
1149           state->escstate = 0;
1150           break;
1151         case 'K':
1152           start = 0;
1153           end = cols;
1154           if (state->csiparam[0] == 0)
1155             start = state->cursor_x;
1156           if (state->csiparam[1] == 1)
1157             end = state->cursor_x;
1158
1159           a2_goto(st, state->cursor_y, state->cursor_x);
1160           for (i = start; i < end; i++)
1161             {
1162               a2_ascii_printc(st, ' ', False, False, False, False);
1163             }
1164           state->escstate = 0;
1165           break;
1166         case 'm': /* Set attributes */
1167           for (i = 0; i <= state->curparam; i++)
1168             {
1169               switch(state->csiparam[i])
1170                 {
1171                 case 0:
1172                   state->termattrib.w = 0;
1173                   break;
1174                 case 1:
1175                   state->termattrib.bf.bold = 1;
1176                   break;
1177                 case 5:
1178                   state->termattrib.bf.blink = 1;
1179                   break;
1180                 case 7:
1181                   state->termattrib.bf.rev = 1;
1182                   break;
1183                 case 21:
1184                 case 22:
1185                   state->termattrib.bf.bold = 0;
1186                   break;
1187                 case 25:
1188                   state->termattrib.bf.blink = 0;
1189                   break;
1190                 case 27:
1191                   state->termattrib.bf.rev = 0;
1192                   break;
1193                 }
1194             }
1195           state->escstate = 0;
1196           break;
1197         case 's': /* Save position */
1198           state->saved_x = state->cursor_x;
1199           state->saved_y = state->cursor_y;
1200           state->escstate = 0;
1201           break;
1202         case 'u': /* Restore position */
1203           state->cursor_x = state->saved_x;
1204           state->cursor_y = state->saved_y;
1205           state->escstate = 0;
1206           break;
1207         case '?': /* DEC Private modes */
1208           if ((state->curparam != 0) || (state->csiparam[0] != 0))
1209             state->escstate = 0;
1210           break;
1211         default:
1212           /* Known unsupported CSIs:
1213            *
1214            * L - Insert blank lines
1215            * M - Delete lines (I don't know what this means...)
1216            * P - Delete characters
1217            * X - Erase characters (difference with P being...?)
1218            * c - Terminal identification
1219            * g - Clear tab stop(s)
1220            * h - Set mode (Mainly due to its complexity and lack of good
1221            docs)
1222            * l - Clear mode
1223            * m - Set mode (Phosphor is, per defenition, green on black)
1224            * n - Status report
1225            * q - Set keyboard LEDs
1226            * r - Set scrolling region (too exhausting - noone uses this,
1227            right?)
1228           */
1229           state->escstate = 0;
1230           break;
1231         }
1232       break;
1233     case 3:
1234       state->escstate = 0;
1235       break;
1236     }
1237   a2_goto(st, state->cursor_y, state->cursor_x);
1238 }
1239
1240
1241 /*
1242   It's fun to put things like "gdb" as the command. For one, it's
1243   amusing how the standard mumble (version, no warranty, it's
1244   GNU/Linux dammit) occupies an entire screen on the Apple ][.
1245 */
1246
1247 static void
1248 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1249 {
1250   apple2_state_t *st=sim->st;
1251   int c;
1252   int i;
1253
1254   struct terminal_controller_data *mine;
1255   if (!sim->controller_data)
1256     sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1257   mine=(struct terminal_controller_data *) sim->controller_data;
1258   mine->dpy = sim->dpy;
1259
1260   mine->fast_p           = global_fast_p;
1261
1262   switch(*stepno) {
1263
1264   case 0:
1265     if (random()%2)
1266       st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1267                                     showing text */
1268     a2_cls(st);
1269     a2_goto(st,0,16);
1270     a2_prints(st, "APPLE ][");
1271     a2_goto(st,2,0);
1272     mine->cursor_y = 2;
1273
1274     if (! mine->tc) {
1275       mine->tc = textclient_open (mine->dpy);
1276       textclient_reshape (mine->tc,
1277                           SCREEN_COLS, SCREEN_ROWS,
1278                           SCREEN_COLS, SCREEN_ROWS);
1279     }
1280
1281     if (! mine->fast_p)
1282       *next_actiontime += 4.0;
1283     *stepno = 10;
1284     break;
1285
1286   case 10:
1287     {
1288       unsigned char buf[1024];
1289       int nr,nwant;
1290       double elapsed;
1291
1292       elapsed=sim->curtime - mine->last_emit_time;
1293       mine->last_emit_time=sim->curtime;
1294       nwant=elapsed*25.0;
1295       if (elapsed>1.0) nwant=1;
1296       if (nwant<1) nwant=1;
1297       if (nwant>4) nwant=4;
1298
1299       if (mine->fast_p)
1300         nwant = sizeof(buf)-1;
1301
1302       nr=terminal_read(mine, buf, nwant);
1303       for (i=0; i<nr; i++) {
1304         c=buf[i];
1305
1306         if (mine->tc)
1307           a2_vt100_printc (sim, mine, c);
1308         else
1309           a2_ascii_printc (st, c, False, False, False, True);
1310       }
1311     }
1312     break;
1313
1314   case A2CONTROLLER_FREE:
1315     terminal_closegen(mine);
1316     free(mine);
1317     mine = 0;
1318     return;
1319   }
1320 }
1321
1322 struct basic_controller_data {
1323   int prog_line;
1324   int x,y,k;
1325   const char * const * progtext;
1326   int progstep;
1327   char *rep_str;
1328   int rep_pos;
1329   double prog_start_time;
1330   char error_buf[256];
1331 };
1332
1333 /*
1334   Adding more programs is easy. Just add a listing here and to all_programs,
1335   then add the state machine to actually execute it to basic_controller.
1336  */
1337 static const char * const moire_program[]={
1338   "10 HGR2\n",
1339   "20 FOR Y = 0 TO 190 STEP 2\n",
1340   "30 HCOLOR=4 : REM BLACK\n",
1341   "40 HPLOT 0,191-Y TO 279,Y\n",
1342   "60 HCOLOR=7 : REM WHITE\n",
1343   "80 HPLOT 0,190-Y TO 279,Y+1\n",
1344   "90 NEXT Y\n",
1345   "100 FOR X = 0 TO 278 STEP 3\n",
1346   "110 HCOLOR=4\n",
1347   "120 HPLOT 279-X,0 TO X,191\n",
1348   "140 HCOLOR=7\n",
1349   "150 HPLOT 278-X,0 TO X+1,191\n",
1350   "160 NEXT X\n",
1351   NULL
1352 };
1353
1354 static const char * const sinewave_program[] = {
1355   "10 HGR\n",
1356   "25 K=0\n",
1357   "30 FOR X = 0 TO 279\n",
1358   "32 HCOLOR= 0\n",
1359   "35 HPLOT X,0 TO X,159\n",
1360   "38 HCOLOR= 3\n",
1361   "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1362   "50 HPLOT X,Y\n",
1363   "60 NEXT X\n",
1364   "70 K=K+4\n",
1365   "80 GOTO 30\n",
1366   NULL
1367 };
1368
1369 #if 0
1370 static const char * const dumb_program[]={
1371   "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1372   "20 GOTO 10\n",
1373   NULL
1374 };
1375 #endif
1376
1377 static const char * const random_lores_program[]={
1378   "1 REM APPLE ][ SCREEN SAVER\n",
1379   "10 GR\n",
1380   "100 COLOR= RND(1)*16\n",
1381
1382   "110 X=RND(1)*40\n",
1383   "120 Y1=RND(1)*40\n",
1384   "130 Y2=RND(1)*40\n",
1385   "140 FOR Y = Y1 TO Y2\n",
1386   "150 PLOT X,Y\n",
1387   "160 NEXT Y\n",
1388
1389   "210 Y=RND(1)*40\n",
1390   "220 X1=RND(1)*40\n",
1391   "230 X2=RND(1)*40\n",
1392   "240 FOR X = X1 TO X2\n",
1393   "250 PLOT X,Y\n",
1394   "260 NEXT X\n",
1395   "300 GOTO 100\n",
1396
1397   NULL
1398 };
1399
1400 static char typo_map[256];
1401
1402 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1403 {
1404   int i,j;
1405   int errc;
1406   int success=0;
1407   err_buf[0]=0;
1408
1409   typo_map['A']='Q';
1410   typo_map['S']='A';
1411   typo_map['D']='S';
1412   typo_map['F']='G';
1413   typo_map['G']='H';
1414   typo_map['H']='J';
1415   typo_map['J']='H';
1416   typo_map['K']='L';
1417   typo_map['L']=';';
1418
1419   typo_map['Q']='1';
1420   typo_map['W']='Q';
1421   typo_map['E']='3';
1422   typo_map['R']='T';
1423   typo_map['T']='Y';
1424   typo_map['Y']='U';
1425   typo_map['U']='Y';
1426   typo_map['I']='O';
1427   typo_map['O']='P';
1428   typo_map['P']='[';
1429
1430   typo_map['Z']='X';
1431   typo_map['X']='C';
1432   typo_map['C']='V';
1433   typo_map['V']='C';
1434   typo_map['B']='N';
1435   typo_map['N']='B';
1436   typo_map['M']='N';
1437   typo_map[',']='.';
1438   typo_map['.']=',';
1439
1440   typo_map['!']='1';
1441   typo_map['@']='2';
1442   typo_map['#']='3';
1443   typo_map['$']='4';
1444   typo_map['%']='5';
1445   typo_map['^']='6';
1446   typo_map['&']='7';
1447   typo_map['*']='8';
1448   typo_map['(']='9';
1449   typo_map[')']='0';
1450
1451   typo_map['1']='Q';
1452   typo_map['2']='W';
1453   typo_map['3']='E';
1454   typo_map['4']='R';
1455   typo_map['5']='T';
1456   typo_map['6']='Y';
1457   typo_map['7']='U';
1458   typo_map['8']='I';
1459   typo_map['9']='O';
1460   typo_map['0']='-';
1461
1462   strcpy(out_buf, orig);
1463   for (i=0; out_buf[i]; i++) {
1464     char *p = out_buf+i;
1465
1466     if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1467       break;
1468
1469     if (isalpha(p[0]) &&
1470         isalpha(p[1]) &&
1471         p[0] != p[1] &&
1472         random()%15==0)
1473       {
1474         int tmp=p[1];
1475         p[1]=p[0];
1476         p[0]=tmp;
1477         success=1;
1478         sprintf(err_buf,"?SYNTAX ERROR\n");
1479         break;
1480       }
1481
1482     if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1483       int remain=strlen(p);
1484       int past=random()%(remain-2)+1;
1485       memmove(p+past+past, p, remain+1);
1486       p[0]=errc;
1487       for (j=0; j<past; j++) {
1488         p[past+j]=010;
1489       }
1490       break;
1491     }
1492   }
1493   return success;
1494 }
1495
1496 static const struct {
1497   const char * const * progtext;
1498   int progstep;
1499 } all_programs[]={
1500   {moire_program, 100},
1501   /*{dumb_program, 200}, */
1502   {sinewave_program, 400},
1503   {random_lores_program, 500},
1504 };
1505
1506 static void
1507 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1508 {
1509   apple2_state_t *st=sim->st;
1510   int i;
1511
1512   struct basic_controller_data *mine;
1513   if (!sim->controller_data)
1514     sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1515   mine=(struct basic_controller_data *) sim->controller_data;
1516
1517   switch (*stepno) {
1518   case 0:
1519     st->gr_mode=0;
1520     a2_cls(st);
1521     a2_goto(st,0,16);
1522     a2_prints(st, "APPLE ][");
1523     a2_goto(st,23,0);
1524     a2_printc(st,']');
1525     sim->typing_rate=0.2;
1526
1527     i=random()%countof(all_programs);
1528     mine->progtext=all_programs[i].progtext;
1529     mine->progstep=all_programs[i].progstep;
1530     mine->prog_line=0;
1531
1532     *next_actiontime += 1.0;
1533     *stepno=10;
1534     break;
1535
1536   case 10:
1537     if (st->cursx==0) a2_printc(st,']');
1538     if (mine->progtext[mine->prog_line]) {
1539       if (random()%4==0) {
1540         int err=make_typo(sim->typing_buf,
1541                           mine->progtext[mine->prog_line],
1542                           mine->error_buf);
1543         sim->typing=sim->typing_buf;
1544         if (err) {
1545           *stepno=11;
1546         } else {
1547           mine->prog_line++;
1548         }
1549       } else {
1550         sim->typing=mine->progtext[mine->prog_line++];
1551       }
1552     } else {
1553       *stepno=15;
1554     }
1555     break;
1556
1557   case 11:
1558     sim->printing=mine->error_buf;
1559     *stepno=12;
1560     break;
1561
1562   case 12:
1563     if (st->cursx==0) a2_printc(st,']');
1564     *next_actiontime+=1.0;
1565     *stepno=10;
1566     break;
1567
1568   case 15:
1569     sim->typing="RUN\n";
1570     mine->y=0;
1571     mine->x=0;
1572     mine->k=0;
1573     mine->prog_start_time=*next_actiontime;
1574     *stepno=mine->progstep;
1575     break;
1576
1577     /* moire_program */
1578   case 100:
1579     st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1580     for (i=0; i<24 && mine->y<192; i++)
1581       {
1582         a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1583         a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1584         mine->y += 2;
1585       }
1586     if (mine->y>=192) {
1587       mine->x = 0;
1588       *stepno = 110;
1589     }
1590     break;
1591
1592   case 110:
1593     for (i=0; i<24 && mine->x<280; i++)
1594       {
1595         a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1596         a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1597         mine->x+=3;
1598       }
1599     if (mine->x >= 280) *stepno=120;
1600     break;
1601
1602   case 120:
1603     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1604     break;
1605
1606     /* dumb_program */
1607   case 200:
1608     mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1609     for (i=0; i<30; i++) {
1610       a2_prints(st, mine->rep_str);
1611     }
1612     *stepno=210;
1613     break;
1614
1615   case 210:
1616     i=random()%strlen(mine->rep_str);
1617     while (mine->rep_pos != i) {
1618       a2_printc(st, mine->rep_str[mine->rep_pos]);
1619       mine->rep_pos++;
1620       if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1621     }
1622     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1623     break;
1624
1625     /* sinewave_program */
1626   case 400:
1627     st->gr_mode=A2_GR_HIRES;
1628     *stepno=410;
1629     break;
1630
1631   case 410:
1632     for (i=0; i<48; i++) {
1633       int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1634       a2_hline(st, 0, mine->x, 0, mine->x, 159);
1635       a2_hplot(st, 3, mine->x, y);
1636       mine->x += 1;
1637       if (mine->x>=279) {
1638         mine->x=0;
1639         mine->k+=4;
1640       }
1641     }
1642     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1643     break;
1644
1645   case 420:
1646     a2_prints(st, "]");
1647     *stepno=999;
1648     break;
1649
1650     /* random_lores_program */
1651   case 500:
1652     st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1653     a2_clear_gr(st);
1654     *stepno=510;
1655
1656   case 510:
1657     for (i=0; i<10; i++) {
1658       int color,x,y,x1,x2,y1,y2;
1659
1660       color=random()%15;
1661       x=random()%40;
1662       y1=random()%48;
1663       y2=random()%48;
1664       for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1665
1666       x1=random()%40;
1667       x2=random()%40;
1668       y=random()%48;
1669       for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1670     }
1671     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1672     break;
1673
1674   case 999:
1675     *stepno=0;
1676     break;
1677
1678   case A2CONTROLLER_FREE:
1679     free(mine);
1680     mine = 0;
1681     break;
1682   }
1683
1684 }
1685
1686 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1687                                      double *next_actiontime) = {
1688   slideshow_controller,
1689   terminal_controller,
1690   basic_controller
1691 };
1692
1693 struct state {
1694   int duration;
1695   Bool random_p;
1696   apple2_sim_t *sim;
1697   void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1698 };
1699
1700
1701 static void *
1702 apple2_init (Display *dpy, Window window)
1703 {
1704   struct state *st = (struct state *) calloc (1, sizeof(*st));
1705   char *s;
1706
1707   st->duration = get_integer_resource (dpy, "duration", "Integer");
1708
1709   st->controller = 0;
1710   if (st->duration < 1) st->duration = 1;
1711
1712   s = get_string_resource (dpy, "mode", "Mode");
1713   if (!s || !*s || !strcasecmp(s, "random"))
1714     st->random_p = True;
1715   else if (!strcasecmp(s, "text"))
1716      st->controller = terminal_controller;
1717   else if (!strcasecmp(s, "slideshow"))
1718      st->controller = slideshow_controller;
1719   else if (!strcasecmp(s, "basic"))
1720      st->controller = basic_controller;
1721   else
1722     {
1723       fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1724                progname, s);
1725       exit (1);
1726     }
1727   if (s) free (s);
1728
1729   global_program = get_string_resource (dpy, "program", "Program");
1730   global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
1731
1732
1733   /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
1734   {
1735     const char *s = getenv ("XSCREENSAVER_STANDALONE");
1736     if (s && *s && strcmp(s, "0"))
1737       {
1738         st->controller = terminal_controller;
1739         st->random_p   = False;
1740         global_program = getenv ("SHELL");
1741         global_fast_p  = True;
1742       }
1743   }
1744
1745
1746   if (! st->random_p) {
1747     if (st->controller == terminal_controller ||
1748         st->controller == slideshow_controller)
1749       st->duration = 999999;  /* these run "forever" */
1750   }
1751
1752   return st;
1753 }
1754
1755 static unsigned long
1756 apple2_draw (Display *dpy, Window window, void *closure)
1757 {
1758   struct state *st = (struct state *) closure;
1759
1760   if (! st->sim) {
1761     if (st->random_p)
1762       st->controller = controllers[random() % (countof(controllers))];
1763     st->sim = apple2_start (dpy, window, st->duration, st->controller);
1764   }
1765
1766   if (! apple2_one_frame (st->sim)) {
1767     st->sim = 0;
1768   }
1769
1770   return 10000;
1771 }
1772
1773 static void
1774 apple2_reshape (Display *dpy, Window window, void *closure, 
1775                  unsigned int w, unsigned int h)
1776 {
1777   struct state *st = (struct state *) closure;
1778   analogtv_reconfigure (st->sim->dec);
1779 }
1780
1781 static Bool
1782 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1783 {
1784   struct state *st = (struct state *) closure;
1785
1786   if (st->controller == terminal_controller &&
1787       event->xany.type == KeyPress) {
1788     terminal_keypress_handler (dpy, event, st->sim->controller_data);
1789     return True;
1790   }
1791
1792   return False;
1793 }
1794
1795 static void
1796 apple2_free (Display *dpy, Window window, void *closure)
1797 {
1798   struct state *st = (struct state *) closure;
1799   if (st->sim) {
1800     st->sim->stepno = A2CONTROLLER_DONE;
1801     if (apple2_one_frame (st->sim))
1802       abort();  /* should have freed! */
1803   }
1804   free (st);
1805 }
1806
1807
1808 XSCREENSAVER_MODULE ("Apple2", apple2)