1 /* xscreensaver, Copyright (c) 1998-2006 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/Xutil.h>
31 # include <X11/Intrinsic.h>
35 # include <sys/ioctl.h>
42 #endif /* HAVE_FORKPTY */
44 #include "screenhack.h"
48 #define countof(x) (sizeof((x))/sizeof((*x)))
50 #define SCREEN_COLS 40
51 #define SCREEN_ROWS 24
55 static Time subproc_relaunch_delay = 3000;
58 /* Given a bitmask, returns the position and width of the field.
61 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
64 for (i = 0; i < 32; i++)
69 for (; i < 32; i++, j++)
70 if (! (mask & (1L << i)))
78 /* Given a value and a field-width, expands the field to fill out 8 bits.
81 spread_bits (unsigned char value, unsigned char width)
86 case 7: return (value << 1) | (value >> 6);
87 case 6: return (value << 2) | (value >> 4);
88 case 5: return (value << 3) | (value >> 2);
89 case 4: return (value << 4) | (value);
90 case 3: return (value << 5) | (value << 2) | (value >> 2);
91 case 2: return (value << 6) | (value << 4) | (value);
92 default: abort(); break;
97 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
98 Scales it (without dithering) to WxH.
101 scale_image (Display *dpy, Window window, XImage *in,
102 int fromx, int fromy, int fromw, int fromh,
103 unsigned int *out, int w, int h)
107 unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
108 unsigned int rsiz=0, gsiz=0, bsiz=0;
109 unsigned int rmsk=0, gmsk=0, bmsk=0;
110 unsigned char spread_map[3][256];
111 XWindowAttributes xgwa;
114 if (fromx + fromw > in->width ||
115 fromy + fromh > in->height)
118 XGetWindowAttributes (dpy, window, &xgwa);
120 /* Compute the field offsets for RGB decoding in the XImage,
121 when in TrueColor mode. Otherwise we use the colormap.
123 if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
124 visual_class (xgwa.screen, xgwa.visual) == GrayScale)
126 int ncolors = visual_cells (xgwa.screen, xgwa.visual);
127 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
128 for (i = 0; i < ncolors; i++)
130 XQueryColors (dpy, xgwa.colormap, colors, ncolors);
134 rmsk = xgwa.visual->red_mask;
135 gmsk = xgwa.visual->green_mask;
136 bmsk = xgwa.visual->blue_mask;
137 decode_mask (rmsk, &rpos, &rsiz);
138 decode_mask (gmsk, &gpos, &gsiz);
139 decode_mask (bmsk, &bpos, &bsiz);
141 for (i = 0; i < 256; i++)
143 spread_map[0][i] = spread_bits (i, rsiz);
144 spread_map[1][i] = spread_bits (i, gsiz);
145 spread_map[2][i] = spread_bits (i, bsiz);
149 scale = (fromw > fromh
151 : (float) fromh / h);
153 /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
155 for (y = 0; y < h-1; y++) /* iterate over dest pixels */
156 for (x = 0; x < w-1; x++)
159 unsigned int r=0, g=0, b=0;
161 int xx1 = x * scale + fromx;
162 int yy1 = y * scale + fromy;
163 int xx2 = (x+1) * scale + fromx;
164 int yy2 = (y+1) * scale + fromy;
166 /* Iterate over the source pixels contributing to this one, and sum. */
167 for (xx = xx1; xx < xx2; xx++)
168 for (yy = yy1; yy < yy2; yy++)
170 unsigned char rr, gg, bb;
171 unsigned long sp = ((xx > in->width || yy > in->height)
172 ? 0 : XGetPixel (in, xx, yy));
175 rr = colors[sp].red & 0xFF;
176 gg = colors[sp].green & 0xFF;
177 bb = colors[sp].blue & 0xFF;
181 rr = (sp & rmsk) >> rpos;
182 gg = (sp & gmsk) >> gpos;
183 bb = (sp & bmsk) >> bpos;
184 rr = spread_map[0][rr];
185 gg = spread_map[1][gg];
186 bb = spread_map[2][bb];
193 /* Scale summed pixel values down to 8/8/8 range */
194 i = (xx2 - xx1) * (yy2 - yy1);
200 out[y * w + x] = (r << 16) | (g << 8) | b;
205 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
206 Picks a random sub-image out of the source image, and scales it to WxH.
209 pick_a2_subimage (Display *dpy, Window window, XImage *in,
210 unsigned int *out, int w, int h)
212 int fromx, fromy, fromw, fromh;
213 if (in->width <= w || in->height <= h)
224 double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
227 } while (fromw > in->width ||
230 dw = (in->width - fromw) / 2; /* near the center! */
231 dh = (in->height - fromh) / 2;
233 fromx = (dw <= 0 ? 0 : (random() % dw) + (dw/2));
234 fromy = (dh <= 0 ? 0 : (random() % dh) + (dh/2));
237 scale_image (dpy, window, in,
238 fromx, fromy, fromw, fromh,
243 /* Floyd-Steinberg dither. Derived from ppmquant.c,
244 Copyright (c) 1989, 1991 by Jef Poskanzer.
247 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
250 Apple ][ color map. Each pixel can only be 1 or 0, but what that
251 means depends on whether it's an odd or even pixel, and whether
252 the high bit in the byte is set or not. If it's 0, it's always
255 static const int a2_cmap[2][2][3] = {
258 {/* odd pixels = blue */ 0x00, 0x80, 0xff},
259 {/* even pixels = red */ 0xff, 0x80, 0x00}
263 {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
264 {/* odd pixels = green */ 0x40, 0xff, 0x40}
269 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]) */
324 for (y = 0; y < h; y++)
325 for (x = 0; x < w; x++)
326 pixels[y][x] = in[y * w + x];
328 for (y = 0; y < h; y++)
334 for (x = 0; x < w + 2; x++)
335 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
337 /* It's too complicated to go back and forth on alternate rows,
338 so we always go left-right here. It doesn't change the result
341 For each group of 7 pixels, we have to try it both with the
342 high bit=0 and =1. For each high bit value, we add up the
343 total error and pick the best one.
345 Because we have to go through each group of bits twice, we
346 don't propagate the error values through this_[rgb]err since
347 it would add them twice. So we keep seperate local_[rgb]err
348 variables for propagating error within the 7-pixel group.
352 for (xbyte=0; xbyte<280; xbyte+=7)
355 int best_error=2000000000;
359 int local_rerr=0, local_gerr=0, local_berr=0;
361 for (hibit=0; hibit<2; hibit++)
366 for (x=xbyte; x<xbyte+7; x++)
370 /* Use Floyd-Steinberg errors to adjust actual color. */
371 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
372 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
373 sb = ((pP[x] ) & 0xFF) * brightness/256;
374 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
375 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
376 sb += (this_berr[x + 1] + local_berr) / fs_scale;
379 else if (sr > maxval) sr = maxval;
381 else if (sg > maxval) sg = maxval;
383 else if (sb > maxval) sb = maxval;
385 /* This is the color we'd get if we set the bit 1. For 0,
387 r2=a2_cmap[hibit][x&1][0];
388 g2=a2_cmap[hibit][x&1][1];
389 b2=a2_cmap[hibit][x&1][2];
392 dist0 and dist1 are the error (Minkowski 2-metric
393 distances in the color space) for choosing 0 and
394 1 respectively. 0 is black, 1 is the color r2,g2,b2.
396 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
397 dist0= sr*sr + sg*sg + sb*sb;
401 byte |= 1 << (x-xbyte);
404 /* Wanted sr but got r2, so propagate sr-r2 */
405 local_rerr = (sr - r2) * fs_scale * 7/16;
406 local_gerr = (sg - g2) * fs_scale * 7/16;
407 local_berr = (sb - b2) * fs_scale * 7/16;
413 /* Wanted sr but got 0, so propagate sr */
414 local_rerr = sr * fs_scale * 7/16;
415 local_gerr = sg * fs_scale * 7/16;
416 local_berr = sb * fs_scale * 7/16;
420 if (tot_error < best_error)
423 best_error = tot_error;
427 /* Avoid alternating 7f and ff in all-white areas, because it makes
428 regular pink vertical lines */
429 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
434 Now that we've chosen values for all 8 bits of the byte, we
435 have to fill in the real pixel values into pP and propagate
436 all the error terms. We end up repeating a lot of the code
440 for (x=xbyte; x<xbyte+7; x++)
442 int bit=(best_byte>>(x-xbyte))&1;
443 hibit=(best_byte>>7)&1;
445 sr = (pP[x] >> 16) & 0xFF;
446 sg = (pP[x] >> 8) & 0xFF;
447 sb = (pP[x] ) & 0xFF;
448 sr += this_rerr[x + 1] / fs_scale;
449 sg += this_gerr[x + 1] / fs_scale;
450 sb += this_berr[x + 1] / fs_scale;
453 else if (sr > maxval) sr = maxval;
455 else if (sg > maxval) sg = maxval;
457 else if (sb > maxval) sb = maxval;
459 r2=a2_cmap[hibit][x&1][0] * bit;
460 g2=a2_cmap[hibit][x&1][1] * bit;
461 b2=a2_cmap[hibit][x&1][2] * bit;
463 pP[x] = (r2<<16) | (g2<<8) | (b2);
465 /* Propagate Floyd-Steinberg error terms. */
466 err = (sr - r2) * fs_scale;
467 this_rerr[x + 2] += (err * 7) / 16;
468 next_rerr[x ] += (err * 3) / 16;
469 next_rerr[x + 1] += (err * 5) / 16;
470 next_rerr[x + 2] += (err ) / 16;
471 err = (sg - g2) * fs_scale;
472 this_gerr[x + 2] += (err * 7) / 16;
473 next_gerr[x ] += (err * 3) / 16;
474 next_gerr[x + 1] += (err * 5) / 16;
475 next_gerr[x + 2] += (err ) / 16;
476 err = (sb - b2) * fs_scale;
477 this_berr[x + 2] += (err * 7) / 16;
478 next_berr[x ] += (err * 3) / 16;
479 next_berr[x + 1] += (err * 5) / 16;
480 next_berr[x + 2] += (err ) / 16;
484 And put the actual byte into out.
487 out[y*(w/7) + xbyte/7] = best_byte;
491 temp_err = this_rerr;
492 this_rerr = next_rerr;
493 next_rerr = temp_err;
494 temp_err = this_gerr;
495 this_gerr = next_gerr;
496 next_gerr = temp_err;
497 temp_err = this_berr;
498 this_berr = next_berr;
499 next_berr = temp_err;
515 /* let's see what we got... */
516 FILE *pipe = popen ("xv -", "w");
517 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
518 for (y = 0; y < h; y++)
519 for (x = 0; x < w; x++)
521 unsigned int r = (pixels[y][x]>>16)&0xff;
522 unsigned int g = (pixels[y][x]>>8)&0xff;
523 unsigned int b = (pixels[y][x]>>0)&0xff;
524 fprintf(pipe, "%c%c%c", r, g, b);
531 typedef struct slideshow_data_s {
533 int render_img_lineno;
534 unsigned char *render_img;
536 Bool image_loading_p;
541 image_loaded_cb (Screen *screen, Window window, Drawable p,
542 const char *name, XRectangle *geometry,
545 Display *dpy = DisplayOfScreen (screen);
546 apple2_sim_t *sim = (apple2_sim_t *) closure;
547 slideshow_data *mine = (slideshow_data *) sim->controller_data;
548 XWindowAttributes xgwa;
552 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
553 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
557 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
561 XGetWindowAttributes (dpy, window, &xgwa);
563 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
564 XFreePixmap (dpy, p);
567 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
568 image (regardless of whether it started as TrueColor/PseudoColor.)
570 pick_a2_subimage (dpy, window, image, buf32, w, h);
572 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
574 a2_dither (buf32, buf8, w, h);
578 mine->image_loading_p = False;
579 mine->img_filename = (name ? strdup (name) : 0);
580 mine->render_img = buf8;
585 static const char *apple2_defaults [] = {
586 ".background: black",
587 ".foreground: white",
590 "*program: xscreensaver-text --cols 40",
591 "*metaSendsESC: True",
598 # endif /* !HAVE_FORKPTY */
604 static XrmOptionDescRec apple2_options [] = {
605 { "-mode", ".mode", XrmoptionSepArg, 0 },
606 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
607 { "-basic", ".mode", XrmoptionNoArg, "basic" },
608 { "-text", ".mode", XrmoptionNoArg, "text" },
609 { "-program", ".program", XrmoptionSepArg, 0 },
610 { "-duration", ".duration", XrmoptionSepArg, 0 },
611 { "-pty", ".usePty", XrmoptionNoArg, "True" },
612 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
613 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
614 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
615 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
616 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
617 { "-fast", ".fast", XrmoptionNoArg, "True" },
623 TODO: this should load 10 images at startup time, then cycle through them
624 to avoid the pause while it loads.
627 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
628 double *next_actiontime)
630 apple2_state_t *st=sim->st;
632 slideshow_data *mine;
634 if (!sim->controller_data)
635 sim->controller_data = calloc (1, sizeof(*mine));
636 mine = (slideshow_data *) sim->controller_data;
644 sim->typing_rate = 0.3;
645 sim->dec->powerup=0.0;
648 a2_prints(st, "APPLE ][");
657 XWindowAttributes xgwa;
659 XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
660 p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height,
662 mine->image_loading_p = True;
663 load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
665 /* pause with a blank screen for a bit, while the image loads in the
667 *next_actiontime += 2.0;
673 if (! mine->image_loading_p) { /* image is finally loaded */
679 *next_actiontime += 3.0;
694 st->gr_mode=A2_GR_HIRES;
695 if (mine->img_filename) {
696 char *basename, *tmp;
699 basename = tmp = strdup (mine->img_filename);
702 char *slash = strchr(basename, '/');
703 if (!slash || !slash[1]) break;
707 char *dot=strchr(basename,'.');
710 if (strlen(basename)>20) basename[20]=0;
711 for (s=basename; *s; s++) {
713 if (*s <= ' ') *s = '_';
715 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
716 sim->typing = sim->typing_buf;
720 sim->typing = "BLOAD IMAGE\n";
722 mine->render_img_lineno=0;
728 *next_actiontime += 0.7;
733 if (mine->render_img_lineno>=192) {
735 sim->typing="POKE 49234,0\n";
740 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
741 a2_display_image_loading(st, mine->render_img,
742 mine->render_img_lineno++);
745 /* The disk would have to seek every 13 sectors == 78 lines.
746 (This ain't no newfangled 16-sector operating system) */
747 if ((mine->render_img_lineno%78)==0) {
748 *next_actiontime += 0.5;
750 *next_actiontime += 0.08;
755 st->gr_mode |= A2_GR_FULL;
757 /* Note that sim->delay is sometimes "infinite" in this controller.
758 These images are kinda dull anyway, so don't leave it on too long. */
759 *next_actiontime += 2;
764 sim->typing="POKE 49235,0\n";
770 st->gr_mode &= ~A2_GR_FULL;
771 if (mine->render_img) {
772 free(mine->render_img);
773 mine->render_img=NULL;
775 if (mine->img_filename) {
776 free(mine->img_filename);
777 mine->img_filename=NULL;
782 case A2CONTROLLER_FREE:
783 free(mine->render_img);
784 free(mine->img_filename);
793 struct terminal_controller_data {
798 int input_available_p;
799 XtIntervalId timeout_id;
803 double last_emit_time;
804 XComposeStatus compose;
809 int cursor_x, cursor_y;
810 int saved_x, saved_y;
813 unsigned int bold : 1;
814 unsigned int blink : 1;
815 unsigned int rev : 1;
819 Bool meta_sends_esc_p;
826 subproc_cb (XtPointer closure, int *source, XtInputId *id)
828 struct terminal_controller_data *mine =
829 (struct terminal_controller_data *) closure;
830 mine->input_available_p = True;
834 launch_text_generator (struct terminal_controller_data *mine)
836 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
838 char *oprogram = get_string_resource (mine->dpy, "program", "Program");
839 char *program = (char *) malloc (strlen (oprogram) + 10);
841 strcpy (program, "( ");
842 strcat (program, oprogram);
843 strcat (program, " ) 2>&1");
845 if (mine->pipe) abort();
848 if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
853 ws.ws_col = SCREEN_COLS;
854 ws.ws_row = SCREEN_ROWS;
855 ws.ws_xpixel = ws.ws_col * 6;
856 ws.ws_ypixel = ws.ws_row * 8;
859 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
862 sprintf (buf, "%.100s: forkpty", progname);
867 /* This is the child fork. */
870 if (putenv("TERM=vt100"))
877 sprintf (buf, "%.100s: %.100s", progname, oprogram);
883 /* This is the parent fork. */
884 mine->pipe = fdopen(fd, "r+");
886 XtAppAddInput (app, fileno (mine->pipe),
887 (XtPointer) (XtInputReadMask | XtInputExceptMask),
888 subproc_cb, (XtPointer) mine);
892 # endif /* HAVE_FORKPTY */
894 if ((mine->pipe = popen (program, "r")))
896 if (mine->pipe_id) abort();
898 XtAppAddInput (app, fileno (mine->pipe),
899 (XtPointer) (XtInputReadMask | XtInputExceptMask),
900 subproc_cb, (XtPointer) mine);
904 sprintf (buf, "%.100s: %.100s", progname, program);
910 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
912 struct terminal_controller_data *mine =
913 (struct terminal_controller_data *) closure;
915 launch_text_generator (mine);
919 terminal_closegen(struct terminal_controller_data *mine)
922 XtRemoveInput (mine->pipe_id);
929 if (mine->timeout_id) {
930 XtRemoveTimeOut(mine->timeout_id);
936 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
938 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
946 if (!mine->input_available_p) return 0;
948 rc=read (fileno (mine->pipe), (void *) buf, n);
949 if (rc>0) mine->lastc=buf[rc-1];
953 terminal_closegen(mine);
955 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
959 /* Set up a timer to re-launch the subproc in a bit. */
961 XtAppAddTimeOut(app, subproc_relaunch_delay,
962 relaunch_generator_timer,
966 mine->input_available_p = False;
972 /* The interpretation of the ModN modifiers is dependent on what keys
973 are bound to them: Mod1 does not necessarily mean "meta". It only
974 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
975 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
976 aren't necessarily the same thing. Icepicks in my forehead!
979 do_icccm_meta_key_stupidity (Display *dpy)
981 unsigned int modbits = 0;
984 XModifierKeymap *modmap = XGetModifierMapping (dpy);
985 for (i = 3; i < 8; i++)
986 for (j = 0; j < modmap->max_keypermod; j++)
988 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
991 if (code == 0) continue;
992 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
993 for (k = 0; k < nsyms; k++)
994 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
995 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
999 XFreeModifiermap (modmap);
1000 # endif /* HAVE_COCOA */
1004 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1007 meta_modifier (Display *dpy)
1009 static Bool done_once = False;
1010 static unsigned int mask = 0;
1013 /* Really, we are supposed to recompute this if a KeymapNotify
1014 event comes in, but fuck it. */
1016 mask = do_icccm_meta_key_stupidity (dpy);
1023 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1025 struct terminal_controller_data *mine =
1026 (struct terminal_controller_data *) data;
1028 unsigned char c = 0;
1029 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1030 if (c == 0 || !mine->pipe)
1033 if (!mine->swap_bs_del_p) ;
1034 else if (c == 127) c = 8;
1035 else if (c == 8) c = 127;
1037 /* If meta was held down, send ESC, or turn on the high bit. */
1038 if (event->xkey.state & meta_modifier (dpy))
1040 if (mine->meta_sends_esc_p)
1041 fputc ('\033', mine->pipe);
1046 fputc (c, mine->pipe);
1047 fflush (mine->pipe);
1049 event->xany.type = 0; /* do not process this event further */
1057 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1058 Bool bold_p, Bool blink_p, Bool rev_p,
1061 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1065 else if ((c >= 'A'+128) || /* upcase and blink */
1066 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1067 c != '\r' && c != '\n' && c!='\t'))
1069 c = (c & 0x1F) | 0x80;
1071 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1076 if (bold_p) c |= 0xc0;
1077 if (blink_p) c = (c & ~0x40) | 0x80;
1078 if (rev_p) c |= 0xc0;
1083 a2_printc_noscroll(st, c);
1088 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1091 apple2_state_t *st=sim->st;
1092 int cols = SCREEN_COLS;
1093 int rows = SCREEN_ROWS;
1098 switch (state->escstate)
1104 /* Dummy case - we don't want the screensaver to beep */
1105 /* #### But maybe this should flash the screen? */
1108 if (state->cursor_x > 0)
1112 if (state->cursor_x < cols - 8)
1114 state->cursor_x = (state->cursor_x & ~7) + 8;
1118 state->cursor_x = 0;
1119 if (state->cursor_y < rows - 1)
1128 if (state->cursor_y < rows - 1)
1134 state->cursor_x = 0;
1138 /* Dummy case - there is one and only one font. */
1142 /* Dummy case - these interrupt escape sequences, so
1143 they don't do anything in this state */
1146 state->escstate = 1;
1149 /* Dummy case - this is supposed to be ignored */
1152 state->escstate = 2;
1153 for(i = 0; i < NPAR; i++)
1154 state->csiparam[i] = 0;
1155 state->curparam = 0;
1158 /* If the cursor is in column 39 and we print a character, then
1159 that character shows up in column 39, and the cursor is no longer
1160 visible on the screen (it's in "column 40".) If another character
1161 is printed, then that character shows up in column 0, and the
1162 cursor moves to column 1.
1164 This is empirically what xterm and gnome-terminal do, so that must
1165 be the right thing. (In xterm, the cursor vanishes, whereas; in
1166 gnome-terminal, the cursor overprints the character in col 39.)
1168 if (state->cursor_x >= cols)
1170 state->cursor_x = 0;
1171 if (state->cursor_y >= rows - 1)
1177 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1178 a2_ascii_printc (st, c,
1179 state->termattrib.bf.bold,
1180 state->termattrib.bf.blink,
1181 state->termattrib.bf.rev,
1193 state->escstate = 0;
1195 case 'c': /* Reset */
1197 state->escstate = 0;
1199 case 'D': /* Linefeed */
1200 if (state->cursor_y < rows - 1)
1204 state->escstate = 0;
1206 case 'E': /* Newline */
1207 state->cursor_x = 0;
1208 state->escstate = 0;
1210 case 'M': /* Reverse newline */
1211 if (state->cursor_y > 0)
1213 state->escstate = 0;
1215 case '7': /* Save state */
1216 state->saved_x = state->cursor_x;
1217 state->saved_y = state->cursor_y;
1218 state->escstate = 0;
1220 case '8': /* Restore state */
1221 state->cursor_x = state->saved_x;
1222 state->cursor_y = state->saved_y;
1223 state->escstate = 0;
1226 state->escstate = 2;
1227 for(i = 0; i < NPAR; i++)
1228 state->csiparam[i] = 0;
1229 state->curparam = 0;
1231 case '%': /* Select charset */
1232 /* No, I don't support UTF-8, since the apple2 font
1233 isn't even Unicode anyway. We must still catch the
1234 last byte, though. */
1237 /* I don't support different fonts either - see above
1239 state->escstate = 3;
1242 /* Escape sequences not supported:
1245 * Z - Terminal identification
1247 * = - Other keypad change
1250 state->escstate = 0;
1259 state->escstate = 0;
1261 case '0': case '1': case '2': case '3': case '4':
1262 case '5': case '6': case '7': case '8': case '9':
1263 if (state->curparam < NPAR)
1264 state->csiparam[state->curparam] =
1265 (state->csiparam[state->curparam] * 10) + (c - '0');
1268 state->csiparam[++state->curparam] = 0;
1271 state->escstate = 3;
1274 for (i = 0; i < state->csiparam[0]; i++)
1276 if(++state->cursor_x > cols)
1278 state->cursor_x = 0;
1279 if (state->cursor_y < rows - 1)
1285 state->escstate = 0;
1288 state->cursor_x = 0;
1290 if (state->csiparam[0] == 0)
1291 state->csiparam[0] = 1;
1292 if ((state->cursor_y -= state->csiparam[0]) < 0)
1293 state->cursor_y = 0;
1294 state->escstate = 0;
1297 state->cursor_x = 0;
1300 if (state->csiparam[0] == 0)
1301 state->csiparam[0] = 1;
1302 if ((state->cursor_y += state->csiparam[0]) >= rows)
1303 state->cursor_y = rows - 1;
1304 state->escstate = 0;
1308 if (state->csiparam[0] == 0)
1309 state->csiparam[0] = 1;
1310 if ((state->cursor_x += state->csiparam[0]) >= cols)
1311 state->cursor_x = cols - 1;
1312 state->escstate = 0;
1315 if (state->csiparam[0] == 0)
1316 state->csiparam[0] = 1;
1317 if ((state->cursor_x -= state->csiparam[0]) < 0)
1318 state->cursor_x = 0;
1319 state->escstate = 0;
1322 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1323 state->cursor_y = rows - 1;
1324 state->escstate = 0;
1328 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1329 state->cursor_x = cols - 1;
1330 state->escstate = 0;
1334 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1335 state->cursor_y = rows - 1;
1336 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1337 state->cursor_x = cols - 1;
1338 if(state->cursor_y < 0)
1339 state->cursor_y = 0;
1340 if(state->cursor_x < 0)
1341 state->cursor_x = 0;
1342 state->escstate = 0;
1347 if (state->csiparam[0] == 0)
1348 start = cols * state->cursor_y + state->cursor_x;
1349 if (state->csiparam[0] == 1)
1350 end = cols * state->cursor_y + state->cursor_x;
1352 a2_goto(st, state->cursor_y, state->cursor_x);
1353 for (i = start; i < end; i++)
1355 a2_ascii_printc(st, ' ', False, False, False, False);
1357 state->escstate = 0;
1362 if (state->csiparam[0] == 0)
1363 start = state->cursor_x;
1364 if (state->csiparam[1] == 1)
1365 end = 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;
1374 case 'm': /* Set attributes */
1375 for (i = 0; i <= state->curparam; i++)
1377 switch(state->csiparam[i])
1380 state->termattrib.w = 0;
1383 state->termattrib.bf.bold = 1;
1386 state->termattrib.bf.blink = 1;
1389 state->termattrib.bf.rev = 1;
1393 state->termattrib.bf.bold = 0;
1396 state->termattrib.bf.blink = 0;
1399 state->termattrib.bf.rev = 0;
1403 state->escstate = 0;
1405 case 's': /* Save position */
1406 state->saved_x = state->cursor_x;
1407 state->saved_y = state->cursor_y;
1408 state->escstate = 0;
1410 case 'u': /* Restore position */
1411 state->cursor_x = state->saved_x;
1412 state->cursor_y = state->saved_y;
1413 state->escstate = 0;
1415 case '?': /* DEC Private modes */
1416 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1417 state->escstate = 0;
1420 /* Known unsupported CSIs:
1422 * L - Insert blank lines
1423 * M - Delete lines (I don't know what this means...)
1424 * P - Delete characters
1425 * X - Erase characters (difference with P being...?)
1426 * c - Terminal identification
1427 * g - Clear tab stop(s)
1428 * h - Set mode (Mainly due to its complexity and lack of good
1431 * m - Set mode (Phosphor is, per defenition, green on black)
1433 * q - Set keyboard LEDs
1434 * r - Set scrolling region (too exhausting - noone uses this,
1437 state->escstate = 0;
1442 state->escstate = 0;
1445 a2_goto(st, state->cursor_y, state->cursor_x);
1450 It's fun to put things like "gdb" as the command. For one, it's
1451 amusing how the standard mumble (version, no warranty, it's
1452 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1456 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1458 apple2_state_t *st=sim->st;
1462 struct terminal_controller_data *mine;
1463 if (!sim->controller_data)
1464 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1465 mine=(struct terminal_controller_data *) sim->controller_data;
1466 mine->dpy = sim->dpy;
1468 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1470 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1472 mine->fast_p = get_boolean_resource (mine->dpy, "fast", "Boolean");
1478 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1482 a2_prints(st, "APPLE ][");
1487 launch_text_generator(mine);
1490 *next_actiontime += 4.0;
1496 unsigned char buf[1024];
1500 elapsed=sim->curtime - mine->last_emit_time;
1501 mine->last_emit_time=sim->curtime;
1503 if (elapsed>1.0) nwant=1;
1504 if (nwant<1) nwant=1;
1505 if (nwant>4) nwant=4;
1508 nwant = sizeof(buf)-1;
1510 nr=terminal_read(mine, buf, nwant);
1511 for (i=0; i<nr; i++) {
1515 a2_vt100_printc (sim, mine, c);
1517 a2_ascii_printc (st, c, False, False, False, True);
1522 case A2CONTROLLER_FREE:
1523 terminal_closegen(mine);
1529 struct basic_controller_data {
1532 const char * const * progtext;
1536 double prog_start_time;
1537 char error_buf[256];
1541 Adding more programs is easy. Just add a listing here and to all_programs,
1542 then add the state machine to actually execute it to basic_controller.
1544 static const char * const moire_program[]={
1546 "20 FOR Y = 0 TO 190 STEP 2\n",
1547 "30 HCOLOR=4 : REM BLACK\n",
1548 "40 HPLOT 0,191-Y TO 279,Y\n",
1549 "60 HCOLOR=7 : REM WHITE\n",
1550 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1552 "100 FOR X = 0 TO 278 STEP 3\n",
1554 "120 HPLOT 279-X,0 TO X,191\n",
1556 "150 HPLOT 278-X,0 TO X+1,191\n",
1561 static const char * const sinewave_program[] = {
1564 "30 FOR X = 0 TO 279\n",
1566 "35 HPLOT X,0 TO X,159\n",
1568 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1577 static const char * const dumb_program[]={
1578 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1584 static const char * const random_lores_program[]={
1585 "1 REM APPLE ][ SCREEN SAVER\n",
1587 "100 COLOR= RND(1)*16\n",
1589 "110 X=RND(1)*40\n",
1590 "120 Y1=RND(1)*40\n",
1591 "130 Y2=RND(1)*40\n",
1592 "140 FOR Y = Y1 TO Y2\n",
1596 "210 Y=RND(1)*40\n",
1597 "220 X1=RND(1)*40\n",
1598 "230 X2=RND(1)*40\n",
1599 "240 FOR X = X1 TO X2\n",
1607 static char typo_map[256];
1609 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1669 strcpy(out_buf, orig);
1670 for (i=0; out_buf[i]; i++) {
1671 char *p = out_buf+i;
1673 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1676 if (isalpha(p[0]) &&
1685 sprintf(err_buf,"?SYNTAX ERROR\n");
1689 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1690 int remain=strlen(p);
1691 int past=random()%(remain-2)+1;
1692 memmove(p+past+past, p, remain+1);
1694 for (j=0; j<past; j++) {
1703 static const struct {
1704 const char * const * progtext;
1707 {moire_program, 100},
1708 /*{dumb_program, 200}, */
1709 {sinewave_program, 400},
1710 {random_lores_program, 500},
1714 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1716 apple2_state_t *st=sim->st;
1719 struct basic_controller_data *mine;
1720 if (!sim->controller_data)
1721 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1722 mine=(struct basic_controller_data *) sim->controller_data;
1729 a2_prints(st, "APPLE ][");
1732 sim->typing_rate=0.2;
1734 i=random()%countof(all_programs);
1735 mine->progtext=all_programs[i].progtext;
1736 mine->progstep=all_programs[i].progstep;
1739 *next_actiontime += 1.0;
1744 if (st->cursx==0) a2_printc(st,']');
1745 if (mine->progtext[mine->prog_line]) {
1746 if (random()%4==0) {
1747 int err=make_typo(sim->typing_buf,
1748 mine->progtext[mine->prog_line],
1750 sim->typing=sim->typing_buf;
1757 sim->typing=mine->progtext[mine->prog_line++];
1765 sim->printing=mine->error_buf;
1770 if (st->cursx==0) a2_printc(st,']');
1771 *next_actiontime+=1.0;
1776 sim->typing="RUN\n";
1780 mine->prog_start_time=*next_actiontime;
1781 *stepno=mine->progstep;
1786 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1787 for (i=0; i<24 && mine->y<192; i++)
1789 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1790 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1800 for (i=0; i<24 && mine->x<280; i++)
1802 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1803 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1806 if (mine->x >= 280) *stepno=120;
1810 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1815 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1816 for (i=0; i<30; i++) {
1817 a2_prints(st, mine->rep_str);
1823 i=random()%strlen(mine->rep_str);
1824 while (mine->rep_pos != i) {
1825 a2_printc(st, mine->rep_str[mine->rep_pos]);
1827 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1829 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1832 /* sinewave_program */
1834 st->gr_mode=A2_GR_HIRES;
1839 for (i=0; i<48; i++) {
1840 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1841 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1842 a2_hplot(st, 3, mine->x, y);
1849 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1857 /* random_lores_program */
1859 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1864 for (i=0; i<10; i++) {
1865 int color,x,y,x1,x2,y1,y2;
1871 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1876 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1878 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1885 case A2CONTROLLER_FREE:
1892 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1893 double *next_actiontime) = {
1894 slideshow_controller,
1895 terminal_controller,
1905 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1910 apple2_init (Display *dpy, Window window)
1912 struct state *st = (struct state *) calloc (1, sizeof(*st));
1915 st->duration = get_integer_resource (dpy, "duration", "Integer");
1918 if (st->duration < 1) st->duration = 1;
1920 s = get_string_resource (dpy, "mode", "Mode");
1921 if (!s || !*s || !strcasecmp(s, "random"))
1922 st->random_p = True;
1923 else if (!strcasecmp(s, "text"))
1924 st->controller = terminal_controller;
1925 else if (!strcasecmp(s, "slideshow"))
1926 st->controller = slideshow_controller;
1927 else if (!strcasecmp(s, "basic"))
1928 st->controller = basic_controller;
1931 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1937 if (! st->random_p) {
1938 if (st->controller == terminal_controller ||
1939 st->controller == slideshow_controller)
1940 st->duration = 999999; /* these run "forever" */
1946 static unsigned long
1947 apple2_draw (Display *dpy, Window window, void *closure)
1949 struct state *st = (struct state *) closure;
1953 st->controller = controllers[random() % (countof(controllers))];
1954 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1957 if (! apple2_one_frame (st->sim)) {
1965 apple2_reshape (Display *dpy, Window window, void *closure,
1966 unsigned int w, unsigned int h)
1968 struct state *st = (struct state *) closure;
1969 analogtv_reconfigure (st->sim->dec);
1973 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1975 struct state *st = (struct state *) closure;
1977 if (st->controller == terminal_controller &&
1978 event->xany.type == KeyPress) {
1979 terminal_keypress_handler (dpy, event, st->sim->controller_data);
1987 apple2_free (Display *dpy, Window window, void *closure)
1989 struct state *st = (struct state *) closure;
1991 st->sim->stepno = A2CONTROLLER_DONE;
1992 if (apple2_one_frame (st->sim))
1993 abort(); /* should have freed! */
1999 XSCREENSAVER_MODULE ("Apple2", apple2)