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