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