1 /* xscreensaver, Copyright (c) 1998-2011 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>
13 * Pty and vt100 emulation by Fredrik Tolf <fredrik@dolda2000.com>
18 #endif /* HAVE_CONFIG_H */
28 # define XK_MISCELLANY
29 # include <X11/keysymdef.h>
30 # include <X11/Xlib.h>
31 # include <X11/Xutil.h>
32 # include <X11/Intrinsic.h>
36 # include <sys/ioctl.h>
43 #endif /* HAVE_FORKPTY */
45 #include "screenhack.h"
49 #define countof(x) (sizeof((x))/sizeof((*x)))
51 #define SCREEN_COLS 40
52 #define SCREEN_ROWS 24
56 static Time subproc_relaunch_delay = 3000;
59 /* Given a bitmask, returns the position and width of the field.
62 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
65 for (i = 0; i < 32; i++)
70 for (; i < 32; i++, j++)
71 if (! (mask & (1L << i)))
79 /* Given a value and a field-width, expands the field to fill out 8 bits.
82 spread_bits (unsigned char value, unsigned char width)
87 case 7: return (value << 1) | (value >> 6);
88 case 6: return (value << 2) | (value >> 4);
89 case 5: return (value << 3) | (value >> 2);
90 case 4: return (value << 4) | (value);
91 case 3: return (value << 5) | (value << 2) | (value >> 2);
92 case 2: return (value << 6) | (value << 4) | (value);
93 default: abort(); break;
98 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
99 Scales it (without dithering) to WxH.
102 scale_image (Display *dpy, Window window, XImage *in,
103 int fromx, int fromy, int fromw, int fromh,
104 unsigned int *out, int w, int h)
108 unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
109 unsigned int rsiz=0, gsiz=0, bsiz=0;
110 unsigned int rmsk=0, gmsk=0, bmsk=0;
111 unsigned char spread_map[3][256];
112 XWindowAttributes xgwa;
115 if (fromx + fromw > in->width ||
116 fromy + fromh > in->height)
119 XGetWindowAttributes (dpy, window, &xgwa);
121 /* Compute the field offsets for RGB decoding in the XImage,
122 when in TrueColor mode. Otherwise we use the colormap.
124 if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
125 visual_class (xgwa.screen, xgwa.visual) == GrayScale)
127 int ncolors = visual_cells (xgwa.screen, xgwa.visual);
128 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
129 for (i = 0; i < ncolors; i++)
131 XQueryColors (dpy, xgwa.colormap, colors, ncolors);
135 rmsk = xgwa.visual->red_mask;
136 gmsk = xgwa.visual->green_mask;
137 bmsk = xgwa.visual->blue_mask;
138 decode_mask (rmsk, &rpos, &rsiz);
139 decode_mask (gmsk, &gpos, &gsiz);
140 decode_mask (bmsk, &bpos, &bsiz);
142 for (i = 0; i < 256; i++)
144 spread_map[0][i] = spread_bits (i, rsiz);
145 spread_map[1][i] = spread_bits (i, gsiz);
146 spread_map[2][i] = spread_bits (i, bsiz);
150 scale = (fromw > fromh
152 : (float) fromh / h);
154 /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
156 for (y = 0; y < h-1; y++) /* iterate over dest pixels */
157 for (x = 0; x < w-1; x++)
160 unsigned int r=0, g=0, b=0;
162 int xx1 = x * scale + fromx;
163 int yy1 = y * scale + fromy;
164 int xx2 = (x+1) * scale + fromx;
165 int yy2 = (y+1) * scale + fromy;
167 /* Iterate over the source pixels contributing to this one, and sum. */
168 for (xx = xx1; xx < xx2; xx++)
169 for (yy = yy1; yy < yy2; yy++)
171 unsigned char rr, gg, bb;
172 unsigned long sp = ((xx > in->width || yy > in->height)
173 ? 0 : XGetPixel (in, xx, yy));
176 rr = colors[sp].red & 0xFF;
177 gg = colors[sp].green & 0xFF;
178 bb = colors[sp].blue & 0xFF;
182 rr = (sp & rmsk) >> rpos;
183 gg = (sp & gmsk) >> gpos;
184 bb = (sp & bmsk) >> bpos;
185 rr = spread_map[0][rr];
186 gg = spread_map[1][gg];
187 bb = spread_map[2][bb];
194 /* Scale summed pixel values down to 8/8/8 range */
195 i = (xx2 - xx1) * (yy2 - yy1);
201 out[y * w + x] = (r << 16) | (g << 8) | b;
206 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
207 Picks a random sub-image out of the source image, and scales it to WxH.
210 pick_a2_subimage (Display *dpy, Window window, XImage *in,
211 unsigned int *out, int w, int h)
213 int fromx, fromy, fromw, fromh;
214 if (in->width <= w || in->height <= h)
225 double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
228 } while (fromw > in->width ||
231 dw = (in->width - fromw) / 2; /* near the center! */
232 dh = (in->height - fromh) / 2;
234 fromx = (dw <= 0 ? 0 : (random() % dw) + (dw/2));
235 fromy = (dh <= 0 ? 0 : (random() % dh) + (dh/2));
238 scale_image (dpy, window, in,
239 fromx, fromy, fromw, fromh,
244 /* Floyd-Steinberg dither. Derived from ppmquant.c,
245 Copyright (c) 1989, 1991 by Jef Poskanzer.
248 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
251 Apple ][ color map. Each pixel can only be 1 or 0, but what that
252 means depends on whether it's an odd or even pixel, and whether
253 the high bit in the byte is set or not. If it's 0, it's always
256 static const int a2_cmap[2][2][3] = {
259 {/* odd pixels = blue */ 0x00, 0x80, 0xff},
260 {/* even pixels = red */ 0xff, 0x80, 0x00}
264 {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
265 {/* odd pixels = green */ 0x40, 0xff, 0x40}
270 unsigned int **pixels;
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++)
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);
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));
309 /* #### do we really need more than one element of "pixels" at once?
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));
315 for (x = 0; x < w + 2; ++x)
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]) */
323 for (y = 0; y < h; y++)
324 for (x = 0; x < w; x++)
325 pixels[y][x] = in[y * w + x];
327 for (y = 0; y < h; y++)
333 for (x = 0; x < w + 2; x++)
334 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
336 /* It's too complicated to go back and forth on alternate rows,
337 so we always go left-right here. It doesn't change the result
340 For each group of 7 pixels, we have to try it both with the
341 high bit=0 and =1. For each high bit value, we add up the
342 total error and pick the best one.
344 Because we have to go through each group of bits twice, we
345 don't propagate the error values through this_[rgb]err since
346 it would add them twice. So we keep seperate local_[rgb]err
347 variables for propagating error within the 7-pixel group.
351 for (xbyte=0; xbyte<280; xbyte+=7)
354 int best_error=2000000000;
358 int local_rerr=0, local_gerr=0, local_berr=0;
360 for (hibit=0; hibit<2; hibit++)
365 for (x=xbyte; x<xbyte+7; x++)
369 /* Use Floyd-Steinberg errors to adjust actual color. */
370 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
371 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
372 sb = ((pP[x] ) & 0xFF) * brightness/256;
373 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
374 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
375 sb += (this_berr[x + 1] + local_berr) / fs_scale;
378 else if (sr > maxval) sr = maxval;
380 else if (sg > maxval) sg = maxval;
382 else if (sb > maxval) sb = maxval;
384 /* This is the color we'd get if we set the bit 1. For 0,
386 r2=a2_cmap[hibit][x&1][0];
387 g2=a2_cmap[hibit][x&1][1];
388 b2=a2_cmap[hibit][x&1][2];
391 dist0 and dist1 are the error (Minkowski 2-metric
392 distances in the color space) for choosing 0 and
393 1 respectively. 0 is black, 1 is the color r2,g2,b2.
395 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
396 dist0= sr*sr + sg*sg + sb*sb;
400 byte |= 1 << (x-xbyte);
403 /* Wanted sr but got r2, so propagate sr-r2 */
404 local_rerr = (sr - r2) * fs_scale * 7/16;
405 local_gerr = (sg - g2) * fs_scale * 7/16;
406 local_berr = (sb - b2) * fs_scale * 7/16;
412 /* Wanted sr but got 0, so propagate sr */
413 local_rerr = sr * fs_scale * 7/16;
414 local_gerr = sg * fs_scale * 7/16;
415 local_berr = sb * fs_scale * 7/16;
419 if (tot_error < best_error)
422 best_error = tot_error;
426 /* Avoid alternating 7f and ff in all-white areas, because it makes
427 regular pink vertical lines */
428 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
433 Now that we've chosen values for all 8 bits of the byte, we
434 have to fill in the real pixel values into pP and propagate
435 all the error terms. We end up repeating a lot of the code
439 for (x=xbyte; x<xbyte+7; x++)
441 int bit=(best_byte>>(x-xbyte))&1;
442 hibit=(best_byte>>7)&1;
444 sr = (pP[x] >> 16) & 0xFF;
445 sg = (pP[x] >> 8) & 0xFF;
446 sb = (pP[x] ) & 0xFF;
447 sr += this_rerr[x + 1] / fs_scale;
448 sg += this_gerr[x + 1] / fs_scale;
449 sb += this_berr[x + 1] / fs_scale;
452 else if (sr > maxval) sr = maxval;
454 else if (sg > maxval) sg = maxval;
456 else if (sb > maxval) sb = maxval;
458 r2=a2_cmap[hibit][x&1][0] * bit;
459 g2=a2_cmap[hibit][x&1][1] * bit;
460 b2=a2_cmap[hibit][x&1][2] * bit;
462 pP[x] = (r2<<16) | (g2<<8) | (b2);
464 /* Propagate Floyd-Steinberg error terms. */
465 err = (sr - r2) * fs_scale;
466 this_rerr[x + 2] += (err * 7) / 16;
467 next_rerr[x ] += (err * 3) / 16;
468 next_rerr[x + 1] += (err * 5) / 16;
469 next_rerr[x + 2] += (err ) / 16;
470 err = (sg - g2) * fs_scale;
471 this_gerr[x + 2] += (err * 7) / 16;
472 next_gerr[x ] += (err * 3) / 16;
473 next_gerr[x + 1] += (err * 5) / 16;
474 next_gerr[x + 2] += (err ) / 16;
475 err = (sb - b2) * fs_scale;
476 this_berr[x + 2] += (err * 7) / 16;
477 next_berr[x ] += (err * 3) / 16;
478 next_berr[x + 1] += (err * 5) / 16;
479 next_berr[x + 2] += (err ) / 16;
483 And put the actual byte into out.
486 out[y*(w/7) + xbyte/7] = best_byte;
490 temp_err = this_rerr;
491 this_rerr = next_rerr;
492 next_rerr = temp_err;
493 temp_err = this_gerr;
494 this_gerr = next_gerr;
495 next_gerr = temp_err;
496 temp_err = this_berr;
497 this_berr = next_berr;
498 next_berr = temp_err;
514 /* let's see what we got... */
515 FILE *pipe = popen ("xv -", "w");
516 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
517 for (y = 0; y < h; y++)
518 for (x = 0; x < w; x++)
520 unsigned int r = (pixels[y][x]>>16)&0xff;
521 unsigned int g = (pixels[y][x]>>8)&0xff;
522 unsigned int b = (pixels[y][x]>>0)&0xff;
523 fprintf(pipe, "%c%c%c", r, g, b);
530 typedef struct slideshow_data_s {
532 int render_img_lineno;
533 unsigned char *render_img;
535 Bool image_loading_p;
540 image_loaded_cb (Screen *screen, Window window, Drawable p,
541 const char *name, XRectangle *geometry,
544 Display *dpy = DisplayOfScreen (screen);
545 apple2_sim_t *sim = (apple2_sim_t *) closure;
546 slideshow_data *mine = (slideshow_data *) sim->controller_data;
547 XWindowAttributes xgwa;
551 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
552 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
556 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
560 XGetWindowAttributes (dpy, window, &xgwa);
562 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
563 XFreePixmap (dpy, p);
566 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
567 image (regardless of whether it started as TrueColor/PseudoColor.)
569 pick_a2_subimage (dpy, window, image, buf32, w, h);
572 XDestroyImage(image);
574 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
576 a2_dither (buf32, buf8, w, h);
580 mine->image_loading_p = False;
581 mine->img_filename = (name ? strdup (name) : 0);
582 mine->render_img = buf8;
587 static const char *apple2_defaults [] = {
588 ".background: black",
589 ".foreground: white",
592 "*program: xscreensaver-text --cols 40",
593 "*metaSendsESC: True",
600 # endif /* !HAVE_FORKPTY */
606 static XrmOptionDescRec apple2_options [] = {
607 { "-mode", ".mode", XrmoptionSepArg, 0 },
608 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
609 { "-basic", ".mode", XrmoptionNoArg, "basic" },
610 { "-text", ".mode", XrmoptionNoArg, "text" },
611 { "-program", ".program", XrmoptionSepArg, 0 },
612 { "-duration", ".duration", XrmoptionSepArg, 0 },
613 { "-pty", ".usePty", XrmoptionNoArg, "True" },
614 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
615 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
616 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
617 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
618 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
619 { "-fast", ".fast", XrmoptionNoArg, "True" },
625 TODO: this should load 10 images at startup time, then cycle through them
626 to avoid the pause while it loads.
629 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
630 double *next_actiontime)
632 apple2_state_t *st=sim->st;
634 slideshow_data *mine;
636 if (!sim->controller_data)
637 sim->controller_data = calloc (1, sizeof(*mine));
638 mine = (slideshow_data *) sim->controller_data;
646 sim->typing_rate = 0.3;
647 sim->dec->powerup=0.0;
650 a2_prints(st, "APPLE ][");
659 XWindowAttributes xgwa;
661 XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
662 p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height,
664 mine->image_loading_p = True;
665 load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
667 /* pause with a blank screen for a bit, while the image loads in the
669 *next_actiontime += 2.0;
675 if (! mine->image_loading_p) { /* image is finally loaded */
681 *next_actiontime += 3.0;
696 st->gr_mode=A2_GR_HIRES;
697 if (mine->img_filename) {
698 char *basename, *tmp;
701 basename = tmp = strdup (mine->img_filename);
704 char *slash = strchr(basename, '/');
705 if (!slash || !slash[1]) break;
709 char *dot=strrchr(basename,'.');
712 if (strlen(basename)>20) basename[20]=0;
713 for (s=basename; *s; s++) {
715 if (*s <= ' ') *s = '_';
717 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
718 sim->typing = sim->typing_buf;
722 sim->typing = "BLOAD IMAGE\n";
724 mine->render_img_lineno=0;
730 *next_actiontime += 0.7;
735 if (mine->render_img_lineno>=192) {
737 sim->typing="POKE 49234,0\n";
742 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
743 a2_display_image_loading(st, mine->render_img,
744 mine->render_img_lineno++);
747 /* The disk would have to seek every 13 sectors == 78 lines.
748 (This ain't no newfangled 16-sector operating system) */
749 if ((mine->render_img_lineno%78)==0) {
750 *next_actiontime += 0.5;
752 *next_actiontime += 0.08;
757 st->gr_mode |= A2_GR_FULL;
759 /* Note that sim->delay is sometimes "infinite" in this controller.
760 These images are kinda dull anyway, so don't leave it on too long. */
761 *next_actiontime += 2;
766 sim->typing="POKE 49235,0\n";
772 st->gr_mode &= ~A2_GR_FULL;
773 if (mine->render_img) {
774 free(mine->render_img);
775 mine->render_img=NULL;
777 if (mine->img_filename) {
778 free(mine->img_filename);
779 mine->img_filename=NULL;
784 case A2CONTROLLER_FREE:
785 free(mine->render_img);
786 free(mine->img_filename);
796 struct terminal_controller_data {
801 int input_available_p;
802 XtIntervalId timeout_id;
806 double last_emit_time;
807 XComposeStatus compose;
812 int cursor_x, cursor_y;
813 int saved_x, saved_y;
816 unsigned int bold : 1;
817 unsigned int blink : 1;
818 unsigned int rev : 1;
822 Bool meta_sends_esc_p;
829 subproc_cb (XtPointer closure, int *source, XtInputId *id)
831 struct terminal_controller_data *mine =
832 (struct terminal_controller_data *) closure;
833 mine->input_available_p = True;
836 /* The structure of closure linkage throughout this code is so amazingly
837 baroque that I can't get to the 'struct state' from where I need it. */
838 static const char *global_program;
839 static Bool global_fast_p;
843 launch_text_generator (struct terminal_controller_data *mine)
845 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
847 char *oprogram = strdup (global_program);
848 char *program = (char *) malloc (strlen (oprogram) + 10);
850 strcpy (program, "( ");
851 strcat (program, oprogram);
852 strcat (program, " ) 2>&1");
854 if (mine->pipe) abort();
857 if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
862 ws.ws_col = SCREEN_COLS;
863 ws.ws_row = SCREEN_ROWS;
864 ws.ws_xpixel = ws.ws_col * 6;
865 ws.ws_ypixel = ws.ws_row * 8;
868 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
871 sprintf (buf, "%.100s: forkpty", progname);
876 /* This is the child fork. */
879 if (putenv("TERM=vt100"))
886 sprintf (buf, "%.100s: %.100s", progname, oprogram);
892 /* This is the parent fork. */
893 mine->pipe = fdopen(fd, "r+");
895 XtAppAddInput (app, fileno (mine->pipe),
896 (XtPointer) (XtInputReadMask | XtInputExceptMask),
897 subproc_cb, (XtPointer) mine);
901 # endif /* HAVE_FORKPTY */
903 if ((mine->pipe = popen (program, "r")))
905 if (mine->pipe_id) abort();
907 XtAppAddInput (app, fileno (mine->pipe),
908 (XtPointer) (XtInputReadMask | XtInputExceptMask),
909 subproc_cb, (XtPointer) mine);
913 sprintf (buf, "%.100s: %.100s", progname, program);
922 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
924 struct terminal_controller_data *mine =
925 (struct terminal_controller_data *) closure;
927 launch_text_generator (mine);
931 terminal_closegen(struct terminal_controller_data *mine)
934 XtRemoveInput (mine->pipe_id);
941 if (mine->timeout_id) {
942 XtRemoveTimeOut(mine->timeout_id);
948 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
950 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
959 !mine->input_available_p ||
963 rc=read (fileno (mine->pipe), (void *) buf, n);
964 if (rc>0) mine->lastc=buf[rc-1];
968 terminal_closegen(mine);
970 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
974 /* Set up a timer to re-launch the subproc in a bit. */
976 XtAppAddTimeOut(app, subproc_relaunch_delay,
977 relaunch_generator_timer,
981 mine->input_available_p = False;
987 /* The interpretation of the ModN modifiers is dependent on what keys
988 are bound to them: Mod1 does not necessarily mean "meta". It only
989 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
990 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
991 aren't necessarily the same thing. Icepicks in my forehead!
994 do_icccm_meta_key_stupidity (Display *dpy)
996 unsigned int modbits = 0;
999 XModifierKeymap *modmap = XGetModifierMapping (dpy);
1000 for (i = 3; i < 8; i++)
1001 for (j = 0; j < modmap->max_keypermod; j++)
1003 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
1006 if (code == 0) continue;
1007 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
1008 for (k = 0; k < nsyms; k++)
1009 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
1010 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
1011 modbits |= (1 << i);
1014 XFreeModifiermap (modmap);
1015 # endif /* HAVE_COCOA */
1019 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1022 meta_modifier (Display *dpy)
1024 static Bool done_once = False;
1025 static unsigned int mask = 0;
1028 /* Really, we are supposed to recompute this if a KeymapNotify
1029 event comes in, but fuck it. */
1031 mask = do_icccm_meta_key_stupidity (dpy);
1038 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1040 struct terminal_controller_data *mine =
1041 (struct terminal_controller_data *) data;
1043 unsigned char c = 0;
1044 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1045 if (c == 0 || !mine->pipe)
1048 if (!mine->swap_bs_del_p) ;
1049 else if (c == 127) c = 8;
1050 else if (c == 8) c = 127;
1052 /* If meta was held down, send ESC, or turn on the high bit. */
1053 if (event->xkey.state & meta_modifier (dpy))
1055 if (mine->meta_sends_esc_p)
1056 fputc ('\033', mine->pipe);
1061 fputc (c, mine->pipe);
1062 fflush (mine->pipe);
1064 event->xany.type = 0; /* do not process this event further */
1072 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1073 Bool bold_p, Bool blink_p, Bool rev_p,
1076 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1080 else if ((c >= 'A'+128) || /* upcase and blink */
1081 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1082 c != '\r' && c != '\n' && c!='\t'))
1084 c = (c & 0x1F) | 0x80;
1086 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1091 if (bold_p) c |= 0xc0;
1092 if (blink_p) c = (c & ~0x40) | 0x80;
1093 if (rev_p) c |= 0xc0;
1098 a2_printc_noscroll(st, c);
1103 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1106 apple2_state_t *st=sim->st;
1107 int cols = SCREEN_COLS;
1108 int rows = SCREEN_ROWS;
1113 switch (state->escstate)
1119 /* Dummy case - we don't want the screensaver to beep */
1120 /* #### But maybe this should flash the screen? */
1123 if (state->cursor_x > 0)
1127 if (state->cursor_x < cols - 8)
1129 state->cursor_x = (state->cursor_x & ~7) + 8;
1133 state->cursor_x = 0;
1134 if (state->cursor_y < rows - 1)
1143 if (state->cursor_y < rows - 1)
1149 state->cursor_x = 0;
1153 /* Dummy case - there is one and only one font. */
1157 /* Dummy case - these interrupt escape sequences, so
1158 they don't do anything in this state */
1161 state->escstate = 1;
1164 /* Dummy case - this is supposed to be ignored */
1167 state->escstate = 2;
1168 for(i = 0; i < NPAR; i++)
1169 state->csiparam[i] = 0;
1170 state->curparam = 0;
1173 /* If the cursor is in column 39 and we print a character, then
1174 that character shows up in column 39, and the cursor is no longer
1175 visible on the screen (it's in "column 40".) If another character
1176 is printed, then that character shows up in column 0, and the
1177 cursor moves to column 1.
1179 This is empirically what xterm and gnome-terminal do, so that must
1180 be the right thing. (In xterm, the cursor vanishes, whereas; in
1181 gnome-terminal, the cursor overprints the character in col 39.)
1183 if (state->cursor_x >= cols)
1185 state->cursor_x = 0;
1186 if (state->cursor_y >= rows - 1)
1192 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1193 a2_ascii_printc (st, c,
1194 state->termattrib.bf.bold,
1195 state->termattrib.bf.blink,
1196 state->termattrib.bf.rev,
1208 state->escstate = 0;
1210 case 'c': /* Reset */
1212 state->escstate = 0;
1214 case 'D': /* Linefeed */
1215 if (state->cursor_y < rows - 1)
1219 state->escstate = 0;
1221 case 'E': /* Newline */
1222 state->cursor_x = 0;
1223 state->escstate = 0;
1225 case 'M': /* Reverse newline */
1226 if (state->cursor_y > 0)
1228 state->escstate = 0;
1230 case '7': /* Save state */
1231 state->saved_x = state->cursor_x;
1232 state->saved_y = state->cursor_y;
1233 state->escstate = 0;
1235 case '8': /* Restore state */
1236 state->cursor_x = state->saved_x;
1237 state->cursor_y = state->saved_y;
1238 state->escstate = 0;
1241 state->escstate = 2;
1242 for(i = 0; i < NPAR; i++)
1243 state->csiparam[i] = 0;
1244 state->curparam = 0;
1246 case '%': /* Select charset */
1247 /* No, I don't support UTF-8, since the apple2 font
1248 isn't even Unicode anyway. We must still catch the
1249 last byte, though. */
1252 /* I don't support different fonts either - see above
1254 state->escstate = 3;
1257 /* Escape sequences not supported:
1260 * Z - Terminal identification
1262 * = - Other keypad change
1265 state->escstate = 0;
1274 state->escstate = 0;
1276 case '0': case '1': case '2': case '3': case '4':
1277 case '5': case '6': case '7': case '8': case '9':
1278 if (state->curparam < NPAR)
1279 state->csiparam[state->curparam] =
1280 (state->csiparam[state->curparam] * 10) + (c - '0');
1283 state->csiparam[++state->curparam] = 0;
1286 state->escstate = 3;
1289 for (i = 0; i < state->csiparam[0]; i++)
1291 if(++state->cursor_x > cols)
1293 state->cursor_x = 0;
1294 if (state->cursor_y < rows - 1)
1300 state->escstate = 0;
1303 state->cursor_x = 0;
1305 if (state->csiparam[0] == 0)
1306 state->csiparam[0] = 1;
1307 if ((state->cursor_y -= state->csiparam[0]) < 0)
1308 state->cursor_y = 0;
1309 state->escstate = 0;
1312 state->cursor_x = 0;
1315 if (state->csiparam[0] == 0)
1316 state->csiparam[0] = 1;
1317 if ((state->cursor_y += state->csiparam[0]) >= rows)
1318 state->cursor_y = rows - 1;
1319 state->escstate = 0;
1323 if (state->csiparam[0] == 0)
1324 state->csiparam[0] = 1;
1325 if ((state->cursor_x += state->csiparam[0]) >= cols)
1326 state->cursor_x = cols - 1;
1327 state->escstate = 0;
1330 if (state->csiparam[0] == 0)
1331 state->csiparam[0] = 1;
1332 if ((state->cursor_x -= state->csiparam[0]) < 0)
1333 state->cursor_x = 0;
1334 state->escstate = 0;
1337 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1338 state->cursor_y = rows - 1;
1339 state->escstate = 0;
1343 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1344 state->cursor_x = cols - 1;
1345 state->escstate = 0;
1349 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1350 state->cursor_y = rows - 1;
1351 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1352 state->cursor_x = cols - 1;
1353 if(state->cursor_y < 0)
1354 state->cursor_y = 0;
1355 if(state->cursor_x < 0)
1356 state->cursor_x = 0;
1357 state->escstate = 0;
1362 if (state->csiparam[0] == 0)
1363 start = cols * state->cursor_y + state->cursor_x;
1364 if (state->csiparam[0] == 1)
1365 end = cols * state->cursor_y + state->cursor_x;
1367 a2_goto(st, state->cursor_y, state->cursor_x);
1368 for (i = start; i < end; i++)
1370 a2_ascii_printc(st, ' ', False, False, False, False);
1372 state->escstate = 0;
1377 if (state->csiparam[0] == 0)
1378 start = state->cursor_x;
1379 if (state->csiparam[1] == 1)
1380 end = state->cursor_x;
1382 a2_goto(st, state->cursor_y, state->cursor_x);
1383 for (i = start; i < end; i++)
1385 a2_ascii_printc(st, ' ', False, False, False, False);
1387 state->escstate = 0;
1389 case 'm': /* Set attributes */
1390 for (i = 0; i <= state->curparam; i++)
1392 switch(state->csiparam[i])
1395 state->termattrib.w = 0;
1398 state->termattrib.bf.bold = 1;
1401 state->termattrib.bf.blink = 1;
1404 state->termattrib.bf.rev = 1;
1408 state->termattrib.bf.bold = 0;
1411 state->termattrib.bf.blink = 0;
1414 state->termattrib.bf.rev = 0;
1418 state->escstate = 0;
1420 case 's': /* Save position */
1421 state->saved_x = state->cursor_x;
1422 state->saved_y = state->cursor_y;
1423 state->escstate = 0;
1425 case 'u': /* Restore position */
1426 state->cursor_x = state->saved_x;
1427 state->cursor_y = state->saved_y;
1428 state->escstate = 0;
1430 case '?': /* DEC Private modes */
1431 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1432 state->escstate = 0;
1435 /* Known unsupported CSIs:
1437 * L - Insert blank lines
1438 * M - Delete lines (I don't know what this means...)
1439 * P - Delete characters
1440 * X - Erase characters (difference with P being...?)
1441 * c - Terminal identification
1442 * g - Clear tab stop(s)
1443 * h - Set mode (Mainly due to its complexity and lack of good
1446 * m - Set mode (Phosphor is, per defenition, green on black)
1448 * q - Set keyboard LEDs
1449 * r - Set scrolling region (too exhausting - noone uses this,
1452 state->escstate = 0;
1457 state->escstate = 0;
1460 a2_goto(st, state->cursor_y, state->cursor_x);
1465 It's fun to put things like "gdb" as the command. For one, it's
1466 amusing how the standard mumble (version, no warranty, it's
1467 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1471 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1473 apple2_state_t *st=sim->st;
1477 struct terminal_controller_data *mine;
1478 if (!sim->controller_data)
1479 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1480 mine=(struct terminal_controller_data *) sim->controller_data;
1481 mine->dpy = sim->dpy;
1483 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1485 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1487 mine->fast_p = global_fast_p;
1493 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1497 a2_prints(st, "APPLE ][");
1502 launch_text_generator(mine);
1505 *next_actiontime += 4.0;
1511 unsigned char buf[1024];
1515 elapsed=sim->curtime - mine->last_emit_time;
1516 mine->last_emit_time=sim->curtime;
1518 if (elapsed>1.0) nwant=1;
1519 if (nwant<1) nwant=1;
1520 if (nwant>4) nwant=4;
1523 nwant = sizeof(buf)-1;
1525 nr=terminal_read(mine, buf, nwant);
1526 for (i=0; i<nr; i++) {
1530 a2_vt100_printc (sim, mine, c);
1532 a2_ascii_printc (st, c, False, False, False, True);
1537 case A2CONTROLLER_FREE:
1538 terminal_closegen(mine);
1545 struct basic_controller_data {
1548 const char * const * progtext;
1552 double prog_start_time;
1553 char error_buf[256];
1557 Adding more programs is easy. Just add a listing here and to all_programs,
1558 then add the state machine to actually execute it to basic_controller.
1560 static const char * const moire_program[]={
1562 "20 FOR Y = 0 TO 190 STEP 2\n",
1563 "30 HCOLOR=4 : REM BLACK\n",
1564 "40 HPLOT 0,191-Y TO 279,Y\n",
1565 "60 HCOLOR=7 : REM WHITE\n",
1566 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1568 "100 FOR X = 0 TO 278 STEP 3\n",
1570 "120 HPLOT 279-X,0 TO X,191\n",
1572 "150 HPLOT 278-X,0 TO X+1,191\n",
1577 static const char * const sinewave_program[] = {
1580 "30 FOR X = 0 TO 279\n",
1582 "35 HPLOT X,0 TO X,159\n",
1584 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1593 static const char * const dumb_program[]={
1594 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1600 static const char * const random_lores_program[]={
1601 "1 REM APPLE ][ SCREEN SAVER\n",
1603 "100 COLOR= RND(1)*16\n",
1605 "110 X=RND(1)*40\n",
1606 "120 Y1=RND(1)*40\n",
1607 "130 Y2=RND(1)*40\n",
1608 "140 FOR Y = Y1 TO Y2\n",
1612 "210 Y=RND(1)*40\n",
1613 "220 X1=RND(1)*40\n",
1614 "230 X2=RND(1)*40\n",
1615 "240 FOR X = X1 TO X2\n",
1623 static char typo_map[256];
1625 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1685 strcpy(out_buf, orig);
1686 for (i=0; out_buf[i]; i++) {
1687 char *p = out_buf+i;
1689 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1692 if (isalpha(p[0]) &&
1701 sprintf(err_buf,"?SYNTAX ERROR\n");
1705 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1706 int remain=strlen(p);
1707 int past=random()%(remain-2)+1;
1708 memmove(p+past+past, p, remain+1);
1710 for (j=0; j<past; j++) {
1719 static const struct {
1720 const char * const * progtext;
1723 {moire_program, 100},
1724 /*{dumb_program, 200}, */
1725 {sinewave_program, 400},
1726 {random_lores_program, 500},
1730 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1732 apple2_state_t *st=sim->st;
1735 struct basic_controller_data *mine;
1736 if (!sim->controller_data)
1737 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1738 mine=(struct basic_controller_data *) sim->controller_data;
1745 a2_prints(st, "APPLE ][");
1748 sim->typing_rate=0.2;
1750 i=random()%countof(all_programs);
1751 mine->progtext=all_programs[i].progtext;
1752 mine->progstep=all_programs[i].progstep;
1755 *next_actiontime += 1.0;
1760 if (st->cursx==0) a2_printc(st,']');
1761 if (mine->progtext[mine->prog_line]) {
1762 if (random()%4==0) {
1763 int err=make_typo(sim->typing_buf,
1764 mine->progtext[mine->prog_line],
1766 sim->typing=sim->typing_buf;
1773 sim->typing=mine->progtext[mine->prog_line++];
1781 sim->printing=mine->error_buf;
1786 if (st->cursx==0) a2_printc(st,']');
1787 *next_actiontime+=1.0;
1792 sim->typing="RUN\n";
1796 mine->prog_start_time=*next_actiontime;
1797 *stepno=mine->progstep;
1802 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1803 for (i=0; i<24 && mine->y<192; i++)
1805 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1806 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1816 for (i=0; i<24 && mine->x<280; i++)
1818 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1819 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1822 if (mine->x >= 280) *stepno=120;
1826 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1831 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1832 for (i=0; i<30; i++) {
1833 a2_prints(st, mine->rep_str);
1839 i=random()%strlen(mine->rep_str);
1840 while (mine->rep_pos != i) {
1841 a2_printc(st, mine->rep_str[mine->rep_pos]);
1843 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1845 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1848 /* sinewave_program */
1850 st->gr_mode=A2_GR_HIRES;
1855 for (i=0; i<48; i++) {
1856 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1857 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1858 a2_hplot(st, 3, mine->x, y);
1865 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1873 /* random_lores_program */
1875 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1880 for (i=0; i<10; i++) {
1881 int color,x,y,x1,x2,y1,y2;
1887 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1892 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1894 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1901 case A2CONTROLLER_FREE:
1909 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1910 double *next_actiontime) = {
1911 slideshow_controller,
1912 terminal_controller,
1920 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1925 apple2_init (Display *dpy, Window window)
1927 struct state *st = (struct state *) calloc (1, sizeof(*st));
1930 st->duration = get_integer_resource (dpy, "duration", "Integer");
1933 if (st->duration < 1) st->duration = 1;
1935 s = get_string_resource (dpy, "mode", "Mode");
1936 if (!s || !*s || !strcasecmp(s, "random"))
1937 st->random_p = True;
1938 else if (!strcasecmp(s, "text"))
1939 st->controller = terminal_controller;
1940 else if (!strcasecmp(s, "slideshow"))
1941 st->controller = slideshow_controller;
1942 else if (!strcasecmp(s, "basic"))
1943 st->controller = basic_controller;
1946 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1952 global_program = get_string_resource (dpy, "program", "Program");
1953 global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
1956 /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
1958 const char *s = getenv ("XSCREENSAVER_STANDALONE");
1959 if (s && *s && strcmp(s, "0"))
1961 st->controller = terminal_controller;
1962 st->random_p = False;
1963 global_program = getenv ("SHELL");
1964 global_fast_p = True;
1969 if (! st->random_p) {
1970 if (st->controller == terminal_controller ||
1971 st->controller == slideshow_controller)
1972 st->duration = 999999; /* these run "forever" */
1978 static unsigned long
1979 apple2_draw (Display *dpy, Window window, void *closure)
1981 struct state *st = (struct state *) closure;
1985 st->controller = controllers[random() % (countof(controllers))];
1986 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1989 if (! apple2_one_frame (st->sim)) {
1997 apple2_reshape (Display *dpy, Window window, void *closure,
1998 unsigned int w, unsigned int h)
2000 struct state *st = (struct state *) closure;
2001 analogtv_reconfigure (st->sim->dec);
2005 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
2007 struct state *st = (struct state *) closure;
2009 if (st->controller == terminal_controller &&
2010 event->xany.type == KeyPress) {
2011 terminal_keypress_handler (dpy, event, st->sim->controller_data);
2019 apple2_free (Display *dpy, Window window, void *closure)
2021 struct state *st = (struct state *) closure;
2023 st->sim->stepno = A2CONTROLLER_DONE;
2024 if (apple2_one_frame (st->sim))
2025 abort(); /* should have freed! */
2031 XSCREENSAVER_MODULE ("Apple2", apple2)