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