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