1 /* xscreensaver, Copyright (c) 1999-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 * Phosphor -- simulate a glass tty with long-sustain phosphor.
12 * Written by Jamie Zawinski <jwz@jwz.org>
13 * Pty and vt100 emulation by Fredrik Tolf <fredrik@dolda2000.com>
18 #endif /* HAVE_CONFIG_H */
21 # include <X11/Intrinsic.h>
24 #include "screenhack.h"
25 #include "textclient.h"
30 #define MAX(a,b) ((a)>(b)?(a):(b))
31 #define MIN(a,b) ((a)<(b)?(a):(b))
37 #define STATE_MAX FADE
39 #define CURSOR_INDEX 128
46 # include "images/6x10font.xbm"
47 #endif /* BUILTIN_FONT */
55 #endif /* FUZZY_BORDER */
68 XWindowAttributes xgwa;
71 int grid_width, grid_height;
72 int char_width, char_height;
81 int unicruds; char unicrud[7];
90 #endif /* FUZZY_BORDER */
94 int cursor_x, cursor_y;
95 XtIntervalId cursor_timer;
108 static void capture_font_bits (p_state *state);
109 static p_char *make_character (p_state *state, int c);
110 static void char_to_pixmap (p_state *state, p_char *pc, int c);
113 /* About font metrics:
115 "lbearing" is the distance from the leftmost pixel of a character to
116 the logical origin of that character. That is, it is the number of
117 pixels of the character which are to the left of its logical origin.
119 "rbearing" is the distance from the logical origin of a character to
120 the rightmost pixel of a character. That is, it is the number of
121 pixels of the character to the right of its logical origin.
123 "descent" is the distance from the bottommost pixel of a character to
124 the logical baseline. That is, it is the number of pixels of the
125 character which are below the baseline.
127 "ascent" is the distance from the logical baseline to the topmost pixel.
128 That is, it is the number of pixels of the character above the baseline.
130 Therefore, the bounding box of the "ink" of a character is
131 lbearing + rbearing by ascent + descent;
133 "width" is the distance from the logical origin of this character to
134 the position where the logical orgin of the next character should be
137 For our purposes, we're only interested in the part of the character
138 lying inside the "width" box. If the characters have ink outside of
139 that box (the "charcell" box) then we're going to lose it. Alas.
143 static void clear (p_state *);
144 static void set_cursor (p_state *, Bool on);
146 static unsigned short scale_color_channel (unsigned short ch1, unsigned short ch2)
148 return (ch1 * 100 + ch2 * 156) >> 8;
152 phosphor_init (Display *dpy, Window window)
156 p_state *state = (p_state *) calloc (sizeof(*state), 1);
157 char *fontname = get_string_resource (dpy, "font", "Font");
161 state->window = window;
163 XGetWindowAttributes (dpy, window, &state->xgwa);
164 /* XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask);*/
166 state->delay = get_integer_resource (dpy, "delay", "Integer");
167 state->pty_p = get_boolean_resource (dpy, "usePty", "UsePty");
169 if (!strcasecmp (fontname, "builtin") ||
170 !strcasecmp (fontname, "(builtin)"))
173 fprintf (stderr, "%s: no builtin font\n", progname);
174 state->font = XLoadQueryFont (dpy, "fixed");
175 #endif /* !BUILTIN_FONT */
179 state->font = XLoadQueryFont (dpy, fontname);
183 fprintf(stderr, "couldn't load font \"%s\"\n", fontname);
184 state->font = XLoadQueryFont (dpy, "fixed");
188 fprintf(stderr, "couldn't load font \"fixed\"");
194 state->scale = get_integer_resource (dpy, "scale", "Integer");
195 state->ticks = STATE_MAX + get_integer_resource (dpy, "ticks", "Integer");
199 state->cursor_blink = get_integer_resource (dpy, "cursor", "Time");
204 state->char_width = (font6x10_width / 256) - 1;
205 state->char_height = font6x10_height;
208 # endif /* BUILTIN_FONT */
210 state->char_width = font->max_bounds.width;
211 state->char_height = font->max_bounds.ascent + font->max_bounds.descent;
214 state->grid_width = state->xgwa.width / (state->char_width * state->scale);
215 state->grid_height = state->xgwa.height /(state->char_height * state->scale);
216 state->cells = (p_cell *) calloc (sizeof(p_cell),
217 state->grid_width * state->grid_height);
218 state->chars = (p_char **) calloc (sizeof(p_char *), 256);
220 state->gcs = (GC *) calloc (sizeof(GC), state->ticks + 1);
223 int ncolors = MAX (1, state->ticks - 3);
224 XColor *colors = (XColor *) calloc (ncolors, sizeof(XColor));
226 double s1, s2, v1, v2;
228 unsigned long fg = get_pixel_resource (state->dpy, state->xgwa.colormap,
229 "foreground", "Foreground");
230 unsigned long bg = get_pixel_resource (state->dpy, state->xgwa.colormap,
231 "background", "Background");
232 unsigned long flare = fg;
234 XColor fg_color, bg_color;
237 XQueryColor (state->dpy, state->xgwa.colormap, &fg_color);
240 XQueryColor (state->dpy, state->xgwa.colormap, &bg_color);
242 /* Now allocate a ramp of colors from the main color to the background. */
243 rgb_to_hsv (scale_color_channel(fg_color.red, bg_color.red),
244 scale_color_channel(fg_color.green, bg_color.green),
245 scale_color_channel(fg_color.blue, bg_color.blue),
247 rgb_to_hsv (bg_color.red, bg_color.green, bg_color.blue, &h2, &s2, &v2);
249 /* Avoid rainbow effects when fading to black/grey/white. */
255 make_color_ramp (state->xgwa.screen, state->xgwa.visual,
256 state->xgwa.colormap,
262 /* Adjust to the number of colors we actually got. */
263 state->ticks = ncolors + STATE_MAX;
265 /* If the foreground is brighter than the background, the flare is white.
266 * Otherwise, the flare is left at the foreground color (i.e. no flare). */
267 rgb_to_hsv (fg_color.red, fg_color.green, fg_color.blue, &h1, &s1, &v1);
271 /* WhitePixel is only for the default visual, which can be overridden
272 * on the command line. */
274 white.green = 0xffff;
276 if (XAllocColor(state->dpy, state->xgwa.colormap, &white))
280 /* Now, GCs all around.
282 state->gcv.font = (font ? font->fid : 0);
283 state->gcv.cap_style = CapRound;
285 state->gcv.line_width = (int) (((long) state->scale) * 1.3);
286 if (state->gcv.line_width == state->scale)
287 state->gcv.line_width++;
288 #else /* !FUZZY_BORDER */
289 state->gcv.line_width = (int) (((long) state->scale) * 0.9);
290 if (state->gcv.line_width >= state->scale)
291 state->gcv.line_width = state->scale - 1;
292 if (state->gcv.line_width < 1)
293 state->gcv.line_width = 1;
294 #endif /* !FUZZY_BORDER */
296 flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth);
298 state->gcv.background = bg;
299 state->gcv.foreground = bg;
300 state->gcs[BLANK] = XCreateGC (state->dpy, state->window, flags,
303 state->gcv.foreground = flare;
304 state->gcs[FLARE] = XCreateGC (state->dpy, state->window, flags,
307 state->gcv.foreground = fg;
308 state->gcs[NORMAL] = XCreateGC (state->dpy, state->window, flags,
311 for (i = 0; i < ncolors; i++)
313 state->gcv.foreground = colors[i].pixel;
314 state->gcs[STATE_MAX + i] = XCreateGC (state->dpy, state->window,
319 capture_font_bits (state);
321 set_cursor (state, True);
325 state->tc = textclient_open (dpy);
326 textclient_reshape (state->tc,
329 state->grid_width - 1,
330 state->grid_height - 1,
337 /* Re-query the window size and update the internal character grid if changed.
340 resize_grid (p_state *state)
342 int ow = state->grid_width;
343 int oh = state->grid_height;
344 p_cell *ocells = state->cells;
347 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
349 state->grid_width = state->xgwa.width /(state->char_width * state->scale);
350 state->grid_height = state->xgwa.height /(state->char_height * state->scale);
352 if (ow == state->grid_width &&
353 oh == state->grid_height)
356 state->cells = (p_cell *) calloc (sizeof(p_cell),
357 state->grid_width * state->grid_height);
359 for (y = 0; y < state->grid_height; y++)
361 for (x = 0; x < state->grid_width; x++)
363 p_cell *ncell = &state->cells [state->grid_width * y + x];
364 if (x < ow && y < oh)
365 *ncell = ocells [ow * y + x];
366 ncell->changed = True;
370 if (state->cursor_x >= state->grid_width)
371 state->cursor_x = state->grid_width-1;
372 if (state->cursor_y >= state->grid_height)
373 state->cursor_y = state->grid_height-1;
381 capture_font_bits (p_state *state)
383 XFontStruct *font = state->font;
384 int safe_width, height;
385 unsigned char string[257];
394 safe_width = state->char_width + 1;
395 height = state->char_height;
396 p2 = XCreatePixmapFromBitmapData (state->dpy, state->window,
397 (char *) font6x10_bits,
403 # endif /* BUILTIN_FONT */
405 safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
406 height = state->char_height;
409 p = XCreatePixmap (state->dpy, state->window,
410 (safe_width * 256), height, 1);
412 for (i = 0; i < 256; i++)
413 string[i] = (unsigned char) i;
416 state->gcv.foreground = 0;
417 state->gcv.background = 0;
418 state->gc0 = XCreateGC (state->dpy, p,
419 (GCForeground | GCBackground),
422 state->gcv.foreground = 1;
423 state->gc1 = XCreateGC (state->dpy, p,
424 ((font ? GCFont : 0) |
425 GCForeground | GCBackground |
426 GCCapStyle | GCLineWidth),
430 jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False);
431 jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False);
436 state->gcv.line_width = (int) (((long) state->scale) * 0.8);
437 if (state->gcv.line_width >= state->scale)
438 state->gcv.line_width = state->scale - 1;
439 if (state->gcv.line_width < 1)
440 state->gcv.line_width = 1;
441 state->gc2 = XCreateGC (state->dpy, p,
442 ((font ? GCFont : 0) |
443 GCForeground | GCBackground |
444 GCCapStyle | GCLineWidth),
447 #endif /* FUZZY_BORDER */
449 XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height);
454 XCopyPlane (state->dpy, p2, p, state->gc1,
455 0, 0, font6x10_width, font6x10_height,
457 XFreePixmap (state->dpy, p2);
460 # endif /* BUILTIN_FONT */
462 for (i = 0; i < 256; i++)
464 if (string[i] < font->min_char_or_byte2 ||
465 string[i] > font->max_char_or_byte2)
467 XDrawString (state->dpy, p, state->gc1,
468 i * safe_width, font->ascent,
469 (char *) (string + i), 1);
473 /* Draw the cursor. */
474 XFillRectangle (state->dpy, p, state->gc1,
475 (CURSOR_INDEX * safe_width), 1,
478 ? font->per_char['n'-font->min_char_or_byte2].width
479 : font->max_bounds.width)
480 : state->char_width),
483 : state->char_height));
485 state->font_bits = XGetImage (state->dpy, p, 0, 0,
486 (safe_width * 256), height, ~0L, XYPixmap);
487 XFreePixmap (state->dpy, p);
489 for (i = 0; i < 256; i++)
490 state->chars[i] = make_character (state, i);
491 state->chars[CURSOR_INDEX] = make_character (state, CURSOR_INDEX);
496 make_character (p_state *state, int c)
498 p_char *pc = (p_char *) malloc (sizeof (*pc));
499 pc->name = (unsigned char) c;
500 pc->width = state->scale * state->char_width;
501 pc->height = state->scale * state->char_height;
502 char_to_pixmap (state, pc, c);
508 char_to_pixmap (p_state *state, p_char *pc, int c)
515 #endif /* FUZZY_BORDER */
519 XFontStruct *font = state->font;
520 int safe_width = (font
521 ? font->max_bounds.rbearing - font->min_bounds.lbearing
522 : state->char_width + 1);
524 int width = state->scale * state->char_width;
525 int height = state->scale * state->char_height;
527 if (font && (c < font->min_char_or_byte2 ||
528 c > font->max_char_or_byte2))
532 p = XCreatePixmap (state->dpy, state->window, width, height, 1);
533 XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height);
536 p2 = XCreatePixmap (state->dpy, state->window, width, height, 1);
537 XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height);
538 #endif /* FUZZY_BORDER */
540 from = safe_width * c;
541 to = safe_width * (c + 1);
544 if (c > 75 && c < 150)
546 printf ("\n=========== %d (%c)\n", c, c);
547 for (y = 0; y < state->char_height; y++)
549 for (x1 = from; x1 < to; x1++)
550 printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". ");
557 for (y = 0; y < state->char_height; y++)
558 for (x1 = from; x1 < to; x1++)
559 if (XGetPixel (state->font_bits, x1, y))
561 int xoff = state->scale / 2;
563 for (x2 = x1; x2 < to; x2++)
564 if (!XGetPixel (state->font_bits, x2, y))
567 XDrawLine (state->dpy, p, gc,
568 (x1 - from) * state->scale + xoff, y * state->scale,
569 (x2 - from) * state->scale + xoff, y * state->scale);
571 XDrawLine (state->dpy, p2, gc2,
572 (x1 - from) * state->scale + xoff, y * state->scale,
573 (x2 - from) * state->scale + xoff, y * state->scale);
574 #endif /* FUZZY_BORDER */
579 /* if (pc->blank_p && c == CURSOR_INDEX)
586 #endif /* FUZZY_BORDER */
590 /* Managing the display.
593 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
594 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
597 set_cursor_1 (p_state *state, Bool on)
599 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
601 p_char *cursor = state->chars[CURSOR_INDEX];
602 int new_state = (on ? NORMAL : FADE);
604 if (cell->p_char != cursor)
605 cell->changed = True;
607 if (cell->state != new_state)
608 cell->changed = True;
610 cell->p_char = cursor;
611 cell->state = new_state;
612 return cell->changed;
616 set_cursor (p_state *state, Bool on)
618 if (set_cursor_1 (state, on))
620 if (state->cursor_timer)
621 XtRemoveTimeOut (state->cursor_timer);
622 state->cursor_timer = 0;
623 cursor_on_timer (state, 0);
629 cursor_off_timer (XtPointer closure, XtIntervalId *id)
631 p_state *state = (p_state *) closure;
632 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
633 set_cursor_1 (state, False);
634 state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink,
635 cursor_on_timer, closure);
639 cursor_on_timer (XtPointer closure, XtIntervalId *id)
641 p_state *state = (p_state *) closure;
642 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
643 set_cursor_1 (state, True);
644 state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink,
645 cursor_off_timer, closure);
650 clear (p_state *state)
655 for (y = 0; y < state->grid_height; y++)
656 for (x = 0; x < state->grid_width; x++)
658 p_cell *cell = &state->cells[state->grid_width * y + x];
659 if (cell->state == FLARE || cell->state == NORMAL)
662 cell->changed = True;
665 set_cursor (state, True);
670 decay (p_state *state)
673 for (y = 0; y < state->grid_height; y++)
674 for (x = 0; x < state->grid_width; x++)
676 p_cell *cell = &state->cells[state->grid_width * y + x];
677 if (cell->state == FLARE)
679 cell->state = NORMAL;
680 cell->changed = True;
682 else if (cell->state >= FADE)
685 if (cell->state >= state->ticks)
687 cell->changed = True;
694 scroll (p_state *state)
698 for (x = 0; x < state->grid_width; x++)
700 p_cell *from = 0, *to = 0;
701 for (y = 1; y < state->grid_height; y++)
703 from = &state->cells[state->grid_width * y + x];
704 to = &state->cells[state->grid_width * (y-1) + x];
706 if ((from->state == FLARE || from->state == NORMAL) &&
707 !from->p_char->blank_p)
710 to->state = NORMAL; /* should be FLARE? Looks bad... */
714 if (to->state == FLARE || to->state == NORMAL)
722 if (to && (to->state == FLARE || to->state == NORMAL))
728 set_cursor (state, True);
733 print_char (p_state *state, int c)
735 int cols = state->grid_width;
736 int rows = state->grid_height;
737 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
740 /* Start the cursor fading (in case we don't end up overwriting it.) */
741 if (cell->state == FLARE || cell->state == NORMAL)
744 cell->changed = True;
748 if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
749 It would be nice if we could just interpret them all
750 the time, but that would require subprocesses to send
751 CRLF line endings instead of bare LF, so that's no good.
757 /* Mostly duplicated in apple2-main.c */
759 switch (state->escstate)
765 /* Dummy case - we don't want the screensaver to beep */
766 /* #### But maybe this should flash the screen? */
769 if (state->cursor_x > 0)
773 if (state->cursor_x < cols - 8)
775 state->cursor_x = (state->cursor_x & ~7) + 8;
780 if (state->cursor_y < rows - 1)
787 # ifndef HAVE_FORKPTY
788 state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
792 if(state->last_c == 13)
794 cell->state = NORMAL;
795 cell->p_char = state->chars[state->bk];
796 cell->changed = True;
798 if (state->cursor_y < rows - 1)
805 cell = &state->cells[cols * state->cursor_y];
806 if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
809 state->bk = cell->p_char->name;
813 /* Dummy case - there is one and only one font. */
817 /* Dummy case - these interrupt escape sequences, so
818 they don't do anything in this state */
824 /* Dummy case - this is supposed to be ignored */
828 for(i = 0; i < NPAR; i++)
829 state->csiparam[i] = 0;
834 /* states 102-106 are for UTF-8 decoding */
836 if ((c & 0xE0) == 0xC0) { /* 110xxxxx: 11 bits, 2 bytes */
838 state->unicrud[0] = c;
839 state->escstate = 102;
841 } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx: 16 bits, 3 bytes */
843 state->unicrud[0] = c;
844 state->escstate = 103;
846 } else if ((c & 0xF8) == 0xF0) { /* 11110xxx: 21 bits, 4 bytes */
848 state->unicrud[0] = c;
849 state->escstate = 104;
851 } else if ((c & 0xFC) == 0xF8) { /* 111110xx: 26 bits, 5 bytes */
853 state->unicrud[0] = c;
854 state->escstate = 105;
856 } else if ((c & 0xFE) == 0xFC) { /* 1111110x: 31 bits, 6 bytes */
858 state->unicrud[0] = c;
859 state->escstate = 106;
865 /* If the cursor is in column 39 and we print a character, then
866 that character shows up in column 39, and the cursor is no
867 longer visible on the screen (it's in "column 40".) If
868 another character is printed, then that character shows up in
869 column 0, and the cursor moves to column 1.
871 This is empirically what xterm and gnome-terminal do, so that
872 must be the right thing. (In xterm, the cursor vanishes,
873 whereas; in gnome-terminal, the cursor overprints the
874 character in col 39.)
877 cell->p_char = state->chars[c];
878 cell->changed = True;
881 if (c != ' ' && cell->p_char->blank_p)
882 cell->p_char = state->chars[CURSOR_INDEX];
884 if (state->cursor_x >= cols - 1 /*####*/)
887 if (state->cursor_y >= rows - 1)
902 case 'c': /* Reset */
906 case 'D': /* Linefeed */
907 if (state->cursor_y < rows - 1)
913 case 'E': /* Newline */
917 case 'M': /* Reverse newline */
918 if (state->cursor_y > 0)
922 case '7': /* Save state */
923 state->saved_x = state->cursor_x;
924 state->saved_y = state->cursor_y;
927 case '8': /* Restore state */
928 state->cursor_x = state->saved_x;
929 state->cursor_y = state->saved_y;
934 for(i = 0; i < NPAR; i++)
935 state->csiparam[i] = 0;
938 case '%': /* Select charset */
939 /* @: Select default (ISO 646 / ISO 8859-1)
941 8: Select UTF-8 (obsolete)
943 We can just ignore this and always process UTF-8, I think?
944 We must still catch the last byte, though.
948 /* I don't support different fonts either - see above
953 /* Escape sequences not supported:
956 * Z - Terminal identification
958 * = - Other keypad change
972 case '0': case '1': case '2': case '3': case '4':
973 case '5': case '6': case '7': case '8': case '9':
974 if (state->curparam < NPAR)
975 state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
978 state->csiparam[++state->curparam] = 0;
984 for (i = 0; i < state->csiparam[0]; i++)
986 if(++state->cursor_x > cols)
989 if (state->cursor_y < rows - 1)
994 cell = &state->cells[cols * state->cursor_y + state->cursor_x];
995 if (cell->state == FLARE || cell->state == NORMAL)
998 cell->changed = True;
1001 state->escstate = 0;
1004 state->cursor_x = 0;
1006 if (state->csiparam[0] == 0)
1007 state->csiparam[0] = 1;
1008 if ((state->cursor_y -= state->csiparam[0]) < 0)
1009 state->cursor_y = 0;
1010 state->escstate = 0;
1013 state->cursor_x = 0;
1016 if (state->csiparam[0] == 0)
1017 state->csiparam[0] = 1;
1018 if ((state->cursor_y += state->csiparam[0]) >= rows - 1 /*####*/)
1019 state->cursor_y = rows - 1;
1020 state->escstate = 0;
1024 if (state->csiparam[0] == 0)
1025 state->csiparam[0] = 1;
1026 if ((state->cursor_x += state->csiparam[0]) >= cols - 1 /*####*/)
1027 state->cursor_x = cols - 1;
1028 state->escstate = 0;
1031 if (state->csiparam[0] == 0)
1032 state->csiparam[0] = 1;
1033 if ((state->cursor_x -= state->csiparam[0]) < 0)
1034 state->cursor_x = 0;
1035 state->escstate = 0;
1038 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1039 state->cursor_y = rows - 1;
1040 state->escstate = 0;
1044 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols - 1 /*####*/)
1045 state->cursor_x = cols - 1;
1046 state->escstate = 0;
1050 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1051 state->cursor_y = rows - 1;
1052 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols - 1 /*####*/)
1053 state->cursor_x = cols - 1;
1054 if(state->cursor_y < 0)
1055 state->cursor_y = 0;
1056 if(state->cursor_x < 0)
1057 state->cursor_x = 0;
1058 state->escstate = 0;
1063 if (state->csiparam[0] == 0)
1064 start = cols * state->cursor_y + state->cursor_x;
1065 if (state->csiparam[0] == 1)
1066 end = cols * state->cursor_y + state->cursor_x;
1067 for (i = start; i < end; i++)
1069 cell = &state->cells[i];
1070 if (cell->state == FLARE || cell->state == NORMAL)
1073 cell->changed = True;
1076 set_cursor (state, True);
1077 state->escstate = 0;
1082 if (state->csiparam[0] == 0)
1083 start = state->cursor_x;
1084 if (state->csiparam[1] == 1)
1085 end = state->cursor_x;
1086 for (i = start; i < end; i++)
1088 if (cell->state == FLARE || cell->state == NORMAL)
1091 cell->changed = True;
1095 state->escstate = 0;
1097 case 'm': /* Set attributes unimplemented (bold, blink, rev) */
1098 state->escstate = 0;
1100 case 's': /* Save position */
1101 state->saved_x = state->cursor_x;
1102 state->saved_y = state->cursor_y;
1103 state->escstate = 0;
1105 case 'u': /* Restore position */
1106 state->cursor_x = state->saved_x;
1107 state->cursor_y = state->saved_y;
1108 state->escstate = 0;
1110 case '?': /* DEC Private modes */
1111 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1112 state->escstate = 0;
1115 /* Known unsupported CSIs:
1117 * L - Insert blank lines
1118 * M - Delete lines (I don't know what this means...)
1119 * P - Delete characters
1120 * X - Erase characters (difference with P being...?)
1121 * c - Terminal identification
1122 * g - Clear tab stop(s)
1123 * h - Set mode (Mainly due to its complexity and lack of good
1126 * m - Set mode (Phosphor is, per defenition, green on black)
1128 * q - Set keyboard LEDs
1129 * r - Set scrolling region (too exhausting - noone uses this,
1132 state->escstate = 0;
1137 state->escstate = 0;
1146 int total = state->escstate - 100; /* see what I did there */
1147 if (state->unicruds < total) {
1148 /* Buffer more bytes of the UTF-8 sequence */
1149 state->unicrud[state->unicruds++] = c;
1152 if (state->unicruds >= total) {
1153 /* Done! Convert it to Latin1 and print that. */
1155 state->unicrud[state->unicruds] = 0;
1156 s = utf8_to_latin1 ((const char *) state->unicrud, False);
1157 state->unicruds = 0;
1158 state->escstate = 0;
1160 c = (unsigned char) s[0];
1173 set_cursor (state, True);
1176 #endif /* HAVE_FORKPTY */
1178 if (c == '\t') c = ' '; /* blah. */
1180 if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
1182 if (c == '\n' && state->last_c == '\r')
1183 ; /* CRLF -- do nothing */
1186 state->cursor_x = 0;
1187 if (state->cursor_y == rows - 1)
1193 else if (c == '\014')
1199 /* #### This should do UTF-8 decoding */
1201 cell->state = FLARE;
1202 cell->p_char = state->chars[c];
1203 cell->changed = True;
1206 if (c != ' ' && cell->p_char->blank_p)
1207 cell->p_char = state->chars[CURSOR_INDEX];
1209 if (state->cursor_x >= cols - 1)
1211 state->cursor_x = 0;
1212 if (state->cursor_y >= rows - 1)
1218 set_cursor (state, True);
1226 update_display (p_state *state, Bool changed_only)
1230 for (y = 0; y < state->grid_height; y++)
1231 for (x = 0; x < state->grid_width; x++)
1233 p_cell *cell = &state->cells[state->grid_width * y + x];
1234 int width, height, tx, ty;
1236 if (changed_only && !cell->changed)
1239 width = state->char_width * state->scale;
1240 height = state->char_height * state->scale;
1244 if (cell->state == BLANK || cell->p_char->blank_p)
1246 XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
1247 tx, ty, width, height);
1252 GC gc1 = state->gcs[cell->state];
1253 GC gc2 = ((cell->state + 2) < state->ticks
1254 ? state->gcs[cell->state + 2]
1256 GC gc3 = (gc2 ? gc2 : gc1);
1258 XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
1259 0, 0, width, height, tx, ty, 1L);
1262 XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
1263 XSetClipOrigin (state->dpy, gc1, tx, ty);
1264 XFillRectangle (state->dpy, state->window, gc1,
1265 tx, ty, width, height);
1266 XSetClipMask (state->dpy, gc1, None);
1268 #else /* !FUZZY_BORDER */
1270 XCopyPlane (state->dpy,
1271 cell->p_char->pixmap, state->window,
1272 state->gcs[cell->state],
1273 0, 0, width, height, tx, ty, 1L);
1275 #endif /* !FUZZY_BORDER */
1278 cell->changed = False;
1283 static unsigned long
1284 phosphor_draw (Display *dpy, Window window, void *closure)
1286 p_state *state = (p_state *) closure;
1288 update_display (state, True);
1291 c = textclient_getc (state->tc);
1293 print_char (state, c);
1295 return state->delay;
1300 phosphor_reshape (Display *dpy, Window window, void *closure,
1301 unsigned int w, unsigned int h)
1303 p_state *state = (p_state *) closure;
1304 Bool changed_p = resize_grid (state);
1306 if (! changed_p) return;
1308 textclient_reshape (state->tc, w, h,
1309 state->grid_width - 1,
1310 state->grid_height - 1,
1316 phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
1318 p_state *state = (p_state *) closure;
1320 if (event->xany.type == Expose)
1321 update_display (state, False);
1322 else if (event->xany.type == KeyPress)
1323 return textclient_putc (state->tc, &event->xkey);
1328 phosphor_free (Display *dpy, Window window, void *closure)
1330 p_state *state = (p_state *) closure;
1332 textclient_close (state->tc);
1333 if (state->cursor_timer)
1334 XtRemoveTimeOut (state->cursor_timer);
1336 /* #### there's more to free here */
1343 static const char *phosphor_defaults [] = {
1344 ".background: Black",
1345 ".foreground: #00FF00",
1347 #if defined(BUILTIN_FONT)
1349 #elif defined(HAVE_COCOA)
1358 "*program: xscreensaver-text",
1360 "*metaSendsESC: True",
1364 #else /* !HAVE_FORKPTY */
1366 #endif /* !HAVE_FORKPTY */
1370 static XrmOptionDescRec phosphor_options [] = {
1371 { "-font", ".font", XrmoptionSepArg, 0 },
1372 { "-scale", ".scale", XrmoptionSepArg, 0 },
1373 { "-ticks", ".ticks", XrmoptionSepArg, 0 },
1374 { "-delay", ".delay", XrmoptionSepArg, 0 },
1375 { "-program", ".program", XrmoptionSepArg, 0 },
1376 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
1377 { "-pty", ".usePty", XrmoptionNoArg, "True" },
1378 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
1379 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
1380 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
1381 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
1386 XSCREENSAVER_MODULE ("Phosphor", phosphor)