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/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);
573 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
575 a2_dither (buf32, buf8, w, h);
579 mine->image_loading_p = False;
580 mine->img_filename = (name ? strdup (name) : 0);
581 mine->render_img = buf8;
586 static const char *apple2_defaults [] = {
587 ".background: black",
588 ".foreground: white",
591 "*program: xscreensaver-text --cols 40",
592 "*metaSendsESC: True",
599 # endif /* !HAVE_FORKPTY */
605 static XrmOptionDescRec apple2_options [] = {
606 { "-mode", ".mode", XrmoptionSepArg, 0 },
607 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
608 { "-basic", ".mode", XrmoptionNoArg, "basic" },
609 { "-text", ".mode", XrmoptionNoArg, "text" },
610 { "-program", ".program", XrmoptionSepArg, 0 },
611 { "-duration", ".duration", XrmoptionSepArg, 0 },
612 { "-pty", ".usePty", XrmoptionNoArg, "True" },
613 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
614 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
615 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
616 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
617 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
618 { "-fast", ".fast", XrmoptionNoArg, "True" },
624 TODO: this should load 10 images at startup time, then cycle through them
625 to avoid the pause while it loads.
628 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
629 double *next_actiontime)
631 apple2_state_t *st=sim->st;
633 slideshow_data *mine;
635 if (!sim->controller_data)
636 sim->controller_data = calloc (1, sizeof(*mine));
637 mine = (slideshow_data *) sim->controller_data;
645 sim->typing_rate = 0.3;
646 sim->dec->powerup=0.0;
649 a2_prints(st, "APPLE ][");
658 XWindowAttributes xgwa;
660 XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
661 p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height,
663 mine->image_loading_p = True;
664 load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
666 /* pause with a blank screen for a bit, while the image loads in the
668 *next_actiontime += 2.0;
674 if (! mine->image_loading_p) { /* image is finally loaded */
680 *next_actiontime += 3.0;
695 st->gr_mode=A2_GR_HIRES;
696 if (mine->img_filename) {
697 char *basename, *tmp;
700 basename = tmp = strdup (mine->img_filename);
703 char *slash = strchr(basename, '/');
704 if (!slash || !slash[1]) break;
708 char *dot=strchr(basename,'.');
711 if (strlen(basename)>20) basename[20]=0;
712 for (s=basename; *s; s++) {
714 if (*s <= ' ') *s = '_';
716 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
717 sim->typing = sim->typing_buf;
721 sim->typing = "BLOAD IMAGE\n";
723 mine->render_img_lineno=0;
729 *next_actiontime += 0.7;
734 if (mine->render_img_lineno>=192) {
736 sim->typing="POKE 49234,0\n";
741 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
742 a2_display_image_loading(st, mine->render_img,
743 mine->render_img_lineno++);
746 /* The disk would have to seek every 13 sectors == 78 lines.
747 (This ain't no newfangled 16-sector operating system) */
748 if ((mine->render_img_lineno%78)==0) {
749 *next_actiontime += 0.5;
751 *next_actiontime += 0.08;
756 st->gr_mode |= A2_GR_FULL;
758 /* Note that sim->delay is sometimes "infinite" in this controller.
759 These images are kinda dull anyway, so don't leave it on too long. */
760 *next_actiontime += 2;
765 sim->typing="POKE 49235,0\n";
771 st->gr_mode &= ~A2_GR_FULL;
772 if (mine->render_img) {
773 free(mine->render_img);
774 mine->render_img=NULL;
776 if (mine->img_filename) {
777 free(mine->img_filename);
778 mine->img_filename=NULL;
783 case A2CONTROLLER_FREE:
784 free(mine->render_img);
785 free(mine->img_filename);
794 struct terminal_controller_data {
799 int input_available_p;
800 XtIntervalId timeout_id;
804 double last_emit_time;
805 XComposeStatus compose;
810 int cursor_x, cursor_y;
811 int saved_x, saved_y;
814 unsigned int bold : 1;
815 unsigned int blink : 1;
816 unsigned int rev : 1;
820 Bool meta_sends_esc_p;
827 subproc_cb (XtPointer closure, int *source, XtInputId *id)
829 struct terminal_controller_data *mine =
830 (struct terminal_controller_data *) closure;
831 mine->input_available_p = True;
835 launch_text_generator (struct terminal_controller_data *mine)
837 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
839 char *oprogram = get_string_resource (mine->dpy, "program", "Program");
840 char *program = (char *) malloc (strlen (oprogram) + 10);
842 strcpy (program, "( ");
843 strcat (program, oprogram);
844 strcat (program, " ) 2>&1");
846 if (mine->pipe) abort();
849 if (get_boolean_resource (mine->dpy, "usePty", "Boolean"))
854 ws.ws_col = SCREEN_COLS;
855 ws.ws_row = SCREEN_ROWS;
856 ws.ws_xpixel = ws.ws_col * 6;
857 ws.ws_ypixel = ws.ws_row * 8;
860 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
863 sprintf (buf, "%.100s: forkpty", progname);
868 /* This is the child fork. */
871 if (putenv("TERM=vt100"))
878 sprintf (buf, "%.100s: %.100s", progname, oprogram);
884 /* This is the parent fork. */
885 mine->pipe = fdopen(fd, "r+");
887 XtAppAddInput (app, fileno (mine->pipe),
888 (XtPointer) (XtInputReadMask | XtInputExceptMask),
889 subproc_cb, (XtPointer) mine);
893 # endif /* HAVE_FORKPTY */
895 if ((mine->pipe = popen (program, "r")))
897 if (mine->pipe_id) abort();
899 XtAppAddInput (app, fileno (mine->pipe),
900 (XtPointer) (XtInputReadMask | XtInputExceptMask),
901 subproc_cb, (XtPointer) mine);
905 sprintf (buf, "%.100s: %.100s", progname, program);
911 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
913 struct terminal_controller_data *mine =
914 (struct terminal_controller_data *) closure;
916 launch_text_generator (mine);
920 terminal_closegen(struct terminal_controller_data *mine)
923 XtRemoveInput (mine->pipe_id);
930 if (mine->timeout_id) {
931 XtRemoveTimeOut(mine->timeout_id);
937 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
939 XtAppContext app = XtDisplayToApplicationContext (mine->dpy);
947 if (!mine->input_available_p) return 0;
949 rc=read (fileno (mine->pipe), (void *) buf, n);
950 if (rc>0) mine->lastc=buf[rc-1];
954 terminal_closegen(mine);
956 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
960 /* Set up a timer to re-launch the subproc in a bit. */
962 XtAppAddTimeOut(app, subproc_relaunch_delay,
963 relaunch_generator_timer,
967 mine->input_available_p = False;
973 /* The interpretation of the ModN modifiers is dependent on what keys
974 are bound to them: Mod1 does not necessarily mean "meta". It only
975 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
976 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
977 aren't necessarily the same thing. Icepicks in my forehead!
980 do_icccm_meta_key_stupidity (Display *dpy)
982 unsigned int modbits = 0;
985 XModifierKeymap *modmap = XGetModifierMapping (dpy);
986 for (i = 3; i < 8; i++)
987 for (j = 0; j < modmap->max_keypermod; j++)
989 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
992 if (code == 0) continue;
993 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
994 for (k = 0; k < nsyms; k++)
995 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
996 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
1000 XFreeModifiermap (modmap);
1001 # endif /* HAVE_COCOA */
1005 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1008 meta_modifier (Display *dpy)
1010 static Bool done_once = False;
1011 static unsigned int mask = 0;
1014 /* Really, we are supposed to recompute this if a KeymapNotify
1015 event comes in, but fuck it. */
1017 mask = do_icccm_meta_key_stupidity (dpy);
1024 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1026 struct terminal_controller_data *mine =
1027 (struct terminal_controller_data *) data;
1029 unsigned char c = 0;
1030 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1031 if (c == 0 || !mine->pipe)
1034 if (!mine->swap_bs_del_p) ;
1035 else if (c == 127) c = 8;
1036 else if (c == 8) c = 127;
1038 /* If meta was held down, send ESC, or turn on the high bit. */
1039 if (event->xkey.state & meta_modifier (dpy))
1041 if (mine->meta_sends_esc_p)
1042 fputc ('\033', mine->pipe);
1047 fputc (c, mine->pipe);
1048 fflush (mine->pipe);
1050 event->xany.type = 0; /* do not process this event further */
1058 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1059 Bool bold_p, Bool blink_p, Bool rev_p,
1062 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1066 else if ((c >= 'A'+128) || /* upcase and blink */
1067 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1068 c != '\r' && c != '\n' && c!='\t'))
1070 c = (c & 0x1F) | 0x80;
1072 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1077 if (bold_p) c |= 0xc0;
1078 if (blink_p) c = (c & ~0x40) | 0x80;
1079 if (rev_p) c |= 0xc0;
1084 a2_printc_noscroll(st, c);
1089 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1092 apple2_state_t *st=sim->st;
1093 int cols = SCREEN_COLS;
1094 int rows = SCREEN_ROWS;
1099 switch (state->escstate)
1105 /* Dummy case - we don't want the screensaver to beep */
1106 /* #### But maybe this should flash the screen? */
1109 if (state->cursor_x > 0)
1113 if (state->cursor_x < cols - 8)
1115 state->cursor_x = (state->cursor_x & ~7) + 8;
1119 state->cursor_x = 0;
1120 if (state->cursor_y < rows - 1)
1129 if (state->cursor_y < rows - 1)
1135 state->cursor_x = 0;
1139 /* Dummy case - there is one and only one font. */
1143 /* Dummy case - these interrupt escape sequences, so
1144 they don't do anything in this state */
1147 state->escstate = 1;
1150 /* Dummy case - this is supposed to be ignored */
1153 state->escstate = 2;
1154 for(i = 0; i < NPAR; i++)
1155 state->csiparam[i] = 0;
1156 state->curparam = 0;
1159 /* If the cursor is in column 39 and we print a character, then
1160 that character shows up in column 39, and the cursor is no longer
1161 visible on the screen (it's in "column 40".) If another character
1162 is printed, then that character shows up in column 0, and the
1163 cursor moves to column 1.
1165 This is empirically what xterm and gnome-terminal do, so that must
1166 be the right thing. (In xterm, the cursor vanishes, whereas; in
1167 gnome-terminal, the cursor overprints the character in col 39.)
1169 if (state->cursor_x >= cols)
1171 state->cursor_x = 0;
1172 if (state->cursor_y >= rows - 1)
1178 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1179 a2_ascii_printc (st, c,
1180 state->termattrib.bf.bold,
1181 state->termattrib.bf.blink,
1182 state->termattrib.bf.rev,
1194 state->escstate = 0;
1196 case 'c': /* Reset */
1198 state->escstate = 0;
1200 case 'D': /* Linefeed */
1201 if (state->cursor_y < rows - 1)
1205 state->escstate = 0;
1207 case 'E': /* Newline */
1208 state->cursor_x = 0;
1209 state->escstate = 0;
1211 case 'M': /* Reverse newline */
1212 if (state->cursor_y > 0)
1214 state->escstate = 0;
1216 case '7': /* Save state */
1217 state->saved_x = state->cursor_x;
1218 state->saved_y = state->cursor_y;
1219 state->escstate = 0;
1221 case '8': /* Restore state */
1222 state->cursor_x = state->saved_x;
1223 state->cursor_y = state->saved_y;
1224 state->escstate = 0;
1227 state->escstate = 2;
1228 for(i = 0; i < NPAR; i++)
1229 state->csiparam[i] = 0;
1230 state->curparam = 0;
1232 case '%': /* Select charset */
1233 /* No, I don't support UTF-8, since the apple2 font
1234 isn't even Unicode anyway. We must still catch the
1235 last byte, though. */
1238 /* I don't support different fonts either - see above
1240 state->escstate = 3;
1243 /* Escape sequences not supported:
1246 * Z - Terminal identification
1248 * = - Other keypad change
1251 state->escstate = 0;
1260 state->escstate = 0;
1262 case '0': case '1': case '2': case '3': case '4':
1263 case '5': case '6': case '7': case '8': case '9':
1264 if (state->curparam < NPAR)
1265 state->csiparam[state->curparam] =
1266 (state->csiparam[state->curparam] * 10) + (c - '0');
1269 state->csiparam[++state->curparam] = 0;
1272 state->escstate = 3;
1275 for (i = 0; i < state->csiparam[0]; i++)
1277 if(++state->cursor_x > cols)
1279 state->cursor_x = 0;
1280 if (state->cursor_y < rows - 1)
1286 state->escstate = 0;
1289 state->cursor_x = 0;
1291 if (state->csiparam[0] == 0)
1292 state->csiparam[0] = 1;
1293 if ((state->cursor_y -= state->csiparam[0]) < 0)
1294 state->cursor_y = 0;
1295 state->escstate = 0;
1298 state->cursor_x = 0;
1301 if (state->csiparam[0] == 0)
1302 state->csiparam[0] = 1;
1303 if ((state->cursor_y += state->csiparam[0]) >= rows)
1304 state->cursor_y = rows - 1;
1305 state->escstate = 0;
1309 if (state->csiparam[0] == 0)
1310 state->csiparam[0] = 1;
1311 if ((state->cursor_x += state->csiparam[0]) >= cols)
1312 state->cursor_x = cols - 1;
1313 state->escstate = 0;
1316 if (state->csiparam[0] == 0)
1317 state->csiparam[0] = 1;
1318 if ((state->cursor_x -= state->csiparam[0]) < 0)
1319 state->cursor_x = 0;
1320 state->escstate = 0;
1323 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1324 state->cursor_y = rows - 1;
1325 state->escstate = 0;
1329 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1330 state->cursor_x = cols - 1;
1331 state->escstate = 0;
1335 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1336 state->cursor_y = rows - 1;
1337 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1338 state->cursor_x = cols - 1;
1339 if(state->cursor_y < 0)
1340 state->cursor_y = 0;
1341 if(state->cursor_x < 0)
1342 state->cursor_x = 0;
1343 state->escstate = 0;
1348 if (state->csiparam[0] == 0)
1349 start = cols * state->cursor_y + state->cursor_x;
1350 if (state->csiparam[0] == 1)
1351 end = cols * state->cursor_y + state->cursor_x;
1353 a2_goto(st, state->cursor_y, state->cursor_x);
1354 for (i = start; i < end; i++)
1356 a2_ascii_printc(st, ' ', False, False, False, False);
1358 state->escstate = 0;
1363 if (state->csiparam[0] == 0)
1364 start = state->cursor_x;
1365 if (state->csiparam[1] == 1)
1366 end = state->cursor_x;
1368 a2_goto(st, state->cursor_y, state->cursor_x);
1369 for (i = start; i < end; i++)
1371 a2_ascii_printc(st, ' ', False, False, False, False);
1373 state->escstate = 0;
1375 case 'm': /* Set attributes */
1376 for (i = 0; i <= state->curparam; i++)
1378 switch(state->csiparam[i])
1381 state->termattrib.w = 0;
1384 state->termattrib.bf.bold = 1;
1387 state->termattrib.bf.blink = 1;
1390 state->termattrib.bf.rev = 1;
1394 state->termattrib.bf.bold = 0;
1397 state->termattrib.bf.blink = 0;
1400 state->termattrib.bf.rev = 0;
1404 state->escstate = 0;
1406 case 's': /* Save position */
1407 state->saved_x = state->cursor_x;
1408 state->saved_y = state->cursor_y;
1409 state->escstate = 0;
1411 case 'u': /* Restore position */
1412 state->cursor_x = state->saved_x;
1413 state->cursor_y = state->saved_y;
1414 state->escstate = 0;
1416 case '?': /* DEC Private modes */
1417 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1418 state->escstate = 0;
1421 /* Known unsupported CSIs:
1423 * L - Insert blank lines
1424 * M - Delete lines (I don't know what this means...)
1425 * P - Delete characters
1426 * X - Erase characters (difference with P being...?)
1427 * c - Terminal identification
1428 * g - Clear tab stop(s)
1429 * h - Set mode (Mainly due to its complexity and lack of good
1432 * m - Set mode (Phosphor is, per defenition, green on black)
1434 * q - Set keyboard LEDs
1435 * r - Set scrolling region (too exhausting - noone uses this,
1438 state->escstate = 0;
1443 state->escstate = 0;
1446 a2_goto(st, state->cursor_y, state->cursor_x);
1451 It's fun to put things like "gdb" as the command. For one, it's
1452 amusing how the standard mumble (version, no warranty, it's
1453 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1457 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1459 apple2_state_t *st=sim->st;
1463 struct terminal_controller_data *mine;
1464 if (!sim->controller_data)
1465 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1466 mine=(struct terminal_controller_data *) sim->controller_data;
1467 mine->dpy = sim->dpy;
1469 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1471 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1473 mine->fast_p = get_boolean_resource (mine->dpy, "fast", "Boolean");
1479 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1483 a2_prints(st, "APPLE ][");
1488 launch_text_generator(mine);
1491 *next_actiontime += 4.0;
1497 unsigned char buf[1024];
1501 elapsed=sim->curtime - mine->last_emit_time;
1502 mine->last_emit_time=sim->curtime;
1504 if (elapsed>1.0) nwant=1;
1505 if (nwant<1) nwant=1;
1506 if (nwant>4) nwant=4;
1509 nwant = sizeof(buf)-1;
1511 nr=terminal_read(mine, buf, nwant);
1512 for (i=0; i<nr; i++) {
1516 a2_vt100_printc (sim, mine, c);
1518 a2_ascii_printc (st, c, False, False, False, True);
1523 case A2CONTROLLER_FREE:
1524 terminal_closegen(mine);
1530 struct basic_controller_data {
1533 const char * const * progtext;
1537 double prog_start_time;
1538 char error_buf[256];
1542 Adding more programs is easy. Just add a listing here and to all_programs,
1543 then add the state machine to actually execute it to basic_controller.
1545 static const char * const moire_program[]={
1547 "20 FOR Y = 0 TO 190 STEP 2\n",
1548 "30 HCOLOR=4 : REM BLACK\n",
1549 "40 HPLOT 0,191-Y TO 279,Y\n",
1550 "60 HCOLOR=7 : REM WHITE\n",
1551 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1553 "100 FOR X = 0 TO 278 STEP 3\n",
1555 "120 HPLOT 279-X,0 TO X,191\n",
1557 "150 HPLOT 278-X,0 TO X+1,191\n",
1562 static const char * const sinewave_program[] = {
1565 "30 FOR X = 0 TO 279\n",
1567 "35 HPLOT X,0 TO X,159\n",
1569 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1578 static const char * const dumb_program[]={
1579 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1585 static const char * const random_lores_program[]={
1586 "1 REM APPLE ][ SCREEN SAVER\n",
1588 "100 COLOR= RND(1)*16\n",
1590 "110 X=RND(1)*40\n",
1591 "120 Y1=RND(1)*40\n",
1592 "130 Y2=RND(1)*40\n",
1593 "140 FOR Y = Y1 TO Y2\n",
1597 "210 Y=RND(1)*40\n",
1598 "220 X1=RND(1)*40\n",
1599 "230 X2=RND(1)*40\n",
1600 "240 FOR X = X1 TO X2\n",
1608 static char typo_map[256];
1610 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1670 strcpy(out_buf, orig);
1671 for (i=0; out_buf[i]; i++) {
1672 char *p = out_buf+i;
1674 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1677 if (isalpha(p[0]) &&
1686 sprintf(err_buf,"?SYNTAX ERROR\n");
1690 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1691 int remain=strlen(p);
1692 int past=random()%(remain-2)+1;
1693 memmove(p+past+past, p, remain+1);
1695 for (j=0; j<past; j++) {
1704 static const struct {
1705 const char * const * progtext;
1708 {moire_program, 100},
1709 /*{dumb_program, 200}, */
1710 {sinewave_program, 400},
1711 {random_lores_program, 500},
1715 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1717 apple2_state_t *st=sim->st;
1720 struct basic_controller_data *mine;
1721 if (!sim->controller_data)
1722 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1723 mine=(struct basic_controller_data *) sim->controller_data;
1730 a2_prints(st, "APPLE ][");
1733 sim->typing_rate=0.2;
1735 i=random()%countof(all_programs);
1736 mine->progtext=all_programs[i].progtext;
1737 mine->progstep=all_programs[i].progstep;
1740 *next_actiontime += 1.0;
1745 if (st->cursx==0) a2_printc(st,']');
1746 if (mine->progtext[mine->prog_line]) {
1747 if (random()%4==0) {
1748 int err=make_typo(sim->typing_buf,
1749 mine->progtext[mine->prog_line],
1751 sim->typing=sim->typing_buf;
1758 sim->typing=mine->progtext[mine->prog_line++];
1766 sim->printing=mine->error_buf;
1771 if (st->cursx==0) a2_printc(st,']');
1772 *next_actiontime+=1.0;
1777 sim->typing="RUN\n";
1781 mine->prog_start_time=*next_actiontime;
1782 *stepno=mine->progstep;
1787 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1788 for (i=0; i<24 && mine->y<192; i++)
1790 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1791 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1801 for (i=0; i<24 && mine->x<280; i++)
1803 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1804 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1807 if (mine->x >= 280) *stepno=120;
1811 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1816 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1817 for (i=0; i<30; i++) {
1818 a2_prints(st, mine->rep_str);
1824 i=random()%strlen(mine->rep_str);
1825 while (mine->rep_pos != i) {
1826 a2_printc(st, mine->rep_str[mine->rep_pos]);
1828 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1830 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1833 /* sinewave_program */
1835 st->gr_mode=A2_GR_HIRES;
1840 for (i=0; i<48; i++) {
1841 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1842 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1843 a2_hplot(st, 3, mine->x, y);
1850 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1858 /* random_lores_program */
1860 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1865 for (i=0; i<10; i++) {
1866 int color,x,y,x1,x2,y1,y2;
1872 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1877 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1879 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1886 case A2CONTROLLER_FREE:
1893 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1894 double *next_actiontime) = {
1895 slideshow_controller,
1896 terminal_controller,
1906 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1911 apple2_init (Display *dpy, Window window)
1913 struct state *st = (struct state *) calloc (1, sizeof(*st));
1916 st->duration = get_integer_resource (dpy, "duration", "Integer");
1919 if (st->duration < 1) st->duration = 1;
1921 s = get_string_resource (dpy, "mode", "Mode");
1922 if (!s || !*s || !strcasecmp(s, "random"))
1923 st->random_p = True;
1924 else if (!strcasecmp(s, "text"))
1925 st->controller = terminal_controller;
1926 else if (!strcasecmp(s, "slideshow"))
1927 st->controller = slideshow_controller;
1928 else if (!strcasecmp(s, "basic"))
1929 st->controller = basic_controller;
1932 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1938 if (! st->random_p) {
1939 if (st->controller == terminal_controller ||
1940 st->controller == slideshow_controller)
1941 st->duration = 999999; /* these run "forever" */
1947 static unsigned long
1948 apple2_draw (Display *dpy, Window window, void *closure)
1950 struct state *st = (struct state *) closure;
1954 st->controller = controllers[random() % (countof(controllers))];
1955 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1958 if (! apple2_one_frame (st->sim)) {
1966 apple2_reshape (Display *dpy, Window window, void *closure,
1967 unsigned int w, unsigned int h)
1969 struct state *st = (struct state *) closure;
1970 analogtv_reconfigure (st->sim->dec);
1974 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1976 struct state *st = (struct state *) closure;
1978 if (st->controller == terminal_controller &&
1979 event->xany.type == KeyPress) {
1980 terminal_keypress_handler (dpy, event, st->sim->controller_data);
1988 apple2_free (Display *dpy, Window window, void *closure)
1990 struct state *st = (struct state *) closure;
1992 st->sim->stepno = A2CONTROLLER_DONE;
1993 if (apple2_one_frame (st->sim))
1994 abort(); /* should have freed! */
2000 XSCREENSAVER_MODULE ("Apple2", apple2)