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