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