1 /* xscreensaver, Copyright (c) 1998-2004 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 #include "screenhack.h"
20 #include <X11/Xutil.h>
21 #include <X11/Intrinsic.h>
24 #include <X11/keysymdef.h>
33 #endif /* HAVE_FORKPTY */
36 #define countof(x) (sizeof((x))/sizeof((*x)))
38 #define SCREEN_COLS 40
39 #define SCREEN_ROWS 24
43 extern XtAppContext app;
45 Time subproc_relaunch_delay = 3000;
48 /* Given a bitmask, returns the position and width of the field.
51 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
54 for (i = 0; i < 32; i++)
59 for (; i < 32; i++, j++)
60 if (! (mask & (1L << i)))
68 /* Given a value and a field-width, expands the field to fill out 8 bits.
71 spread_bits (unsigned char value, unsigned char width)
76 case 7: return (value << 1) | (value >> 6);
77 case 6: return (value << 2) | (value >> 4);
78 case 5: return (value << 3) | (value >> 2);
79 case 4: return (value << 4) | (value);
80 case 3: return (value << 5) | (value << 2) | (value >> 2);
81 case 2: return (value << 6) | (value << 4) | (value);
82 default: abort(); break;
87 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
88 Scales it (without dithering) to WxH.
91 scale_image (Display *dpy, Window window, XImage *in,
92 int fromx, int fromy, int fromw, int fromh,
93 unsigned int *out, int w, int h)
97 unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
98 unsigned int rsiz=0, gsiz=0, bsiz=0;
99 unsigned int rmsk=0, gmsk=0, bmsk=0;
100 unsigned char spread_map[3][256];
101 XWindowAttributes xgwa;
104 if (fromx + fromw > in->width ||
105 fromy + fromh > in->height)
108 XGetWindowAttributes (dpy, window, &xgwa);
110 /* Compute the field offsets for RGB decoding in the XImage,
111 when in TrueColor mode. Otherwise we use the colormap.
113 if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
114 visual_class (xgwa.screen, xgwa.visual) == GrayScale)
116 int ncolors = visual_cells (xgwa.screen, xgwa.visual);
117 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
118 for (i = 0; i < ncolors; i++)
120 XQueryColors (dpy, xgwa.colormap, colors, ncolors);
124 rmsk = xgwa.visual->red_mask;
125 gmsk = xgwa.visual->green_mask;
126 bmsk = xgwa.visual->blue_mask;
127 decode_mask (rmsk, &rpos, &rsiz);
128 decode_mask (gmsk, &gpos, &gsiz);
129 decode_mask (bmsk, &bpos, &bsiz);
131 for (i = 0; i < 256; i++)
133 spread_map[0][i] = spread_bits (i, rsiz);
134 spread_map[1][i] = spread_bits (i, gsiz);
135 spread_map[2][i] = spread_bits (i, bsiz);
139 scale = (fromw > fromh
141 : (float) fromh / h);
143 /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
145 for (y = 0; y < h-1; y++) /* iterate over dest pixels */
146 for (x = 0; x < w-1; x++)
149 unsigned int r=0, g=0, b=0;
151 int xx1 = x * scale + fromx;
152 int yy1 = y * scale + fromy;
153 int xx2 = (x+1) * scale + fromx;
154 int yy2 = (y+1) * scale + fromy;
156 /* Iterate over the source pixels contributing to this one, and sum. */
157 for (xx = xx1; xx < xx2; xx++)
158 for (yy = yy1; yy < yy2; yy++)
160 unsigned char rr, gg, bb;
161 unsigned long sp = ((xx > in->width || yy > in->height)
162 ? 0 : XGetPixel (in, xx, yy));
165 rr = colors[sp].red & 0xFF;
166 gg = colors[sp].green & 0xFF;
167 bb = colors[sp].blue & 0xFF;
171 rr = (sp & rmsk) >> rpos;
172 gg = (sp & gmsk) >> gpos;
173 bb = (sp & bmsk) >> bpos;
174 rr = spread_map[0][rr];
175 gg = spread_map[1][gg];
176 bb = spread_map[2][bb];
183 /* Scale summed pixel values down to 8/8/8 range */
184 i = (xx2 - xx1) * (yy2 - yy1);
190 out[y * w + x] = (r << 16) | (g << 8) | b;
195 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
196 Picks a random sub-image out of the source image, and scales it to WxH.
199 pick_a2_subimage (Display *dpy, Window window, XImage *in,
200 unsigned int *out, int w, int h)
202 int fromx, fromy, fromw, fromh;
203 if (in->width <= w || in->height <= h)
214 double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
217 } while (fromw > in->width ||
220 dw = (in->width - fromw) / 2; /* near the center! */
221 dh = (in->height - fromh) / 2;
223 fromx = (random() % dw) + (dw/2);
224 fromy = (random() % dh) + (dh/2);
227 scale_image (dpy, window, in,
228 fromx, fromy, fromw, fromh,
233 /* Floyd-Steinberg dither. Derived from ppmquant.c,
234 Copyright (c) 1989, 1991 by Jef Poskanzer.
237 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
240 Apple ][ color map. Each pixel can only be 1 or 0, but what that
241 means depends on whether it's an odd or even pixel, and whether
242 the high bit in the byte is set or not. If it's 0, it's always
245 static const int a2_cmap[2][2][3] = {
248 {/* odd pixels = blue */ 0x00, 0x80, 0xff},
249 {/* even pixels = red */ 0xff, 0x80, 0x00}
253 {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
254 {/* odd pixels = green */ 0x40, 0xff, 0x40}
259 unsigned int **pixels;
275 FILE *pipe = popen ("xv -", "w");
276 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
277 for (y = 0; y < h; y++)
278 for (x = 0; x < w; x++)
280 unsigned int p = in[y * w + x];
281 unsigned int r = (p >> 16) & 0xFF;
282 unsigned int g = (p >> 8) & 0xFF;
283 unsigned int b = (p ) & 0xFF;
284 fprintf(pipe, "%c%c%c", r, g, b);
290 /* Initialize Floyd-Steinberg error vectors. */
291 this_rerr = (long *) calloc (w + 2, sizeof(long));
292 next_rerr = (long *) calloc (w + 2, sizeof(long));
293 this_gerr = (long *) calloc (w + 2, sizeof(long));
294 next_gerr = (long *) calloc (w + 2, sizeof(long));
295 this_berr = (long *) calloc (w + 2, sizeof(long));
296 next_berr = (long *) calloc (w + 2, sizeof(long));
299 /* #### do we really need more than one element of "pixels" at once?
301 pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
302 for (y = 0; y < h; y++)
303 pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
305 for (x = 0; x < w + 2; ++x)
307 this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
308 this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
309 this_berr[x] = random() % (fs_scale * 2) - fs_scale;
310 /* (random errors in [-1 .. 1]) */
314 for (y = 0; y < h; y++)
315 for (x = 0; x < w; x++)
316 pixels[y][x] = in[y * w + x];
318 for (y = 0; y < h; y++)
324 for (x = 0; x < w + 2; x++)
325 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
327 /* It's too complicated to go back and forth on alternate rows,
328 so we always go left-right here. It doesn't change the result
331 For each group of 7 pixels, we have to try it both with the
332 high bit=0 and =1. For each high bit value, we add up the
333 total error and pick the best one.
335 Because we have to go through each group of bits twice, we
336 don't propagate the error values through this_[rgb]err since
337 it would add them twice. So we keep seperate local_[rgb]err
338 variables for propagating error within the 7-pixel group.
342 for (xbyte=0; xbyte<280; xbyte+=7)
345 int best_error=2000000000;
349 int local_rerr=0, local_gerr=0, local_berr=0;
351 for (hibit=0; hibit<2; hibit++)
356 for (x=xbyte; x<xbyte+7; x++)
360 /* Use Floyd-Steinberg errors to adjust actual color. */
361 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
362 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
363 sb = ((pP[x] ) & 0xFF) * brightness/256;
364 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
365 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
366 sb += (this_berr[x + 1] + local_berr) / fs_scale;
369 else if (sr > maxval) sr = maxval;
371 else if (sg > maxval) sg = maxval;
373 else if (sb > maxval) sb = maxval;
375 /* This is the color we'd get if we set the bit 1. For 0,
377 r2=a2_cmap[hibit][x&1][0];
378 g2=a2_cmap[hibit][x&1][1];
379 b2=a2_cmap[hibit][x&1][2];
382 dist0 and dist1 are the error (Minkowski 2-metric
383 distances in the color space) for choosing 0 and
384 1 respectively. 0 is black, 1 is the color r2,g2,b2.
386 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
387 dist0= sr*sr + sg*sg + sb*sb;
391 byte |= 1 << (x-xbyte);
394 /* Wanted sr but got r2, so propagate sr-r2 */
395 local_rerr = (sr - r2) * fs_scale * 7/16;
396 local_gerr = (sg - g2) * fs_scale * 7/16;
397 local_berr = (sb - b2) * fs_scale * 7/16;
403 /* Wanted sr but got 0, so propagate sr */
404 local_rerr = sr * fs_scale * 7/16;
405 local_gerr = sg * fs_scale * 7/16;
406 local_berr = sb * fs_scale * 7/16;
410 if (tot_error < best_error)
413 best_error = tot_error;
417 /* Avoid alternating 7f and ff in all-white areas, because it makes
418 regular pink vertical lines */
419 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
424 Now that we've chosen values for all 8 bits of the byte, we
425 have to fill in the real pixel values into pP and propagate
426 all the error terms. We end up repeating a lot of the code
430 for (x=xbyte; x<xbyte+7; x++)
432 int bit=(best_byte>>(x-xbyte))&1;
433 hibit=(best_byte>>7)&1;
435 sr = (pP[x] >> 16) & 0xFF;
436 sg = (pP[x] >> 8) & 0xFF;
437 sb = (pP[x] ) & 0xFF;
438 sr += this_rerr[x + 1] / fs_scale;
439 sg += this_gerr[x + 1] / fs_scale;
440 sb += this_berr[x + 1] / fs_scale;
443 else if (sr > maxval) sr = maxval;
445 else if (sg > maxval) sg = maxval;
447 else if (sb > maxval) sb = maxval;
449 r2=a2_cmap[hibit][x&1][0] * bit;
450 g2=a2_cmap[hibit][x&1][1] * bit;
451 b2=a2_cmap[hibit][x&1][2] * bit;
453 pP[x] = (r2<<16) | (g2<<8) | (b2);
455 /* Propagate Floyd-Steinberg error terms. */
456 err = (sr - r2) * fs_scale;
457 this_rerr[x + 2] += (err * 7) / 16;
458 next_rerr[x ] += (err * 3) / 16;
459 next_rerr[x + 1] += (err * 5) / 16;
460 next_rerr[x + 2] += (err ) / 16;
461 err = (sg - g2) * fs_scale;
462 this_gerr[x + 2] += (err * 7) / 16;
463 next_gerr[x ] += (err * 3) / 16;
464 next_gerr[x + 1] += (err * 5) / 16;
465 next_gerr[x + 2] += (err ) / 16;
466 err = (sb - b2) * fs_scale;
467 this_berr[x + 2] += (err * 7) / 16;
468 next_berr[x ] += (err * 3) / 16;
469 next_berr[x + 1] += (err * 5) / 16;
470 next_berr[x + 2] += (err ) / 16;
474 And put the actual byte into out.
477 out[y*(w/7) + xbyte/7] = best_byte;
481 temp_err = this_rerr;
482 this_rerr = next_rerr;
483 next_rerr = temp_err;
484 temp_err = this_gerr;
485 this_gerr = next_gerr;
486 next_gerr = temp_err;
487 temp_err = this_berr;
488 this_berr = next_berr;
489 next_berr = temp_err;
505 /* let's see what we got... */
506 FILE *pipe = popen ("xv -", "w");
507 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
508 for (y = 0; y < h; y++)
509 for (x = 0; x < w; x++)
511 unsigned int r = (pixels[y][x]>>16)&0xff;
512 unsigned int g = (pixels[y][x]>>8)&0xff;
513 unsigned int b = (pixels[y][x]>>0)&0xff;
514 fprintf(pipe, "%c%c%c", r, g, b);
522 static unsigned char *
523 load_image (Display *dpy, Window window, char **image_filename_r)
525 XWindowAttributes xgwa;
531 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
532 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
536 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
540 XGetWindowAttributes (dpy, window, &xgwa);
541 p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
542 load_random_image (xgwa.screen, window, p, image_filename_r);
543 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
544 XFreePixmap (dpy, p);
547 /* Make sure the window's background is not set to None, and get the
548 grabbed bits (if any) off it as soon as possible. */
549 XSetWindowBackground (dpy, window,
550 get_pixel_resource ("background", "Background",
551 dpy, xgwa.colormap));
552 XClearWindow (dpy, window);
554 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
555 image (regardless of whether it started as TrueColor/PseudoColor.)
557 pick_a2_subimage (dpy, window, image, buf32, w, h);
559 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
561 a2_dither (buf32, buf8, w, h);
568 char *progclass = "Apple2";
570 char *defaults [] = {
571 ".background: black",
572 ".foreground: white",
575 "*metaSendsESC: True",
582 # endif /* !HAVE_FORKPTY */
588 XrmOptionDescRec options [] = {
589 { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" },
590 { "-basic", ".mode", XrmoptionNoArg, "basic" },
591 { "-text", ".mode", XrmoptionNoArg, "text" },
592 { "-program", ".program", XrmoptionSepArg, 0 },
593 { "-duration", ".duration", XrmoptionSepArg, 0 },
594 { "-pty", ".usePty", XrmoptionNoArg, "True" },
595 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
596 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
597 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
598 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
599 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
600 { "-fast", ".fast", XrmoptionNoArg, "True" },
606 TODO: this should load 10 images at startup time, then cycle through them
607 to avoid the pause while it loads.
610 void slideshow_controller(apple2_sim_t *sim, int *stepno,
611 double *next_actiontime)
613 apple2_state_t *st=sim->st;
617 int render_img_lineno;
622 if (!sim->controller_data)
623 sim->controller_data = calloc(sizeof(struct mydata),1);
624 mine=(struct mydata *) sim->controller_data;
632 sim->typing_rate = 0.3;
633 sim->dec->powerup=0.0;
636 a2_prints(st, "APPLE ][");
640 *next_actiontime += 4.0;
644 mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename);
650 *next_actiontime += 3.0;
664 st->gr_mode=A2_GR_HIRES;
665 if (mine->img_filename) {
666 char *basename, *tmp;
669 basename = tmp = strdup (mine->img_filename);
672 char *slash = strchr(basename, '/');
673 if (!slash || !slash[1]) break;
677 char *dot=strchr(basename,'.');
680 if (strlen(basename)>20) basename[20]=0;
681 for (s=basename; *s; s++) *s = toupper (*s);
682 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
683 sim->typing = sim->typing_buf;
687 sim->typing = "BLOAD IMAGE\n";
689 mine->render_img_lineno=0;
695 *next_actiontime += 0.7;
700 if (mine->render_img_lineno>=192) {
702 sim->typing="POKE 49234,0\n";
707 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
708 a2_display_image_loading(st, mine->render_img,
709 mine->render_img_lineno++);
712 /* The disk would have to seek every 13 sectors == 78 lines.
713 (This ain't no newfangled 16-sector operating system) */
714 if ((mine->render_img_lineno%78)==0) {
715 *next_actiontime += 0.5;
717 *next_actiontime += 0.08;
722 st->gr_mode |= A2_GR_FULL;
724 *next_actiontime += sim->delay;
729 sim->typing="POKE 49235,0\n";
735 st->gr_mode &= ~A2_GR_FULL;
736 if (mine->render_img) {
737 free(mine->render_img);
738 mine->render_img=NULL;
740 if (mine->img_filename) {
741 free(mine->img_filename);
742 mine->img_filename=NULL;
747 case A2CONTROLLER_FREE:
748 free(mine->render_img);
749 free(mine->img_filename);
758 struct terminal_controller_data {
762 int input_available_p;
763 XtIntervalId timeout_id;
767 double last_emit_time;
768 XComposeStatus compose;
773 int cursor_x, cursor_y;
774 int saved_x, saved_y;
777 unsigned int bold : 1;
778 unsigned int blink : 1;
779 unsigned int rev : 1;
783 Bool meta_sends_esc_p;
790 subproc_cb (XtPointer closure, int *source, XtInputId *id)
792 struct terminal_controller_data *mine =
793 (struct terminal_controller_data *) closure;
794 mine->input_available_p = True;
798 launch_text_generator (struct terminal_controller_data *mine)
801 char *oprogram = get_string_resource ("program", "Program");
804 if (!oprogram || !*oprogram)
805 oprogram = FORTUNE_PROGRAM;
807 program = (char *) malloc (strlen (oprogram) + 10);
809 strcpy (program, "( ");
810 strcat (program, oprogram);
811 strcat (program, " ) 2>&1");
813 if (mine->pipe) abort();
816 if (get_boolean_resource ("usePty", "Boolean"))
821 ws.ws_col = SCREEN_COLS;
822 ws.ws_row = SCREEN_ROWS;
823 ws.ws_xpixel = ws.ws_col * 6;
824 ws.ws_ypixel = ws.ws_row * 8;
827 if((mine->pid = forkpty(&fd, NULL, NULL, &ws)) < 0)
830 sprintf (buf, "%.100s: forkpty", progname);
835 /* This is the child fork. */
836 if (putenv("TERM=vt100"))
838 execl("/bin/sh", "/bin/sh", "-c", oprogram, NULL);
839 sprintf (buf, "%.100s: %.100s", progname, oprogram);
845 /* This is the parent fork. */
846 mine->pipe = fdopen(fd, "r+");
848 XtAppAddInput (app, fileno (mine->pipe),
849 (XtPointer) (XtInputReadMask | XtInputExceptMask),
850 subproc_cb, (XtPointer) mine);
854 # endif /* HAVE_FORKPTY */
856 if ((mine->pipe = popen (program, "r")))
858 if (mine->pipe_id) abort();
860 XtAppAddInput (app, fileno (mine->pipe),
861 (XtPointer) (XtInputReadMask | XtInputExceptMask),
862 subproc_cb, (XtPointer) mine);
866 sprintf (buf, "%.100s: %.100s", progname, program);
872 relaunch_generator_timer (XtPointer closure, XtIntervalId *id)
874 struct terminal_controller_data *mine =
875 (struct terminal_controller_data *) closure;
877 launch_text_generator (mine);
881 terminal_closegen(struct terminal_controller_data *mine)
884 XtRemoveInput (mine->pipe_id);
891 if (mine->timeout_id) {
892 XtRemoveTimeOut(mine->timeout_id);
898 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
907 if (!mine->input_available_p) return 0;
909 rc=read (fileno (mine->pipe), (void *) buf, n);
910 if (rc>0) mine->lastc=buf[rc-1];
914 terminal_closegen(mine);
916 if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */
920 /* Set up a timer to re-launch the subproc in a bit. */
922 XtAppAddTimeOut(app, subproc_relaunch_delay,
923 relaunch_generator_timer,
927 mine->input_available_p = False;
933 /* The interpretation of the ModN modifiers is dependent on what keys
934 are bound to them: Mod1 does not necessarily mean "meta". It only
935 means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on
936 Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt
937 aren't necessarily the same thing. Icepicks in my forehead!
940 do_icccm_meta_key_stupidity (Display *dpy)
942 unsigned int modbits = 0;
944 XModifierKeymap *modmap = XGetModifierMapping (dpy);
945 for (i = 3; i < 8; i++)
946 for (j = 0; j < modmap->max_keypermod; j++)
948 int code = modmap->modifiermap[i * modmap->max_keypermod + j];
951 if (code == 0) continue;
952 syms = XGetKeyboardMapping (dpy, code, 1, &nsyms);
953 for (k = 0; k < nsyms; k++)
954 if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R ||
955 syms[k] == XK_Alt_L || syms[k] == XK_Alt_R)
959 XFreeModifiermap (modmap);
963 /* Returns a mask of the bit or bits of a KeyPress event that mean "meta".
966 meta_modifier (Display *dpy)
968 static Bool done_once = False;
969 static unsigned int mask = 0;
972 /* Really, we are supposed to recompute this if a KeymapNotify
973 event comes in, but fuck it. */
975 mask = do_icccm_meta_key_stupidity (dpy);
982 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
984 struct terminal_controller_data *mine =
985 (struct terminal_controller_data *) data;
988 XLookupString (&event->xkey, (char *) &c, 1, &keysym, &mine->compose);
989 if (c == 0 || !mine->pipe)
992 if (!mine->swap_bs_del_p) ;
993 else if (c == 127) c = 8;
994 else if (c == 8) c = 127;
996 /* If meta was held down, send ESC, or turn on the high bit. */
997 if (event->xkey.state & meta_modifier (dpy))
999 if (mine->meta_sends_esc_p)
1000 fputc ('\033', mine->pipe);
1005 fputc (c, mine->pipe);
1006 fflush (mine->pipe);
1008 event->xany.type = 0; /* do not process this event further */
1015 a2_ascii_printc (apple2_state_t *st, unsigned char c,
1016 Bool bold_p, Bool blink_p, Bool rev_p,
1019 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
1023 else if ((c >= 'A'+128) || /* upcase and blink */
1024 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
1025 c != '\r' && c != '\n' && c!='\t'))
1027 c = (c & 0x1F) | 0x80;
1029 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
1034 if (bold_p) c |= 0xc0;
1035 if (blink_p) c = (c & ~0x40) | 0x80;
1036 if (rev_p) c |= 0xc0;
1041 a2_printc_noscroll(st, c);
1046 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
1049 apple2_state_t *st=sim->st;
1050 int cols = SCREEN_COLS;
1051 int rows = SCREEN_ROWS;
1056 switch (state->escstate)
1062 /* Dummy case - we don't want the screensaver to beep */
1063 /* #### But maybe this should flash the screen? */
1066 if (state->cursor_x > 0)
1070 if (state->cursor_x < cols - 8)
1072 state->cursor_x = (state->cursor_x & ~7) + 8;
1076 state->cursor_x = 0;
1077 if (state->cursor_y < rows - 1)
1086 if (state->cursor_y < rows - 1)
1092 state->cursor_x = 0;
1096 /* Dummy case - there is one and only one font. */
1100 /* Dummy case - these interrupt escape sequences, so
1101 they don't do anything in this state */
1104 state->escstate = 1;
1107 /* Dummy case - this is supposed to be ignored */
1110 state->escstate = 2;
1111 for(i = 0; i < NPAR; i++)
1112 state->csiparam[i] = 0;
1113 state->curparam = 0;
1116 /* If the cursor is in column 39 and we print a character, then
1117 that character shows up in column 39, and the cursor is no longer
1118 visible on the screen (it's in "column 40".) If another character
1119 is printed, then that character shows up in column 0, and the
1120 cursor moves to column 1.
1122 This is empirically what xterm and gnome-terminal do, so that must
1123 be the right thing. (In xterm, the cursor vanishes, whereas; in
1124 gnome-terminal, the cursor overprints the character in col 39.)
1126 if (state->cursor_x >= cols)
1128 state->cursor_x = 0;
1129 if (state->cursor_y >= rows - 1)
1135 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
1136 a2_ascii_printc (st, c,
1137 state->termattrib.bf.bold,
1138 state->termattrib.bf.blink,
1139 state->termattrib.bf.rev,
1151 state->escstate = 0;
1153 case 'c': /* Reset */
1155 state->escstate = 0;
1157 case 'D': /* Linefeed */
1158 if (state->cursor_y < rows - 1)
1162 state->escstate = 0;
1164 case 'E': /* Newline */
1165 state->cursor_x = 0;
1166 state->escstate = 0;
1168 case 'M': /* Reverse newline */
1169 if (state->cursor_y > 0)
1171 state->escstate = 0;
1173 case '7': /* Save state */
1174 state->saved_x = state->cursor_x;
1175 state->saved_y = state->cursor_y;
1176 state->escstate = 0;
1178 case '8': /* Restore state */
1179 state->cursor_x = state->saved_x;
1180 state->cursor_y = state->saved_y;
1181 state->escstate = 0;
1184 state->escstate = 2;
1185 for(i = 0; i < NPAR; i++)
1186 state->csiparam[i] = 0;
1187 state->curparam = 0;
1189 case '%': /* Select charset */
1190 /* No, I don't support UTF-8, since the apple2 font
1191 isn't even Unicode anyway. We must still catch the
1192 last byte, though. */
1195 /* I don't support different fonts either - see above
1197 state->escstate = 3;
1200 /* Escape sequences not supported:
1203 * Z - Terminal identification
1205 * = - Other keypad change
1208 state->escstate = 0;
1217 state->escstate = 0;
1219 case '0': case '1': case '2': case '3': case '4':
1220 case '5': case '6': case '7': case '8': case '9':
1221 if (state->curparam < NPAR)
1222 state->csiparam[state->curparam] =
1223 (state->csiparam[state->curparam] * 10) + (c - '0');
1226 state->csiparam[++state->curparam] = 0;
1229 state->escstate = 3;
1232 for (i = 0; i < state->csiparam[0]; i++)
1234 if(++state->cursor_x > cols)
1236 state->cursor_x = 0;
1237 if (state->cursor_y < rows - 1)
1243 state->escstate = 0;
1246 state->cursor_x = 0;
1248 if (state->csiparam[0] == 0)
1249 state->csiparam[0] = 1;
1250 if ((state->cursor_y -= state->csiparam[0]) < 0)
1251 state->cursor_y = 0;
1252 state->escstate = 0;
1255 state->cursor_x = 0;
1258 if (state->csiparam[0] == 0)
1259 state->csiparam[0] = 1;
1260 if ((state->cursor_y += state->csiparam[0]) >= rows)
1261 state->cursor_y = rows - 1;
1262 state->escstate = 0;
1266 if (state->csiparam[0] == 0)
1267 state->csiparam[0] = 1;
1268 if ((state->cursor_x += state->csiparam[0]) >= cols)
1269 state->cursor_x = cols - 1;
1270 state->escstate = 0;
1273 if (state->csiparam[0] == 0)
1274 state->csiparam[0] = 1;
1275 if ((state->cursor_x -= state->csiparam[0]) < 0)
1276 state->cursor_x = 0;
1277 state->escstate = 0;
1280 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1281 state->cursor_y = rows - 1;
1282 state->escstate = 0;
1286 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1287 state->cursor_x = cols - 1;
1288 state->escstate = 0;
1292 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1293 state->cursor_y = rows - 1;
1294 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1295 state->cursor_x = cols - 1;
1296 if(state->cursor_y < 0)
1297 state->cursor_y = 0;
1298 if(state->cursor_x < 0)
1299 state->cursor_x = 0;
1300 state->escstate = 0;
1305 if (state->csiparam[0] == 0)
1306 start = cols * state->cursor_y + state->cursor_x;
1307 if (state->csiparam[0] == 1)
1308 end = cols * state->cursor_y + state->cursor_x;
1310 a2_goto(st, state->cursor_y, state->cursor_x);
1311 for (i = start; i < end; i++)
1313 a2_ascii_printc(st, ' ', False, False, False, False);
1315 state->escstate = 0;
1320 if (state->csiparam[0] == 0)
1321 start = state->cursor_x;
1322 if (state->csiparam[1] == 1)
1323 end = state->cursor_x;
1325 a2_goto(st, state->cursor_y, state->cursor_x);
1326 for (i = start; i < end; i++)
1328 a2_ascii_printc(st, ' ', False, False, False, False);
1330 state->escstate = 0;
1332 case 'm': /* Set attributes */
1333 for (i = 0; i <= state->curparam; i++)
1335 switch(state->csiparam[i])
1338 state->termattrib.w = 0;
1341 state->termattrib.bf.bold = 1;
1344 state->termattrib.bf.blink = 1;
1347 state->termattrib.bf.rev = 1;
1351 state->termattrib.bf.bold = 0;
1354 state->termattrib.bf.blink = 0;
1357 state->termattrib.bf.rev = 0;
1361 state->escstate = 0;
1363 case 's': /* Save position */
1364 state->saved_x = state->cursor_x;
1365 state->saved_y = state->cursor_y;
1366 state->escstate = 0;
1368 case 'u': /* Restore position */
1369 state->cursor_x = state->saved_x;
1370 state->cursor_y = state->saved_y;
1371 state->escstate = 0;
1373 case '?': /* DEC Private modes */
1374 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1375 state->escstate = 0;
1378 /* Known unsupported CSIs:
1380 * L - Insert blank lines
1381 * M - Delete lines (I don't know what this means...)
1382 * P - Delete characters
1383 * X - Erase characters (difference with P being...?)
1384 * c - Terminal identification
1385 * g - Clear tab stop(s)
1386 * h - Set mode (Mainly due to its complexity and lack of good
1389 * m - Set mode (Phosphor is, per defenition, green on black)
1391 * q - Set keyboard LEDs
1392 * r - Set scrolling region (too exhausting - noone uses this,
1395 state->escstate = 0;
1400 state->escstate = 0;
1403 a2_goto(st, state->cursor_y, state->cursor_x);
1408 It's fun to put things like "gdb" as the command. For one, it's
1409 amusing how the standard mumble (version, no warranty, it's
1410 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1414 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1416 apple2_state_t *st=sim->st;
1420 struct terminal_controller_data *mine;
1421 if (!sim->controller_data)
1422 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1423 mine=(struct terminal_controller_data *) sim->controller_data;
1425 mine->meta_sends_esc_p = get_boolean_resource ("metaSendsESC", "Boolean");
1426 mine->swap_bs_del_p = get_boolean_resource ("swapBSDEL", "Boolean");
1427 mine->fast_p = get_boolean_resource ("fast", "Boolean");
1429 sim->dec->key_handler = terminal_keypress_handler;
1430 sim->dec->key_data = mine;
1436 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1440 a2_prints(st, "APPLE ][");
1445 launch_text_generator(mine);
1448 *next_actiontime += 4.0;
1454 unsigned char buf[1024];
1458 elapsed=sim->curtime - mine->last_emit_time;
1459 mine->last_emit_time=sim->curtime;
1461 if (elapsed>1.0) nwant=1;
1462 if (nwant<1) nwant=1;
1463 if (nwant>4) nwant=4;
1466 nwant = sizeof(buf)-1;
1468 nr=terminal_read(mine, buf, nwant);
1469 for (i=0; i<nr; i++) {
1473 a2_vt100_printc (sim, mine, c);
1475 a2_ascii_printc (st, c, False, False, False, True);
1480 case A2CONTROLLER_FREE:
1481 terminal_closegen(mine);
1487 struct basic_controller_data {
1494 double prog_start_time;
1495 char error_buf[256];
1499 Adding more programs is easy. Just add a listing here and to all_programs,
1500 then add the state machine to actually execute it to basic_controller.
1502 static char *moire_program[]={
1504 "20 FOR Y = 0 TO 191 STEP 2\n",
1505 "30 HCOLOR=4 : REM BLACK\n",
1506 "40 HLINE 0,191-Y TO 279,Y\n",
1507 "60 HCOLOR=7 : REM WHITE\n",
1508 "80 HLINE 0,190-Y TO 279,Y+1\n",
1510 "100 FOR X = 0 TO 279 STEP 3\n",
1512 "120 HLINE 279-X,0 TO X,192\n",
1514 "150 HLINE 278-X,0 TO X+1,192\n",
1519 static char *sinewave_program[] = {
1522 "30 FOR X = 0 TO 279\n",
1524 "35 HLINE X,0 TO X,159\n",
1526 "40 Y = 80 + SIN(15*(X-K)/279)\n",
1535 static char *dumb_program[]={
1536 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1542 static char *random_lores_program[]={
1543 "1 REM APPLE ][ SCREEN SAVER\n",
1545 "100 COLOR= RND(1)*16\n",
1547 "110 X=RND(1)*40\n",
1548 "120 Y1=RND(1)*48\n",
1549 "130 Y2=RND(1)*48\n",
1550 "140 FOR Y = Y1 TO Y2\n",
1554 "210 Y=RND(1)*48\n",
1555 "220 X1=RND(1)*40\n",
1556 "230 X2=RND(1)*40\n",
1557 "240 FOR X = X1 TO X2\n",
1565 static char typo_map[256];
1567 int make_typo(char *out_buf, char *orig, char *err_buf)
1627 strcpy(out_buf, orig);
1628 for (i=0; out_buf[i]; i++) {
1629 char *p = out_buf+i;
1631 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1634 if (isalpha(p[0]) &&
1643 sprintf(err_buf,"?SYNTAX ERROR\n");
1647 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) {
1648 int remain=strlen(p);
1649 int past=random()%(remain-2)+1;
1650 memmove(p+past+past, p, remain+1);
1652 for (j=0; j<past; j++) {
1665 {moire_program, 100},
1666 /*{dumb_program, 200}, */
1667 {sinewave_program, 400},
1668 {random_lores_program, 500},
1672 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1674 apple2_state_t *st=sim->st;
1677 struct basic_controller_data *mine;
1678 if (!sim->controller_data)
1679 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1680 mine=(struct basic_controller_data *) sim->controller_data;
1687 a2_prints(st, "APPLE ][");
1690 sim->typing_rate=0.2;
1692 i=random()%countof(all_programs);
1693 mine->progtext=all_programs[i].progtext;
1694 mine->progstep=all_programs[i].progstep;
1697 *next_actiontime += 1.0;
1702 if (st->cursx==0) a2_printc(st,']');
1703 if (mine->progtext[mine->prog_line]) {
1704 if (random()%4==0) {
1705 int err=make_typo(sim->typing_buf,
1706 mine->progtext[mine->prog_line],
1708 sim->typing=sim->typing_buf;
1715 sim->typing=mine->progtext[mine->prog_line++];
1723 sim->printing=mine->error_buf;
1728 if (st->cursx==0) a2_printc(st,']');
1729 *next_actiontime+=1.0;
1734 sim->typing="RUN\n";
1738 mine->prog_start_time=*next_actiontime;
1739 *stepno=mine->progstep;
1744 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1745 for (i=0; i<24 && mine->y<192; i++)
1747 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1748 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1758 for (i=0; i<24 && mine->x<280; i++)
1760 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1761 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1764 if (mine->x >= 280) *stepno=120;
1768 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1773 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1774 for (i=0; i<30; i++) {
1775 a2_prints(st, mine->rep_str);
1781 i=random()%strlen(mine->rep_str);
1782 while (mine->rep_pos != i) {
1783 a2_printc(st, mine->rep_str[mine->rep_pos]);
1785 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1787 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1790 /* sinewave_program */
1792 st->gr_mode=A2_GR_HIRES;
1797 for (i=0; i<48; i++) {
1798 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1799 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1800 a2_hplot(st, 3, mine->x, y);
1807 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1815 /* random_lores_program */
1817 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1822 for (i=0; i<10; i++) {
1823 int color,x,y,x1,x2,y1,y2;
1829 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1834 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1836 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1843 case A2CONTROLLER_FREE:
1850 void (*controllers[])(apple2_sim_t *sim, int *stepno,
1851 double *next_actiontime) = {
1852 slideshow_controller,
1853 terminal_controller,
1858 screenhack (Display *dpy, Window window)
1860 int duration = get_integer_resource ("duration", "Integer");
1862 void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime);
1864 if (duration < 1) duration = 1;
1866 s = get_string_resource ("mode", "Mode");
1867 if (!s || !*s || !strcasecmp(s, "random"))
1868 controller = controllers[random() % (countof(controllers))];
1869 else if (!strcasecmp(s, "text"))
1870 controller = terminal_controller;
1871 else if (!strcasecmp(s, "slideshow"))
1872 controller = slideshow_controller;
1873 else if (!strcasecmp(s, "basic"))
1874 controller = basic_controller;
1877 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1882 if (!get_boolean_resource ("root", "Boolean"))
1884 XWindowAttributes xgwa;
1885 XGetWindowAttributes (dpy, window, &xgwa);
1886 XSelectInput (dpy, window,
1887 xgwa.your_event_mask |
1888 KeyPressMask | ButtonPressMask | ExposureMask);
1891 apple2 (dpy, window, duration, controller);