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;
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;
837 launch_text_generator (struct terminal_controller_data *mine)
839 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
841 char *oprogram = get_string_resource (mine->dpy, "program", "Program");
842 char *program = (char *) malloc (strlen (oprogram) + 10);
844 strcpy (program, "( ");
845 strcat (program, oprogram);
846 strcat (program, " ) 2>&1");
848 if (mine->pipe) abort();
851 if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
856 ws.ws_col = SCREEN_COLS;
857 ws.ws_row = SCREEN_ROWS;
858 ws.ws_xpixel = ws.ws_col * 6;
859 ws.ws_ypixel = ws.ws_row * 8;
862 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
865 sprintf (buf, "%.100s: forkpty", progname);
870 /* This is the child fork. */
873 if (putenv("TERM=vt100"))
880 sprintf (buf, "%.100s: %.100s", progname, oprogram);
886 /* This is the parent fork. */
887 mine->pipe = fdopen(fd, "r+");
889 XtAppAddInput (app, fileno (mine->pipe),
890 (XtPointer) (XtInputReadMask | XtInputExceptMask),
891 subproc_cb, (XtPointer) mine);
895 # endif /* HAVE_FORKPTY */
897 if ((mine->pipe = popen (program, "r")))
899 if (mine->pipe_id) abort();
901 XtAppAddInput (app, fileno (mine->pipe),
902 (XtPointer) (XtInputReadMask | XtInputExceptMask),
903 subproc_cb, (XtPointer) mine);
907 sprintf (buf, "%.100s: %.100s", progname, program);
916 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
918 struct terminal_controller_data *mine =
919 (struct terminal_controller_data *) closure;
921 launch_text_generator (mine);
925 terminal_closegen(struct terminal_controller_data *mine)
928 XtRemoveInput (mine->pipe_id);
935 if (mine->timeout_id) {
936 XtRemoveTimeOut(mine->timeout_id);
942 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
944 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
953 !mine->input_available_p ||
957 rc=read (fileno (mine->pipe), (void *) buf, n);
958 if (rc>0) mine->lastc=buf[rc-1];
962 terminal_closegen(mine);
964 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
968 /* Set up a timer to re-launch the subproc in a bit. */
970 XtAppAddTimeOut(app, subproc_relaunch_delay,
971 relaunch_generator_timer,
975 mine->input_available_p = False;
981 /* The interpretation of the ModN modifiers is dependent on what keys
982 are bound to them: Mod1 does not necessarily mean "meta". It only
983 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
984 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
985 aren't necessarily the same thing. Icepicks in my forehead!
988 do_icccm_meta_key_stupidity (Display *dpy)
990 unsigned int modbits = 0;
993 XModifierKeymap *modmap = XGetModifierMapping (dpy);
994 for (i = 3; i < 8; i++)
995 for (j = 0; j < modmap->max_keypermod; j++)
997 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
1000 if (code == 0) continue;
1001 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
1002 for (k = 0; k < nsyms; k++)
1003 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
1004 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
1005 modbits |= (1 << i);
1008 XFreeModifiermap (modmap);
1009 # endif /* HAVE_COCOA */
1013 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1016 meta_modifier (Display *dpy)
1018 static Bool done_once = False;
1019 static unsigned int mask = 0;
1022 /* Really, we are supposed to recompute this if a KeymapNotify
1023 event comes in, but fuck it. */
1025 mask = do_icccm_meta_key_stupidity (dpy);
1032 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1034 struct terminal_controller_data *mine =
1035 (struct terminal_controller_data *) data;
1037 unsigned char c = 0;
1038 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1039 if (c == 0 || !mine->pipe)
1042 if (!mine->swap_bs_del_p) ;
1043 else if (c == 127) c = 8;
1044 else if (c == 8) c = 127;
1046 /* If meta was held down, send ESC, or turn on the high bit. */
1047 if (event->xkey.state & meta_modifier (dpy))
1049 if (mine->meta_sends_esc_p)
1050 fputc ('\033', mine->pipe);
1055 fputc (c, mine->pipe);
1056 fflush (mine->pipe);
1058 event->xany.type = 0; /* do not process this event further */
1066 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1067 Bool bold_p, Bool blink_p, Bool rev_p,
1070 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1074 else if ((c >= 'A'+128) || /* upcase and blink */
1075 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1076 c != '\r' && c != '\n' && c!='\t'))
1078 c = (c & 0x1F) | 0x80;
1080 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1085 if (bold_p) c |= 0xc0;
1086 if (blink_p) c = (c & ~0x40) | 0x80;
1087 if (rev_p) c |= 0xc0;
1092 a2_printc_noscroll(st, c);
1097 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1100 apple2_state_t *st=sim->st;
1101 int cols = SCREEN_COLS;
1102 int rows = SCREEN_ROWS;
1107 switch (state->escstate)
1113 /* Dummy case - we don't want the screensaver to beep */
1114 /* #### But maybe this should flash the screen? */
1117 if (state->cursor_x > 0)
1121 if (state->cursor_x < cols - 8)
1123 state->cursor_x = (state->cursor_x & ~7) + 8;
1127 state->cursor_x = 0;
1128 if (state->cursor_y < rows - 1)
1137 if (state->cursor_y < rows - 1)
1143 state->cursor_x = 0;
1147 /* Dummy case - there is one and only one font. */
1151 /* Dummy case - these interrupt escape sequences, so
1152 they don't do anything in this state */
1155 state->escstate = 1;
1158 /* Dummy case - this is supposed to be ignored */
1161 state->escstate = 2;
1162 for(i = 0; i < NPAR; i++)
1163 state->csiparam[i] = 0;
1164 state->curparam = 0;
1167 /* If the cursor is in column 39 and we print a character, then
1168 that character shows up in column 39, and the cursor is no longer
1169 visible on the screen (it's in "column 40".) If another character
1170 is printed, then that character shows up in column 0, and the
1171 cursor moves to column 1.
1173 This is empirically what xterm and gnome-terminal do, so that must
1174 be the right thing. (In xterm, the cursor vanishes, whereas; in
1175 gnome-terminal, the cursor overprints the character in col 39.)
1177 if (state->cursor_x >= cols)
1179 state->cursor_x = 0;
1180 if (state->cursor_y >= rows - 1)
1186 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1187 a2_ascii_printc (st, c,
1188 state->termattrib.bf.bold,
1189 state->termattrib.bf.blink,
1190 state->termattrib.bf.rev,
1202 state->escstate = 0;
1204 case 'c': /* Reset */
1206 state->escstate = 0;
1208 case 'D': /* Linefeed */
1209 if (state->cursor_y < rows - 1)
1213 state->escstate = 0;
1215 case 'E': /* Newline */
1216 state->cursor_x = 0;
1217 state->escstate = 0;
1219 case 'M': /* Reverse newline */
1220 if (state->cursor_y > 0)
1222 state->escstate = 0;
1224 case '7': /* Save state */
1225 state->saved_x = state->cursor_x;
1226 state->saved_y = state->cursor_y;
1227 state->escstate = 0;
1229 case '8': /* Restore state */
1230 state->cursor_x = state->saved_x;
1231 state->cursor_y = state->saved_y;
1232 state->escstate = 0;
1235 state->escstate = 2;
1236 for(i = 0; i < NPAR; i++)
1237 state->csiparam[i] = 0;
1238 state->curparam = 0;
1240 case '%': /* Select charset */
1241 /* No, I don't support UTF-8, since the apple2 font
1242 isn't even Unicode anyway. We must still catch the
1243 last byte, though. */
1246 /* I don't support different fonts either - see above
1248 state->escstate = 3;
1251 /* Escape sequences not supported:
1254 * Z - Terminal identification
1256 * = - Other keypad change
1259 state->escstate = 0;
1268 state->escstate = 0;
1270 case '0': case '1': case '2': case '3': case '4':
1271 case '5': case '6': case '7': case '8': case '9':
1272 if (state->curparam < NPAR)
1273 state->csiparam[state->curparam] =
1274 (state->csiparam[state->curparam] * 10) + (c - '0');
1277 state->csiparam[++state->curparam] = 0;
1280 state->escstate = 3;
1283 for (i = 0; i < state->csiparam[0]; i++)
1285 if(++state->cursor_x > cols)
1287 state->cursor_x = 0;
1288 if (state->cursor_y < rows - 1)
1294 state->escstate = 0;
1297 state->cursor_x = 0;
1299 if (state->csiparam[0] == 0)
1300 state->csiparam[0] = 1;
1301 if ((state->cursor_y -= state->csiparam[0]) < 0)
1302 state->cursor_y = 0;
1303 state->escstate = 0;
1306 state->cursor_x = 0;
1309 if (state->csiparam[0] == 0)
1310 state->csiparam[0] = 1;
1311 if ((state->cursor_y += state->csiparam[0]) >= rows)
1312 state->cursor_y = rows - 1;
1313 state->escstate = 0;
1317 if (state->csiparam[0] == 0)
1318 state->csiparam[0] = 1;
1319 if ((state->cursor_x += state->csiparam[0]) >= cols)
1320 state->cursor_x = cols - 1;
1321 state->escstate = 0;
1324 if (state->csiparam[0] == 0)
1325 state->csiparam[0] = 1;
1326 if ((state->cursor_x -= state->csiparam[0]) < 0)
1327 state->cursor_x = 0;
1328 state->escstate = 0;
1331 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1332 state->cursor_y = rows - 1;
1333 state->escstate = 0;
1337 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1338 state->cursor_x = cols - 1;
1339 state->escstate = 0;
1343 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1344 state->cursor_y = rows - 1;
1345 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1346 state->cursor_x = cols - 1;
1347 if(state->cursor_y < 0)
1348 state->cursor_y = 0;
1349 if(state->cursor_x < 0)
1350 state->cursor_x = 0;
1351 state->escstate = 0;
1356 if (state->csiparam[0] == 0)
1357 start = cols * state->cursor_y + state->cursor_x;
1358 if (state->csiparam[0] == 1)
1359 end = cols * state->cursor_y + state->cursor_x;
1361 a2_goto(st, state->cursor_y, state->cursor_x);
1362 for (i = start; i < end; i++)
1364 a2_ascii_printc(st, ' ', False, False, False, False);
1366 state->escstate = 0;
1371 if (state->csiparam[0] == 0)
1372 start = state->cursor_x;
1373 if (state->csiparam[1] == 1)
1374 end = state->cursor_x;
1376 a2_goto(st, state->cursor_y, state->cursor_x);
1377 for (i = start; i < end; i++)
1379 a2_ascii_printc(st, ' ', False, False, False, False);
1381 state->escstate = 0;
1383 case 'm': /* Set attributes */
1384 for (i = 0; i <= state->curparam; i++)
1386 switch(state->csiparam[i])
1389 state->termattrib.w = 0;
1392 state->termattrib.bf.bold = 1;
1395 state->termattrib.bf.blink = 1;
1398 state->termattrib.bf.rev = 1;
1402 state->termattrib.bf.bold = 0;
1405 state->termattrib.bf.blink = 0;
1408 state->termattrib.bf.rev = 0;
1412 state->escstate = 0;
1414 case 's': /* Save position */
1415 state->saved_x = state->cursor_x;
1416 state->saved_y = state->cursor_y;
1417 state->escstate = 0;
1419 case 'u': /* Restore position */
1420 state->cursor_x = state->saved_x;
1421 state->cursor_y = state->saved_y;
1422 state->escstate = 0;
1424 case '?': /* DEC Private modes */
1425 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1426 state->escstate = 0;
1429 /* Known unsupported CSIs:
1431 * L - Insert blank lines
1432 * M - Delete lines (I don't know what this means...)
1433 * P - Delete characters
1434 * X - Erase characters (difference with P being...?)
1435 * c - Terminal identification
1436 * g - Clear tab stop(s)
1437 * h - Set mode (Mainly due to its complexity and lack of good
1440 * m - Set mode (Phosphor is, per defenition, green on black)
1442 * q - Set keyboard LEDs
1443 * r - Set scrolling region (too exhausting - noone uses this,
1446 state->escstate = 0;
1451 state->escstate = 0;
1454 a2_goto(st, state->cursor_y, state->cursor_x);
1459 It's fun to put things like "gdb" as the command. For one, it's
1460 amusing how the standard mumble (version, no warranty, it's
1461 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1465 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1467 apple2_state_t *st=sim->st;
1471 struct terminal_controller_data *mine;
1472 if (!sim->controller_data)
1473 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1474 mine=(struct terminal_controller_data *) sim->controller_data;
1475 mine->dpy = sim->dpy;
1477 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1479 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1481 mine->fast_p = get_boolean_resource (mine->dpy, "fast", "Boolean");
1487 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1491 a2_prints(st, "APPLE ][");
1496 launch_text_generator(mine);
1499 *next_actiontime += 4.0;
1505 unsigned char buf[1024];
1509 elapsed=sim->curtime - mine->last_emit_time;
1510 mine->last_emit_time=sim->curtime;
1512 if (elapsed>1.0) nwant=1;
1513 if (nwant<1) nwant=1;
1514 if (nwant>4) nwant=4;
1517 nwant = sizeof(buf)-1;
1519 nr=terminal_read(mine, buf, nwant);
1520 for (i=0; i<nr; i++) {
1524 a2_vt100_printc (sim, mine, c);
1526 a2_ascii_printc (st, c, False, False, False, True);
1531 case A2CONTROLLER_FREE:
1532 terminal_closegen(mine);
1539 struct basic_controller_data {
1542 const char * const * progtext;
1546 double prog_start_time;
1547 char error_buf[256];
1551 Adding more programs is easy. Just add a listing here and to all_programs,
1552 then add the state machine to actually execute it to basic_controller.
1554 static const char * const moire_program[]={
1556 "20 FOR Y = 0 TO 190 STEP 2\n",
1557 "30 HCOLOR=4 : REM BLACK\n",
1558 "40 HPLOT 0,191-Y TO 279,Y\n",
1559 "60 HCOLOR=7 : REM WHITE\n",
1560 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1562 "100 FOR X = 0 TO 278 STEP 3\n",
1564 "120 HPLOT 279-X,0 TO X,191\n",
1566 "150 HPLOT 278-X,0 TO X+1,191\n",
1571 static const char * const sinewave_program[] = {
1574 "30 FOR X = 0 TO 279\n",
1576 "35 HPLOT X,0 TO X,159\n",
1578 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1587 static const char * const dumb_program[]={
1588 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1594 static const char * const random_lores_program[]={
1595 "1 REM APPLE ][ SCREEN SAVER\n",
1597 "100 COLOR= RND(1)*16\n",
1599 "110 X=RND(1)*40\n",
1600 "120 Y1=RND(1)*40\n",
1601 "130 Y2=RND(1)*40\n",
1602 "140 FOR Y = Y1 TO Y2\n",
1606 "210 Y=RND(1)*40\n",
1607 "220 X1=RND(1)*40\n",
1608 "230 X2=RND(1)*40\n",
1609 "240 FOR X = X1 TO X2\n",
1617 static char typo_map[256];
1619 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1679 strcpy(out_buf, orig);
1680 for (i=0; out_buf[i]; i++) {
1681 char *p = out_buf+i;
1683 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1686 if (isalpha(p[0]) &&
1695 sprintf(err_buf,"?SYNTAX ERROR\n");
1699 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1700 int remain=strlen(p);
1701 int past=random()%(remain-2)+1;
1702 memmove(p+past+past, p, remain+1);
1704 for (j=0; j<past; j++) {
1713 static const struct {
1714 const char * const * progtext;
1717 {moire_program, 100},
1718 /*{dumb_program, 200}, */
1719 {sinewave_program, 400},
1720 {random_lores_program, 500},
1724 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1726 apple2_state_t *st=sim->st;
1729 struct basic_controller_data *mine;
1730 if (!sim->controller_data)
1731 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1732 mine=(struct basic_controller_data *) sim->controller_data;
1739 a2_prints(st, "APPLE ][");
1742 sim->typing_rate=0.2;
1744 i=random()%countof(all_programs);
1745 mine->progtext=all_programs[i].progtext;
1746 mine->progstep=all_programs[i].progstep;
1749 *next_actiontime += 1.0;
1754 if (st->cursx==0) a2_printc(st,']');
1755 if (mine->progtext[mine->prog_line]) {
1756 if (random()%4==0) {
1757 int err=make_typo(sim->typing_buf,
1758 mine->progtext[mine->prog_line],
1760 sim->typing=sim->typing_buf;
1767 sim->typing=mine->progtext[mine->prog_line++];
1775 sim->printing=mine->error_buf;
1780 if (st->cursx==0) a2_printc(st,']');
1781 *next_actiontime+=1.0;
1786 sim->typing="RUN\n";
1790 mine->prog_start_time=*next_actiontime;
1791 *stepno=mine->progstep;
1796 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1797 for (i=0; i<24 && mine->y<192; i++)
1799 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1800 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1810 for (i=0; i<24 && mine->x<280; i++)
1812 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1813 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1816 if (mine->x >= 280) *stepno=120;
1820 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1825 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1826 for (i=0; i<30; i++) {
1827 a2_prints(st, mine->rep_str);
1833 i=random()%strlen(mine->rep_str);
1834 while (mine->rep_pos != i) {
1835 a2_printc(st, mine->rep_str[mine->rep_pos]);
1837 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1839 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1842 /* sinewave_program */
1844 st->gr_mode=A2_GR_HIRES;
1849 for (i=0; i<48; i++) {
1850 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1851 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1852 a2_hplot(st, 3, mine->x, y);
1859 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1867 /* random_lores_program */
1869 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1874 for (i=0; i<10; i++) {
1875 int color,x,y,x1,x2,y1,y2;
1881 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1886 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1888 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1895 case A2CONTROLLER_FREE:
1903 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1904 double *next_actiontime) = {
1905 slideshow_controller,
1906 terminal_controller,
1916 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1921 apple2_init (Display *dpy, Window window)
1923 struct state *st = (struct state *) calloc (1, sizeof(*st));
1926 st->duration = get_integer_resource (dpy, "duration", "Integer");
1929 if (st->duration < 1) st->duration = 1;
1931 s = get_string_resource (dpy, "mode", "Mode");
1932 if (!s || !*s || !strcasecmp(s, "random"))
1933 st->random_p = True;
1934 else if (!strcasecmp(s, "text"))
1935 st->controller = terminal_controller;
1936 else if (!strcasecmp(s, "slideshow"))
1937 st->controller = slideshow_controller;
1938 else if (!strcasecmp(s, "basic"))
1939 st->controller = basic_controller;
1942 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1948 if (! st->random_p) {
1949 if (st->controller == terminal_controller ||
1950 st->controller == slideshow_controller)
1951 st->duration = 999999; /* these run "forever" */
1957 static unsigned long
1958 apple2_draw (Display *dpy, Window window, void *closure)
1960 struct state *st = (struct state *) closure;
1964 st->controller = controllers[random() % (countof(controllers))];
1965 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1968 if (! apple2_one_frame (st->sim)) {
1976 apple2_reshape (Display *dpy, Window window, void *closure,
1977 unsigned int w, unsigned int h)
1979 struct state *st = (struct state *) closure;
1980 analogtv_reconfigure (st->sim->dec);
1984 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1986 struct state *st = (struct state *) closure;
1988 if (st->controller == terminal_controller &&
1989 event->xany.type == KeyPress) {
1990 terminal_keypress_handler (dpy, event, st->sim->controller_data);
1998 apple2_free (Display *dpy, Window window, void *closure)
2000 struct state *st = (struct state *) closure;
2002 st->sim->stepno = A2CONTROLLER_DONE;
2003 if (apple2_one_frame (st->sim))
2004 abort(); /* should have freed! */
2010 XSCREENSAVER_MODULE ("Apple2", apple2)