1 /* xscreensaver, Copyright (c) 1998-2010 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;
286 FILE *pipe = popen ("xv -", "w");
287 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
288 for (y = 0; y < h; y++)
289 for (x = 0; x < w; x++)
291 unsigned int p = in[y * w + x];
292 unsigned int r = (p >> 16) & 0xFF;
293 unsigned int g = (p >> 8) & 0xFF;
294 unsigned int b = (p ) & 0xFF;
295 fprintf(pipe, "%c%c%c", r, g, b);
301 /* Initialize Floyd-Steinberg error vectors. */
302 this_rerr = (long *) calloc (w + 2, sizeof(long));
303 next_rerr = (long *) calloc (w + 2, sizeof(long));
304 this_gerr = (long *) calloc (w + 2, sizeof(long));
305 next_gerr = (long *) calloc (w + 2, sizeof(long));
306 this_berr = (long *) calloc (w + 2, sizeof(long));
307 next_berr = (long *) calloc (w + 2, sizeof(long));
310 /* #### do we really need more than one element of "pixels" at once?
312 pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
313 for (y = 0; y < h; y++)
314 pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
316 for (x = 0; x < w + 2; ++x)
318 this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
319 this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
320 this_berr[x] = random() % (fs_scale * 2) - fs_scale;
321 /* (random errors in [-1 .. 1]) */
325 for (y = 0; y < h; y++)
326 for (x = 0; x < w; x++)
327 pixels[y][x] = in[y * w + x];
329 for (y = 0; y < h; y++)
335 for (x = 0; x < w + 2; x++)
336 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
338 /* It's too complicated to go back and forth on alternate rows,
339 so we always go left-right here. It doesn't change the result
342 For each group of 7 pixels, we have to try it both with the
343 high bit=0 and =1. For each high bit value, we add up the
344 total error and pick the best one.
346 Because we have to go through each group of bits twice, we
347 don't propagate the error values through this_[rgb]err since
348 it would add them twice. So we keep seperate local_[rgb]err
349 variables for propagating error within the 7-pixel group.
353 for (xbyte=0; xbyte<280; xbyte+=7)
356 int best_error=2000000000;
360 int local_rerr=0, local_gerr=0, local_berr=0;
362 for (hibit=0; hibit<2; hibit++)
367 for (x=xbyte; x<xbyte+7; x++)
371 /* Use Floyd-Steinberg errors to adjust actual color. */
372 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
373 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
374 sb = ((pP[x] ) & 0xFF) * brightness/256;
375 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
376 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
377 sb += (this_berr[x + 1] + local_berr) / fs_scale;
380 else if (sr > maxval) sr = maxval;
382 else if (sg > maxval) sg = maxval;
384 else if (sb > maxval) sb = maxval;
386 /* This is the color we'd get if we set the bit 1. For 0,
388 r2=a2_cmap[hibit][x&1][0];
389 g2=a2_cmap[hibit][x&1][1];
390 b2=a2_cmap[hibit][x&1][2];
393 dist0 and dist1 are the error (Minkowski 2-metric
394 distances in the color space) for choosing 0 and
395 1 respectively. 0 is black, 1 is the color r2,g2,b2.
397 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
398 dist0= sr*sr + sg*sg + sb*sb;
402 byte |= 1 << (x-xbyte);
405 /* Wanted sr but got r2, so propagate sr-r2 */
406 local_rerr = (sr - r2) * fs_scale * 7/16;
407 local_gerr = (sg - g2) * fs_scale * 7/16;
408 local_berr = (sb - b2) * fs_scale * 7/16;
414 /* Wanted sr but got 0, so propagate sr */
415 local_rerr = sr * fs_scale * 7/16;
416 local_gerr = sg * fs_scale * 7/16;
417 local_berr = sb * fs_scale * 7/16;
421 if (tot_error < best_error)
424 best_error = tot_error;
428 /* Avoid alternating 7f and ff in all-white areas, because it makes
429 regular pink vertical lines */
430 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
435 Now that we've chosen values for all 8 bits of the byte, we
436 have to fill in the real pixel values into pP and propagate
437 all the error terms. We end up repeating a lot of the code
441 for (x=xbyte; x<xbyte+7; x++)
443 int bit=(best_byte>>(x-xbyte))&1;
444 hibit=(best_byte>>7)&1;
446 sr = (pP[x] >> 16) & 0xFF;
447 sg = (pP[x] >> 8) & 0xFF;
448 sb = (pP[x] ) & 0xFF;
449 sr += this_rerr[x + 1] / fs_scale;
450 sg += this_gerr[x + 1] / fs_scale;
451 sb += this_berr[x + 1] / fs_scale;
454 else if (sr > maxval) sr = maxval;
456 else if (sg > maxval) sg = maxval;
458 else if (sb > maxval) sb = maxval;
460 r2=a2_cmap[hibit][x&1][0] * bit;
461 g2=a2_cmap[hibit][x&1][1] * bit;
462 b2=a2_cmap[hibit][x&1][2] * bit;
464 pP[x] = (r2<<16) | (g2<<8) | (b2);
466 /* Propagate Floyd-Steinberg error terms. */
467 err = (sr - r2) * fs_scale;
468 this_rerr[x + 2] += (err * 7) / 16;
469 next_rerr[x ] += (err * 3) / 16;
470 next_rerr[x + 1] += (err * 5) / 16;
471 next_rerr[x + 2] += (err ) / 16;
472 err = (sg - g2) * fs_scale;
473 this_gerr[x + 2] += (err * 7) / 16;
474 next_gerr[x ] += (err * 3) / 16;
475 next_gerr[x + 1] += (err * 5) / 16;
476 next_gerr[x + 2] += (err ) / 16;
477 err = (sb - b2) * fs_scale;
478 this_berr[x + 2] += (err * 7) / 16;
479 next_berr[x ] += (err * 3) / 16;
480 next_berr[x + 1] += (err * 5) / 16;
481 next_berr[x + 2] += (err ) / 16;
485 And put the actual byte into out.
488 out[y*(w/7) + xbyte/7] = best_byte;
492 temp_err = this_rerr;
493 this_rerr = next_rerr;
494 next_rerr = temp_err;
495 temp_err = this_gerr;
496 this_gerr = next_gerr;
497 next_gerr = temp_err;
498 temp_err = this_berr;
499 this_berr = next_berr;
500 next_berr = temp_err;
516 /* let's see what we got... */
517 FILE *pipe = popen ("xv -", "w");
518 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
519 for (y = 0; y < h; y++)
520 for (x = 0; x < w; x++)
522 unsigned int r = (pixels[y][x]>>16)&0xff;
523 unsigned int g = (pixels[y][x]>>8)&0xff;
524 unsigned int b = (pixels[y][x]>>0)&0xff;
525 fprintf(pipe, "%c%c%c", r, g, b);
532 typedef struct slideshow_data_s {
534 int render_img_lineno;
535 unsigned char *render_img;
537 Bool image_loading_p;
542 image_loaded_cb (Screen *screen, Window window, Drawable p,
543 const char *name, XRectangle *geometry,
546 Display *dpy = DisplayOfScreen (screen);
547 apple2_sim_t *sim = (apple2_sim_t *) closure;
548 slideshow_data *mine = (slideshow_data *) sim->controller_data;
549 XWindowAttributes xgwa;
553 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
554 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
558 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
562 XGetWindowAttributes (dpy, window, &xgwa);
564 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
565 XFreePixmap (dpy, p);
568 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
569 image (regardless of whether it started as TrueColor/PseudoColor.)
571 pick_a2_subimage (dpy, window, image, buf32, w, h);
574 XDestroyImage(image);
576 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
578 a2_dither (buf32, buf8, w, h);
582 mine->image_loading_p = False;
583 mine->img_filename = (name ? strdup (name) : 0);
584 mine->render_img = buf8;
589 static const char *apple2_defaults [] = {
590 ".background: black",
591 ".foreground: white",
594 "*program: xscreensaver-text --cols 40",
595 "*metaSendsESC: True",
602 # endif /* !HAVE_FORKPTY */
608 static XrmOptionDescRec apple2_options [] = {
609 { "-mode", ".mode", XrmoptionSepArg, 0 },
610 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
611 { "-basic", ".mode", XrmoptionNoArg, "basic" },
612 { "-text", ".mode", XrmoptionNoArg, "text" },
613 { "-program", ".program", XrmoptionSepArg, 0 },
614 { "-duration", ".duration", XrmoptionSepArg, 0 },
615 { "-pty", ".usePty", XrmoptionNoArg, "True" },
616 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
617 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
618 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
619 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
620 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
621 { "-fast", ".fast", XrmoptionNoArg, "True" },
627 TODO: this should load 10 images at startup time, then cycle through them
628 to avoid the pause while it loads.
631 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
632 double *next_actiontime)
634 apple2_state_t *st=sim->st;
636 slideshow_data *mine;
638 if (!sim->controller_data)
639 sim->controller_data = calloc (1, sizeof(*mine));
640 mine = (slideshow_data *) sim->controller_data;
648 sim->typing_rate = 0.3;
649 sim->dec->powerup=0.0;
652 a2_prints(st, "APPLE ][");
661 XWindowAttributes xgwa;
663 XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
664 p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height,
666 mine->image_loading_p = True;
667 load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
669 /* pause with a blank screen for a bit, while the image loads in the
671 *next_actiontime += 2.0;
677 if (! mine->image_loading_p) { /* image is finally loaded */
683 *next_actiontime += 3.0;
698 st->gr_mode=A2_GR_HIRES;
699 if (mine->img_filename) {
700 char *basename, *tmp;
703 basename = tmp = strdup (mine->img_filename);
706 char *slash = strchr(basename, '/');
707 if (!slash || !slash[1]) break;
711 char *dot=strchr(basename,'.');
714 if (strlen(basename)>20) basename[20]=0;
715 for (s=basename; *s; s++) {
717 if (*s <= ' ') *s = '_';
719 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
720 sim->typing = sim->typing_buf;
724 sim->typing = "BLOAD IMAGE\n";
726 mine->render_img_lineno=0;
732 *next_actiontime += 0.7;
737 if (mine->render_img_lineno>=192) {
739 sim->typing="POKE 49234,0\n";
744 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
745 a2_display_image_loading(st, mine->render_img,
746 mine->render_img_lineno++);
749 /* The disk would have to seek every 13 sectors == 78 lines.
750 (This ain't no newfangled 16-sector operating system) */
751 if ((mine->render_img_lineno%78)==0) {
752 *next_actiontime += 0.5;
754 *next_actiontime += 0.08;
759 st->gr_mode |= A2_GR_FULL;
761 /* Note that sim->delay is sometimes "infinite" in this controller.
762 These images are kinda dull anyway, so don't leave it on too long. */
763 *next_actiontime += 2;
768 sim->typing="POKE 49235,0\n";
774 st->gr_mode &= ~A2_GR_FULL;
775 if (mine->render_img) {
776 free(mine->render_img);
777 mine->render_img=NULL;
779 if (mine->img_filename) {
780 free(mine->img_filename);
781 mine->img_filename=NULL;
786 case A2CONTROLLER_FREE:
787 free(mine->render_img);
788 free(mine->img_filename);
798 struct terminal_controller_data {
803 int input_available_p;
804 XtIntervalId timeout_id;
808 double last_emit_time;
809 XComposeStatus compose;
814 int cursor_x, cursor_y;
815 int saved_x, saved_y;
818 unsigned int bold : 1;
819 unsigned int blink : 1;
820 unsigned int rev : 1;
824 Bool meta_sends_esc_p;
831 subproc_cb (XtPointer closure, int *source, XtInputId *id)
833 struct terminal_controller_data *mine =
834 (struct terminal_controller_data *) closure;
835 mine->input_available_p = True;
839 launch_text_generator (struct terminal_controller_data *mine)
841 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
843 char *oprogram = get_string_resource (mine->dpy, "program", "Program");
844 char *program = (char *) malloc (strlen (oprogram) + 10);
846 strcpy (program, "( ");
847 strcat (program, oprogram);
848 strcat (program, " ) 2>&1");
850 if (mine->pipe) abort();
853 if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
858 ws.ws_col = SCREEN_COLS;
859 ws.ws_row = SCREEN_ROWS;
860 ws.ws_xpixel = ws.ws_col * 6;
861 ws.ws_ypixel = ws.ws_row * 8;
864 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
867 sprintf (buf, "%.100s: forkpty", progname);
872 /* This is the child fork. */
875 if (putenv("TERM=vt100"))
882 sprintf (buf, "%.100s: %.100s", progname, oprogram);
888 /* This is the parent fork. */
889 mine->pipe = fdopen(fd, "r+");
891 XtAppAddInput (app, fileno (mine->pipe),
892 (XtPointer) (XtInputReadMask | XtInputExceptMask),
893 subproc_cb, (XtPointer) mine);
897 # endif /* HAVE_FORKPTY */
899 if ((mine->pipe = popen (program, "r")))
901 if (mine->pipe_id) abort();
903 XtAppAddInput (app, fileno (mine->pipe),
904 (XtPointer) (XtInputReadMask | XtInputExceptMask),
905 subproc_cb, (XtPointer) mine);
909 sprintf (buf, "%.100s: %.100s", progname, program);
918 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
920 struct terminal_controller_data *mine =
921 (struct terminal_controller_data *) closure;
923 launch_text_generator (mine);
927 terminal_closegen(struct terminal_controller_data *mine)
930 XtRemoveInput (mine->pipe_id);
937 if (mine->timeout_id) {
938 XtRemoveTimeOut(mine->timeout_id);
944 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
946 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
955 !mine->input_available_p ||
959 rc=read (fileno (mine->pipe), (void *) buf, n);
960 if (rc>0) mine->lastc=buf[rc-1];
964 terminal_closegen(mine);
966 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
970 /* Set up a timer to re-launch the subproc in a bit. */
972 XtAppAddTimeOut(app, subproc_relaunch_delay,
973 relaunch_generator_timer,
977 mine->input_available_p = False;
983 /* The interpretation of the ModN modifiers is dependent on what keys
984 are bound to them: Mod1 does not necessarily mean "meta". It only
985 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
986 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
987 aren't necessarily the same thing. Icepicks in my forehead!
990 do_icccm_meta_key_stupidity (Display *dpy)
992 unsigned int modbits = 0;
995 XModifierKeymap *modmap = XGetModifierMapping (dpy);
996 for (i = 3; i < 8; i++)
997 for (j = 0; j < modmap->max_keypermod; j++)
999 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
1002 if (code == 0) continue;
1003 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
1004 for (k = 0; k < nsyms; k++)
1005 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
1006 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
1007 modbits |= (1 << i);
1010 XFreeModifiermap (modmap);
1011 # endif /* HAVE_COCOA */
1015 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1018 meta_modifier (Display *dpy)
1020 static Bool done_once = False;
1021 static unsigned int mask = 0;
1024 /* Really, we are supposed to recompute this if a KeymapNotify
1025 event comes in, but fuck it. */
1027 mask = do_icccm_meta_key_stupidity (dpy);
1034 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1036 struct terminal_controller_data *mine =
1037 (struct terminal_controller_data *) data;
1039 unsigned char c = 0;
1040 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1041 if (c == 0 || !mine->pipe)
1044 if (!mine->swap_bs_del_p) ;
1045 else if (c == 127) c = 8;
1046 else if (c == 8) c = 127;
1048 /* If meta was held down, send ESC, or turn on the high bit. */
1049 if (event->xkey.state & meta_modifier (dpy))
1051 if (mine->meta_sends_esc_p)
1052 fputc ('\033', mine->pipe);
1057 fputc (c, mine->pipe);
1058 fflush (mine->pipe);
1060 event->xany.type = 0; /* do not process this event further */
1068 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1069 Bool bold_p, Bool blink_p, Bool rev_p,
1072 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1076 else if ((c >= 'A'+128) || /* upcase and blink */
1077 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1078 c != '\r' && c != '\n' && c!='\t'))
1080 c = (c & 0x1F) | 0x80;
1082 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1087 if (bold_p) c |= 0xc0;
1088 if (blink_p) c = (c & ~0x40) | 0x80;
1089 if (rev_p) c |= 0xc0;
1094 a2_printc_noscroll(st, c);
1099 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1102 apple2_state_t *st=sim->st;
1103 int cols = SCREEN_COLS;
1104 int rows = SCREEN_ROWS;
1109 switch (state->escstate)
1115 /* Dummy case - we don't want the screensaver to beep */
1116 /* #### But maybe this should flash the screen? */
1119 if (state->cursor_x > 0)
1123 if (state->cursor_x < cols - 8)
1125 state->cursor_x = (state->cursor_x & ~7) + 8;
1129 state->cursor_x = 0;
1130 if (state->cursor_y < rows - 1)
1139 if (state->cursor_y < rows - 1)
1145 state->cursor_x = 0;
1149 /* Dummy case - there is one and only one font. */
1153 /* Dummy case - these interrupt escape sequences, so
1154 they don't do anything in this state */
1157 state->escstate = 1;
1160 /* Dummy case - this is supposed to be ignored */
1163 state->escstate = 2;
1164 for(i = 0; i < NPAR; i++)
1165 state->csiparam[i] = 0;
1166 state->curparam = 0;
1169 /* If the cursor is in column 39 and we print a character, then
1170 that character shows up in column 39, and the cursor is no longer
1171 visible on the screen (it's in "column 40".) If another character
1172 is printed, then that character shows up in column 0, and the
1173 cursor moves to column 1.
1175 This is empirically what xterm and gnome-terminal do, so that must
1176 be the right thing. (In xterm, the cursor vanishes, whereas; in
1177 gnome-terminal, the cursor overprints the character in col 39.)
1179 if (state->cursor_x >= cols)
1181 state->cursor_x = 0;
1182 if (state->cursor_y >= rows - 1)
1188 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1189 a2_ascii_printc (st, c,
1190 state->termattrib.bf.bold,
1191 state->termattrib.bf.blink,
1192 state->termattrib.bf.rev,
1204 state->escstate = 0;
1206 case 'c': /* Reset */
1208 state->escstate = 0;
1210 case 'D': /* Linefeed */
1211 if (state->cursor_y < rows - 1)
1215 state->escstate = 0;
1217 case 'E': /* Newline */
1218 state->cursor_x = 0;
1219 state->escstate = 0;
1221 case 'M': /* Reverse newline */
1222 if (state->cursor_y > 0)
1224 state->escstate = 0;
1226 case '7': /* Save state */
1227 state->saved_x = state->cursor_x;
1228 state->saved_y = state->cursor_y;
1229 state->escstate = 0;
1231 case '8': /* Restore state */
1232 state->cursor_x = state->saved_x;
1233 state->cursor_y = state->saved_y;
1234 state->escstate = 0;
1237 state->escstate = 2;
1238 for(i = 0; i < NPAR; i++)
1239 state->csiparam[i] = 0;
1240 state->curparam = 0;
1242 case '%': /* Select charset */
1243 /* No, I don't support UTF-8, since the apple2 font
1244 isn't even Unicode anyway. We must still catch the
1245 last byte, though. */
1248 /* I don't support different fonts either - see above
1250 state->escstate = 3;
1253 /* Escape sequences not supported:
1256 * Z - Terminal identification
1258 * = - Other keypad change
1261 state->escstate = 0;
1270 state->escstate = 0;
1272 case '0': case '1': case '2': case '3': case '4':
1273 case '5': case '6': case '7': case '8': case '9':
1274 if (state->curparam < NPAR)
1275 state->csiparam[state->curparam] =
1276 (state->csiparam[state->curparam] * 10) + (c - '0');
1279 state->csiparam[++state->curparam] = 0;
1282 state->escstate = 3;
1285 for (i = 0; i < state->csiparam[0]; i++)
1287 if(++state->cursor_x > cols)
1289 state->cursor_x = 0;
1290 if (state->cursor_y < rows - 1)
1296 state->escstate = 0;
1299 state->cursor_x = 0;
1301 if (state->csiparam[0] == 0)
1302 state->csiparam[0] = 1;
1303 if ((state->cursor_y -= state->csiparam[0]) < 0)
1304 state->cursor_y = 0;
1305 state->escstate = 0;
1308 state->cursor_x = 0;
1311 if (state->csiparam[0] == 0)
1312 state->csiparam[0] = 1;
1313 if ((state->cursor_y += state->csiparam[0]) >= rows)
1314 state->cursor_y = rows - 1;
1315 state->escstate = 0;
1319 if (state->csiparam[0] == 0)
1320 state->csiparam[0] = 1;
1321 if ((state->cursor_x += state->csiparam[0]) >= cols)
1322 state->cursor_x = cols - 1;
1323 state->escstate = 0;
1326 if (state->csiparam[0] == 0)
1327 state->csiparam[0] = 1;
1328 if ((state->cursor_x -= state->csiparam[0]) < 0)
1329 state->cursor_x = 0;
1330 state->escstate = 0;
1333 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1334 state->cursor_y = rows - 1;
1335 state->escstate = 0;
1339 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1340 state->cursor_x = cols - 1;
1341 state->escstate = 0;
1345 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1346 state->cursor_y = rows - 1;
1347 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1348 state->cursor_x = cols - 1;
1349 if(state->cursor_y < 0)
1350 state->cursor_y = 0;
1351 if(state->cursor_x < 0)
1352 state->cursor_x = 0;
1353 state->escstate = 0;
1358 if (state->csiparam[0] == 0)
1359 start = cols * state->cursor_y + state->cursor_x;
1360 if (state->csiparam[0] == 1)
1361 end = cols * state->cursor_y + state->cursor_x;
1363 a2_goto(st, state->cursor_y, state->cursor_x);
1364 for (i = start; i < end; i++)
1366 a2_ascii_printc(st, ' ', False, False, False, False);
1368 state->escstate = 0;
1373 if (state->csiparam[0] == 0)
1374 start = state->cursor_x;
1375 if (state->csiparam[1] == 1)
1376 end = state->cursor_x;
1378 a2_goto(st, state->cursor_y, state->cursor_x);
1379 for (i = start; i < end; i++)
1381 a2_ascii_printc(st, ' ', False, False, False, False);
1383 state->escstate = 0;
1385 case 'm': /* Set attributes */
1386 for (i = 0; i <= state->curparam; i++)
1388 switch(state->csiparam[i])
1391 state->termattrib.w = 0;
1394 state->termattrib.bf.bold = 1;
1397 state->termattrib.bf.blink = 1;
1400 state->termattrib.bf.rev = 1;
1404 state->termattrib.bf.bold = 0;
1407 state->termattrib.bf.blink = 0;
1410 state->termattrib.bf.rev = 0;
1414 state->escstate = 0;
1416 case 's': /* Save position */
1417 state->saved_x = state->cursor_x;
1418 state->saved_y = state->cursor_y;
1419 state->escstate = 0;
1421 case 'u': /* Restore position */
1422 state->cursor_x = state->saved_x;
1423 state->cursor_y = state->saved_y;
1424 state->escstate = 0;
1426 case '?': /* DEC Private modes */
1427 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1428 state->escstate = 0;
1431 /* Known unsupported CSIs:
1433 * L - Insert blank lines
1434 * M - Delete lines (I don't know what this means...)
1435 * P - Delete characters
1436 * X - Erase characters (difference with P being...?)
1437 * c - Terminal identification
1438 * g - Clear tab stop(s)
1439 * h - Set mode (Mainly due to its complexity and lack of good
1442 * m - Set mode (Phosphor is, per defenition, green on black)
1444 * q - Set keyboard LEDs
1445 * r - Set scrolling region (too exhausting - noone uses this,
1448 state->escstate = 0;
1453 state->escstate = 0;
1456 a2_goto(st, state->cursor_y, state->cursor_x);
1461 It's fun to put things like "gdb" as the command. For one, it's
1462 amusing how the standard mumble (version, no warranty, it's
1463 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1467 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1469 apple2_state_t *st=sim->st;
1473 struct terminal_controller_data *mine;
1474 if (!sim->controller_data)
1475 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1476 mine=(struct terminal_controller_data *) sim->controller_data;
1477 mine->dpy = sim->dpy;
1479 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1481 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1483 mine->fast_p = get_boolean_resource (mine->dpy, "fast", "Boolean");
1489 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1493 a2_prints(st, "APPLE ][");
1498 launch_text_generator(mine);
1501 *next_actiontime += 4.0;
1507 unsigned char buf[1024];
1511 elapsed=sim->curtime - mine->last_emit_time;
1512 mine->last_emit_time=sim->curtime;
1514 if (elapsed>1.0) nwant=1;
1515 if (nwant<1) nwant=1;
1516 if (nwant>4) nwant=4;
1519 nwant = sizeof(buf)-1;
1521 nr=terminal_read(mine, buf, nwant);
1522 for (i=0; i<nr; i++) {
1526 a2_vt100_printc (sim, mine, c);
1528 a2_ascii_printc (st, c, False, False, False, True);
1533 case A2CONTROLLER_FREE:
1534 terminal_closegen(mine);
1541 struct basic_controller_data {
1544 const char * const * progtext;
1548 double prog_start_time;
1549 char error_buf[256];
1553 Adding more programs is easy. Just add a listing here and to all_programs,
1554 then add the state machine to actually execute it to basic_controller.
1556 static const char * const moire_program[]={
1558 "20 FOR Y = 0 TO 190 STEP 2\n",
1559 "30 HCOLOR=4 : REM BLACK\n",
1560 "40 HPLOT 0,191-Y TO 279,Y\n",
1561 "60 HCOLOR=7 : REM WHITE\n",
1562 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1564 "100 FOR X = 0 TO 278 STEP 3\n",
1566 "120 HPLOT 279-X,0 TO X,191\n",
1568 "150 HPLOT 278-X,0 TO X+1,191\n",
1573 static const char * const sinewave_program[] = {
1576 "30 FOR X = 0 TO 279\n",
1578 "35 HPLOT X,0 TO X,159\n",
1580 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1589 static const char * const dumb_program[]={
1590 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1596 static const char * const random_lores_program[]={
1597 "1 REM APPLE ][ SCREEN SAVER\n",
1599 "100 COLOR= RND(1)*16\n",
1601 "110 X=RND(1)*40\n",
1602 "120 Y1=RND(1)*40\n",
1603 "130 Y2=RND(1)*40\n",
1604 "140 FOR Y = Y1 TO Y2\n",
1608 "210 Y=RND(1)*40\n",
1609 "220 X1=RND(1)*40\n",
1610 "230 X2=RND(1)*40\n",
1611 "240 FOR X = X1 TO X2\n",
1619 static char typo_map[256];
1621 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1681 strcpy(out_buf, orig);
1682 for (i=0; out_buf[i]; i++) {
1683 char *p = out_buf+i;
1685 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1688 if (isalpha(p[0]) &&
1697 sprintf(err_buf,"?SYNTAX ERROR\n");
1701 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1702 int remain=strlen(p);
1703 int past=random()%(remain-2)+1;
1704 memmove(p+past+past, p, remain+1);
1706 for (j=0; j<past; j++) {
1715 static const struct {
1716 const char * const * progtext;
1719 {moire_program, 100},
1720 /*{dumb_program, 200}, */
1721 {sinewave_program, 400},
1722 {random_lores_program, 500},
1726 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1728 apple2_state_t *st=sim->st;
1731 struct basic_controller_data *mine;
1732 if (!sim->controller_data)
1733 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1734 mine=(struct basic_controller_data *) sim->controller_data;
1741 a2_prints(st, "APPLE ][");
1744 sim->typing_rate=0.2;
1746 i=random()%countof(all_programs);
1747 mine->progtext=all_programs[i].progtext;
1748 mine->progstep=all_programs[i].progstep;
1751 *next_actiontime += 1.0;
1756 if (st->cursx==0) a2_printc(st,']');
1757 if (mine->progtext[mine->prog_line]) {
1758 if (random()%4==0) {
1759 int err=make_typo(sim->typing_buf,
1760 mine->progtext[mine->prog_line],
1762 sim->typing=sim->typing_buf;
1769 sim->typing=mine->progtext[mine->prog_line++];
1777 sim->printing=mine->error_buf;
1782 if (st->cursx==0) a2_printc(st,']');
1783 *next_actiontime+=1.0;
1788 sim->typing="RUN\n";
1792 mine->prog_start_time=*next_actiontime;
1793 *stepno=mine->progstep;
1798 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1799 for (i=0; i<24 && mine->y<192; i++)
1801 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1802 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1812 for (i=0; i<24 && mine->x<280; i++)
1814 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1815 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1818 if (mine->x >= 280) *stepno=120;
1822 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1827 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1828 for (i=0; i<30; i++) {
1829 a2_prints(st, mine->rep_str);
1835 i=random()%strlen(mine->rep_str);
1836 while (mine->rep_pos != i) {
1837 a2_printc(st, mine->rep_str[mine->rep_pos]);
1839 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1841 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1844 /* sinewave_program */
1846 st->gr_mode=A2_GR_HIRES;
1851 for (i=0; i<48; i++) {
1852 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1853 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1854 a2_hplot(st, 3, mine->x, y);
1861 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1869 /* random_lores_program */
1871 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1876 for (i=0; i<10; i++) {
1877 int color,x,y,x1,x2,y1,y2;
1883 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1888 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1890 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1897 case A2CONTROLLER_FREE:
1905 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1906 double *next_actiontime) = {
1907 slideshow_controller,
1908 terminal_controller,
1918 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1923 apple2_init (Display *dpy, Window window)
1925 struct state *st = (struct state *) calloc (1, sizeof(*st));
1928 st->duration = get_integer_resource (dpy, "duration", "Integer");
1931 if (st->duration < 1) st->duration = 1;
1933 s = get_string_resource (dpy, "mode", "Mode");
1934 if (!s || !*s || !strcasecmp(s, "random"))
1935 st->random_p = True;
1936 else if (!strcasecmp(s, "text"))
1937 st->controller = terminal_controller;
1938 else if (!strcasecmp(s, "slideshow"))
1939 st->controller = slideshow_controller;
1940 else if (!strcasecmp(s, "basic"))
1941 st->controller = basic_controller;
1944 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1950 if (! st->random_p) {
1951 if (st->controller == terminal_controller ||
1952 st->controller == slideshow_controller)
1953 st->duration = 999999; /* these run "forever" */
1959 static unsigned long
1960 apple2_draw (Display *dpy, Window window, void *closure)
1962 struct state *st = (struct state *) closure;
1966 st->controller = controllers[random() % (countof(controllers))];
1967 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1970 if (! apple2_one_frame (st->sim)) {
1978 apple2_reshape (Display *dpy, Window window, void *closure,
1979 unsigned int w, unsigned int h)
1981 struct state *st = (struct state *) closure;
1982 analogtv_reconfigure (st->sim->dec);
1986 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1988 struct state *st = (struct state *) closure;
1990 if (st->controller == terminal_controller &&
1991 event->xany.type == KeyPress) {
1992 terminal_keypress_handler (dpy, event, st->sim->controller_data);
2000 apple2_free (Display *dpy, Window window, void *closure)
2002 struct state *st = (struct state *) closure;
2004 st->sim->stepno = A2CONTROLLER_DONE;
2005 if (apple2_one_frame (st->sim))
2006 abort(); /* should have freed! */
2012 XSCREENSAVER_MODULE ("Apple2", apple2)