1 /* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski <jwz@jwz.org>
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
11 * Apple ][ CRT simulator, by Trevor Blackwell <tlb@tlb.org>
12 * with additional work by Jamie Zawinski <jwz@jwz.org>
16 #include "screenhack.h"
18 #include <X11/Xutil.h>
19 #include <X11/Intrinsic.h>
23 #define countof(x) (sizeof((x))/sizeof((*x)))
27 extern XtAppContext app;
29 Time subproc_relaunch_delay = 3000;
32 /* Given a bitmask, returns the position and width of the field.
35 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
38 for (i = 0; i < 32; i++)
43 for (; i < 32; i++, j++)
44 if (! (mask & (1L << i)))
52 /* Given a value and a field-width, expands the field to fill out 8 bits.
55 spread_bits (unsigned char value, unsigned char width)
60 case 7: return (value << 1) | (value >> 6);
61 case 6: return (value << 2) | (value >> 4);
62 case 5: return (value << 3) | (value >> 2);
63 case 4: return (value << 4) | (value);
64 case 3: return (value << 5) | (value << 2) | (value >> 2);
65 case 2: return (value << 6) | (value << 4) | (value);
66 default: abort(); break;
71 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
72 Scales it (without dithering) to WxH.
75 scale_image (Display *dpy, Window window, XImage *in,
76 int fromx, int fromy, int fromw, int fromh,
77 unsigned int *out, int w, int h)
81 unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
82 unsigned int rsiz=0, gsiz=0, bsiz=0;
83 unsigned int rmsk=0, gmsk=0, bmsk=0;
84 unsigned char spread_map[3][256];
85 XWindowAttributes xgwa;
88 if (fromx + fromw > in->width ||
89 fromy + fromh > in->height)
92 XGetWindowAttributes (dpy, window, &xgwa);
94 /* Compute the field offsets for RGB decoding in the XImage,
95 when in TrueColor mode. Otherwise we use the colormap.
97 if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
98 visual_class (xgwa.screen, xgwa.visual) == GrayScale)
100 int ncolors = visual_cells (xgwa.screen, xgwa.visual);
101 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
102 for (i = 0; i < ncolors; i++)
104 XQueryColors (dpy, xgwa.colormap, colors, ncolors);
108 rmsk = xgwa.visual->red_mask;
109 gmsk = xgwa.visual->green_mask;
110 bmsk = xgwa.visual->blue_mask;
111 decode_mask (rmsk, &rpos, &rsiz);
112 decode_mask (gmsk, &gpos, &gsiz);
113 decode_mask (bmsk, &bpos, &bsiz);
115 for (i = 0; i < 256; i++)
117 spread_map[0][i] = spread_bits (i, rsiz);
118 spread_map[1][i] = spread_bits (i, gsiz);
119 spread_map[2][i] = spread_bits (i, bsiz);
123 scale = (fromw > fromh
125 : (float) fromh / h);
127 /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
129 for (y = 0; y < h-1; y++) /* iterate over dest pixels */
130 for (x = 0; x < w-1; x++)
133 unsigned int r=0, g=0, b=0;
135 int xx1 = x * scale + fromx;
136 int yy1 = y * scale + fromy;
137 int xx2 = (x+1) * scale + fromx;
138 int yy2 = (y+1) * scale + fromy;
140 /* Iterate over the source pixels contributing to this one, and sum. */
141 for (xx = xx1; xx < xx2; xx++)
142 for (yy = yy1; yy < yy2; yy++)
144 unsigned char rr, gg, bb;
145 unsigned long sp = ((xx > in->width || yy > in->height)
146 ? 0 : XGetPixel (in, xx, yy));
149 rr = colors[sp].red & 0xFF;
150 gg = colors[sp].green & 0xFF;
151 bb = colors[sp].blue & 0xFF;
155 rr = (sp & rmsk) >> rpos;
156 gg = (sp & gmsk) >> gpos;
157 bb = (sp & bmsk) >> bpos;
158 rr = spread_map[0][rr];
159 gg = spread_map[1][gg];
160 bb = spread_map[2][bb];
167 /* Scale summed pixel values down to 8/8/8 range */
168 i = (xx2 - xx1) * (yy2 - yy1);
174 out[y * w + x] = (r << 16) | (g << 8) | b;
179 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
180 Picks a random sub-image out of the source image, and scales it to WxH.
183 pick_a2_subimage (Display *dpy, Window window, XImage *in,
184 unsigned int *out, int w, int h)
186 int fromx, fromy, fromw, fromh;
187 if (in->width <= w || in->height <= h)
198 double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
201 } while (fromw > in->width ||
204 dw = (in->width - fromw) / 2; /* near the center! */
205 dh = (in->height - fromh) / 2;
207 fromx = (random() % dw) + (dw/2);
208 fromy = (random() % dh) + (dh/2);
211 scale_image (dpy, window, in,
212 fromx, fromy, fromw, fromh,
217 /* Floyd-Steinberg dither. Derived from ppmquant.c,
218 Copyright (c) 1989, 1991 by Jef Poskanzer.
221 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
224 Apple ][ color map. Each pixel can only be 1 or 0, but what that
225 means depends on whether it's an odd or even pixel, and whether
226 the high bit in the byte is set or not. If it's 0, it's always
229 static const int a2_cmap[2][2][3] = {
232 {/* odd pixels = blue */ 0x00, 0x80, 0xff},
233 {/* even pixels = red */ 0xff, 0x80, 0x00}
237 {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
238 {/* odd pixels = green */ 0x40, 0xff, 0x40}
243 unsigned int **pixels;
259 FILE *pipe = popen ("xv -", "w");
260 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
261 for (y = 0; y < h; y++)
262 for (x = 0; x < w; x++)
264 unsigned int p = in[y * w + x];
265 unsigned int r = (p >> 16) & 0xFF;
266 unsigned int g = (p >> 8) & 0xFF;
267 unsigned int b = (p ) & 0xFF;
268 fprintf(pipe, "%c%c%c", r, g, b);
274 /* Initialize Floyd-Steinberg error vectors. */
275 this_rerr = (long *) calloc (w + 2, sizeof(long));
276 next_rerr = (long *) calloc (w + 2, sizeof(long));
277 this_gerr = (long *) calloc (w + 2, sizeof(long));
278 next_gerr = (long *) calloc (w + 2, sizeof(long));
279 this_berr = (long *) calloc (w + 2, sizeof(long));
280 next_berr = (long *) calloc (w + 2, sizeof(long));
283 /* #### do we really need more than one element of "pixels" at once?
285 pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
286 for (y = 0; y < h; y++)
287 pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
289 for (x = 0; x < w + 2; ++x)
291 this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
292 this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
293 this_berr[x] = random() % (fs_scale * 2) - fs_scale;
294 /* (random errors in [-1 .. 1]) */
298 for (y = 0; y < h; y++)
299 for (x = 0; x < w; x++)
300 pixels[y][x] = in[y * w + x];
302 for (y = 0; y < h; y++)
308 for (x = 0; x < w + 2; x++)
309 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
311 /* It's too complicated to go back and forth on alternate rows,
312 so we always go left-right here. It doesn't change the result
315 For each group of 7 pixels, we have to try it both with the
316 high bit=0 and =1. For each high bit value, we add up the
317 total error and pick the best one.
319 Because we have to go through each group of bits twice, we
320 don't propagate the error values through this_[rgb]err since
321 it would add them twice. So we keep seperate local_[rgb]err
322 variables for propagating error within the 7-pixel group.
326 for (xbyte=0; xbyte<280; xbyte+=7)
329 int best_error=2000000000;
333 int local_rerr=0, local_gerr=0, local_berr=0;
335 for (hibit=0; hibit<2; hibit++)
340 for (x=xbyte; x<xbyte+7; x++)
344 /* Use Floyd-Steinberg errors to adjust actual color. */
345 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
346 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
347 sb = ((pP[x] ) & 0xFF) * brightness/256;
348 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
349 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
350 sb += (this_berr[x + 1] + local_berr) / fs_scale;
353 else if (sr > maxval) sr = maxval;
355 else if (sg > maxval) sg = maxval;
357 else if (sb > maxval) sb = maxval;
359 /* This is the color we'd get if we set the bit 1. For 0,
361 r2=a2_cmap[hibit][x&1][0];
362 g2=a2_cmap[hibit][x&1][1];
363 b2=a2_cmap[hibit][x&1][2];
366 dist0 and dist1 are the error (Minkowski 2-metric
367 distances in the color space) for choosing 0 and
368 1 respectively. 0 is black, 1 is the color r2,g2,b2.
370 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
371 dist0= sr*sr + sg*sg + sb*sb;
375 byte |= 1 << (x-xbyte);
378 /* Wanted sr but got r2, so propagate sr-r2 */
379 local_rerr = (sr - r2) * fs_scale * 7/16;
380 local_gerr = (sg - g2) * fs_scale * 7/16;
381 local_berr = (sb - b2) * fs_scale * 7/16;
387 /* Wanted sr but got 0, so propagate sr */
388 local_rerr = sr * fs_scale * 7/16;
389 local_gerr = sg * fs_scale * 7/16;
390 local_berr = sb * fs_scale * 7/16;
394 if (tot_error < best_error)
397 best_error = tot_error;
401 /* Avoid alternating 7f and ff in all-white areas, because it makes
402 regular pink vertical lines */
403 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
408 Now that we've chosen values for all 8 bits of the byte, we
409 have to fill in the real pixel values into pP and propagate
410 all the error terms. We end up repeating a lot of the code
414 for (x=xbyte; x<xbyte+7; x++)
416 int bit=(best_byte>>(x-xbyte))&1;
417 hibit=(best_byte>>7)&1;
419 sr = (pP[x] >> 16) & 0xFF;
420 sg = (pP[x] >> 8) & 0xFF;
421 sb = (pP[x] ) & 0xFF;
422 sr += this_rerr[x + 1] / fs_scale;
423 sg += this_gerr[x + 1] / fs_scale;
424 sb += this_berr[x + 1] / fs_scale;
427 else if (sr > maxval) sr = maxval;
429 else if (sg > maxval) sg = maxval;
431 else if (sb > maxval) sb = maxval;
433 r2=a2_cmap[hibit][x&1][0] * bit;
434 g2=a2_cmap[hibit][x&1][1] * bit;
435 b2=a2_cmap[hibit][x&1][2] * bit;
437 pP[x] = (r2<<16) | (g2<<8) | (b2);
439 /* Propagate Floyd-Steinberg error terms. */
440 err = (sr - r2) * fs_scale;
441 this_rerr[x + 2] += (err * 7) / 16;
442 next_rerr[x ] += (err * 3) / 16;
443 next_rerr[x + 1] += (err * 5) / 16;
444 next_rerr[x + 2] += (err ) / 16;
445 err = (sg - g2) * fs_scale;
446 this_gerr[x + 2] += (err * 7) / 16;
447 next_gerr[x ] += (err * 3) / 16;
448 next_gerr[x + 1] += (err * 5) / 16;
449 next_gerr[x + 2] += (err ) / 16;
450 err = (sb - b2) * fs_scale;
451 this_berr[x + 2] += (err * 7) / 16;
452 next_berr[x ] += (err * 3) / 16;
453 next_berr[x + 1] += (err * 5) / 16;
454 next_berr[x + 2] += (err ) / 16;
458 And put the actual byte into out.
461 out[y*(w/7) + xbyte/7] = best_byte;
465 temp_err = this_rerr;
466 this_rerr = next_rerr;
467 next_rerr = temp_err;
468 temp_err = this_gerr;
469 this_gerr = next_gerr;
470 next_gerr = temp_err;
471 temp_err = this_berr;
472 this_berr = next_berr;
473 next_berr = temp_err;
489 /* let's see what we got... */
490 FILE *pipe = popen ("xv -", "w");
491 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
492 for (y = 0; y < h; y++)
493 for (x = 0; x < w; x++)
495 unsigned int r = (pixels[y][x]>>16)&0xff;
496 unsigned int g = (pixels[y][x]>>8)&0xff;
497 unsigned int b = (pixels[y][x]>>0)&0xff;
498 fprintf(pipe, "%c%c%c", r, g, b);
506 static unsigned char *
507 load_image (Display *dpy, Window window, char **image_filename_r)
509 XWindowAttributes xgwa;
515 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
516 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
520 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
524 XGetWindowAttributes (dpy, window, &xgwa);
525 p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
526 load_random_image (xgwa.screen, window, p, image_filename_r);
527 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
528 XFreePixmap (dpy, p);
531 /* Make sure the window's background is not set to None, and get the
532 grabbed bits (if any) off it as soon as possible. */
533 XSetWindowBackground (dpy, window,
534 get_pixel_resource ("background", "Background",
535 dpy, xgwa.colormap));
536 XClearWindow (dpy, window);
538 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
539 image (regardless of whether it started as TrueColor/PseudoColor.)
541 pick_a2_subimage (dpy, window, image, buf32, w, h);
543 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
545 a2_dither (buf32, buf8, w, h);
552 char *progclass = "Apple2";
554 char *defaults [] = {
561 XrmOptionDescRec options [] = {
562 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
563 { "-basic", ".mode", XrmoptionNoArg, "basic" },
564 { "-text", ".mode", XrmoptionNoArg, "text" },
565 { "-program", ".program", XrmoptionSepArg, 0 },
566 { "-duration", ".duration", XrmoptionSepArg, 0 },
572 TODO: this should load 10 images at startup time, then cycle through them
573 to avoid the pause while it loads.
576 void slideshow_controller(apple2_sim_t *sim, int *stepno,
577 double *next_actiontime)
579 apple2_state_t *st=sim->st;
583 int render_img_lineno;
588 if (!sim->controller_data)
589 sim->controller_data = calloc(sizeof(struct mydata),1);
590 mine=(struct mydata *) sim->controller_data;
598 sim->typing_rate = 0.3;
599 sim->dec->powerup=0.0;
602 a2_prints(st, "APPLE ][");
606 *next_actiontime += 4.0;
610 mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename);
616 *next_actiontime += 3.0;
630 st->gr_mode=A2_GR_HIRES;
631 if (mine->img_filename) {
632 char *basename, *tmp;
635 basename = tmp = strdup (mine->img_filename);
638 char *slash = strchr(basename, '/');
639 if (!slash || !slash[1]) break;
643 char *dot=strchr(basename,'.');
646 if (strlen(basename)>20) basename[20]=0;
647 for (s=basename; *s; s++) *s = toupper (*s);
648 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
649 sim->typing = sim->typing_buf;
653 sim->typing = "BLOAD IMAGE\n";
655 mine->render_img_lineno=0;
661 *next_actiontime += 0.7;
666 if (mine->render_img_lineno>=192) {
668 sim->typing="POKE 49234,0\n";
673 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
674 a2_display_image_loading(st, mine->render_img,
675 mine->render_img_lineno++);
678 /* The disk would have to seek every 13 sectors == 78 lines.
679 (This ain't no newfangled 16-sector operating system) */
680 if ((mine->render_img_lineno%78)==0) {
681 *next_actiontime += 0.5;
683 *next_actiontime += 0.08;
688 st->gr_mode |= A2_GR_FULL;
690 *next_actiontime += sim->delay;
695 sim->typing="POKE 49235,0\n";
701 st->gr_mode &= ~A2_GR_FULL;
702 if (mine->render_img) {
703 free(mine->render_img);
704 mine->render_img=NULL;
706 if (mine->img_filename) {
707 free(mine->img_filename);
708 mine->img_filename=NULL;
713 case A2CONTROLLER_FREE:
714 free(mine->render_img);
715 free(mine->img_filename);
722 struct terminal_controller_data {
725 int input_available_p;
726 XtIntervalId timeout_id;
730 double last_emit_time;
734 subproc_cb (XtPointer closure, int *source, XtInputId *id)
736 struct terminal_controller_data *mine =
737 (struct terminal_controller_data *) closure;
738 mine->input_available_p = True;
742 launch_text_generator (struct terminal_controller_data *mine)
744 char *oprogram = get_string_resource ("program", "Program");
747 if (!oprogram || !*oprogram)
748 oprogram = FORTUNE_PROGRAM;
750 program = (char *) malloc (strlen (oprogram) + 10);
752 strcpy (program, "( ");
753 strcat (program, oprogram);
754 strcat (program, " ) 2>&1");
756 if (mine->pipe) abort();
757 if ((mine->pipe = popen (program, "r")))
759 if (mine->pipe_id) abort();
761 XtAppAddInput (app, fileno (mine->pipe),
762 (XtPointer) (XtInputReadMask | XtInputExceptMask),
763 subproc_cb, (XtPointer) mine);
772 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
774 struct terminal_controller_data *mine =
775 (struct terminal_controller_data *) closure;
777 launch_text_generator (mine);
781 terminal_closegen(struct terminal_controller_data *mine)
784 XtRemoveInput (mine->pipe_id);
791 if (mine->timeout_id) {
792 XtRemoveTimeOut(mine->timeout_id);
798 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
807 if (!mine->input_available_p) return 0;
809 rc=read (fileno (mine->pipe), (void *) buf, n);
810 if (rc>0) mine->lastc=buf[rc-1];
814 terminal_closegen(mine);
816 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
820 /* Set up a timer to re-launch the subproc in a bit. */
822 XtAppAddTimeOut(app, subproc_relaunch_delay,
823 relaunch_generator_timer,
827 mine->input_available_p = False;
834 It's fun to put things like "gdb" as the command. For one, it's
835 amusing how the standard mumble (version, no warranty, it's
836 GNU/Linux dammit) occupies an entire screen on the Apple ][.
840 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
842 apple2_state_t *st=sim->st;
846 struct terminal_controller_data *mine;
847 if (!sim->controller_data)
848 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
849 mine=(struct terminal_controller_data *) sim->controller_data;
855 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
859 a2_prints(st, "APPLE ][");
863 launch_text_generator(mine);
865 *next_actiontime += 4.0;
871 unsigned char buf[5];
875 elapsed=sim->curtime - mine->last_emit_time;
876 mine->last_emit_time=sim->curtime;
878 if (elapsed>1.0) nwant=1;
879 if (nwant<1) nwant=1;
880 if (nwant>4) nwant=4;
882 nr=terminal_read(mine, buf, nwant);
883 for (i=0; i<nr; i++) {
887 else if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
889 a2_printc(st, c&0xDF);
891 else if ((c >= 'A'+128) || /* upcase and blink */
892 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
893 c != '\r' && c != '\n' && c!='\t'))
895 a2_printc(st, (c & 0x1F) | 0x80);
897 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
899 a2_printc(st, c | 0x80);
908 case A2CONTROLLER_FREE:
909 terminal_closegen(mine);
915 struct basic_controller_data {
922 double prog_start_time;
927 Adding more programs is easy. Just add a listing here and to all_programs,
928 then add the state machine to actually execute it to basic_controller.
930 static char *moire_program[]={
932 "20 FOR Y = 0 TO 191 STEP 2\n",
933 "30 HCOLOR=4 : REM BLACK\n",
934 "40 HLINE 0,191-Y TO 279,Y\n",
935 "60 HCOLOR=7 : REM WHITE\n",
936 "80 HLINE 0,190-Y TO 279,Y+1\n",
938 "100 FOR X = 0 TO 279 STEP 3\n",
940 "120 HLINE 279-X,0 TO X,192\n",
942 "150 HLINE 278-X,0 TO X+1,192\n",
947 static char *sinewave_program[] = {
950 "30 FOR X = 0 TO 279\n",
952 "35 HLINE X,0 TO X,159\n",
954 "40 Y = 80 + SIN(15*(X-K)/279)\n",
963 static char *dumb_program[]={
964 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
970 static char *random_lores_program[]={
971 "1 REM APPLE ][ SCREEN SAVER\n",
973 "100 COLOR= RND(1)*16\n",
976 "120 Y1=RND(1)*48\n",
977 "130 Y2=RND(1)*48\n",
978 "140 FOR Y = Y1 TO Y2\n",
983 "220 X1=RND(1)*40\n",
984 "230 X2=RND(1)*40\n",
985 "240 FOR X = X1 TO X2\n",
993 static char typo_map[256];
995 int make_typo(char *out_buf, char *orig, char *err_buf)
1055 strcpy(out_buf, orig);
1056 for (i=0; out_buf[i]; i++) {
1057 char *p = out_buf+i;
1059 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1062 if (isalpha(p[0]) &&
1071 sprintf(err_buf,"?SYNTAX ERROR\n");
1075 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) {
1076 int remain=strlen(p);
1077 int past=random()%(remain-2)+1;
1078 memmove(p+past+past, p, remain+1);
1080 for (j=0; j<past; j++) {
1093 {moire_program, 100},
1094 /*{dumb_program, 200}, */
1095 {sinewave_program, 400},
1096 {random_lores_program, 500},
1100 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1102 apple2_state_t *st=sim->st;
1105 struct basic_controller_data *mine;
1106 if (!sim->controller_data)
1107 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1108 mine=(struct basic_controller_data *) sim->controller_data;
1115 a2_prints(st, "APPLE ][");
1118 sim->typing_rate=0.2;
1120 i=random()%countof(all_programs);
1121 mine->progtext=all_programs[i].progtext;
1122 mine->progstep=all_programs[i].progstep;
1125 *next_actiontime += 1.0;
1130 if (st->cursx==0) a2_printc(st,']');
1131 if (mine->progtext[mine->prog_line]) {
1132 if (random()%4==0) {
1133 int err=make_typo(sim->typing_buf,
1134 mine->progtext[mine->prog_line],
1136 sim->typing=sim->typing_buf;
1143 sim->typing=mine->progtext[mine->prog_line++];
1151 sim->printing=mine->error_buf;
1156 if (st->cursx==0) a2_printc(st,']');
1157 *next_actiontime+=1.0;
1162 sim->typing="RUN\n";
1166 mine->prog_start_time=*next_actiontime;
1167 *stepno=mine->progstep;
1172 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1173 for (i=0; i<24 && mine->y<192; i++)
1175 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1176 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1186 for (i=0; i<24 && mine->x<280; i++)
1188 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1189 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1192 if (mine->x >= 280) *stepno=120;
1196 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1201 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1202 for (i=0; i<30; i++) {
1203 a2_prints(st, mine->rep_str);
1209 i=random()%strlen(mine->rep_str);
1210 while (mine->rep_pos != i) {
1211 a2_printc(st, mine->rep_str[mine->rep_pos]);
1213 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1215 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1218 /* sinewave_program */
1220 st->gr_mode=A2_GR_HIRES;
1225 for (i=0; i<48; i++) {
1226 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1227 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1228 a2_hplot(st, 3, mine->x, y);
1235 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1243 /* random_lores_program */
1245 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1250 for (i=0; i<10; i++) {
1251 int color,x,y,x1,x2,y1,y2;
1257 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1262 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1264 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1271 case A2CONTROLLER_FREE:
1278 void (*controllers[])(apple2_sim_t *sim, int *stepno,
1279 double *next_actiontime) = {
1280 slideshow_controller,
1281 terminal_controller,
1286 screenhack (Display *dpy, Window window)
1288 int duration = get_integer_resource ("duration", "Integer");
1290 void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime);
1292 if (duration < 1) duration = 1;
1294 s = get_string_resource ("mode", "Mode");
1295 if (!s || !*s || !strcasecmp(s, "random"))
1296 controller = controllers[random() % (countof(controllers))];
1297 else if (!strcasecmp(s, "text"))
1298 controller = terminal_controller;
1299 else if (!strcasecmp(s, "slideshow"))
1300 controller = slideshow_controller;
1301 else if (!strcasecmp(s, "basic"))
1302 controller = basic_controller;
1305 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1310 if (!get_boolean_resource ("root", "Boolean"))
1312 XWindowAttributes xgwa;
1313 XGetWindowAttributes (dpy, window, &xgwa);
1314 XSelectInput (dpy, window,
1315 xgwa.your_event_mask |
1316 KeyPressMask | ButtonPressMask | ExposureMask);
1319 apple2 (dpy, window, duration, controller);