X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=hacks%2Fphosphor.c;h=825ad56931d25ff9858ae5a2744ed8841924cb8e;hb=d5186197bc394e10a4402f7f6d23fbb14103bc50;hp=01818b75590f707d2de41770b64915052390fdfc;hpb=9c9d475ff889ed8be02e8ce8c17da28b93278fca;p=xscreensaver diff --git a/hacks/phosphor.c b/hacks/phosphor.c index 01818b75..825ad569 100644 --- a/hacks/phosphor.c +++ b/hacks/phosphor.c @@ -1,4 +1,4 @@ -/* xscreensaver, Copyright (c) 1999, 2000, 2004 Jamie Zawinski +/* xscreensaver, Copyright (c) 1999-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 @@ -13,29 +13,17 @@ * Pty and vt100 emulation by Fredrik Tolf */ -#include "screenhack.h" - -#include -#include -#include - -#include -#include -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif /* HAVE_CONFIG_H */ -#define XK_MISCELLANY -#include - -#ifdef HAVE_FORKPTY -# ifdef HAVE_PTY_H -# include -# endif -# ifdef HAVE_UTIL_H -# include -# endif -#endif /* HAVE_FORKPTY */ +#ifndef HAVE_COCOA +# include +#endif -extern XtAppContext app; +#include "screenhack.h" +#include "textclient.h" +#include "utf8wc.h" #define FUZZY_BORDER @@ -52,6 +40,12 @@ extern XtAppContext app; #define NPAR 16 +#define BUILTIN_FONT + +#ifdef BUILTIN_FONT +# include "images/6x10font.xbm" +#endif /* BUILTIN_FONT */ + typedef struct { unsigned char name; int width, height; @@ -73,16 +67,19 @@ typedef struct { Window window; XWindowAttributes xgwa; XFontStruct *font; + const char *program; int grid_width, grid_height; int char_width, char_height; int saved_x, saved_y; int scale; int ticks; int mode; - pid_t pid; + int escstate; int csiparam[NPAR]; int curparam; + int unicruds; char unicrud[7]; + p_char **chars; p_cell *cells; XGCValues gcv; @@ -97,23 +94,20 @@ typedef struct { int cursor_x, cursor_y; XtIntervalId cursor_timer; Time cursor_blink; + int delay; + Bool pty_p; - FILE *pipe; - XtInputId pipe_id; - Bool input_available_p; - Time subproc_relaunch_delay; - XComposeStatus compose; - Bool meta_sends_esc_p; - Bool swap_bs_del_p; + text_data *tc; + + char last_c; + int bk; } p_state; static void capture_font_bits (p_state *state); static p_char *make_character (p_state *state, int c); -static void drain_input (p_state *state); static void char_to_pixmap (p_state *state, p_char *pc, int c); -static void launch_text_generator (p_state *state); /* About font metrics: @@ -145,72 +139,77 @@ static void launch_text_generator (p_state *state); that box (the "charcell" box) then we're going to lose it. Alas. */ -static p_state * -init_phosphor (Display *dpy, Window window) + +static void clear (p_state *); +static void set_cursor (p_state *, Bool on); + +static unsigned short scale_color_channel (unsigned short ch1, unsigned short ch2) +{ + return (ch1 * 100 + ch2 * 156) >> 8; +} + +static void * +phosphor_init (Display *dpy, Window window) { int i; unsigned long flags; p_state *state = (p_state *) calloc (sizeof(*state), 1); - char *fontname = get_string_resource ("font", "Font"); + char *fontname = get_string_resource (dpy, "font", "Font"); XFontStruct *font; state->dpy = dpy; state->window = window; XGetWindowAttributes (dpy, window, &state->xgwa); - XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask); +/* XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask);*/ - state->meta_sends_esc_p = get_boolean_resource ("metaSendsESC", "Boolean"); - state->swap_bs_del_p = get_boolean_resource ("swapBSDEL", "Boolean"); + state->delay = get_integer_resource (dpy, "delay", "Integer"); + state->pty_p = get_boolean_resource (dpy, "usePty", "UsePty"); - state->font = XLoadQueryFont (dpy, fontname); - - if (!state->font) + if (!strcasecmp (fontname, "builtin") || + !strcasecmp (fontname, "(builtin)")) { - fprintf(stderr, "couldn't load font \"%s\"\n", fontname); +#ifndef BUILTIN_FONT + fprintf (stderr, "%s: no builtin font\n", progname); state->font = XLoadQueryFont (dpy, "fixed"); +#endif /* !BUILTIN_FONT */ } - if (!state->font) + else { - fprintf(stderr, "couldn't load font \"fixed\""); - exit(1); + state->font = XLoadQueryFont (dpy, fontname); + + if (!state->font) + { + fprintf(stderr, "couldn't load font \"%s\"\n", fontname); + state->font = XLoadQueryFont (dpy, "fixed"); + } + if (!state->font) + { + fprintf(stderr, "couldn't load font \"fixed\""); + exit(1); + } } font = state->font; - state->scale = get_integer_resource ("scale", "Integer"); - state->ticks = STATE_MAX + get_integer_resource ("ticks", "Integer"); + state->scale = get_integer_resource (dpy, "scale", "Integer"); + state->ticks = STATE_MAX + get_integer_resource (dpy, "ticks", "Integer"); state->escstate = 0; - { - char *s = get_string_resource ("mode", "Integer"); - state->mode = 0; - if (!s || !*s || !strcasecmp (s, "pipe")) - state->mode = 0; - else if (!strcasecmp (s, "pty")) - state->mode = 1; - else - fprintf (stderr, "%s: mode must be either `pipe' or `pty', not `%s'\n", - progname, s); - -#ifndef HAVE_FORKPTY - fprintf (stderr, "%s: no pty support on this system; using -pipe mode.\n", - progname); - state->mode = 0; -#endif /* HAVE_FORKPTY */ - } -#if 0 - for (i = 0; i < font->n_properties; i++) - if (font->properties[i].name == XA_FONT) - printf ("font: %s\n", XGetAtomName(dpy, font->properties[i].card32)); -#endif /* 0 */ + state->cursor_blink = get_integer_resource (dpy, "cursor", "Time"); - state->cursor_blink = get_integer_resource ("cursor", "Time"); - state->subproc_relaunch_delay = - (1000 * get_integer_resource ("relaunch", "Time")); - - state->char_width = font->max_bounds.width; - state->char_height = font->max_bounds.ascent + font->max_bounds.descent; +# ifdef BUILTIN_FONT + if (! font) + { + state->char_width = (font6x10_width / 256) - 1; + state->char_height = font6x10_height; + } + else +# endif /* BUILTIN_FONT */ + { + state->char_width = font->max_bounds.width; + state->char_height = font->max_bounds.ascent + font->max_bounds.descent; + } state->grid_width = state->xgwa.width / (state->char_width * state->scale); state->grid_height = state->xgwa.height /(state->char_height * state->scale); @@ -226,27 +225,35 @@ init_phosphor (Display *dpy, Window window) int h1, h2; double s1, s2, v1, v2; - unsigned long fg = get_pixel_resource ("foreground", "Foreground", - state->dpy, state->xgwa.colormap); - unsigned long bg = get_pixel_resource ("background", "Background", - state->dpy, state->xgwa.colormap); - unsigned long flare = get_pixel_resource ("flareForeground", "Foreground", - state->dpy,state->xgwa.colormap); - unsigned long fade = get_pixel_resource ("fadeForeground", "Foreground", - state->dpy,state->xgwa.colormap); + unsigned long fg = get_pixel_resource (state->dpy, state->xgwa.colormap, + "foreground", "Foreground"); + unsigned long bg = get_pixel_resource (state->dpy, state->xgwa.colormap, + "background", "Background"); + unsigned long flare = fg; - XColor start, end; + XColor fg_color, bg_color; - start.pixel = fade; - XQueryColor (state->dpy, state->xgwa.colormap, &start); + fg_color.pixel = fg; + XQueryColor (state->dpy, state->xgwa.colormap, &fg_color); - end.pixel = bg; - XQueryColor (state->dpy, state->xgwa.colormap, &end); + bg_color.pixel = bg; + XQueryColor (state->dpy, state->xgwa.colormap, &bg_color); /* Now allocate a ramp of colors from the main color to the background. */ - rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); - rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2); - make_color_ramp (state->dpy, state->xgwa.colormap, + rgb_to_hsv (scale_color_channel(fg_color.red, bg_color.red), + scale_color_channel(fg_color.green, bg_color.green), + scale_color_channel(fg_color.blue, bg_color.blue), + &h1, &s1, &v1); + rgb_to_hsv (bg_color.red, bg_color.green, bg_color.blue, &h2, &s2, &v2); + + /* Avoid rainbow effects when fading to black/grey/white. */ + if (s2 < 0.003) + h2 = h1; + if (s1 < 0.003) + h1 = h2; + + make_color_ramp (state->xgwa.screen, state->xgwa.visual, + state->xgwa.colormap, h1, s1, v1, h2, s2, v2, colors, &ncolors, @@ -255,9 +262,24 @@ init_phosphor (Display *dpy, Window window) /* Adjust to the number of colors we actually got. */ state->ticks = ncolors + STATE_MAX; + /* If the foreground is brighter than the background, the flare is white. + * Otherwise, the flare is left at the foreground color (i.e. no flare). */ + rgb_to_hsv (fg_color.red, fg_color.green, fg_color.blue, &h1, &s1, &v1); + if (v2 <= v1) + { + XColor white; + /* WhitePixel is only for the default visual, which can be overridden + * on the command line. */ + white.red = 0xffff; + white.green = 0xffff; + white.blue = 0xffff; + if (XAllocColor(state->dpy, state->xgwa.colormap, &white)) + flare = white.pixel; + } + /* Now, GCs all around. */ - state->gcv.font = font->fid; + state->gcv.font = (font ? font->fid : 0); state->gcv.cap_style = CapRound; #ifdef FUZZY_BORDER state->gcv.line_width = (int) (((long) state->scale) * 1.3); @@ -296,7 +318,17 @@ init_phosphor (Display *dpy, Window window) capture_font_bits (state); - launch_text_generator (state); + set_cursor (state, True); + +/* clear (state);*/ + + state->tc = textclient_open (dpy); + textclient_reshape (state->tc, + state->xgwa.width, + state->xgwa.height, + state->grid_width - 1, + state->grid_height - 1, + 0); return state; } @@ -304,7 +336,7 @@ init_phosphor (Display *dpy, Window window) /* Re-query the window size and update the internal character grid if changed. */ -static void +static Bool resize_grid (p_state *state) { int ow = state->grid_width; @@ -319,7 +351,7 @@ resize_grid (p_state *state) if (ow == state->grid_width && oh == state->grid_height) - return; + return False; state->cells = (p_cell *) calloc (sizeof(p_cell), state->grid_width * state->grid_height); @@ -341,6 +373,7 @@ resize_grid (p_state *state) state->cursor_y = state->grid_height-1; free (ocells); + return True; } @@ -348,12 +381,33 @@ static void capture_font_bits (p_state *state) { XFontStruct *font = state->font; - int safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing; - int height = state->char_height; + int safe_width, height; unsigned char string[257]; int i; - Pixmap p = XCreatePixmap (state->dpy, state->window, - (safe_width * 256), height, 1); + Pixmap p; + +# ifdef BUILTIN_FONT + Pixmap p2 = 0; + + if (!font) + { + safe_width = state->char_width + 1; + height = state->char_height; + p2 = XCreatePixmapFromBitmapData (state->dpy, state->window, + (char *) font6x10_bits, + font6x10_width, + font6x10_height, + 1, 0, 1); + } + else +# endif /* BUILTIN_FONT */ + { + safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing; + height = state->char_height; + } + + p = XCreatePixmap (state->dpy, state->window, + (safe_width * 256), height, 1); for (i = 0; i < 256; i++) string[i] = (unsigned char) i; @@ -367,10 +421,16 @@ capture_font_bits (p_state *state) state->gcv.foreground = 1; state->gc1 = XCreateGC (state->dpy, p, - (GCFont | GCForeground | GCBackground | + ((font ? GCFont : 0) | + GCForeground | GCBackground | GCCapStyle | GCLineWidth), &state->gcv); +#ifdef HAVE_COCOA + jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False); + jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False); +#endif + #ifdef FUZZY_BORDER { state->gcv.line_width = (int) (((long) state->scale) * 0.8); @@ -379,7 +439,8 @@ capture_font_bits (p_state *state) if (state->gcv.line_width < 1) state->gcv.line_width = 1; state->gc2 = XCreateGC (state->dpy, p, - (GCFont | GCForeground | GCBackground | + ((font ? GCFont : 0) | + GCForeground | GCBackground | GCCapStyle | GCLineWidth), &state->gcv); } @@ -387,31 +448,40 @@ capture_font_bits (p_state *state) XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height); - for (i = 0; i < 256; i++) +# ifdef BUILTIN_FONT + if (p2) { - if (string[i] < font->min_char_or_byte2 || - string[i] > font->max_char_or_byte2) - continue; - XDrawString (state->dpy, p, state->gc1, - i * safe_width, font->ascent, - (char *) (string + i), 1); + XCopyPlane (state->dpy, p2, p, state->gc1, + 0, 0, font6x10_width, font6x10_height, + 0, 0, 1); + XFreePixmap (state->dpy, p2); + } + else +# endif /* BUILTIN_FONT */ + { + for (i = 0; i < 256; i++) + { + if (string[i] < font->min_char_or_byte2 || + string[i] > font->max_char_or_byte2) + continue; + XDrawString (state->dpy, p, state->gc1, + i * safe_width, font->ascent, + (char *) (string + i), 1); + } } /* Draw the cursor. */ XFillRectangle (state->dpy, p, state->gc1, (CURSOR_INDEX * safe_width), 1, - (font->per_char - ? font->per_char['n'-font->min_char_or_byte2].width - : font->max_bounds.width), - font->ascent - 1); + (font + ? (font->per_char + ? font->per_char['n'-font->min_char_or_byte2].width + : font->max_bounds.width) + : state->char_width), + (font + ? font->ascent - 1 + : state->char_height)); -#if 0 - XCopyPlane (state->dpy, p, state->window, state->gcs[FLARE], - 0, 0, (safe_width * 256), height, 0, 0, 1L); - XSync(state->dpy, False); -#endif - - XSync (state->dpy, False); state->font_bits = XGetImage (state->dpy, p, 0, 0, (safe_width * 256), height, ~0L, XYPixmap); XFreePixmap (state->dpy, p); @@ -447,13 +517,15 @@ char_to_pixmap (p_state *state, p_char *pc, int c) int x1, y; XFontStruct *font = state->font; - int safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing; + int safe_width = (font + ? font->max_bounds.rbearing - font->min_bounds.lbearing + : state->char_width + 1); int width = state->scale * state->char_width; int height = state->scale * state->char_height; - if (c < font->min_char_or_byte2 || - c > font->max_char_or_byte2) + if (font && (c < font->min_char_or_byte2 || + c > font->max_char_or_byte2)) goto DONE; gc = state->gc1; @@ -553,12 +625,11 @@ set_cursor (p_state *state, Bool on) } - - static void cursor_off_timer (XtPointer closure, XtIntervalId *id) { p_state *state = (p_state *) closure; + XtAppContext app = XtDisplayToApplicationContext (state->dpy); set_cursor_1 (state, False); state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink, cursor_on_timer, closure); @@ -568,6 +639,7 @@ static void cursor_on_timer (XtPointer closure, XtIntervalId *id) { p_state *state = (p_state *) closure; + XtAppContext app = XtDisplayToApplicationContext (state->dpy); set_cursor_1 (state, True); state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink, cursor_off_timer, closure); @@ -660,12 +732,10 @@ scroll (p_state *state) static void print_char (p_state *state, int c) { - static char last_c = 0; - static int bk; - + int cols = state->grid_width; + int rows = state->grid_height; p_cell *cell = &state->cells[state->grid_width * state->cursor_y + state->cursor_x]; - int i, start, end; /* Start the cursor fading (in case we don't end up overwriting it.) */ if (cell->state == FLARE || cell->state == NORMAL) @@ -674,12 +744,18 @@ print_char (p_state *state, int c) cell->changed = True; } - if (state->pid) /* Only interpret VT100 sequences if running in pty-mode. - It would be nice if we could just interpret them all - the time, but that would require subprocesses to send - CRLF line endings instead of bare LF, so that's no good. - */ +#ifdef HAVE_FORKPTY + if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode. + It would be nice if we could just interpret them all + the time, but that would require subprocesses to send + CRLF line endings instead of bare LF, so that's no good. + */ { + int i; + int start, end; + + /* Mostly duplicated in apple2-main.c */ + switch (state->escstate) { case 0: @@ -694,45 +770,47 @@ print_char (p_state *state, int c) state->cursor_x--; break; case 9: /* HT */ - if (state->cursor_x < state->grid_width - 8) + if (state->cursor_x < cols - 8) { state->cursor_x = (state->cursor_x & ~7) + 8; } else { state->cursor_x = 0; - if (state->cursor_y < state->grid_height - 1) + if (state->cursor_y < rows - 1) state->cursor_y++; else scroll (state); } 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(last_c == 13) + if(state->last_c == 13) { cell->state = NORMAL; - cell->p_char = state->chars[bk]; + cell->p_char = state->chars[state->bk]; cell->changed = True; } - if (state->cursor_y < state->grid_height - 1) + if (state->cursor_y < rows - 1) state->cursor_y++; else scroll (state); break; case 13: /* CR */ state->cursor_x = 0; - cell = &state->cells[state->grid_width * state->cursor_y]; + cell = &state->cells[cols * state->cursor_y]; if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX)) - bk = ' '; + state->bk = ' '; else - bk = cell->p_char->name; + state->bk = cell->p_char->name; break; case 14: /* SO */ case 15: /* SI */ - /* Dummy case - I don't want to load several fonts for - the maybe two programs world-wide that use that */ + /* Dummy case - there is one and only one font. */ break; case 24: /* CAN */ case 26: /* SUB */ @@ -752,6 +830,49 @@ print_char (p_state *state, int c) 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; + } + + PRINT: + + /* 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.) + */ cell->state = FLARE; cell->p_char = state->chars[c]; cell->changed = True; @@ -760,10 +881,10 @@ print_char (p_state *state, int c) if (c != ' ' && cell->p_char->blank_p) cell->p_char = state->chars[CURSOR_INDEX]; - if (state->cursor_x >= state->grid_width - 1) + if (state->cursor_x >= cols - 1 /*####*/) { state->cursor_x = 0; - if (state->cursor_y >= state->grid_height - 1) + if (state->cursor_y >= rows - 1) scroll (state); else state->cursor_y++; @@ -783,7 +904,7 @@ print_char (p_state *state, int c) state->escstate = 0; break; case 'D': /* Linefeed */ - if (state->cursor_y < state->grid_height - 1) + if (state->cursor_y < rows - 1) state->cursor_y++; else scroll (state); @@ -814,10 +935,14 @@ print_char (p_state *state, int c) state->csiparam[i] = 0; state->curparam = 0; break; - case '%': /* Select charset */ - /* No, I don't support UTF-8, since the phosphor font - isn't even Unicode anyway. We must still catch the - last byte, though. */ + 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 @@ -858,15 +983,15 @@ print_char (p_state *state, int c) case '@': for (i = 0; i < state->csiparam[0]; i++) { - if(++state->cursor_x > state->grid_width) + if(++state->cursor_x > cols) { state->cursor_x = 0; - if (state->cursor_y < state->grid_height - 1) + if (state->cursor_y < rows - 1) state->cursor_y++; else scroll (state); } - cell = &state->cells[state->grid_width * state->cursor_y + state->cursor_x]; + cell = &state->cells[cols * state->cursor_y + state->cursor_x]; if (cell->state == FLARE || cell->state == NORMAL) { cell->state = FADE; @@ -890,16 +1015,16 @@ print_char (p_state *state, int c) case 'B': if (state->csiparam[0] == 0) state->csiparam[0] = 1; - if ((state->cursor_y += state->csiparam[0]) >= state->grid_height - 1) - state->cursor_y = state->grid_height - 1; + if ((state->cursor_y += state->csiparam[0]) >= rows - 1 /*####*/) + 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]) >= state->grid_width - 1) - state->cursor_x = state->grid_width - 1; + if ((state->cursor_x += state->csiparam[0]) >= cols - 1 /*####*/) + state->cursor_x = cols - 1; state->escstate = 0; break; case 'D': @@ -910,22 +1035,22 @@ print_char (p_state *state, int c) state->escstate = 0; break; case 'd': - if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1) - state->cursor_y = state->grid_height - 1; + if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/) + state->cursor_y = rows - 1; state->escstate = 0; break; case '`': case 'G': - if ((state->cursor_x = (state->csiparam[0] - 1)) >= state->grid_width - 1) - state->cursor_x = state->grid_width - 1; + if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols - 1 /*####*/) + state->cursor_x = cols - 1; state->escstate = 0; break; case 'f': case 'H': - if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1) - state->cursor_y = state->grid_height - 1; - if ((state->cursor_x = (state->csiparam[1] - 1)) >= state->grid_width - 1) - state->cursor_x = state->grid_width - 1; + if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/) + state->cursor_y = rows - 1; + if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols - 1 /*####*/) + state->cursor_x = cols - 1; if(state->cursor_y < 0) state->cursor_y = 0; if(state->cursor_x < 0) @@ -934,11 +1059,11 @@ print_char (p_state *state, int c) break; case 'J': start = 0; - end = state->grid_height * state->grid_width; + end = rows * cols; if (state->csiparam[0] == 0) - start = state->grid_width * state->cursor_y + state->cursor_x; + start = cols * state->cursor_y + state->cursor_x; if (state->csiparam[0] == 1) - end = state->grid_width * state->cursor_y + state->cursor_x; + end = cols * state->cursor_y + state->cursor_x; for (i = start; i < end; i++) { cell = &state->cells[i]; @@ -953,7 +1078,7 @@ print_char (p_state *state, int c) break; case 'K': start = 0; - end = state->grid_width; + end = cols; if (state->csiparam[0] == 0) start = state->cursor_x; if (state->csiparam[1] == 1) @@ -969,6 +1094,9 @@ print_char (p_state *state, int c) } state->escstate = 0; break; + case 'm': /* Set attributes unimplemented (bold, blink, rev) */ + state->escstate = 0; + break; case 's': /* Save position */ state->saved_x = state->cursor_x; state->saved_y = state->cursor_y; @@ -1008,21 +1136,55 @@ print_char (p_state *state, int c) case 3: state->escstate = 0; break; + + 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; + } + + if (state->unicruds >= total) { + /* Done! Convert it to Latin1 and print that. */ + char *s; + state->unicrud[state->unicruds] = 0; + s = utf8_to_latin1 ((const char *) state->unicrud, False); + state->unicruds = 0; + state->escstate = 0; + if (s) { + c = (unsigned char) s[0]; + free (s); + goto PRINT; + } else { + c = 0; + } + } + } + break; + + default: + abort(); } set_cursor (state, True); } else +#endif /* HAVE_FORKPTY */ { if (c == '\t') c = ' '; /* blah. */ if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */ { - if (c == '\n' && last_c == '\r') + if (c == '\n' && state->last_c == '\r') ; /* CRLF -- do nothing */ else { state->cursor_x = 0; - if (state->cursor_y == state->grid_height - 1) + if (state->cursor_y == rows - 1) scroll (state); else state->cursor_y++; @@ -1034,6 +1196,8 @@ print_char (p_state *state, int c) } else { + /* #### This should do UTF-8 decoding */ + cell->state = FLARE; cell->p_char = state->chars[c]; cell->changed = True; @@ -1042,10 +1206,10 @@ print_char (p_state *state, int c) if (c != ' ' && cell->p_char->blank_p) cell->p_char = state->chars[CURSOR_INDEX]; - if (state->cursor_x >= state->grid_width - 1) + if (state->cursor_x >= cols - 1) { state->cursor_x = 0; - if (state->cursor_y >= state->grid_height - 1) + if (state->cursor_y >= rows - 1) scroll (state); else state->cursor_y++; @@ -1054,7 +1218,7 @@ print_char (p_state *state, int c) set_cursor (state, True); } - last_c = c; + state->last_c = c; } @@ -1116,295 +1280,101 @@ update_display (p_state *state, Bool changed_only) } -static void -run_phosphor (p_state *state) +static unsigned long +phosphor_draw (Display *dpy, Window window, void *closure) { + p_state *state = (p_state *) closure; + int c; update_display (state, True); decay (state); - drain_input (state); -} - -/* Subprocess. - */ + c = textclient_getc (state->tc); + if (c > 0) + print_char (state, c); -static void -subproc_cb (XtPointer closure, int *source, XtInputId *id) -{ - p_state *state = (p_state *) closure; - state->input_available_p = True; + return state->delay; } static void -launch_text_generator (p_state *state) -{ - char buf[255]; - char *oprogram = get_string_resource ("program", "Program"); - -#ifdef HAVE_FORKPTY - if(state->mode == 1) - { - int fd; - struct winsize ws; - - ws.ws_row = state->grid_height - 1; - ws.ws_col = state->grid_width - 2; - ws.ws_xpixel = state->xgwa.width; - ws.ws_ypixel = state->xgwa.height; - - state->pipe = NULL; - if((state->pid = forkpty(&fd, NULL, NULL, &ws)) < 0) - { - /* Unable to fork */ - sprintf (buf, "%.100s: forkpty", progname); - perror(buf); - } - else if(!state->pid) - { - /* This is the child fork. */ - if (putenv("TERM=vt100")) - abort(); - execl("/bin/sh", "/bin/sh", "-c", oprogram, NULL); - sprintf (buf, "%.100s: %.100s", progname, oprogram); - perror(buf); - exit(1); - } - else - { - /* This is the parent fork. */ - state->pipe = fdopen(fd, "r+"); - state->pipe_id = - XtAppAddInput (app, fileno (state->pipe), - (XtPointer) (XtInputReadMask | XtInputExceptMask), - subproc_cb, (XtPointer) state); - } - } - else -#endif /* HAVE_FORKPTY */ - { - char *program = (char *) malloc (strlen (oprogram) + 10); - - strcpy (program, "( "); - strcat (program, oprogram); - strcat (program, " ) 2>&1"); - - /* don't mess up controlling terminal if someone dumbly does - "-pipe -program tcsh". */ - fclose (stdin); - - if ((state->pipe = popen (program, "r"))) - { - state->pipe_id = - XtAppAddInput (app, fileno (state->pipe), - (XtPointer) (XtInputReadMask | XtInputExceptMask), - subproc_cb, (XtPointer) state); - } - else - { - sprintf (buf, "%.100s: %.100s", progname, program); - perror (buf); - } - } -} - - -static void -relaunch_generator_timer (XtPointer closure, XtIntervalId *id) +phosphor_reshape (Display *dpy, Window window, void *closure, + unsigned int w, unsigned int h) { p_state *state = (p_state *) closure; - launch_text_generator (state); -} - - -static void -drain_input (p_state *state) -{ - if (state->input_available_p) - { - unsigned char s[2]; - int n = read (fileno (state->pipe), (void *) s, 1); - if (n == 1) - { - print_char (state, s[0]); - } - else - { - XtRemoveInput (state->pipe_id); - state->pipe_id = 0; - if (state->pid) - { - waitpid(state->pid, NULL, 0); - fclose (state->pipe); - } - else - { - pclose (state->pipe); - } - state->pipe = 0; + Bool changed_p = resize_grid (state); - if (state->cursor_x != 0) /* break line if unbroken */ - print_char (state, '\n'); /* blank line */ - print_char (state, '\n'); + if (! changed_p) return; - /* Set up a timer to re-launch the subproc in a bit. */ - XtAppAddTimeOut (app, state->subproc_relaunch_delay, - relaunch_generator_timer, - (XtPointer) state); - } - - state->input_available_p = False; - } + textclient_reshape (state->tc, w, h, + state->grid_width - 1, + state->grid_height - 1, + 0); } -/* The interpretation of the ModN modifiers is dependent on what keys - are bound to them: Mod1 does not necessarily mean "meta". It only - means "meta" if Meta_L or Meta_R are bound to it. If Meta_L is on - Mod5, then Mod5 is the one that means Meta. Oh, and Meta and Alt - aren't necessarily the same thing. Icepicks in my forehead! - */ -static unsigned int -do_icccm_meta_key_stupidity (Display *dpy) +static Bool +phosphor_event (Display *dpy, Window window, void *closure, XEvent *event) { - unsigned int modbits = 0; - int i, j, k; - XModifierKeymap *modmap = XGetModifierMapping (dpy); - for (i = 3; i < 8; i++) - for (j = 0; j < modmap->max_keypermod; j++) - { - int code = modmap->modifiermap[i * modmap->max_keypermod + j]; - KeySym *syms; - int nsyms = 0; - if (code == 0) continue; - syms = XGetKeyboardMapping (dpy, code, 1, &nsyms); - for (k = 0; k < nsyms; k++) - if (syms[k] == XK_Meta_L || syms[k] == XK_Meta_R || - syms[k] == XK_Alt_L || syms[k] == XK_Alt_R) - modbits |= (1 << i); - XFree (syms); - } - XFreeModifiermap (modmap); - return modbits; -} + p_state *state = (p_state *) closure; -/* Returns a mask of the bit or bits of a KeyPress event that mean "meta". - */ -static unsigned int -meta_modifier (Display *dpy) -{ - static Bool done_once = False; - static unsigned int mask = 0; - if (!done_once) - { - /* Really, we are supposed to recompute this if a KeymapNotify - event comes in, but fuck it. */ - done_once = True; - mask = do_icccm_meta_key_stupidity (dpy); - } - return mask; + if (event->xany.type == Expose) + update_display (state, False); + else if (event->xany.type == KeyPress) + return textclient_putc (state->tc, &event->xkey); + return False; } - static void -handle_events (p_state *state) +phosphor_free (Display *dpy, Window window, void *closure) { - XSync (state->dpy, False); - while (XPending (state->dpy)) - { - XEvent event; - XNextEvent (state->dpy, &event); + p_state *state = (p_state *) closure; - if (event.xany.type == ConfigureNotify) - { - resize_grid (state); + textclient_close (state->tc); + if (state->cursor_timer) + XtRemoveTimeOut (state->cursor_timer); -# if defined(HAVE_FORKPTY) && defined(TIOCSWINSZ) - if (state->pid) - { - /* Tell the sub-process that the screen size has changed. */ - struct winsize ws; - ws.ws_row = state->grid_height - 1; - ws.ws_col = state->grid_width - 2; - ws.ws_xpixel = state->xgwa.width; - ws.ws_ypixel = state->xgwa.height; - ioctl (fileno (state->pipe), TIOCSWINSZ, &ws); - kill (state->pid, SIGWINCH); - } -# endif /* HAVE_FORKPTY && TIOCSWINSZ */ - } - else if (event.xany.type == Expose) - { - update_display (state, False); - } - else if (event.xany.type == KeyPress) - { - KeySym keysym; - unsigned char c = 0; - XLookupString (&event.xkey, (char *) &c, 1, &keysym, - &state->compose); - if (c != 0 && state->pipe) - { - if (!state->swap_bs_del_p) ; - else if (c == 127) c = 8; - else if (c == 8) c = 127; - - /* If meta was held down, send ESC, or turn on the high bit. */ - if (event.xkey.state & meta_modifier (state->dpy)) - { - if (state->meta_sends_esc_p) - fputc ('\033', state->pipe); - else - c |= 0x80; - } - - fputc (c, state->pipe); - fflush (state->pipe); - event.xany.type = 0; /* don't interpret this event defaultly. */ - } - } + /* #### there's more to free here */ - screenhack_handle_event (state->dpy, &event); - } - - if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) - XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); + free (state); } - -char *progclass = "Phosphor"; -char *defaults [] = { +static const char *phosphor_defaults [] = { ".background: Black", - ".foreground: Green", - "*fadeForeground: DarkGreen", - "*flareForeground: White", + ".foreground: #00FF00", + "*fpsSolid: true", +#if defined(BUILTIN_FONT) + "*font: (builtin)", +#elif defined(HAVE_COCOA) + "*font: Monaco 15", +#else "*font: fixed", +#endif "*scale: 6", "*ticks: 20", "*delay: 50000", "*cursor: 333", - "*program: " FORTUNE_PROGRAM, + "*program: xscreensaver-text", "*relaunch: 5", "*metaSendsESC: True", "*swapBSDEL: True", #ifdef HAVE_FORKPTY - "*mode: pty", + "*usePty: True", #else /* !HAVE_FORKPTY */ - "*mode: pipe", + "*usePty: False", #endif /* !HAVE_FORKPTY */ 0 }; -XrmOptionDescRec options [] = { +static XrmOptionDescRec phosphor_options [] = { { "-font", ".font", XrmoptionSepArg, 0 }, { "-scale", ".scale", XrmoptionSepArg, 0 }, { "-ticks", ".ticks", XrmoptionSepArg, 0 }, { "-delay", ".delay", XrmoptionSepArg, 0 }, { "-program", ".program", XrmoptionSepArg, 0 }, - { "-pty", ".mode", XrmoptionNoArg, "pty" }, - { "-pipe", ".mode", XrmoptionNoArg, "pipe" }, + { "-pipe", ".usePty", XrmoptionNoArg, "False" }, + { "-pty", ".usePty", XrmoptionNoArg, "True" }, { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" }, { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" }, { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" }, @@ -1413,19 +1383,4 @@ XrmOptionDescRec options [] = { }; -void -screenhack (Display *dpy, Window window) -{ - int delay = get_integer_resource ("delay", "Integer"); - p_state *state = init_phosphor (dpy, window); - - clear (state); - - while (1) - { - run_phosphor (state); - XSync (dpy, False); - handle_events (state); - if (delay) usleep (delay); - } -} +XSCREENSAVER_MODULE ("Phosphor", phosphor)