X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fapple2-main.c;h=3063b58d787f720324f9d700d7948586d0f420b0;hb=4361b69d3178d7fc98d0388f9a223af6c2651aba;hp=ae38787f9856b0912b7b7e6b347595e9dd215333;hpb=96a411663168b0ba5432b407a83be55f3df0c802;p=xscreensaver diff --git a/hacks/apple2-main.c b/hacks/apple2-main.c index ae38787f..3063b58d 100644 --- a/hacks/apple2-main.c +++ b/hacks/apple2-main.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1998-2003 Jamie Zawinski +/* xscreensaver, Copyright (c) 1998-2014 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -10,23 +10,30 @@ * * Apple ][ CRT simulator, by Trevor Blackwell * with additional work by Jamie Zawinski + * Pty and vt100 emulation by Fredrik Tolf */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ + #include +#include + +#ifdef HAVE_UNISTD_H +# include +#endif + #include "screenhack.h" #include "apple2.h" -#include -#include -#include +#include "textclient.h" +#include "utf8wc.h" #undef countof #define countof(x) (sizeof((x))/sizeof((*x))) -#define DEBUG - -extern XtAppContext app; - -Time subproc_relaunch_delay = 3000; +#define SCREEN_COLS 40 +#define SCREEN_ROWS 24 /* Given a bitmask, returns the position and width of the field. @@ -80,7 +87,7 @@ scale_image (Display *dpy, Window window, XImage *in, int x, y, i; unsigned int rpos=0, gpos=0, bpos=0; /* bitfield positions */ unsigned int rsiz=0, gsiz=0, bsiz=0; - unsigned int rmsk=0, gmsk=0, bmsk=0; + unsigned long rmsk=0, gmsk=0, bmsk=0; unsigned char spread_map[3][256]; XWindowAttributes xgwa; XColor *colors = 0; @@ -105,9 +112,7 @@ scale_image (Display *dpy, Window window, XImage *in, } else { - rmsk = xgwa.visual->red_mask; - gmsk = xgwa.visual->green_mask; - bmsk = xgwa.visual->blue_mask; + visual_rgb_masks (xgwa.screen, xgwa.visual, &rmsk, &gmsk, &bmsk); decode_mask (rmsk, &rpos, &rsiz); decode_mask (gmsk, &gpos, &gsiz); decode_mask (bmsk, &bpos, &bsiz); @@ -204,8 +209,8 @@ pick_a2_subimage (Display *dpy, Window window, XImage *in, dw = (in->width - fromw) / 2; /* near the center! */ dh = (in->height - fromh) / 2; - fromx = (random() % dw) + (dw/2); - fromy = (random() % dh) + (dh/2); + fromx = (dw <= 0 ? 0 : (random() % dw) + (dw/2)); + fromy = (dh <= 0 ? 0 : (random() % dh) + (dh/2)); } scale_image (dpy, window, in, @@ -252,7 +257,6 @@ a2_dither (unsigned int *in, unsigned char *out, int w, int h) long *temp_err; int fs_scale = 1024; int brightness = 75; - int fs_direction; #if 0 { @@ -293,7 +297,6 @@ a2_dither (unsigned int *in, unsigned char *out, int w, int h) this_berr[x] = random() % (fs_scale * 2) - fs_scale; /* (random errors in [-1 .. 1]) */ } - fs_direction = 1; for (y = 0; y < h; y++) for (x = 0; x < w; x++) @@ -502,13 +505,24 @@ a2_dither (unsigned int *in, unsigned char *out, int w, int h) #endif } +typedef struct slideshow_data_s { + int slideno; + int render_img_lineno; + unsigned char *render_img; + char *img_filename; + Bool image_loading_p; +} slideshow_data; + -static unsigned char * -load_image (Display *dpy, Window window, char **image_filename_r) +static void +image_loaded_cb (Screen *screen, Window window, Drawable p, + const char *name, XRectangle *geometry, + void *closure) { + Display *dpy = DisplayOfScreen (screen); + apple2_sim_t *sim = (apple2_sim_t *) closure; + slideshow_data *mine = (slideshow_data *) sim->controller_data; XWindowAttributes xgwa; - Pixmap p; - int w = 280; int h = 192; XImage *image; @@ -522,48 +536,65 @@ load_image (Display *dpy, Window window, char **image_filename_r) } XGetWindowAttributes (dpy, window, &xgwa); - p = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth); - load_random_image (xgwa.screen, window, p, image_filename_r); + image = XGetImage (dpy, p, 0, 0, xgwa.width, xgwa.height, ~0, ZPixmap); XFreePixmap (dpy, p); p = 0; - /* Make sure the window's background is not set to None, and get the - grabbed bits (if any) off it as soon as possible. */ - XSetWindowBackground (dpy, window, - get_pixel_resource ("background", "Background", - dpy, xgwa.colormap)); - XClearWindow (dpy, window); - /* Scale the XImage down to Apple][ size, and convert it to a 32bpp image (regardless of whether it started as TrueColor/PseudoColor.) */ pick_a2_subimage (dpy, window, image, buf32, w, h); + free(image->data); + image->data = 0; + XDestroyImage(image); /* Then dither the 32bpp image to a 6-color Apple][ colormap. */ a2_dither (buf32, buf8, w, h); free (buf32); - return buf8; + + mine->image_loading_p = False; + mine->img_filename = (name ? strdup (name) : 0); + mine->render_img = buf8; } -char *progclass = "Apple2"; -char *defaults [] = { +static const char *apple2_defaults [] = { + ".background: black", + ".foreground: white", "*mode: random", - "*duration: 20", + "*duration: 60", + "*program: xscreensaver-text --cols 40", + "*metaSendsESC: True", + "*swapBSDEL: True", + "*fast: False", +# ifdef HAVE_FORKPTY + "*usePty: True", +#else + "*usePty: False", +# endif /* !HAVE_FORKPTY */ + ANALOGTV_DEFAULTS 0 }; -XrmOptionDescRec options [] = { - { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" }, - { "-basic", ".mode", XrmoptionNoArg, "basic" }, - { "-text", ".mode", XrmoptionNoArg, "text" }, +static XrmOptionDescRec apple2_options [] = { + { "-mode", ".mode", XrmoptionSepArg, 0 }, + { "-slideshow", ".mode", XrmoptionNoArg, "slideshow" }, + { "-basic", ".mode", XrmoptionNoArg, "basic" }, + { "-text", ".mode", XrmoptionNoArg, "text" }, { "-program", ".program", XrmoptionSepArg, 0 }, { "-duration", ".duration", XrmoptionSepArg, 0 }, + { "-pty", ".usePty", XrmoptionNoArg, "True" }, + { "-pipe", ".usePty", XrmoptionNoArg, "False" }, + { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" }, + { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" }, + { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" }, + { "-del", ".swapBSDEL", XrmoptionNoArg, "True" }, + { "-fast", ".fast", XrmoptionNoArg, "True" }, ANALOGTV_OPTIONS { 0, 0, 0, 0 } }; @@ -573,21 +604,16 @@ XrmOptionDescRec options [] = { to avoid the pause while it loads. */ -void slideshow_controller(apple2_sim_t *sim, int *stepno, - double *next_actiontime) +static void slideshow_controller(apple2_sim_t *sim, int *stepno, + double *next_actiontime) { apple2_state_t *st=sim->st; int i; - struct mydata { - int slideno; - int render_img_lineno; - u_char *render_img; - char *img_filename; - } *mine; + slideshow_data *mine; if (!sim->controller_data) - sim->controller_data = calloc(sizeof(struct mydata),1); - mine=(struct mydata *) sim->controller_data; + sim->controller_data = calloc (1, sizeof(*mine)); + mine = (slideshow_data *) sim->controller_data; switch(*stepno) { @@ -603,17 +629,35 @@ void slideshow_controller(apple2_sim_t *sim, int *stepno, a2_goto(st,23,0); a2_printc(st,']'); - *next_actiontime += 4.0; *stepno=10; + break; case 10: - mine->render_img = load_image (sim->dpy, sim->window, &mine->img_filename); - if (st->gr_mode) { - *stepno=30; - } else { - *stepno=20; + { + XWindowAttributes xgwa; + Pixmap p; + XGetWindowAttributes (sim->dpy, sim->window, &xgwa); + p = XCreatePixmap (sim->dpy, sim->window, xgwa.width, xgwa.height, + xgwa.depth); + mine->image_loading_p = True; + load_image_async (xgwa.screen, sim->window, p, image_loaded_cb, sim); + + /* pause with a blank screen for a bit, while the image loads in the + background. */ + *next_actiontime += 2.0; + *stepno=11; + } + break; + + case 11: + if (! mine->image_loading_p) { /* image is finally loaded */ + if (st->gr_mode) { + *stepno=30; + } else { + *stepno=20; + } + *next_actiontime += 3.0; } - *next_actiontime += 3.0; break; case 20: @@ -640,11 +684,14 @@ void slideshow_controller(apple2_sim_t *sim, int *stepno, basename = slash+1; } { - char *dot=strchr(basename,'.'); + char *dot=strrchr(basename,'.'); if (dot) *dot=0; } if (strlen(basename)>20) basename[20]=0; - for (s=basename; *s; s++) *s = toupper (*s); + for (s=basename; *s; s++) { + *s = toupper (*s); + if (*s <= ' ') *s = '_'; + } sprintf(sim->typing_buf, "BLOAD %s\n", basename); sim->typing = sim->typing_buf; @@ -687,7 +734,9 @@ void slideshow_controller(apple2_sim_t *sim, int *stepno, case 50: st->gr_mode |= A2_GR_FULL; *stepno=60; - *next_actiontime += sim->delay; + /* Note that sim->delay is sometimes "infinite" in this controller. + These images are kinda dull anyway, so don't leave it on too long. */ + *next_actiontime += 2; break; case 60: @@ -710,123 +759,568 @@ void slideshow_controller(apple2_sim_t *sim, int *stepno, *stepno=10; break; + case 80: + /* Do nothing, just wait */ + *next_actiontime += 2.0; + *stepno = A2CONTROLLER_FREE; + break; + case A2CONTROLLER_FREE: + /* It is possible that still image is being loaded, + in that case mine cannot be freed, because + callback function tries to use it, so wait. + */ + if (mine->image_loading_p) { + *stepno = 80; + break; + } free(mine->render_img); free(mine->img_filename); free(mine); + mine = 0; return; } } +#define NPAR 16 + struct terminal_controller_data { - FILE *pipe; - int pipe_id; - int input_available_p; - XtIntervalId timeout_id; + Display *dpy; char curword[256]; unsigned char lastc; - int fake_nl; double last_emit_time; + text_data *tc; + + int escstate; + int csiparam[NPAR]; + int curparam; + int cursor_x, cursor_y; + int saved_x, saved_y; + int unicruds; char unicrud[7]; + union { + struct { + unsigned int bold : 1; + unsigned int blink : 1; + unsigned int rev : 1; + } bf; + int w; + } termattrib; + Bool fast_p; + }; + +/* The structure of closure linkage throughout this code is so amazingly + baroque that I can't get to the 'struct state' from where I need it. */ +static const char *global_program; +static Bool global_fast_p; + + static void -subproc_cb (XtPointer closure, int *source, XtInputId *id) +terminal_closegen(struct terminal_controller_data *mine) { - struct terminal_controller_data *mine = - (struct terminal_controller_data *) closure; - mine->input_available_p = True; + if (mine->tc) { + textclient_close (mine->tc); + mine->tc = 0; + } } -static void -launch_text_generator (struct terminal_controller_data *mine) +static int +terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n) { - char *oprogram = get_string_resource ("program", "Program"); - char *program; + if (!mine || !mine->tc) { + return 0; + } else { + int i, count = 0; + for (i = 0; i < n; i++) { + int c = textclient_getc (mine->tc); + if (c <= 0) break; + buf[i] = c; + mine->lastc = c; + count++; + } + return count; + } +} - if (!oprogram || !*oprogram) - oprogram = FORTUNE_PROGRAM; - program = (char *) malloc (strlen (oprogram) + 10); +static int +terminal_keypress_handler (Display *dpy, XEvent *event, void *data) +{ + struct terminal_controller_data *mine = + (struct terminal_controller_data *) data; + mine->dpy = dpy; + if (event->xany.type == KeyPress && mine->tc) + return textclient_putc (mine->tc, &event->xkey); + return 0; +} - strcpy (program, "( "); - strcat (program, oprogram); - strcat (program, " ) 2>&1"); - if (mine->pipe) abort(); - if ((mine->pipe = popen (program, "r"))) +static void +a2_ascii_printc (apple2_state_t *st, unsigned char c, + Bool bold_p, Bool blink_p, Bool rev_p, + Bool scroll_p) +{ + if (c >= 'a' && c <= 'z') /* upcase lower-case chars */ { - if (mine->pipe_id) abort(); - mine->pipe_id = - XtAppAddInput (app, fileno (mine->pipe), - (XtPointer) (XtInputReadMask | XtInputExceptMask), - subproc_cb, (XtPointer) mine); + c &= 0xDF; } - else + else if ((c >= 'A'+128) || /* upcase and blink */ + (c < ' ' && c != 014 && /* high-bit & ctl chrs */ + c != '\r' && c != '\n' && c!='\t')) { - perror (program); + c = (c & 0x1F) | 0x80; + } + else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */ + { + c |= 0x80; } -} -static void -relaunch_generator_timer (XtPointer closure, XtIntervalId *id) -{ - struct terminal_controller_data *mine = - (struct terminal_controller_data *) closure; - mine->timeout_id=0; - launch_text_generator (mine); -} + if (bold_p) c |= 0xc0; + if (blink_p) c = (c & ~0x40) | 0x80; + if (rev_p) c |= 0xc0; -static void -terminal_closegen(struct terminal_controller_data *mine) -{ - if (mine->pipe_id) { - XtRemoveInput (mine->pipe_id); - mine->pipe_id = 0; - } - if (mine->pipe) { - pclose (mine->pipe); - mine->pipe = 0; - } - if (mine->timeout_id) { - XtRemoveTimeOut(mine->timeout_id); - mine->timeout_id=0; - } + if (scroll_p) + a2_printc(st, c); + else + a2_printc_noscroll(st, c); } -static int -terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n) + +static void +a2_vt100_printc (apple2_sim_t *sim, struct terminal_controller_data *state, + unsigned char c) { - int rc; - if (mine->fake_nl) { - buf[0]='\n'; - mine->fake_nl=0; - return 1; - } + apple2_state_t *st=sim->st; + int cols = SCREEN_COLS; + int rows = SCREEN_ROWS; - if (!mine->input_available_p) return 0; + int i; + int start, end; - rc=read (fileno (mine->pipe), (void *) buf, n); - if (rc>0) mine->lastc=buf[rc-1]; + /* Mostly duplicated in phosphor.c */ - if (rc<=0) + switch (state->escstate) { - terminal_closegen(mine); + case 0: + switch (c) + { + case 7: /* BEL */ + /* Dummy case - we don't want the screensaver to beep */ + /* #### But maybe this should flash the screen? */ + break; + case 8: /* BS */ + if (state->cursor_x > 0) + state->cursor_x--; + break; + case 9: /* HT */ + if (state->cursor_x < cols - 8) + { + state->cursor_x = (state->cursor_x & ~7) + 8; + } + else + { + state->cursor_x = 0; + if (state->cursor_y < rows - 1) + state->cursor_y++; + else + a2_scroll (st); + } + break; + case 10: /* LF */ +# ifndef HAVE_FORKPTY + state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */ +# endif + case 11: /* VT */ + case 12: /* FF */ + if (state->cursor_y < rows - 1) + state->cursor_y++; + else + a2_scroll (st); + break; + case 13: /* CR */ + state->cursor_x = 0; + break; + case 14: /* SO */ + case 15: /* SI */ + /* Dummy case - there is one and only one font. */ + break; + case 24: /* CAN */ + case 26: /* SUB */ + /* Dummy case - these interrupt escape sequences, so + they don't do anything in this state */ + break; + case 27: /* ESC */ + state->escstate = 1; + break; + case 127: /* DEL */ + /* Dummy case - this is supposed to be ignored */ + break; + case 155: /* CSI */ + state->escstate = 2; + for(i = 0; i < NPAR; i++) + state->csiparam[i] = 0; + state->curparam = 0; + break; + default: + + /* states 102-106 are for UTF-8 decoding */ + + if ((c & 0xE0) == 0xC0) { /* 110xxxxx - 11 bits, 2 bytes */ + state->unicruds = 1; + state->unicrud[0] = c; + state->escstate = 102; + break; + } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx - 16 bits, 3 bytes */ + state->unicruds = 1; + state->unicrud[0] = c; + state->escstate = 103; + break; + } else if ((c & 0xF8) == 0xF0) { /* 11110xxx - 21 bits, 4 bytes */ + state->unicruds = 1; + state->unicrud[0] = c; + state->escstate = 104; + break; + } else if ((c & 0xFC) == 0xF8) { /* 111110xx - 26 bits, 5 bytes */ + state->unicruds = 1; + state->unicrud[0] = c; + state->escstate = 105; + break; + } else if ((c & 0xFE) == 0xFC) { /* 1111110x - 31 bits, 6 bytes */ + state->unicruds = 1; + state->unicrud[0] = c; + state->escstate = 106; + break; + } - if (mine->lastc != '\n') { /* add a newline at eof if there wasn't one */ - mine->fake_nl=1; - } + PRINT: - /* Set up a timer to re-launch the subproc in a bit. */ - mine->timeout_id = - XtAppAddTimeOut(app, subproc_relaunch_delay, - relaunch_generator_timer, - (XtPointer) mine); - } + /* If the cursor is in column 39 and we print a character, then + that character shows up in column 39, and the cursor is no longer + visible on the screen (it's in "column 40".) If another character + is printed, then that character shows up in column 0, and the + cursor moves to column 1. + + This is empirically what xterm and gnome-terminal do, so that must + be the right thing. (In xterm, the cursor vanishes, whereas; in + gnome-terminal, the cursor overprints the character in col 39.) + */ + if (state->cursor_x >= cols) + { + state->cursor_x = 0; + if (state->cursor_y >= rows - 1) + a2_scroll (st); + else + state->cursor_y++; + } + + a2_goto(st, state->cursor_y, state->cursor_x); /* clips range */ + a2_ascii_printc (st, c, + state->termattrib.bf.bold, + state->termattrib.bf.blink, + state->termattrib.bf.rev, + False); + state->cursor_x++; + + break; + } + break; + case 1: + switch (c) + { + case 24: /* CAN */ + case 26: /* SUB */ + state->escstate = 0; + break; + case 'c': /* Reset */ + a2_cls(st); + state->escstate = 0; + break; + case 'D': /* Linefeed */ + if (state->cursor_y < rows - 1) + state->cursor_y++; + else + a2_scroll (st); + state->escstate = 0; + break; + case 'E': /* Newline */ + state->cursor_x = 0; + state->escstate = 0; + break; + case 'M': /* Reverse newline */ + if (state->cursor_y > 0) + state->cursor_y--; + state->escstate = 0; + break; + case '7': /* Save state */ + state->saved_x = state->cursor_x; + state->saved_y = state->cursor_y; + state->escstate = 0; + break; + case '8': /* Restore state */ + state->cursor_x = state->saved_x; + state->cursor_y = state->saved_y; + state->escstate = 0; + break; + case '[': /* CSI */ + state->escstate = 2; + for(i = 0; i < NPAR; i++) + state->csiparam[i] = 0; + state->curparam = 0; + break; + case '%': /* Select charset */ + /* @: Select default (ISO 646 / ISO 8859-1) + G: Select UTF-8 + 8: Select UTF-8 (obsolete) + + We can just ignore this and always process UTF-8, I think? + We must still catch the last byte, though. + */ + case '(': + case ')': + /* I don't support different fonts either - see above + for SO and SI */ + state->escstate = 3; + break; + default: + /* Escape sequences not supported: + * + * H - Set tab stop + * Z - Terminal identification + * > - Keypad change + * = - Other keypad change + * ] - OS command + */ + state->escstate = 0; + break; + } + break; + case 2: + switch (c) + { + case 24: /* CAN */ + case 26: /* SUB */ + state->escstate = 0; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (state->curparam < NPAR) + state->csiparam[state->curparam] = + (state->csiparam[state->curparam] * 10) + (c - '0'); + break; + case ';': + state->csiparam[++state->curparam] = 0; + break; + case '[': + state->escstate = 3; + break; + case '@': + for (i = 0; i < state->csiparam[0]; i++) + { + if(++state->cursor_x > cols) + { + state->cursor_x = 0; + if (state->cursor_y < rows - 1) + state->cursor_y++; + else + a2_scroll (st); + } + } + state->escstate = 0; + break; + case 'F': + state->cursor_x = 0; + case 'A': + if (state->csiparam[0] == 0) + state->csiparam[0] = 1; + if ((state->cursor_y -= state->csiparam[0]) < 0) + state->cursor_y = 0; + state->escstate = 0; + break; + case 'E': + state->cursor_x = 0; + case 'e': + case 'B': + if (state->csiparam[0] == 0) + state->csiparam[0] = 1; + if ((state->cursor_y += state->csiparam[0]) >= rows) + state->cursor_y = rows - 1; + state->escstate = 0; + break; + case 'a': + case 'C': + if (state->csiparam[0] == 0) + state->csiparam[0] = 1; + if ((state->cursor_x += state->csiparam[0]) >= cols) + state->cursor_x = cols - 1; + state->escstate = 0; + break; + case 'D': + if (state->csiparam[0] == 0) + state->csiparam[0] = 1; + if ((state->cursor_x -= state->csiparam[0]) < 0) + state->cursor_x = 0; + state->escstate = 0; + break; + case 'd': + if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows) + state->cursor_y = rows - 1; + state->escstate = 0; + break; + case '`': + case 'G': + if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols) + state->cursor_x = cols - 1; + state->escstate = 0; + break; + case 'f': + case 'H': + if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows) + state->cursor_y = rows - 1; + if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols) + state->cursor_x = cols - 1; + if(state->cursor_y < 0) + state->cursor_y = 0; + if(state->cursor_x < 0) + state->cursor_x = 0; + state->escstate = 0; + break; + case 'J': + start = 0; + end = rows * cols; + if (state->csiparam[0] == 0) + start = cols * state->cursor_y + state->cursor_x; + if (state->csiparam[0] == 1) + end = cols * state->cursor_y + state->cursor_x; + + a2_goto(st, state->cursor_y, state->cursor_x); + for (i = start; i < end; i++) + { + a2_ascii_printc(st, ' ', False, False, False, False); + } + state->escstate = 0; + break; + case 'K': + start = 0; + end = cols; + if (state->csiparam[0] == 0) + start = state->cursor_x; + if (state->csiparam[1] == 1) + end = state->cursor_x; + + a2_goto(st, state->cursor_y, state->cursor_x); + for (i = start; i < end; i++) + { + a2_ascii_printc(st, ' ', False, False, False, False); + } + state->escstate = 0; + break; + case 'm': /* Set attributes */ + for (i = 0; i <= state->curparam; i++) + { + switch(state->csiparam[i]) + { + case 0: + state->termattrib.w = 0; + break; + case 1: + state->termattrib.bf.bold = 1; + break; + case 5: + state->termattrib.bf.blink = 1; + break; + case 7: + state->termattrib.bf.rev = 1; + break; + case 21: + case 22: + state->termattrib.bf.bold = 0; + break; + case 25: + state->termattrib.bf.blink = 0; + break; + case 27: + state->termattrib.bf.rev = 0; + break; + } + } + state->escstate = 0; + break; + case 's': /* Save position */ + state->saved_x = state->cursor_x; + state->saved_y = state->cursor_y; + state->escstate = 0; + break; + case 'u': /* Restore position */ + state->cursor_x = state->saved_x; + state->cursor_y = state->saved_y; + state->escstate = 0; + break; + case '?': /* DEC Private modes */ + if ((state->curparam != 0) || (state->csiparam[0] != 0)) + state->escstate = 0; + break; + default: + /* Known unsupported CSIs: + * + * L - Insert blank lines + * M - Delete lines (I don't know what this means...) + * P - Delete characters + * X - Erase characters (difference with P being...?) + * c - Terminal identification + * g - Clear tab stop(s) + * h - Set mode (Mainly due to its complexity and lack of good + docs) + * l - Clear mode + * m - Set mode (Phosphor is, per defenition, green on black) + * n - Status report + * q - Set keyboard LEDs + * r - Set scrolling region (too exhausting - noone uses this, + right?) + */ + state->escstate = 0; + break; + } + break; + case 3: + state->escstate = 0; + break; - mine->input_available_p = False; + case 102: + case 103: + case 104: + case 105: + case 106: + { + int total = state->escstate - 100; /* see what I did there */ + if (state->unicruds < total) { + /* Buffer more bytes of the UTF-8 sequence */ + state->unicrud[state->unicruds++] = c; + } - return rc; + if (state->unicruds >= total) { + /* Done! Convert it to ASCII and print that. */ + char *s; + state->unicrud[state->unicruds] = 0; + s = utf8_to_latin1 ((const char *) state->unicrud, True); + state->unicruds = 0; + state->escstate = 0; + if (s) { + c = s[0]; + free (s); + goto PRINT; + } else { + /* c = 0; */ + } + } + } + break; + + default: + abort(); + } + a2_goto(st, state->cursor_y, state->cursor_x); } @@ -836,7 +1330,7 @@ terminal_read(struct terminal_controller_data *mine, unsigned char *buf, int n) GNU/Linux dammit) occupies an entire screen on the Apple ][. */ -void +static void terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) { apple2_state_t *st=sim->st; @@ -847,6 +1341,9 @@ terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) if (!sim->controller_data) sim->controller_data=calloc(sizeof(struct terminal_controller_data),1); mine=(struct terminal_controller_data *) sim->controller_data; + mine->dpy = sim->dpy; + + mine->fast_p = global_fast_p; switch(*stepno) { @@ -858,49 +1355,57 @@ terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) a2_goto(st,0,16); a2_prints(st, "APPLE ]["); a2_goto(st,2,0); + mine->cursor_y = 2; + + if (! mine->tc) { + mine->tc = textclient_open (mine->dpy); + textclient_reshape (mine->tc, + SCREEN_COLS, SCREEN_ROWS, + SCREEN_COLS, SCREEN_ROWS, + 0); + } - if (! mine->pipe) - launch_text_generator(mine); - - *next_actiontime += 4.0; + if (! mine->fast_p) + *next_actiontime += 4.0; *stepno = 10; + + mine->last_emit_time = sim->curtime; break; case 10: + case 11: { - unsigned char buf[5]; + Bool first_line_p = (*stepno == 10); + unsigned char buf[1024]; int nr,nwant; double elapsed; elapsed=sim->curtime - mine->last_emit_time; - mine->last_emit_time=sim->curtime; - nwant=elapsed*25.0; - if (elapsed>1.0) nwant=1; - if (nwant<1) nwant=1; - if (nwant>4) nwant=4; + + nwant = elapsed * 25.0; /* characters per second */ + + if (first_line_p) { + *stepno = 11; + nwant = 1; + } + + if (nwant > 40) nwant = 40; + + if (mine->fast_p) + nwant = sizeof(buf)-1; + + if (nwant <= 0) break; + + mine->last_emit_time = sim->curtime; nr=terminal_read(mine, buf, nwant); for (i=0; i= 'a' && c <= 'z') /* upcase lower-case chars */ - { - a2_printc(st, c&0xDF); - } - else if ((c >= 'A'+128) || /* upcase and blink */ - (c < ' ' && c != 014 && /* high-bit & ctl chrs */ - c != '\r' && c != '\n' && c!='\t')) - { - a2_printc(st, (c & 0x1F) | 0x80); - } - else if (c >= 'A' && c <= 'Z') /* invert upper-case chars */ - { - a2_printc(st, c | 0x80); - } - else { - a2_printc(st, c); - } + + if (mine->tc) + a2_vt100_printc (sim, mine, c); + else + a2_ascii_printc (st, c, False, False, False, True); } } break; @@ -908,6 +1413,7 @@ terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) case A2CONTROLLER_FREE: terminal_closegen(mine); free(mine); + mine = 0; return; } } @@ -915,7 +1421,7 @@ terminal_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) struct basic_controller_data { int prog_line; int x,y,k; - char **progtext; + const char * const * progtext; int progstep; char *rep_str; int rep_pos; @@ -927,31 +1433,31 @@ struct basic_controller_data { Adding more programs is easy. Just add a listing here and to all_programs, then add the state machine to actually execute it to basic_controller. */ -static char *moire_program[]={ +static const char * const moire_program[]={ "10 HGR2\n", - "20 FOR Y = 0 TO 191 STEP 2\n", + "20 FOR Y = 0 TO 190 STEP 2\n", "30 HCOLOR=4 : REM BLACK\n", - "40 HLINE 0,191-Y TO 279,Y\n", + "40 HPLOT 0,191-Y TO 279,Y\n", "60 HCOLOR=7 : REM WHITE\n", - "80 HLINE 0,190-Y TO 279,Y+1\n", + "80 HPLOT 0,190-Y TO 279,Y+1\n", "90 NEXT Y\n", - "100 FOR X = 0 TO 279 STEP 3\n", + "100 FOR X = 0 TO 278 STEP 3\n", "110 HCOLOR=4\n", - "120 HLINE 279-X,0 TO X,192\n", + "120 HPLOT 279-X,0 TO X,191\n", "140 HCOLOR=7\n", - "150 HLINE 278-X,0 TO X+1,192\n", + "150 HPLOT 278-X,0 TO X+1,191\n", "160 NEXT X\n", NULL }; -static char *sinewave_program[] = { +static const char * const sinewave_program[] = { "10 HGR\n", "25 K=0\n", "30 FOR X = 0 TO 279\n", "32 HCOLOR= 0\n", - "35 HLINE X,0 TO X,159\n", + "35 HPLOT X,0 TO X,159\n", "38 HCOLOR= 3\n", - "40 Y = 80 + SIN(15*(X-K)/279)\n", + "40 Y = 80 + SIN(15*(X-K)/279) * 40\n", "50 HPLOT X,Y\n", "60 NEXT X\n", "70 K=K+4\n", @@ -960,26 +1466,26 @@ static char *sinewave_program[] = { }; #if 0 -static char *dumb_program[]={ +static const char * const dumb_program[]={ "10 PRINT \"APPLE ][ ROOLZ! TRS-80 DROOLZ!\"\n", "20 GOTO 10\n", NULL }; #endif -static char *random_lores_program[]={ +static const char * const random_lores_program[]={ "1 REM APPLE ][ SCREEN SAVER\n", "10 GR\n", "100 COLOR= RND(1)*16\n", "110 X=RND(1)*40\n", - "120 Y1=RND(1)*48\n", - "130 Y2=RND(1)*48\n", + "120 Y1=RND(1)*40\n", + "130 Y2=RND(1)*40\n", "140 FOR Y = Y1 TO Y2\n", "150 PLOT X,Y\n", "160 NEXT Y\n", - "210 Y=RND(1)*48\n", + "210 Y=RND(1)*40\n", "220 X1=RND(1)*40\n", "230 X2=RND(1)*40\n", "240 FOR X = X1 TO X2\n", @@ -992,7 +1498,7 @@ static char *random_lores_program[]={ static char typo_map[256]; -int make_typo(char *out_buf, char *orig, char *err_buf) +static int make_typo(char *out_buf, const char *orig, char *err_buf) { int i,j; int errc; @@ -1072,7 +1578,7 @@ int make_typo(char *out_buf, char *orig, char *err_buf) break; } - if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(u_char)p[0]])) { + if (random()%10==0 && strlen(p)>=4 && (errc=typo_map[(int)(unsigned char)p[0]])) { int remain=strlen(p); int past=random()%(remain-2)+1; memmove(p+past+past, p, remain+1); @@ -1086,8 +1592,8 @@ int make_typo(char *out_buf, char *orig, char *err_buf) return success; } -struct { - char **progtext; +static const struct { + const char * const * progtext; int progstep; } all_programs[]={ {moire_program, 100}, @@ -1096,7 +1602,7 @@ struct { {random_lores_program, 500}, }; -void +static void basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) { apple2_state_t *st=sim->st; @@ -1270,52 +1776,138 @@ basic_controller(apple2_sim_t *sim, int *stepno, double *next_actiontime) case A2CONTROLLER_FREE: free(mine); + mine = 0; break; } } -void (*controllers[])(apple2_sim_t *sim, int *stepno, - double *next_actiontime) = { +static void (* const controllers[]) (apple2_sim_t *sim, int *stepno, + double *next_actiontime) = { slideshow_controller, terminal_controller, basic_controller }; -void -screenhack (Display *dpy, Window window) +struct state { + int duration; + Bool random_p; + apple2_sim_t *sim; + void (*controller) (apple2_sim_t *sim, int *stepno, double *next_actiontime); +}; + + +static void * +apple2_init (Display *dpy, Window window) { - int duration = get_integer_resource ("duration", "Integer"); + struct state *st = (struct state *) calloc (1, sizeof(*st)); char *s; - void (*controller)(apple2_sim_t *sim, int *stepno, double *next_actiontime); - if (duration < 1) duration = 1; + st->duration = get_integer_resource (dpy, "duration", "Integer"); + + st->controller = 0; + if (st->duration < 1) st->duration = 1; - s = get_string_resource ("mode", "Mode"); + s = get_string_resource (dpy, "mode", "Mode"); if (!s || !*s || !strcasecmp(s, "random")) - controller = controllers[random() % (countof(controllers))]; + st->random_p = True; else if (!strcasecmp(s, "text")) - controller = terminal_controller; + st->controller = terminal_controller; else if (!strcasecmp(s, "slideshow")) - controller = slideshow_controller; + st->controller = slideshow_controller; else if (!strcasecmp(s, "basic")) - controller = basic_controller; + st->controller = basic_controller; else { fprintf (stderr, "%s: mode must be text, slideshow, or random; not %s\n", progname, s); exit (1); } + if (s) free (s); - if (!get_boolean_resource ("root", "Boolean")) - { - XWindowAttributes xgwa; - XGetWindowAttributes (dpy, window, &xgwa); - XSelectInput (dpy, window, - xgwa.your_event_mask | - KeyPressMask | ButtonPressMask | ExposureMask); - } + global_program = get_string_resource (dpy, "program", "Program"); + global_fast_p = get_boolean_resource (dpy, "fast", "Boolean"); + + + /* Kludge for MacOS standalone mode: see OSX/SaverRunner.m. */ + { + const char *s = getenv ("XSCREENSAVER_STANDALONE"); + if (s && *s && strcmp(s, "0")) + { + st->controller = terminal_controller; + st->random_p = False; + global_program = getenv ("SHELL"); + global_fast_p = True; + } + } + + + if (! st->random_p) { + if (st->controller == terminal_controller || + st->controller == slideshow_controller) + st->duration = 999999; /* these run "forever" */ + } + + return st; +} + +static unsigned long +apple2_draw (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + + if (! st->sim) { + if (st->random_p) + st->controller = controllers[random() % (countof(controllers))]; + st->sim = apple2_start (dpy, window, st->duration, st->controller); + } + + if (! apple2_one_frame (st->sim)) { + st->sim = 0; + } + +#ifdef HAVE_MOBILE + return 0; +#else + return 5000; +#endif +} - apple2 (dpy, window, duration, controller); - XSync (dpy, False); +static void +apple2_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) +{ + struct state *st = (struct state *) closure; + if (st->sim) + analogtv_reconfigure (st->sim->dec); } + +static Bool +apple2_event (Display *dpy, Window window, void *closure, XEvent *event) +{ + struct state *st = (struct state *) closure; + + if (st->sim && + st->controller == terminal_controller && + event->xany.type == KeyPress) { + terminal_keypress_handler (dpy, event, st->sim->controller_data); + return True; + } + + return False; +} + +static void +apple2_free (Display *dpy, Window window, void *closure) +{ + struct state *st = (struct state *) closure; + if (st->sim) { + st->sim->stepno = A2CONTROLLER_DONE; + if (apple2_one_frame (st->sim)) + abort(); /* should have freed! */ + } + free (st); +} + + +XSCREENSAVER_MODULE ("Apple2", apple2)