http://www.jwz.org/xscreensaver/xscreensaver-5.13.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
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 static void
837 launch_text_generator (struct terminal_controller_data *mine)
838 {
839   XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
840   char buf[255];
841   char *oprogram = get_string_resource (mine->dpy, "program", "Program");
842   char *program = (char *) malloc (strlen (oprogram) + 10);
843
844   strcpy (program, "( ");
845   strcat (program, oprogram);
846   strcat (program, " ) 2>&1");
847
848   if (mine->pipe) abort();
849
850 # ifdef HAVE_FORKPTY
851   if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
852     {
853       int fd;
854       struct winsize ws;
855
856       ws.ws_col = SCREEN_COLS;
857       ws.ws_row = SCREEN_ROWS;
858       ws.ws_xpixel = ws.ws_col * 6;
859       ws.ws_ypixel = ws.ws_row * 8;
860       
861       mine->pipe = NULL;
862       if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
863         {
864           /* Unable to fork */
865           sprintf (buf, "%.100s: forkpty", progname);
866           perror(buf);
867         }
868       else if(!mine->pid)
869         {
870           /* This is the child fork. */
871           char *av[10];
872           int i = 0;
873           if (putenv("TERM=vt100"))
874             abort();
875           av[i++] = "/bin/sh";
876           av[i++] = "-c";
877           av[i++] = oprogram;
878           av[i] = 0;
879           execvp (av[0], av);
880           sprintf (buf, "%.100s: %.100s", progname, oprogram);
881           perror(buf);
882           exit(1);
883         }
884       else
885         {
886           /* This is the parent fork. */
887           mine->pipe = fdopen(fd, "r+");
888           mine->pipe_id =
889             XtAppAddInput (app, fileno (mine->pipe),
890                            (XtPointer) (XtInputReadMask | XtInputExceptMask),
891                            subproc_cb, (XtPointer) mine);
892         }
893     }
894   else
895 # endif /* HAVE_FORKPTY */
896
897   if ((mine->pipe = popen (program, "r")))
898     {
899       if (mine->pipe_id) abort();
900       mine->pipe_id =
901         XtAppAddInput (app, fileno (mine->pipe),
902                        (XtPointer) (XtInputReadMask | XtInputExceptMask),
903                        subproc_cb, (XtPointer) mine);
904     }
905   else
906     {
907       sprintf (buf, "%.100s: %.100s", progname, program);
908       perror(buf);
909     }
910
911   free(oprogram);
912   free(program);
913 }
914
915 static void
916 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
917 {
918   struct terminal_controller_data *mine =
919     (struct terminal_controller_data *) closure;
920   mine->timeout_id=0;
921   launch_text_generator (mine);
922 }
923
924 static void
925 terminal_closegen(struct terminal_controller_data *mine)
926 {
927   if (mine->pipe_id) {
928     XtRemoveInput (mine->pipe_id);
929     mine->pipe_id = 0;
930   }
931   if (mine->pipe) {
932     pclose (mine->pipe);
933     mine->pipe = 0;
934   }
935   if (mine->timeout_id) {
936     XtRemoveTimeOut(mine->timeout_id);
937     mine->timeout_id=0;
938   }
939 }
940
941 static int
942 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
943 {
944   XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
945   int rc;
946   if (mine->fake_nl) {
947     buf[0]='\n';
948     mine->fake_nl=0;
949     return 1;
950   }
951
952   if (!mine || 
953       !mine->input_available_p ||
954       !mine->pipe)
955     return 0;
956
957   rc=read (fileno (mine->pipe), (void *) buf, n);
958   if (rc>0) mine->lastc=buf[rc-1];
959
960   if (rc<=0)
961     {
962       terminal_closegen(mine);
963
964       if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
965         mine->fake_nl=1;
966       }
967
968       /* Set up a timer to re-launch the subproc in a bit. */
969       mine->timeout_id =
970         XtAppAddTimeOut(app, subproc_relaunch_delay,
971                         relaunch_generator_timer,
972                         (XtPointer) mine);
973     }
974
975   mine->input_available_p = False;
976
977   return rc;
978 }
979
980
981 /* The interpretation of the ModN modifiers is dependent on what keys
982    are bound to them: Mod1 does not necessarily mean "meta".  It only
983    means "meta" if Meta_L or Meta_R are bound to it.  If Meta_L is on
984    Mod5, then Mod5 is the one that means Meta.  Oh, and Meta and Alt
985    aren't necessarily the same thing.  Icepicks in my forehead!
986  */
987 static unsigned int
988 do_icccm_meta_key_stupidity (Display *dpy)
989 {
990   unsigned int modbits = 0;
991 # ifndef HAVE_COCOA
992   int i, j, k;
993   XModifierKeymap *modmap = XGetModifierMapping (dpy);
994   for (i = 3; i < 8; i++)
995     for (j = 0; j < modmap->max_keypermod; j++)
996       {
997         int code = modmap->modifiermap[i * modmap->max_keypermod + j];
998         KeySym *syms;
999         int nsyms = 0;
1000         if (code == 0) continue;
1001         syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
1002         for (k = 0; k < nsyms; k++)
1003           if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
1004               syms[k] == XK_Alt_L  || syms[k] == XK_Alt_R)
1005             modbits |= (1 << i);
1006         XFree (syms);
1007       }
1008   XFreeModifiermap (modmap);
1009 # endif /* HAVE_COCOA */
1010   return modbits;
1011 }
1012
1013 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta". 
1014  */
1015 static unsigned int
1016 meta_modifier (Display *dpy)
1017 {
1018   static Bool done_once = False;
1019   static unsigned int mask = 0;
1020   if (!done_once)
1021     {
1022       /* Really, we are supposed to recompute this if a KeymapNotify
1023          event comes in, but fuck it. */
1024       done_once = True;
1025       mask = do_icccm_meta_key_stupidity (dpy);
1026     }
1027   return mask;
1028 }
1029
1030
1031 static int
1032 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1033 {
1034   struct terminal_controller_data *mine =
1035     (struct terminal_controller_data *) data;
1036   KeySym keysym;
1037   unsigned char c = 0;
1038   XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1039   if (c == 0 || !mine->pipe)
1040     return 0;
1041
1042   if (!mine->swap_bs_del_p) ;
1043   else if (c == 127) c = 8;
1044   else if (c == 8)   c = 127;
1045
1046   /* If meta was held down, send ESC, or turn on the high bit. */
1047   if (event->xkey.state & meta_modifier (dpy))
1048     {
1049       if (mine->meta_sends_esc_p)
1050         fputc ('\033', mine->pipe);
1051       else
1052         c |= 0x80;
1053     }
1054
1055   fputc (c, mine->pipe);
1056   fflush (mine->pipe);
1057
1058   event->xany.type = 0;  /* do not process this event further */
1059
1060   mine->dpy = dpy;
1061   return 0;
1062 }
1063
1064
1065 static void
1066 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1067                  Bool bold_p, Bool blink_p, Bool rev_p,
1068                  Bool scroll_p)
1069 {
1070   if (c >= 'a' && c <= 'z')            /* upcase lower-case chars */
1071     {
1072       c &= 0xDF;
1073     }
1074   else if ((c >= 'A'+128) ||                    /* upcase and blink */
1075            (c < ' ' && c != 014 &&              /* high-bit & ctl chrs */
1076             c != '\r' && c != '\n' && c!='\t'))
1077     {
1078       c = (c & 0x1F) | 0x80;
1079     }
1080   else if (c >= 'A' && c <= 'Z')            /* invert upper-case chars */
1081     {
1082       c |= 0x80;
1083     }
1084
1085   if (bold_p)  c |= 0xc0;
1086   if (blink_p) c = (c & ~0x40) | 0x80;
1087   if (rev_p)   c |= 0xc0;
1088
1089   if (scroll_p)
1090     a2_printc(st, c);
1091   else
1092     a2_printc_noscroll(st, c);
1093 }
1094
1095
1096 static void
1097 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1098                  unsigned char c)
1099 {
1100   apple2_state_t *st=sim->st;
1101   int cols = SCREEN_COLS;
1102   int rows = SCREEN_ROWS;
1103
1104   int i;
1105   int start, end;
1106
1107   switch (state->escstate)
1108     {
1109     case 0:
1110       switch (c)
1111         {
1112         case 7: /* BEL */
1113           /* Dummy case - we don't want the screensaver to beep */
1114           /* #### But maybe this should flash the screen? */
1115           break;
1116         case 8: /* BS */
1117           if (state->cursor_x > 0)
1118             state->cursor_x--;
1119           break;
1120         case 9: /* HT */
1121           if (state->cursor_x < cols - 8)
1122             {
1123               state->cursor_x = (state->cursor_x & ~7) + 8;
1124             }
1125           else
1126             {
1127               state->cursor_x = 0;
1128               if (state->cursor_y < rows - 1)
1129                 state->cursor_y++;
1130               else
1131                 a2_scroll (st);
1132             }
1133           break;
1134         case 10: /* LF */
1135         case 11: /* VT */
1136         case 12: /* FF */
1137           if (state->cursor_y < rows - 1)
1138             state->cursor_y++;
1139           else
1140             a2_scroll (st);
1141           break;
1142         case 13: /* CR */
1143           state->cursor_x = 0;
1144           break;
1145         case 14: /* SO */
1146         case 15: /* SI */
1147           /* Dummy case - there is one and only one font. */
1148           break;
1149         case 24: /* CAN */
1150         case 26: /* SUB */
1151           /* Dummy case - these interrupt escape sequences, so
1152              they don't do anything in this state */
1153           break;
1154         case 27: /* ESC */
1155           state->escstate = 1;
1156           break;
1157         case 127: /* DEL */
1158           /* Dummy case - this is supposed to be ignored */
1159           break;
1160         case 155: /* CSI */
1161           state->escstate = 2;
1162           for(i = 0; i < NPAR; i++)
1163             state->csiparam[i] = 0;
1164           state->curparam = 0;
1165           break;
1166         default:
1167           /* If the cursor is in column 39 and we print a character, then
1168              that character shows up in column 39, and the cursor is no longer
1169              visible on the screen (it's in "column 40".)  If another character
1170              is printed, then that character shows up in column 0, and the
1171              cursor moves to column 1.
1172
1173              This is empirically what xterm and gnome-terminal do, so that must
1174              be the right thing.  (In xterm, the cursor vanishes, whereas; in
1175              gnome-terminal, the cursor overprints the character in col 39.)
1176            */
1177           if (state->cursor_x >= cols)
1178             {
1179               state->cursor_x = 0;
1180               if (state->cursor_y >= rows - 1)
1181                 a2_scroll (st);
1182               else
1183                 state->cursor_y++;
1184             }
1185
1186           a2_goto(st, state->cursor_y, state->cursor_x);  /* clips range */
1187           a2_ascii_printc (st, c,
1188                            state->termattrib.bf.bold,
1189                            state->termattrib.bf.blink,
1190                            state->termattrib.bf.rev,
1191                            False);
1192           state->cursor_x++;
1193
1194           break;
1195         }
1196       break;
1197     case 1:
1198       switch (c)
1199         {
1200         case 24: /* CAN */
1201         case 26: /* SUB */
1202           state->escstate = 0;
1203           break;
1204         case 'c': /* Reset */
1205           a2_cls(st);
1206           state->escstate = 0;
1207           break;
1208         case 'D': /* Linefeed */
1209           if (state->cursor_y < rows - 1)
1210             state->cursor_y++;
1211           else
1212             a2_scroll (st);
1213           state->escstate = 0;
1214           break;
1215         case 'E': /* Newline */
1216           state->cursor_x = 0;
1217           state->escstate = 0;
1218           break;
1219         case 'M': /* Reverse newline */
1220           if (state->cursor_y > 0)
1221             state->cursor_y--;
1222           state->escstate = 0;
1223           break;
1224         case '7': /* Save state */
1225           state->saved_x = state->cursor_x;
1226           state->saved_y = state->cursor_y;
1227           state->escstate = 0;
1228           break;
1229         case '8': /* Restore state */
1230           state->cursor_x = state->saved_x;
1231           state->cursor_y = state->saved_y;
1232           state->escstate = 0;
1233           break;
1234         case '[': /* CSI */
1235           state->escstate = 2;
1236           for(i = 0; i < NPAR; i++)
1237             state->csiparam[i] = 0;
1238           state->curparam = 0;
1239           break;
1240         case '%': /* Select charset */
1241           /* No, I don't support UTF-8, since the apple2 font
1242              isn't even Unicode anyway. We must still catch the
1243              last byte, though. */
1244         case '(':
1245         case ')':
1246           /* I don't support different fonts either - see above
1247              for SO and SI */
1248           state->escstate = 3;
1249           break;
1250         default:
1251           /* Escape sequences not supported:
1252            * 
1253            * H - Set tab stop
1254            * Z - Terminal identification
1255            * > - Keypad change
1256            * = - Other keypad change
1257            * ] - OS command
1258            */
1259           state->escstate = 0;
1260           break;
1261         }
1262       break;
1263     case 2:
1264       switch (c)
1265         {
1266         case 24: /* CAN */
1267         case 26: /* SUB */
1268           state->escstate = 0;
1269           break;
1270         case '0': case '1': case '2': case '3': case '4':
1271         case '5': case '6': case '7': case '8': case '9':
1272           if (state->curparam < NPAR)
1273             state->csiparam[state->curparam] =
1274               (state->csiparam[state->curparam] * 10) + (c - '0');
1275           break;
1276         case ';':
1277           state->csiparam[++state->curparam] = 0;
1278           break;
1279         case '[':
1280           state->escstate = 3;
1281           break;
1282         case '@':
1283           for (i = 0; i < state->csiparam[0]; i++)
1284             {
1285               if(++state->cursor_x > cols)
1286                 {
1287                   state->cursor_x = 0;
1288                   if (state->cursor_y < rows - 1)
1289                     state->cursor_y++;
1290                   else
1291                     a2_scroll (st);
1292                 }
1293             }
1294           state->escstate = 0;
1295           break;
1296         case 'F':
1297           state->cursor_x = 0;
1298         case 'A':
1299           if (state->csiparam[0] == 0)
1300             state->csiparam[0] = 1;
1301           if ((state->cursor_y -= state->csiparam[0]) < 0)
1302             state->cursor_y = 0;
1303           state->escstate = 0;
1304           break;
1305         case 'E':
1306           state->cursor_x = 0;
1307         case 'e':
1308         case 'B':
1309           if (state->csiparam[0] == 0)
1310             state->csiparam[0] = 1;
1311           if ((state->cursor_y += state->csiparam[0]) >= rows)
1312             state->cursor_y = rows - 1;
1313           state->escstate = 0;
1314           break;
1315         case 'a':
1316         case 'C':
1317           if (state->csiparam[0] == 0)
1318             state->csiparam[0] = 1;
1319           if ((state->cursor_x += state->csiparam[0]) >= cols)
1320             state->cursor_x = cols - 1;
1321           state->escstate = 0;
1322           break;
1323         case 'D':
1324           if (state->csiparam[0] == 0)
1325             state->csiparam[0] = 1;
1326           if ((state->cursor_x -= state->csiparam[0]) < 0)
1327             state->cursor_x = 0;
1328           state->escstate = 0;
1329           break;
1330         case 'd':
1331           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1332             state->cursor_y = rows - 1;
1333           state->escstate = 0;
1334           break;
1335         case '`':
1336         case 'G':
1337           if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1338             state->cursor_x = cols - 1;
1339           state->escstate = 0;
1340           break;
1341         case 'f':
1342         case 'H':
1343           if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1344             state->cursor_y = rows - 1;
1345           if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1346             state->cursor_x = cols - 1;
1347           if(state->cursor_y < 0)
1348             state->cursor_y = 0;
1349           if(state->cursor_x < 0)
1350             state->cursor_x = 0;
1351           state->escstate = 0;
1352           break;
1353         case 'J':
1354           start = 0;
1355           end = rows * cols;
1356           if (state->csiparam[0] == 0)
1357             start = cols * state->cursor_y + state->cursor_x;
1358           if (state->csiparam[0] == 1)
1359             end = cols * state->cursor_y + state->cursor_x;
1360
1361           a2_goto(st, state->cursor_y, state->cursor_x);
1362           for (i = start; i < end; i++)
1363             {
1364               a2_ascii_printc(st, ' ', False, False, False, False);
1365             }
1366           state->escstate = 0;
1367           break;
1368         case 'K':
1369           start = 0;
1370           end = cols;
1371           if (state->csiparam[0] == 0)
1372             start = state->cursor_x;
1373           if (state->csiparam[1] == 1)
1374             end = state->cursor_x;
1375
1376           a2_goto(st, state->cursor_y, state->cursor_x);
1377           for (i = start; i < end; i++)
1378             {
1379               a2_ascii_printc(st, ' ', False, False, False, False);
1380             }
1381           state->escstate = 0;
1382           break;
1383         case 'm': /* Set attributes */
1384           for (i = 0; i <= state->curparam; i++)
1385             {
1386               switch(state->csiparam[i])
1387                 {
1388                 case 0:
1389                   state->termattrib.w = 0;
1390                   break;
1391                 case 1:
1392                   state->termattrib.bf.bold = 1;
1393                   break;
1394                 case 5:
1395                   state->termattrib.bf.blink = 1;
1396                   break;
1397                 case 7:
1398                   state->termattrib.bf.rev = 1;
1399                   break;
1400                 case 21:
1401                 case 22:
1402                   state->termattrib.bf.bold = 0;
1403                   break;
1404                 case 25:
1405                   state->termattrib.bf.blink = 0;
1406                   break;
1407                 case 27:
1408                   state->termattrib.bf.rev = 0;
1409                   break;
1410                 }
1411             }
1412           state->escstate = 0;
1413           break;
1414         case 's': /* Save position */
1415           state->saved_x = state->cursor_x;
1416           state->saved_y = state->cursor_y;
1417           state->escstate = 0;
1418           break;
1419         case 'u': /* Restore position */
1420           state->cursor_x = state->saved_x;
1421           state->cursor_y = state->saved_y;
1422           state->escstate = 0;
1423           break;
1424         case '?': /* DEC Private modes */
1425           if ((state->curparam != 0) || (state->csiparam[0] != 0))
1426             state->escstate = 0;
1427           break;
1428         default:
1429           /* Known unsupported CSIs:
1430            *
1431            * L - Insert blank lines
1432            * M - Delete lines (I don't know what this means...)
1433            * P - Delete characters
1434            * X - Erase characters (difference with P being...?)
1435            * c - Terminal identification
1436            * g - Clear tab stop(s)
1437            * h - Set mode (Mainly due to its complexity and lack of good
1438            docs)
1439            * l - Clear mode
1440            * m - Set mode (Phosphor is, per defenition, green on black)
1441            * n - Status report
1442            * q - Set keyboard LEDs
1443            * r - Set scrolling region (too exhausting - noone uses this,
1444            right?)
1445           */
1446           state->escstate = 0;
1447           break;
1448         }
1449       break;
1450     case 3:
1451       state->escstate = 0;
1452       break;
1453     }
1454   a2_goto(st, state->cursor_y, state->cursor_x);
1455 }
1456
1457
1458 /*
1459   It's fun to put things like "gdb" as the command. For one, it's
1460   amusing how the standard mumble (version, no warranty, it's
1461   GNU/Linux dammit) occupies an entire screen on the Apple ][.
1462 */
1463
1464 static void
1465 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1466 {
1467   apple2_state_t *st=sim->st;
1468   int c;
1469   int i;
1470
1471   struct terminal_controller_data *mine;
1472   if (!sim->controller_data)
1473     sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1474   mine=(struct terminal_controller_data *) sim->controller_data;
1475   mine->dpy = sim->dpy;
1476
1477   mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1478                                                  "Boolean");
1479   mine->swap_bs_del_p    = get_boolean_resource (mine->dpy, "swapBSDEL", 
1480                                                  "Boolean");
1481   mine->fast_p           = get_boolean_resource (mine->dpy, "fast", "Boolean");
1482
1483   switch(*stepno) {
1484
1485   case 0:
1486     if (random()%2)
1487       st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1488                                     showing text */
1489     a2_cls(st);
1490     a2_goto(st,0,16);
1491     a2_prints(st, "APPLE ][");
1492     a2_goto(st,2,0);
1493     mine->cursor_y = 2;
1494
1495     if (! mine->pipe)
1496       launch_text_generator(mine);
1497
1498     if (! mine->fast_p)
1499       *next_actiontime += 4.0;
1500     *stepno = 10;
1501     break;
1502
1503   case 10:
1504     {
1505       unsigned char buf[1024];
1506       int nr,nwant;
1507       double elapsed;
1508
1509       elapsed=sim->curtime - mine->last_emit_time;
1510       mine->last_emit_time=sim->curtime;
1511       nwant=elapsed*25.0;
1512       if (elapsed>1.0) nwant=1;
1513       if (nwant<1) nwant=1;
1514       if (nwant>4) nwant=4;
1515
1516       if (mine->fast_p)
1517         nwant = sizeof(buf)-1;
1518
1519       nr=terminal_read(mine, buf, nwant);
1520       for (i=0; i<nr; i++) {
1521         c=buf[i];
1522
1523         if (mine->pid)
1524           a2_vt100_printc (sim, mine, c);
1525         else
1526           a2_ascii_printc (st, c, False, False, False, True);
1527       }
1528     }
1529     break;
1530
1531   case A2CONTROLLER_FREE:
1532     terminal_closegen(mine);
1533     free(mine);
1534     mine = 0;
1535     return;
1536   }
1537 }
1538
1539 struct basic_controller_data {
1540   int prog_line;
1541   int x,y,k;
1542   const char * const * progtext;
1543   int progstep;
1544   char *rep_str;
1545   int rep_pos;
1546   double prog_start_time;
1547   char error_buf[256];
1548 };
1549
1550 /*
1551   Adding more programs is easy. Just add a listing here and to all_programs,
1552   then add the state machine to actually execute it to basic_controller.
1553  */
1554 static const char * const moire_program[]={
1555   "10 HGR2\n",
1556   "20 FOR Y = 0 TO 190 STEP 2\n",
1557   "30 HCOLOR=4 : REM BLACK\n",
1558   "40 HPLOT 0,191-Y TO 279,Y\n",
1559   "60 HCOLOR=7 : REM WHITE\n",
1560   "80 HPLOT 0,190-Y TO 279,Y+1\n",
1561   "90 NEXT Y\n",
1562   "100 FOR X = 0 TO 278 STEP 3\n",
1563   "110 HCOLOR=4\n",
1564   "120 HPLOT 279-X,0 TO X,191\n",
1565   "140 HCOLOR=7\n",
1566   "150 HPLOT 278-X,0 TO X+1,191\n",
1567   "160 NEXT X\n",
1568   NULL
1569 };
1570
1571 static const char * const sinewave_program[] = {
1572   "10 HGR\n",
1573   "25 K=0\n",
1574   "30 FOR X = 0 TO 279\n",
1575   "32 HCOLOR= 0\n",
1576   "35 HPLOT X,0 TO X,159\n",
1577   "38 HCOLOR= 3\n",
1578   "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1579   "50 HPLOT X,Y\n",
1580   "60 NEXT X\n",
1581   "70 K=K+4\n",
1582   "80 GOTO 30\n",
1583   NULL
1584 };
1585
1586 #if 0
1587 static const char * const dumb_program[]={
1588   "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1589   "20 GOTO 10\n",
1590   NULL
1591 };
1592 #endif
1593
1594 static const char * const random_lores_program[]={
1595   "1 REM APPLE ][ SCREEN SAVER\n",
1596   "10 GR\n",
1597   "100 COLOR= RND(1)*16\n",
1598
1599   "110 X=RND(1)*40\n",
1600   "120 Y1=RND(1)*40\n",
1601   "130 Y2=RND(1)*40\n",
1602   "140 FOR Y = Y1 TO Y2\n",
1603   "150 PLOT X,Y\n",
1604   "160 NEXT Y\n",
1605
1606   "210 Y=RND(1)*40\n",
1607   "220 X1=RND(1)*40\n",
1608   "230 X2=RND(1)*40\n",
1609   "240 FOR X = X1 TO X2\n",
1610   "250 PLOT X,Y\n",
1611   "260 NEXT X\n",
1612   "300 GOTO 100\n",
1613
1614   NULL
1615 };
1616
1617 static char typo_map[256];
1618
1619 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1620 {
1621   int i,j;
1622   int errc;
1623   int success=0;
1624   err_buf[0]=0;
1625
1626   typo_map['A']='Q';
1627   typo_map['S']='A';
1628   typo_map['D']='S';
1629   typo_map['F']='G';
1630   typo_map['G']='H';
1631   typo_map['H']='J';
1632   typo_map['J']='H';
1633   typo_map['K']='L';
1634   typo_map['L']=';';
1635
1636   typo_map['Q']='1';
1637   typo_map['W']='Q';
1638   typo_map['E']='3';
1639   typo_map['R']='T';
1640   typo_map['T']='Y';
1641   typo_map['Y']='U';
1642   typo_map['U']='Y';
1643   typo_map['I']='O';
1644   typo_map['O']='P';
1645   typo_map['P']='[';
1646
1647   typo_map['Z']='X';
1648   typo_map['X']='C';
1649   typo_map['C']='V';
1650   typo_map['V']='C';
1651   typo_map['B']='N';
1652   typo_map['N']='B';
1653   typo_map['M']='N';
1654   typo_map[',']='.';
1655   typo_map['.']=',';
1656
1657   typo_map['!']='1';
1658   typo_map['@']='2';
1659   typo_map['#']='3';
1660   typo_map['$']='4';
1661   typo_map['%']='5';
1662   typo_map['^']='6';
1663   typo_map['&']='7';
1664   typo_map['*']='8';
1665   typo_map['(']='9';
1666   typo_map[')']='0';
1667
1668   typo_map['1']='Q';
1669   typo_map['2']='W';
1670   typo_map['3']='E';
1671   typo_map['4']='R';
1672   typo_map['5']='T';
1673   typo_map['6']='Y';
1674   typo_map['7']='U';
1675   typo_map['8']='I';
1676   typo_map['9']='O';
1677   typo_map['0']='-';
1678
1679   strcpy(out_buf, orig);
1680   for (i=0; out_buf[i]; i++) {
1681     char *p = out_buf+i;
1682
1683     if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1684       break;
1685
1686     if (isalpha(p[0]) &&
1687         isalpha(p[1]) &&
1688         p[0] != p[1] &&
1689         random()%15==0)
1690       {
1691         int tmp=p[1];
1692         p[1]=p[0];
1693         p[0]=tmp;
1694         success=1;
1695         sprintf(err_buf,"?SYNTAX ERROR\n");
1696         break;
1697       }
1698
1699     if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1700       int remain=strlen(p);
1701       int past=random()%(remain-2)+1;
1702       memmove(p+past+past, p, remain+1);
1703       p[0]=errc;
1704       for (j=0; j<past; j++) {
1705         p[past+j]=010;
1706       }
1707       break;
1708     }
1709   }
1710   return success;
1711 }
1712
1713 static const struct {
1714   const char * const * progtext;
1715   int progstep;
1716 } all_programs[]={
1717   {moire_program, 100},
1718   /*{dumb_program, 200}, */
1719   {sinewave_program, 400},
1720   {random_lores_program, 500},
1721 };
1722
1723 static void
1724 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1725 {
1726   apple2_state_t *st=sim->st;
1727   int i;
1728
1729   struct basic_controller_data *mine;
1730   if (!sim->controller_data)
1731     sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1732   mine=(struct basic_controller_data *) sim->controller_data;
1733
1734   switch (*stepno) {
1735   case 0:
1736     st->gr_mode=0;
1737     a2_cls(st);
1738     a2_goto(st,0,16);
1739     a2_prints(st, "APPLE ][");
1740     a2_goto(st,23,0);
1741     a2_printc(st,']');
1742     sim->typing_rate=0.2;
1743
1744     i=random()%countof(all_programs);
1745     mine->progtext=all_programs[i].progtext;
1746     mine->progstep=all_programs[i].progstep;
1747     mine->prog_line=0;
1748
1749     *next_actiontime += 1.0;
1750     *stepno=10;
1751     break;
1752
1753   case 10:
1754     if (st->cursx==0) a2_printc(st,']');
1755     if (mine->progtext[mine->prog_line]) {
1756       if (random()%4==0) {
1757         int err=make_typo(sim->typing_buf,
1758                           mine->progtext[mine->prog_line],
1759                           mine->error_buf);
1760         sim->typing=sim->typing_buf;
1761         if (err) {
1762           *stepno=11;
1763         } else {
1764           mine->prog_line++;
1765         }
1766       } else {
1767         sim->typing=mine->progtext[mine->prog_line++];
1768       }
1769     } else {
1770       *stepno=15;
1771     }
1772     break;
1773
1774   case 11:
1775     sim->printing=mine->error_buf;
1776     *stepno=12;
1777     break;
1778
1779   case 12:
1780     if (st->cursx==0) a2_printc(st,']');
1781     *next_actiontime+=1.0;
1782     *stepno=10;
1783     break;
1784
1785   case 15:
1786     sim->typing="RUN\n";
1787     mine->y=0;
1788     mine->x=0;
1789     mine->k=0;
1790     mine->prog_start_time=*next_actiontime;
1791     *stepno=mine->progstep;
1792     break;
1793
1794     /* moire_program */
1795   case 100:
1796     st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1797     for (i=0; i<24 && mine->y<192; i++)
1798       {
1799         a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1800         a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1801         mine->y += 2;
1802       }
1803     if (mine->y>=192) {
1804       mine->x = 0;
1805       *stepno = 110;
1806     }
1807     break;
1808
1809   case 110:
1810     for (i=0; i<24 && mine->x<280; i++)
1811       {
1812         a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1813         a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1814         mine->x+=3;
1815       }
1816     if (mine->x >= 280) *stepno=120;
1817     break;
1818
1819   case 120:
1820     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1821     break;
1822
1823     /* dumb_program */
1824   case 200:
1825     mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1826     for (i=0; i<30; i++) {
1827       a2_prints(st, mine->rep_str);
1828     }
1829     *stepno=210;
1830     break;
1831
1832   case 210:
1833     i=random()%strlen(mine->rep_str);
1834     while (mine->rep_pos != i) {
1835       a2_printc(st, mine->rep_str[mine->rep_pos]);
1836       mine->rep_pos++;
1837       if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1838     }
1839     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1840     break;
1841
1842     /* sinewave_program */
1843   case 400:
1844     st->gr_mode=A2_GR_HIRES;
1845     *stepno=410;
1846     break;
1847
1848   case 410:
1849     for (i=0; i<48; i++) {
1850       int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1851       a2_hline(st, 0, mine->x, 0, mine->x, 159);
1852       a2_hplot(st, 3, mine->x, y);
1853       mine->x += 1;
1854       if (mine->x>=279) {
1855         mine->x=0;
1856         mine->k+=4;
1857       }
1858     }
1859     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1860     break;
1861
1862   case 420:
1863     a2_prints(st, "]");
1864     *stepno=999;
1865     break;
1866
1867     /* random_lores_program */
1868   case 500:
1869     st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1870     a2_clear_gr(st);
1871     *stepno=510;
1872
1873   case 510:
1874     for (i=0; i<10; i++) {
1875       int color,x,y,x1,x2,y1,y2;
1876
1877       color=random()%15;
1878       x=random()%40;
1879       y1=random()%48;
1880       y2=random()%48;
1881       for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1882
1883       x1=random()%40;
1884       x2=random()%40;
1885       y=random()%48;
1886       for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1887     }
1888     if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1889     break;
1890
1891   case 999:
1892     *stepno=0;
1893     break;
1894
1895   case A2CONTROLLER_FREE:
1896     free(mine);
1897     mine = 0;
1898     break;
1899   }
1900
1901 }
1902
1903 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1904                                      double *next_actiontime) = {
1905   slideshow_controller,
1906   terminal_controller,
1907   basic_controller
1908 };
1909
1910
1911
1912 struct state {
1913   int duration;
1914   Bool random_p;
1915   apple2_sim_t *sim;
1916   void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1917 };
1918
1919
1920 static void *
1921 apple2_init (Display *dpy, Window window)
1922 {
1923   struct state *st = (struct state *) calloc (1, sizeof(*st));
1924   char *s;
1925
1926   st->duration = get_integer_resource (dpy, "duration", "Integer");
1927
1928   st->controller = 0;
1929   if (st->duration < 1) st->duration = 1;
1930
1931   s = get_string_resource (dpy, "mode", "Mode");
1932   if (!s || !*s || !strcasecmp(s, "random"))
1933     st->random_p = True;
1934   else if (!strcasecmp(s, "text"))
1935      st->controller = terminal_controller;
1936   else if (!strcasecmp(s, "slideshow"))
1937      st->controller = slideshow_controller;
1938   else if (!strcasecmp(s, "basic"))
1939      st->controller = basic_controller;
1940   else
1941     {
1942       fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1943                progname, s);
1944       exit (1);
1945     }
1946   if (s) free (s);
1947
1948   if (! st->random_p) {
1949     if (st->controller == terminal_controller ||
1950         st->controller == slideshow_controller)
1951       st->duration = 999999;  /* these run "forever" */
1952   }
1953
1954   return st;
1955 }
1956
1957 static unsigned long
1958 apple2_draw (Display *dpy, Window window, void *closure)
1959 {
1960   struct state *st = (struct state *) closure;
1961
1962   if (! st->sim) {
1963     if (st->random_p)
1964       st->controller = controllers[random() % (countof(controllers))];
1965     st->sim = apple2_start (dpy, window, st->duration, st->controller);
1966   }
1967
1968   if (! apple2_one_frame (st->sim)) {
1969     st->sim = 0;
1970   }
1971
1972   return 10000;
1973 }
1974
1975 static void
1976 apple2_reshape (Display *dpy, Window window, void *closure, 
1977                  unsigned int w, unsigned int h)
1978 {
1979   struct state *st = (struct state *) closure;
1980   analogtv_reconfigure (st->sim->dec);
1981 }
1982
1983 static Bool
1984 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1985 {
1986   struct state *st = (struct state *) closure;
1987
1988   if (st->controller == terminal_controller &&
1989       event->xany.type == KeyPress) {
1990     terminal_keypress_handler (dpy, event, st->sim->controller_data);
1991     return True;
1992   }
1993
1994   return False;
1995 }
1996
1997 static void
1998 apple2_free (Display *dpy, Window window, void *closure)
1999 {
2000   struct state *st = (struct state *) closure;
2001   if (st->sim) {
2002     st->sim->stepno = A2CONTROLLER_DONE;
2003     if (apple2_one_frame (st->sim))
2004       abort();  /* should have freed! */
2005   }
2006   free (st);
2007 }
2008
2009
2010 XSCREENSAVER_MODULE ("Apple2", apple2)