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);
948 !mine->input_available_p ||
952 rc=read (fileno (mine->pipe), (void *) buf, n);
953 if (rc>0) mine->lastc=buf[rc-1];
957 terminal_closegen(mine);
959 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
963 /* Set up a timer to re-launch the subproc in a bit. */
965 XtAppAddTimeOut(app, subproc_relaunch_delay,
966 relaunch_generator_timer,
970 mine->input_available_p = False;
976 /* The interpretation of the ModN modifiers is dependent on what keys
977 are bound to them: Mod1 does not necessarily mean "meta". It only
978 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
979 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
980 aren't necessarily the same thing. Icepicks in my forehead!
983 do_icccm_meta_key_stupidity (Display *dpy)
985 unsigned int modbits = 0;
988 XModifierKeymap *modmap = XGetModifierMapping (dpy);
989 for (i = 3; i < 8; i++)
990 for (j = 0; j < modmap->max_keypermod; j++)
992 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
995 if (code == 0) continue;
996 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
997 for (k = 0; k < nsyms; k++)
998 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
999 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
1000 modbits |= (1 << i);
1003 XFreeModifiermap (modmap);
1004 # endif /* HAVE_COCOA */
1008 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
1011 meta_modifier (Display *dpy)
1013 static Bool done_once = False;
1014 static unsigned int mask = 0;
1017 /* Really, we are supposed to recompute this if a KeymapNotify
1018 event comes in, but fuck it. */
1020 mask = do_icccm_meta_key_stupidity (dpy);
1027 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
1029 struct terminal_controller_data *mine =
1030 (struct terminal_controller_data *) data;
1032 unsigned char c = 0;
1033 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
1034 if (c == 0 || !mine->pipe)
1037 if (!mine->swap_bs_del_p) ;
1038 else if (c == 127) c = 8;
1039 else if (c == 8) c = 127;
1041 /* If meta was held down, send ESC, or turn on the high bit. */
1042 if (event->xkey.state & meta_modifier (dpy))
1044 if (mine->meta_sends_esc_p)
1045 fputc ('\033', mine->pipe);
1050 fputc (c, mine->pipe);
1051 fflush (mine->pipe);
1053 event->xany.type = 0; /* do not process this event further */
1061 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1062 Bool bold_p, Bool blink_p, Bool rev_p,
1065 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1069 else if ((c >= 'A'+128) || /* upcase and blink */
1070 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1071 c != '\r' && c != '\n' && c!='\t'))
1073 c = (c & 0x1F) | 0x80;
1075 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1080 if (bold_p) c |= 0xc0;
1081 if (blink_p) c = (c & ~0x40) | 0x80;
1082 if (rev_p) c |= 0xc0;
1087 a2_printc_noscroll(st, c);
1092 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1095 apple2_state_t *st=sim->st;
1096 int cols = SCREEN_COLS;
1097 int rows = SCREEN_ROWS;
1102 switch (state->escstate)
1108 /* Dummy case - we don't want the screensaver to beep */
1109 /* #### But maybe this should flash the screen? */
1112 if (state->cursor_x > 0)
1116 if (state->cursor_x < cols - 8)
1118 state->cursor_x = (state->cursor_x & ~7) + 8;
1122 state->cursor_x = 0;
1123 if (state->cursor_y < rows - 1)
1132 if (state->cursor_y < rows - 1)
1138 state->cursor_x = 0;
1142 /* Dummy case - there is one and only one font. */
1146 /* Dummy case - these interrupt escape sequences, so
1147 they don't do anything in this state */
1150 state->escstate = 1;
1153 /* Dummy case - this is supposed to be ignored */
1156 state->escstate = 2;
1157 for(i = 0; i < NPAR; i++)
1158 state->csiparam[i] = 0;
1159 state->curparam = 0;
1162 /* If the cursor is in column 39 and we print a character, then
1163 that character shows up in column 39, and the cursor is no longer
1164 visible on the screen (it's in "column 40".) If another character
1165 is printed, then that character shows up in column 0, and the
1166 cursor moves to column 1.
1168 This is empirically what xterm and gnome-terminal do, so that must
1169 be the right thing. (In xterm, the cursor vanishes, whereas; in
1170 gnome-terminal, the cursor overprints the character in col 39.)
1172 if (state->cursor_x >= cols)
1174 state->cursor_x = 0;
1175 if (state->cursor_y >= rows - 1)
1181 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1182 a2_ascii_printc (st, c,
1183 state->termattrib.bf.bold,
1184 state->termattrib.bf.blink,
1185 state->termattrib.bf.rev,
1197 state->escstate = 0;
1199 case 'c': /* Reset */
1201 state->escstate = 0;
1203 case 'D': /* Linefeed */
1204 if (state->cursor_y < rows - 1)
1208 state->escstate = 0;
1210 case 'E': /* Newline */
1211 state->cursor_x = 0;
1212 state->escstate = 0;
1214 case 'M': /* Reverse newline */
1215 if (state->cursor_y > 0)
1217 state->escstate = 0;
1219 case '7': /* Save state */
1220 state->saved_x = state->cursor_x;
1221 state->saved_y = state->cursor_y;
1222 state->escstate = 0;
1224 case '8': /* Restore state */
1225 state->cursor_x = state->saved_x;
1226 state->cursor_y = state->saved_y;
1227 state->escstate = 0;
1230 state->escstate = 2;
1231 for(i = 0; i < NPAR; i++)
1232 state->csiparam[i] = 0;
1233 state->curparam = 0;
1235 case '%': /* Select charset */
1236 /* No, I don't support UTF-8, since the apple2 font
1237 isn't even Unicode anyway. We must still catch the
1238 last byte, though. */
1241 /* I don't support different fonts either - see above
1243 state->escstate = 3;
1246 /* Escape sequences not supported:
1249 * Z - Terminal identification
1251 * = - Other keypad change
1254 state->escstate = 0;
1263 state->escstate = 0;
1265 case '0': case '1': case '2': case '3': case '4':
1266 case '5': case '6': case '7': case '8': case '9':
1267 if (state->curparam < NPAR)
1268 state->csiparam[state->curparam] =
1269 (state->csiparam[state->curparam] * 10) + (c - '0');
1272 state->csiparam[++state->curparam] = 0;
1275 state->escstate = 3;
1278 for (i = 0; i < state->csiparam[0]; i++)
1280 if(++state->cursor_x > cols)
1282 state->cursor_x = 0;
1283 if (state->cursor_y < rows - 1)
1289 state->escstate = 0;
1292 state->cursor_x = 0;
1294 if (state->csiparam[0] == 0)
1295 state->csiparam[0] = 1;
1296 if ((state->cursor_y -= state->csiparam[0]) < 0)
1297 state->cursor_y = 0;
1298 state->escstate = 0;
1301 state->cursor_x = 0;
1304 if (state->csiparam[0] == 0)
1305 state->csiparam[0] = 1;
1306 if ((state->cursor_y += state->csiparam[0]) >= rows)
1307 state->cursor_y = rows - 1;
1308 state->escstate = 0;
1312 if (state->csiparam[0] == 0)
1313 state->csiparam[0] = 1;
1314 if ((state->cursor_x += state->csiparam[0]) >= cols)
1315 state->cursor_x = cols - 1;
1316 state->escstate = 0;
1319 if (state->csiparam[0] == 0)
1320 state->csiparam[0] = 1;
1321 if ((state->cursor_x -= state->csiparam[0]) < 0)
1322 state->cursor_x = 0;
1323 state->escstate = 0;
1326 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1327 state->cursor_y = rows - 1;
1328 state->escstate = 0;
1332 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1333 state->cursor_x = cols - 1;
1334 state->escstate = 0;
1338 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1339 state->cursor_y = rows - 1;
1340 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1341 state->cursor_x = cols - 1;
1342 if(state->cursor_y < 0)
1343 state->cursor_y = 0;
1344 if(state->cursor_x < 0)
1345 state->cursor_x = 0;
1346 state->escstate = 0;
1351 if (state->csiparam[0] == 0)
1352 start = cols * state->cursor_y + state->cursor_x;
1353 if (state->csiparam[0] == 1)
1354 end = cols * state->cursor_y + state->cursor_x;
1356 a2_goto(st, state->cursor_y, state->cursor_x);
1357 for (i = start; i < end; i++)
1359 a2_ascii_printc(st, ' ', False, False, False, False);
1361 state->escstate = 0;
1366 if (state->csiparam[0] == 0)
1367 start = state->cursor_x;
1368 if (state->csiparam[1] == 1)
1369 end = state->cursor_x;
1371 a2_goto(st, state->cursor_y, state->cursor_x);
1372 for (i = start; i < end; i++)
1374 a2_ascii_printc(st, ' ', False, False, False, False);
1376 state->escstate = 0;
1378 case 'm': /* Set attributes */
1379 for (i = 0; i <= state->curparam; i++)
1381 switch(state->csiparam[i])
1384 state->termattrib.w = 0;
1387 state->termattrib.bf.bold = 1;
1390 state->termattrib.bf.blink = 1;
1393 state->termattrib.bf.rev = 1;
1397 state->termattrib.bf.bold = 0;
1400 state->termattrib.bf.blink = 0;
1403 state->termattrib.bf.rev = 0;
1407 state->escstate = 0;
1409 case 's': /* Save position */
1410 state->saved_x = state->cursor_x;
1411 state->saved_y = state->cursor_y;
1412 state->escstate = 0;
1414 case 'u': /* Restore position */
1415 state->cursor_x = state->saved_x;
1416 state->cursor_y = state->saved_y;
1417 state->escstate = 0;
1419 case '?': /* DEC Private modes */
1420 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1421 state->escstate = 0;
1424 /* Known unsupported CSIs:
1426 * L - Insert blank lines
1427 * M - Delete lines (I don't know what this means...)
1428 * P - Delete characters
1429 * X - Erase characters (difference with P being...?)
1430 * c - Terminal identification
1431 * g - Clear tab stop(s)
1432 * h - Set mode (Mainly due to its complexity and lack of good
1435 * m - Set mode (Phosphor is, per defenition, green on black)
1437 * q - Set keyboard LEDs
1438 * r - Set scrolling region (too exhausting - noone uses this,
1441 state->escstate = 0;
1446 state->escstate = 0;
1449 a2_goto(st, state->cursor_y, state->cursor_x);
1454 It's fun to put things like "gdb" as the command. For one, it's
1455 amusing how the standard mumble (version, no warranty, it's
1456 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1460 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1462 apple2_state_t *st=sim->st;
1466 struct terminal_controller_data *mine;
1467 if (!sim->controller_data)
1468 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1469 mine=(struct terminal_controller_data *) sim->controller_data;
1470 mine->dpy = sim->dpy;
1472 mine->meta_sends_esc_p = get_boolean_resource (mine->dpy, "metaSendsESC",
1474 mine->swap_bs_del_p = get_boolean_resource (mine->dpy, "swapBSDEL",
1476 mine->fast_p = get_boolean_resource (mine->dpy, "fast", "Boolean");
1482 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1486 a2_prints(st, "APPLE ][");
1491 launch_text_generator(mine);
1494 *next_actiontime += 4.0;
1500 unsigned char buf[1024];
1504 elapsed=sim->curtime - mine->last_emit_time;
1505 mine->last_emit_time=sim->curtime;
1507 if (elapsed>1.0) nwant=1;
1508 if (nwant<1) nwant=1;
1509 if (nwant>4) nwant=4;
1512 nwant = sizeof(buf)-1;
1514 nr=terminal_read(mine, buf, nwant);
1515 for (i=0; i<nr; i++) {
1519 a2_vt100_printc (sim, mine, c);
1521 a2_ascii_printc (st, c, False, False, False, True);
1526 case A2CONTROLLER_FREE:
1527 terminal_closegen(mine);
1533 struct basic_controller_data {
1536 const char * const * progtext;
1540 double prog_start_time;
1541 char error_buf[256];
1545 Adding more programs is easy. Just add a listing here and to all_programs,
1546 then add the state machine to actually execute it to basic_controller.
1548 static const char * const moire_program[]={
1550 "20 FOR Y = 0 TO 190 STEP 2\n",
1551 "30 HCOLOR=4 : REM BLACK\n",
1552 "40 HPLOT 0,191-Y TO 279,Y\n",
1553 "60 HCOLOR=7 : REM WHITE\n",
1554 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1556 "100 FOR X = 0 TO 278 STEP 3\n",
1558 "120 HPLOT 279-X,0 TO X,191\n",
1560 "150 HPLOT 278-X,0 TO X+1,191\n",
1565 static const char * const sinewave_program[] = {
1568 "30 FOR X = 0 TO 279\n",
1570 "35 HPLOT X,0 TO X,159\n",
1572 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1581 static const char * const dumb_program[]={
1582 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1588 static const char * const random_lores_program[]={
1589 "1 REM APPLE ][ SCREEN SAVER\n",
1591 "100 COLOR= RND(1)*16\n",
1593 "110 X=RND(1)*40\n",
1594 "120 Y1=RND(1)*40\n",
1595 "130 Y2=RND(1)*40\n",
1596 "140 FOR Y = Y1 TO Y2\n",
1600 "210 Y=RND(1)*40\n",
1601 "220 X1=RND(1)*40\n",
1602 "230 X2=RND(1)*40\n",
1603 "240 FOR X = X1 TO X2\n",
1611 static char typo_map[256];
1613 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1673 strcpy(out_buf, orig);
1674 for (i=0; out_buf[i]; i++) {
1675 char *p = out_buf+i;
1677 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1680 if (isalpha(p[0]) &&
1689 sprintf(err_buf,"?SYNTAX ERROR\n");
1693 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1694 int remain=strlen(p);
1695 int past=random()%(remain-2)+1;
1696 memmove(p+past+past, p, remain+1);
1698 for (j=0; j<past; j++) {
1707 static const struct {
1708 const char * const * progtext;
1711 {moire_program, 100},
1712 /*{dumb_program, 200}, */
1713 {sinewave_program, 400},
1714 {random_lores_program, 500},
1718 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1720 apple2_state_t *st=sim->st;
1723 struct basic_controller_data *mine;
1724 if (!sim->controller_data)
1725 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1726 mine=(struct basic_controller_data *) sim->controller_data;
1733 a2_prints(st, "APPLE ][");
1736 sim->typing_rate=0.2;
1738 i=random()%countof(all_programs);
1739 mine->progtext=all_programs[i].progtext;
1740 mine->progstep=all_programs[i].progstep;
1743 *next_actiontime += 1.0;
1748 if (st->cursx==0) a2_printc(st,']');
1749 if (mine->progtext[mine->prog_line]) {
1750 if (random()%4==0) {
1751 int err=make_typo(sim->typing_buf,
1752 mine->progtext[mine->prog_line],
1754 sim->typing=sim->typing_buf;
1761 sim->typing=mine->progtext[mine->prog_line++];
1769 sim->printing=mine->error_buf;
1774 if (st->cursx==0) a2_printc(st,']');
1775 *next_actiontime+=1.0;
1780 sim->typing="RUN\n";
1784 mine->prog_start_time=*next_actiontime;
1785 *stepno=mine->progstep;
1790 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1791 for (i=0; i<24 && mine->y<192; i++)
1793 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1794 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1804 for (i=0; i<24 && mine->x<280; i++)
1806 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1807 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1810 if (mine->x >= 280) *stepno=120;
1814 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1819 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1820 for (i=0; i<30; i++) {
1821 a2_prints(st, mine->rep_str);
1827 i=random()%strlen(mine->rep_str);
1828 while (mine->rep_pos != i) {
1829 a2_printc(st, mine->rep_str[mine->rep_pos]);
1831 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1833 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1836 /* sinewave_program */
1838 st->gr_mode=A2_GR_HIRES;
1843 for (i=0; i<48; i++) {
1844 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1845 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1846 a2_hplot(st, 3, mine->x, y);
1853 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1861 /* random_lores_program */
1863 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1868 for (i=0; i<10; i++) {
1869 int color,x,y,x1,x2,y1,y2;
1875 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1880 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1882 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1889 case A2CONTROLLER_FREE:
1896 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1897 double *next_actiontime) = {
1898 slideshow_controller,
1899 terminal_controller,
1909 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1914 apple2_init (Display *dpy, Window window)
1916 struct state *st = (struct state *) calloc (1, sizeof(*st));
1919 st->duration = get_integer_resource (dpy, "duration", "Integer");
1922 if (st->duration < 1) st->duration = 1;
1924 s = get_string_resource (dpy, "mode", "Mode");
1925 if (!s || !*s || !strcasecmp(s, "random"))
1926 st->random_p = True;
1927 else if (!strcasecmp(s, "text"))
1928 st->controller = terminal_controller;
1929 else if (!strcasecmp(s, "slideshow"))
1930 st->controller = slideshow_controller;
1931 else if (!strcasecmp(s, "basic"))
1932 st->controller = basic_controller;
1935 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1941 if (! st->random_p) {
1942 if (st->controller == terminal_controller ||
1943 st->controller == slideshow_controller)
1944 st->duration = 999999; /* these run "forever" */
1950 static unsigned long
1951 apple2_draw (Display *dpy, Window window, void *closure)
1953 struct state *st = (struct state *) closure;
1957 st->controller = controllers[random() % (countof(controllers))];
1958 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1961 if (! apple2_one_frame (st->sim)) {
1969 apple2_reshape (Display *dpy, Window window, void *closure,
1970 unsigned int w, unsigned int h)
1972 struct state *st = (struct state *) closure;
1973 analogtv_reconfigure (st->sim->dec);
1977 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1979 struct state *st = (struct state *) closure;
1981 if (st->controller == terminal_controller &&
1982 event->xany.type == KeyPress) {
1983 terminal_keypress_handler (dpy, event, st->sim->controller_data);
1991 apple2_free (Display *dpy, Window window, void *closure)
1993 struct state *st = (struct state *) closure;
1995 st->sim->stepno = A2CONTROLLER_DONE;
1996 if (apple2_one_frame (st->sim))
1997 abort(); /* should have freed! */
2003 XSCREENSAVER_MODULE ("Apple2", apple2)