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