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