1 /* xscreensaver, Copyright (c) 1998-2014 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 */
27 #include "screenhack.h"
29 #include "textclient.h"
32 #define countof(x) (sizeof((x))/sizeof((*x)))
34 #define SCREEN_COLS 40
35 #define SCREEN_ROWS 24
40 /* Given a bitmask, returns the position and width of the field.
43 decode_mask (unsigned int mask, unsigned int *pos_ret, unsigned int *size_ret)
46 for (i = 0; i < 32; i++)
51 for (; i < 32; i++, j++)
52 if (! (mask & (1L << i)))
60 /* Given a value and a field-width, expands the field to fill out 8 bits.
63 spread_bits (unsigned char value, unsigned char width)
68 case 7: return (value << 1) | (value >> 6);
69 case 6: return (value << 2) | (value >> 4);
70 case 5: return (value << 3) | (value >> 2);
71 case 4: return (value << 4) | (value);
72 case 3: return (value << 5) | (value << 2) | (value >> 2);
73 case 2: return (value << 6) | (value << 4) | (value);
74 default: abort(); break;
79 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
80 Scales it (without dithering) to WxH.
83 scale_image (Display *dpy, Window window, XImage *in,
84 int fromx, int fromy, int fromw, int fromh,
85 unsigned int *out, int w, int h)
89 unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */
90 unsigned int rsiz=0, gsiz=0, bsiz=0;
91 unsigned int rmsk=0, gmsk=0, bmsk=0;
92 unsigned char spread_map[3][256];
93 XWindowAttributes xgwa;
96 if (fromx + fromw > in->width ||
97 fromy + fromh > in->height)
100 XGetWindowAttributes (dpy, window, &xgwa);
102 /* Compute the field offsets for RGB decoding in the XImage,
103 when in TrueColor mode. Otherwise we use the colormap.
105 if (visual_class (xgwa.screen, xgwa.visual) == PseudoColor ||
106 visual_class (xgwa.screen, xgwa.visual) == GrayScale)
108 int ncolors = visual_cells (xgwa.screen, xgwa.visual);
109 colors = (XColor *) calloc (sizeof (*colors), ncolors+1);
110 for (i = 0; i < ncolors; i++)
112 XQueryColors (dpy, xgwa.colormap, colors, ncolors);
116 rmsk = xgwa.visual->red_mask;
117 gmsk = xgwa.visual->green_mask;
118 bmsk = xgwa.visual->blue_mask;
119 decode_mask (rmsk, &rpos, &rsiz);
120 decode_mask (gmsk, &gpos, &gsiz);
121 decode_mask (bmsk, &bpos, &bsiz);
123 for (i = 0; i < 256; i++)
125 spread_map[0][i] = spread_bits (i, rsiz);
126 spread_map[1][i] = spread_bits (i, gsiz);
127 spread_map[2][i] = spread_bits (i, bsiz);
131 scale = (fromw > fromh
133 : (float) fromh / h);
135 /* Scale the pixmap from window size to Apple][ screen size (but 32bpp)
137 for (y = 0; y < h-1; y++) /* iterate over dest pixels */
138 for (x = 0; x < w-1; x++)
141 unsigned int r=0, g=0, b=0;
143 int xx1 = x * scale + fromx;
144 int yy1 = y * scale + fromy;
145 int xx2 = (x+1) * scale + fromx;
146 int yy2 = (y+1) * scale + fromy;
148 /* Iterate over the source pixels contributing to this one, and sum. */
149 for (xx = xx1; xx < xx2; xx++)
150 for (yy = yy1; yy < yy2; yy++)
152 unsigned char rr, gg, bb;
153 unsigned long sp = ((xx > in->width || yy > in->height)
154 ? 0 : XGetPixel (in, xx, yy));
157 rr = colors[sp].red & 0xFF;
158 gg = colors[sp].green & 0xFF;
159 bb = colors[sp].blue & 0xFF;
163 rr = (sp & rmsk) >> rpos;
164 gg = (sp & gmsk) >> gpos;
165 bb = (sp & bmsk) >> bpos;
166 rr = spread_map[0][rr];
167 gg = spread_map[1][gg];
168 bb = spread_map[2][bb];
175 /* Scale summed pixel values down to 8/8/8 range */
176 i = (xx2 - xx1) * (yy2 - yy1);
182 out[y * w + x] = (r << 16) | (g << 8) | b;
187 /* Convert an XImage (of any size/depth/visual) to a 32bpp RGB array.
188 Picks a random sub-image out of the source image, and scales it to WxH.
191 pick_a2_subimage (Display *dpy, Window window, XImage *in,
192 unsigned int *out, int w, int h)
194 int fromx, fromy, fromw, fromh;
195 if (in->width <= w || in->height <= h)
206 double scale = (0.5 + frand(0.7) + frand(0.7) + frand(0.7));
209 } while (fromw > in->width ||
212 dw = (in->width - fromw) / 2; /* near the center! */
213 dh = (in->height - fromh) / 2;
215 fromx = (dw <= 0 ? 0 : (random() % dw) + (dw/2));
216 fromy = (dh <= 0 ? 0 : (random() % dh) + (dh/2));
219 scale_image (dpy, window, in,
220 fromx, fromy, fromw, fromh,
225 /* Floyd-Steinberg dither. Derived from ppmquant.c,
226 Copyright (c) 1989, 1991 by Jef Poskanzer.
229 a2_dither (unsigned int *in, unsigned char *out, int w, int h)
232 Apple ][ color map. Each pixel can only be 1 or 0, but what that
233 means depends on whether it's an odd or even pixel, and whether
234 the high bit in the byte is set or not. If it's 0, it's always
237 static const int a2_cmap[2][2][3] = {
240 {/* odd pixels = blue */ 0x00, 0x80, 0xff},
241 {/* even pixels = red */ 0xff, 0x80, 0x00}
245 {/* even pixels = purple */ 0xa0, 0x40, 0xa0},
246 {/* odd pixels = green */ 0x40, 0xff, 0x40}
251 unsigned int **pixels;
266 FILE *pipe = popen ("xv -", "w");
267 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
268 for (y = 0; y < h; y++)
269 for (x = 0; x < w; x++)
271 unsigned int p = in[y * w + x];
272 unsigned int r = (p >> 16) & 0xFF;
273 unsigned int g = (p >> 8) & 0xFF;
274 unsigned int b = (p ) & 0xFF;
275 fprintf(pipe, "%c%c%c", r, g, b);
281 /* Initialize Floyd-Steinberg error vectors. */
282 this_rerr = (long *) calloc (w + 2, sizeof(long));
283 next_rerr = (long *) calloc (w + 2, sizeof(long));
284 this_gerr = (long *) calloc (w + 2, sizeof(long));
285 next_gerr = (long *) calloc (w + 2, sizeof(long));
286 this_berr = (long *) calloc (w + 2, sizeof(long));
287 next_berr = (long *) calloc (w + 2, sizeof(long));
290 /* #### do we really need more than one element of "pixels" at once?
292 pixels = (unsigned int **) malloc (h * sizeof (unsigned int *));
293 for (y = 0; y < h; y++)
294 pixels[y] = (unsigned int *) malloc (w * sizeof (unsigned int));
296 for (x = 0; x < w + 2; ++x)
298 this_rerr[x] = random() % (fs_scale * 2) - fs_scale;
299 this_gerr[x] = random() % (fs_scale * 2) - fs_scale;
300 this_berr[x] = random() % (fs_scale * 2) - fs_scale;
301 /* (random errors in [-1 .. 1]) */
304 for (y = 0; y < h; y++)
305 for (x = 0; x < w; x++)
306 pixels[y][x] = in[y * w + x];
308 for (y = 0; y < h; y++)
314 for (x = 0; x < w + 2; x++)
315 next_rerr[x] = next_gerr[x] = next_berr[x] = 0;
317 /* It's too complicated to go back and forth on alternate rows,
318 so we always go left-right here. It doesn't change the result
321 For each group of 7 pixels, we have to try it both with the
322 high bit=0 and =1. For each high bit value, we add up the
323 total error and pick the best one.
325 Because we have to go through each group of bits twice, we
326 don't propagate the error values through this_[rgb]err since
327 it would add them twice. So we keep seperate local_[rgb]err
328 variables for propagating error within the 7-pixel group.
332 for (xbyte=0; xbyte<280; xbyte+=7)
335 int best_error=2000000000;
339 int local_rerr=0, local_gerr=0, local_berr=0;
341 for (hibit=0; hibit<2; hibit++)
346 for (x=xbyte; x<xbyte+7; x++)
350 /* Use Floyd-Steinberg errors to adjust actual color. */
351 sr = ((pP[x] >> 16) & 0xFF) * brightness/256;
352 sg = ((pP[x] >> 8) & 0xFF) * brightness/256;
353 sb = ((pP[x] ) & 0xFF) * brightness/256;
354 sr += (this_rerr[x + 1] + local_rerr) / fs_scale;
355 sg += (this_gerr[x + 1] + local_gerr) / fs_scale;
356 sb += (this_berr[x + 1] + local_berr) / fs_scale;
359 else if (sr > maxval) sr = maxval;
361 else if (sg > maxval) sg = maxval;
363 else if (sb > maxval) sb = maxval;
365 /* This is the color we'd get if we set the bit 1. For 0,
367 r2=a2_cmap[hibit][x&1][0];
368 g2=a2_cmap[hibit][x&1][1];
369 b2=a2_cmap[hibit][x&1][2];
372 dist0 and dist1 are the error (Minkowski 2-metric
373 distances in the color space) for choosing 0 and
374 1 respectively. 0 is black, 1 is the color r2,g2,b2.
376 dist1= (sr-r2)*(sr-r2) + (sg-g2)*(sg-g2) + (sb-b2)*(sb-b2);
377 dist0= sr*sr + sg*sg + sb*sb;
381 byte |= 1 << (x-xbyte);
384 /* Wanted sr but got r2, so propagate sr-r2 */
385 local_rerr = (sr - r2) * fs_scale * 7/16;
386 local_gerr = (sg - g2) * fs_scale * 7/16;
387 local_berr = (sb - b2) * fs_scale * 7/16;
393 /* Wanted sr but got 0, so propagate sr */
394 local_rerr = sr * fs_scale * 7/16;
395 local_gerr = sg * fs_scale * 7/16;
396 local_berr = sb * fs_scale * 7/16;
400 if (tot_error < best_error)
403 best_error = tot_error;
407 /* Avoid alternating 7f and ff in all-white areas, because it makes
408 regular pink vertical lines */
409 if ((best_byte&0x7f)==0x7f && (prev_byte&0x7f)==0x7f)
414 Now that we've chosen values for all 8 bits of the byte, we
415 have to fill in the real pixel values into pP and propagate
416 all the error terms. We end up repeating a lot of the code
420 for (x=xbyte; x<xbyte+7; x++)
422 int bit=(best_byte>>(x-xbyte))&1;
423 hibit=(best_byte>>7)&1;
425 sr = (pP[x] >> 16) & 0xFF;
426 sg = (pP[x] >> 8) & 0xFF;
427 sb = (pP[x] ) & 0xFF;
428 sr += this_rerr[x + 1] / fs_scale;
429 sg += this_gerr[x + 1] / fs_scale;
430 sb += this_berr[x + 1] / fs_scale;
433 else if (sr > maxval) sr = maxval;
435 else if (sg > maxval) sg = maxval;
437 else if (sb > maxval) sb = maxval;
439 r2=a2_cmap[hibit][x&1][0] * bit;
440 g2=a2_cmap[hibit][x&1][1] * bit;
441 b2=a2_cmap[hibit][x&1][2] * bit;
443 pP[x] = (r2<<16) | (g2<<8) | (b2);
445 /* Propagate Floyd-Steinberg error terms. */
446 err = (sr - r2) * fs_scale;
447 this_rerr[x + 2] += (err * 7) / 16;
448 next_rerr[x ] += (err * 3) / 16;
449 next_rerr[x + 1] += (err * 5) / 16;
450 next_rerr[x + 2] += (err ) / 16;
451 err = (sg - g2) * fs_scale;
452 this_gerr[x + 2] += (err * 7) / 16;
453 next_gerr[x ] += (err * 3) / 16;
454 next_gerr[x + 1] += (err * 5) / 16;
455 next_gerr[x + 2] += (err ) / 16;
456 err = (sb - b2) * fs_scale;
457 this_berr[x + 2] += (err * 7) / 16;
458 next_berr[x ] += (err * 3) / 16;
459 next_berr[x + 1] += (err * 5) / 16;
460 next_berr[x + 2] += (err ) / 16;
464 And put the actual byte into out.
467 out[y*(w/7) + xbyte/7] = best_byte;
471 temp_err = this_rerr;
472 this_rerr = next_rerr;
473 next_rerr = temp_err;
474 temp_err = this_gerr;
475 this_gerr = next_gerr;
476 next_gerr = temp_err;
477 temp_err = this_berr;
478 this_berr = next_berr;
479 next_berr = temp_err;
495 /* let's see what we got... */
496 FILE *pipe = popen ("xv -", "w");
497 fprintf (pipe, "P6\n%d %d\n%d\n", w, h, 255);
498 for (y = 0; y < h; y++)
499 for (x = 0; x < w; x++)
501 unsigned int r = (pixels[y][x]>>16)&0xff;
502 unsigned int g = (pixels[y][x]>>8)&0xff;
503 unsigned int b = (pixels[y][x]>>0)&0xff;
504 fprintf(pipe, "%c%c%c", r, g, b);
511 typedef struct slideshow_data_s {
513 int render_img_lineno;
514 unsigned char *render_img;
516 Bool image_loading_p;
521 image_loaded_cb (Screen *screen, Window window, Drawable p,
522 const char *name, XRectangle *geometry,
525 Display *dpy = DisplayOfScreen (screen);
526 apple2_sim_t *sim = (apple2_sim_t *) closure;
527 slideshow_data *mine = (slideshow_data *) sim->controller_data;
528 XWindowAttributes xgwa;
532 unsigned int *buf32 = (unsigned int *) calloc (w, h * 4);
533 unsigned char *buf8 = (unsigned char *) calloc (w/7, h);
537 fprintf (stderr, "%s: out of memory (%dx%d)\n", progname, w, h);
541 XGetWindowAttributes (dpy, window, &xgwa);
543 image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap);
544 XFreePixmap (dpy, p);
547 /* Scale the XImage down to Apple][ size, and convert it to a 32bpp
548 image (regardless of whether it started as TrueColor/PseudoColor.)
550 pick_a2_subimage (dpy, window, image, buf32, w, h);
553 XDestroyImage(image);
555 /* Then dither the 32bpp image to a 6-color Apple][ colormap.
557 a2_dither (buf32, buf8, w, h);
561 mine->image_loading_p = False;
562 mine->img_filename = (name ? strdup (name) : 0);
563 mine->render_img = buf8;
568 static const char *apple2_defaults [] = {
569 ".background: black",
570 ".foreground: white",
573 "*program: xscreensaver-text --cols 40",
574 "*metaSendsESC: True",
581 # endif /* !HAVE_FORKPTY */
587 static XrmOptionDescRec apple2_options [] = {
588 { "-mode", ".mode", XrmoptionSepArg, 0 },
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 static void slideshow_controller(apple2_sim_t *sim, int *stepno,
611 double *next_actiontime)
613 apple2_state_t *st=sim->st;
615 slideshow_data *mine;
617 if (!sim->controller_data)
618 sim->controller_data = calloc (1, sizeof(*mine));
619 mine = (slideshow_data *) sim->controller_data;
627 sim->typing_rate = 0.3;
628 sim->dec->powerup=0.0;
631 a2_prints(st, "APPLE ][");
640 XWindowAttributes xgwa;
642 XGetWindowAttributes (sim->dpy, sim->window, &xgwa);
643 p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height,
645 mine->image_loading_p = True;
646 load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim);
648 /* pause with a blank screen for a bit, while the image loads in the
650 *next_actiontime += 2.0;
656 if (! mine->image_loading_p) { /* image is finally loaded */
662 *next_actiontime += 3.0;
677 st->gr_mode=A2_GR_HIRES;
678 if (mine->img_filename) {
679 char *basename, *tmp;
682 basename = tmp = strdup (mine->img_filename);
685 char *slash = strchr(basename, '/');
686 if (!slash || !slash[1]) break;
690 char *dot=strrchr(basename,'.');
693 if (strlen(basename)>20) basename[20]=0;
694 for (s=basename; *s; s++) {
696 if (*s <= ' ') *s = '_';
698 sprintf(sim->typing_buf, "BLOAD %s\n", basename);
699 sim->typing = sim->typing_buf;
703 sim->typing = "BLOAD IMAGE\n";
705 mine->render_img_lineno=0;
711 *next_actiontime += 0.7;
716 if (mine->render_img_lineno>=192) {
718 sim->typing="POKE 49234,0\n";
723 for (i=0; i<6 && mine->render_img_lineno<192; i++) {
724 a2_display_image_loading(st, mine->render_img,
725 mine->render_img_lineno++);
728 /* The disk would have to seek every 13 sectors == 78 lines.
729 (This ain't no newfangled 16-sector operating system) */
730 if ((mine->render_img_lineno%78)==0) {
731 *next_actiontime += 0.5;
733 *next_actiontime += 0.08;
738 st->gr_mode |= A2_GR_FULL;
740 /* Note that sim->delay is sometimes "infinite" in this controller.
741 These images are kinda dull anyway, so don't leave it on too long. */
742 *next_actiontime += 2;
747 sim->typing="POKE 49235,0\n";
753 st->gr_mode &= ~A2_GR_FULL;
754 if (mine->render_img) {
755 free(mine->render_img);
756 mine->render_img=NULL;
758 if (mine->img_filename) {
759 free(mine->img_filename);
760 mine->img_filename=NULL;
766 /* Do nothing, just wait */
767 *next_actiontime += 2.0;
768 *stepno = A2CONTROLLER_FREE;
771 case A2CONTROLLER_FREE:
772 /* It is possible that still image is being loaded,
773 in that case mine cannot be freed, because
774 callback function tries to use it, so wait.
776 if (mine->image_loading_p) {
780 free(mine->render_img);
781 free(mine->img_filename);
791 struct terminal_controller_data {
795 double last_emit_time;
801 int cursor_x, cursor_y;
802 int saved_x, saved_y;
805 unsigned int bold : 1;
806 unsigned int blink : 1;
807 unsigned int rev : 1;
816 /* The structure of closure linkage throughout this code is so amazingly
817 baroque that I can't get to the 'struct state' from where I need it. */
818 static const char *global_program;
819 static Bool global_fast_p;
823 terminal_closegen(struct terminal_controller_data *mine)
826 textclient_close (mine->tc);
832 terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n)
834 if (!mine || !mine->tc) {
838 for (i = 0; i < n; i++) {
839 int c = textclient_getc (mine->tc);
851 terminal_keypress_handler (Display *dpy, XEvent *event, void *data)
853 struct terminal_controller_data *mine =
854 (struct terminal_controller_data *) data;
856 if (event->xany.type == KeyPress && mine->tc)
857 return textclient_putc (mine->tc, &event->xkey);
863 a2_ascii_printc (apple2_state_t *st, unsigned char c,
864 Bool bold_p, Bool blink_p, Bool rev_p,
867 if (c >= 'a' && c <= 'z') /* upcase lower-case chars */
871 else if ((c >= 'A'+128) || /* upcase and blink */
872 (c < ' ' && c != 014 && /* high-bit & ctl chrs */
873 c != '\r' && c != '\n' && c!='\t'))
875 c = (c & 0x1F) | 0x80;
877 else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */
882 if (bold_p) c |= 0xc0;
883 if (blink_p) c = (c & ~0x40) | 0x80;
884 if (rev_p) c |= 0xc0;
889 a2_printc_noscroll(st, c);
894 a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state,
897 apple2_state_t *st=sim->st;
898 int cols = SCREEN_COLS;
899 int rows = SCREEN_ROWS;
904 switch (state->escstate)
910 /* Dummy case - we don't want the screensaver to beep */
911 /* #### But maybe this should flash the screen? */
914 if (state->cursor_x > 0)
918 if (state->cursor_x < cols - 8)
920 state->cursor_x = (state->cursor_x & ~7) + 8;
925 if (state->cursor_y < rows - 1)
932 # ifndef HAVE_FORKPTY
933 state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
937 if (state->cursor_y < rows - 1)
947 /* Dummy case - there is one and only one font. */
951 /* Dummy case - these interrupt escape sequences, so
952 they don't do anything in this state */
958 /* Dummy case - this is supposed to be ignored */
962 for(i = 0; i < NPAR; i++)
963 state->csiparam[i] = 0;
967 /* If the cursor is in column 39 and we print a character, then
968 that character shows up in column 39, and the cursor is no longer
969 visible on the screen (it's in "column 40".) If another character
970 is printed, then that character shows up in column 0, and the
971 cursor moves to column 1.
973 This is empirically what xterm and gnome-terminal do, so that must
974 be the right thing. (In xterm, the cursor vanishes, whereas; in
975 gnome-terminal, the cursor overprints the character in col 39.)
977 if (state->cursor_x >= cols)
980 if (state->cursor_y >= rows - 1)
986 a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */
987 a2_ascii_printc (st, c,
988 state->termattrib.bf.bold,
989 state->termattrib.bf.blink,
990 state->termattrib.bf.rev,
1002 state->escstate = 0;
1004 case 'c': /* Reset */
1006 state->escstate = 0;
1008 case 'D': /* Linefeed */
1009 if (state->cursor_y < rows - 1)
1013 state->escstate = 0;
1015 case 'E': /* Newline */
1016 state->cursor_x = 0;
1017 state->escstate = 0;
1019 case 'M': /* Reverse newline */
1020 if (state->cursor_y > 0)
1022 state->escstate = 0;
1024 case '7': /* Save state */
1025 state->saved_x = state->cursor_x;
1026 state->saved_y = state->cursor_y;
1027 state->escstate = 0;
1029 case '8': /* Restore state */
1030 state->cursor_x = state->saved_x;
1031 state->cursor_y = state->saved_y;
1032 state->escstate = 0;
1035 state->escstate = 2;
1036 for(i = 0; i < NPAR; i++)
1037 state->csiparam[i] = 0;
1038 state->curparam = 0;
1040 case '%': /* Select charset */
1041 /* No, I don't support UTF-8, since the apple2 font
1042 isn't even Unicode anyway. We must still catch the
1043 last byte, though. */
1046 /* I don't support different fonts either - see above
1048 state->escstate = 3;
1051 /* Escape sequences not supported:
1054 * Z - Terminal identification
1056 * = - Other keypad change
1059 state->escstate = 0;
1068 state->escstate = 0;
1070 case '0': case '1': case '2': case '3': case '4':
1071 case '5': case '6': case '7': case '8': case '9':
1072 if (state->curparam < NPAR)
1073 state->csiparam[state->curparam] =
1074 (state->csiparam[state->curparam] * 10) + (c - '0');
1077 state->csiparam[++state->curparam] = 0;
1080 state->escstate = 3;
1083 for (i = 0; i < state->csiparam[0]; i++)
1085 if(++state->cursor_x > cols)
1087 state->cursor_x = 0;
1088 if (state->cursor_y < rows - 1)
1094 state->escstate = 0;
1097 state->cursor_x = 0;
1099 if (state->csiparam[0] == 0)
1100 state->csiparam[0] = 1;
1101 if ((state->cursor_y -= state->csiparam[0]) < 0)
1102 state->cursor_y = 0;
1103 state->escstate = 0;
1106 state->cursor_x = 0;
1109 if (state->csiparam[0] == 0)
1110 state->csiparam[0] = 1;
1111 if ((state->cursor_y += state->csiparam[0]) >= rows)
1112 state->cursor_y = rows - 1;
1113 state->escstate = 0;
1117 if (state->csiparam[0] == 0)
1118 state->csiparam[0] = 1;
1119 if ((state->cursor_x += state->csiparam[0]) >= cols)
1120 state->cursor_x = cols - 1;
1121 state->escstate = 0;
1124 if (state->csiparam[0] == 0)
1125 state->csiparam[0] = 1;
1126 if ((state->cursor_x -= state->csiparam[0]) < 0)
1127 state->cursor_x = 0;
1128 state->escstate = 0;
1131 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1132 state->cursor_y = rows - 1;
1133 state->escstate = 0;
1137 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols)
1138 state->cursor_x = cols - 1;
1139 state->escstate = 0;
1143 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows)
1144 state->cursor_y = rows - 1;
1145 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols)
1146 state->cursor_x = cols - 1;
1147 if(state->cursor_y < 0)
1148 state->cursor_y = 0;
1149 if(state->cursor_x < 0)
1150 state->cursor_x = 0;
1151 state->escstate = 0;
1156 if (state->csiparam[0] == 0)
1157 start = cols * state->cursor_y + state->cursor_x;
1158 if (state->csiparam[0] == 1)
1159 end = cols * state->cursor_y + state->cursor_x;
1161 a2_goto(st, state->cursor_y, state->cursor_x);
1162 for (i = start; i < end; i++)
1164 a2_ascii_printc(st, ' ', False, False, False, False);
1166 state->escstate = 0;
1171 if (state->csiparam[0] == 0)
1172 start = state->cursor_x;
1173 if (state->csiparam[1] == 1)
1174 end = state->cursor_x;
1176 a2_goto(st, state->cursor_y, state->cursor_x);
1177 for (i = start; i < end; i++)
1179 a2_ascii_printc(st, ' ', False, False, False, False);
1181 state->escstate = 0;
1183 case 'm': /* Set attributes */
1184 for (i = 0; i <= state->curparam; i++)
1186 switch(state->csiparam[i])
1189 state->termattrib.w = 0;
1192 state->termattrib.bf.bold = 1;
1195 state->termattrib.bf.blink = 1;
1198 state->termattrib.bf.rev = 1;
1202 state->termattrib.bf.bold = 0;
1205 state->termattrib.bf.blink = 0;
1208 state->termattrib.bf.rev = 0;
1212 state->escstate = 0;
1214 case 's': /* Save position */
1215 state->saved_x = state->cursor_x;
1216 state->saved_y = state->cursor_y;
1217 state->escstate = 0;
1219 case 'u': /* Restore position */
1220 state->cursor_x = state->saved_x;
1221 state->cursor_y = state->saved_y;
1222 state->escstate = 0;
1224 case '?': /* DEC Private modes */
1225 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1226 state->escstate = 0;
1229 /* Known unsupported CSIs:
1231 * L - Insert blank lines
1232 * M - Delete lines (I don't know what this means...)
1233 * P - Delete characters
1234 * X - Erase characters (difference with P being...?)
1235 * c - Terminal identification
1236 * g - Clear tab stop(s)
1237 * h - Set mode (Mainly due to its complexity and lack of good
1240 * m - Set mode (Phosphor is, per defenition, green on black)
1242 * q - Set keyboard LEDs
1243 * r - Set scrolling region (too exhausting - noone uses this,
1246 state->escstate = 0;
1251 state->escstate = 0;
1254 a2_goto(st, state->cursor_y, state->cursor_x);
1259 It's fun to put things like "gdb" as the command. For one, it's
1260 amusing how the standard mumble (version, no warranty, it's
1261 GNU/Linux dammit) occupies an entire screen on the Apple ][.
1265 terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1267 apple2_state_t *st=sim->st;
1271 struct terminal_controller_data *mine;
1272 if (!sim->controller_data)
1273 sim->controller_data=calloc(sizeof(struct terminal_controller_data),1);
1274 mine=(struct terminal_controller_data *) sim->controller_data;
1275 mine->dpy = sim->dpy;
1277 mine->fast_p = global_fast_p;
1283 st->gr_mode |= A2_GR_FULL; /* Turn on color mode even through it's
1287 a2_prints(st, "APPLE ][");
1292 mine->tc = textclient_open (mine->dpy);
1293 textclient_reshape (mine->tc,
1294 SCREEN_COLS, SCREEN_ROWS,
1295 SCREEN_COLS, SCREEN_ROWS,
1300 *next_actiontime += 4.0;
1306 unsigned char buf[1024];
1310 elapsed=sim->curtime - mine->last_emit_time;
1311 mine->last_emit_time=sim->curtime;
1313 if (elapsed>1.0) nwant=1;
1314 if (nwant<1) nwant=1;
1315 if (nwant>4) nwant=4;
1318 nwant = sizeof(buf)-1;
1320 nr=terminal_read(mine, buf, nwant);
1321 for (i=0; i<nr; i++) {
1325 a2_vt100_printc (sim, mine, c);
1327 a2_ascii_printc (st, c, False, False, False, True);
1332 case A2CONTROLLER_FREE:
1333 terminal_closegen(mine);
1340 struct basic_controller_data {
1343 const char * const * progtext;
1347 double prog_start_time;
1348 char error_buf[256];
1352 Adding more programs is easy. Just add a listing here and to all_programs,
1353 then add the state machine to actually execute it to basic_controller.
1355 static const char * const moire_program[]={
1357 "20 FOR Y = 0 TO 190 STEP 2\n",
1358 "30 HCOLOR=4 : REM BLACK\n",
1359 "40 HPLOT 0,191-Y TO 279,Y\n",
1360 "60 HCOLOR=7 : REM WHITE\n",
1361 "80 HPLOT 0,190-Y TO 279,Y+1\n",
1363 "100 FOR X = 0 TO 278 STEP 3\n",
1365 "120 HPLOT 279-X,0 TO X,191\n",
1367 "150 HPLOT 278-X,0 TO X+1,191\n",
1372 static const char * const sinewave_program[] = {
1375 "30 FOR X = 0 TO 279\n",
1377 "35 HPLOT X,0 TO X,159\n",
1379 "40 Y = 80 + SIN(15*(X-K)/279) * 40\n",
1388 static const char * const dumb_program[]={
1389 "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n",
1395 static const char * const random_lores_program[]={
1396 "1 REM APPLE ][ SCREEN SAVER\n",
1398 "100 COLOR= RND(1)*16\n",
1400 "110 X=RND(1)*40\n",
1401 "120 Y1=RND(1)*40\n",
1402 "130 Y2=RND(1)*40\n",
1403 "140 FOR Y = Y1 TO Y2\n",
1407 "210 Y=RND(1)*40\n",
1408 "220 X1=RND(1)*40\n",
1409 "230 X2=RND(1)*40\n",
1410 "240 FOR X = X1 TO X2\n",
1418 static char typo_map[256];
1420 static int make_typo(char *out_buf, const char *orig, char *err_buf)
1480 strcpy(out_buf, orig);
1481 for (i=0; out_buf[i]; i++) {
1482 char *p = out_buf+i;
1484 if (i>2 && p[-2]=='R' && p[-1]=='E' && p[0]=='M')
1487 if (isalpha(p[0]) &&
1496 sprintf(err_buf,"?SYNTAX ERROR\n");
1500 if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) {
1501 int remain=strlen(p);
1502 int past=random()%(remain-2)+1;
1503 memmove(p+past+past, p, remain+1);
1505 for (j=0; j<past; j++) {
1514 static const struct {
1515 const char * const * progtext;
1518 {moire_program, 100},
1519 /*{dumb_program, 200}, */
1520 {sinewave_program, 400},
1521 {random_lores_program, 500},
1525 basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime)
1527 apple2_state_t *st=sim->st;
1530 struct basic_controller_data *mine;
1531 if (!sim->controller_data)
1532 sim->controller_data=calloc(sizeof(struct basic_controller_data),1);
1533 mine=(struct basic_controller_data *) sim->controller_data;
1540 a2_prints(st, "APPLE ][");
1543 sim->typing_rate=0.2;
1545 i=random()%countof(all_programs);
1546 mine->progtext=all_programs[i].progtext;
1547 mine->progstep=all_programs[i].progstep;
1550 *next_actiontime += 1.0;
1555 if (st->cursx==0) a2_printc(st,']');
1556 if (mine->progtext[mine->prog_line]) {
1557 if (random()%4==0) {
1558 int err=make_typo(sim->typing_buf,
1559 mine->progtext[mine->prog_line],
1561 sim->typing=sim->typing_buf;
1568 sim->typing=mine->progtext[mine->prog_line++];
1576 sim->printing=mine->error_buf;
1581 if (st->cursx==0) a2_printc(st,']');
1582 *next_actiontime+=1.0;
1587 sim->typing="RUN\n";
1591 mine->prog_start_time=*next_actiontime;
1592 *stepno=mine->progstep;
1597 st->gr_mode=A2_GR_HIRES|A2_GR_FULL;
1598 for (i=0; i<24 && mine->y<192; i++)
1600 a2_hline(st, 4, 0, 191-mine->y, 279, mine->y);
1601 a2_hline(st, 7, 0, 191-mine->y-1, 279, mine->y+1);
1611 for (i=0; i<24 && mine->x<280; i++)
1613 a2_hline(st, 4, 279-mine->x, 0, mine->x, 192);
1614 a2_hline(st, 7, 279-mine->x-1, 0, mine->x+1, 192);
1617 if (mine->x >= 280) *stepno=120;
1621 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1626 mine->rep_str="\nAPPLE ][ ROOLZ! TRS-80 DROOLZ!";
1627 for (i=0; i<30; i++) {
1628 a2_prints(st, mine->rep_str);
1634 i=random()%strlen(mine->rep_str);
1635 while (mine->rep_pos != i) {
1636 a2_printc(st, mine->rep_str[mine->rep_pos]);
1638 if (!mine->rep_str[mine->rep_pos]) mine->rep_pos=0;
1640 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1643 /* sinewave_program */
1645 st->gr_mode=A2_GR_HIRES;
1650 for (i=0; i<48; i++) {
1651 int y=80 + (int)(75.0*sin(15.0*(mine->x-mine->k)/279.0));
1652 a2_hline(st, 0, mine->x, 0, mine->x, 159);
1653 a2_hplot(st, 3, mine->x, y);
1660 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1668 /* random_lores_program */
1670 st->gr_mode=A2_GR_LORES|A2_GR_FULL;
1675 for (i=0; i<10; i++) {
1676 int color,x,y,x1,x2,y1,y2;
1682 for (y=y1; y<y2; y++) a2_plot(st, color, x, y);
1687 for (x=x1; x<x2; x++) a2_plot(st, color, x, y);
1689 if (*next_actiontime > mine->prog_start_time+sim->delay) *stepno=999;
1696 case A2CONTROLLER_FREE:
1704 static void (* const controllers[]) (apple2_sim_t *sim, int *stepno,
1705 double *next_actiontime) = {
1706 slideshow_controller,
1707 terminal_controller,
1715 void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime);
1720 apple2_init (Display *dpy, Window window)
1722 struct state *st = (struct state *) calloc (1, sizeof(*st));
1725 st->duration = get_integer_resource (dpy, "duration", "Integer");
1728 if (st->duration < 1) st->duration = 1;
1730 s = get_string_resource (dpy, "mode", "Mode");
1731 if (!s || !*s || !strcasecmp(s, "random"))
1732 st->random_p = True;
1733 else if (!strcasecmp(s, "text"))
1734 st->controller = terminal_controller;
1735 else if (!strcasecmp(s, "slideshow"))
1736 st->controller = slideshow_controller;
1737 else if (!strcasecmp(s, "basic"))
1738 st->controller = basic_controller;
1741 fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n",
1747 global_program = get_string_resource (dpy, "program", "Program");
1748 global_fast_p = get_boolean_resource (dpy, "fast", "Boolean");
1751 /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */
1753 const char *s = getenv ("XSCREENSAVER_STANDALONE");
1754 if (s && *s && strcmp(s, "0"))
1756 st->controller = terminal_controller;
1757 st->random_p = False;
1758 global_program = getenv ("SHELL");
1759 global_fast_p = True;
1764 if (! st->random_p) {
1765 if (st->controller == terminal_controller ||
1766 st->controller == slideshow_controller)
1767 st->duration = 999999; /* these run "forever" */
1773 static unsigned long
1774 apple2_draw (Display *dpy, Window window, void *closure)
1776 struct state *st = (struct state *) closure;
1780 st->controller = controllers[random() % (countof(controllers))];
1781 st->sim = apple2_start (dpy, window, st->duration, st->controller);
1784 if (! apple2_one_frame (st->sim)) {
1796 apple2_reshape (Display *dpy, Window window, void *closure,
1797 unsigned int w, unsigned int h)
1799 struct state *st = (struct state *) closure;
1801 analogtv_reconfigure (st->sim->dec);
1805 apple2_event (Display *dpy, Window window, void *closure, XEvent *event)
1807 struct state *st = (struct state *) closure;
1810 st->controller == terminal_controller &&
1811 event->xany.type == KeyPress) {
1812 terminal_keypress_handler (dpy, event, st->sim->controller_data);
1820 apple2_free (Display *dpy, Window window, void *closure)
1822 struct state *st = (struct state *) closure;
1824 st->sim->stepno = A2CONTROLLER_DONE;
1825 if (apple2_one_frame (st->sim))
1826 abort(); /* should have freed! */
1832 XSCREENSAVER_MODULE ("Apple2", apple2)