1 /* xscreensaver, Copyright (c) 1999-2012 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"
29 #define MAX(a,b) ((a)>(b)?(a):(b))
30 #define MIN(a,b) ((a)<(b)?(a):(b))
36 #define STATE_MAX FADE
38 #define CURSOR_INDEX 128
45 # include "images/6x10font.xbm"
46 #endif /* BUILTIN_FONT */
54 #endif /* FUZZY_BORDER */
67 XWindowAttributes xgwa;
70 int grid_width, grid_height;
71 int char_width, char_height;
86 #endif /* FUZZY_BORDER */
90 int cursor_x, cursor_y;
91 XtIntervalId cursor_timer;
104 static void capture_font_bits (p_state *state);
105 static p_char *make_character (p_state *state, int c);
106 static void char_to_pixmap (p_state *state, p_char *pc, int c);
109 /* About font metrics:
111 "lbearing" is the distance from the leftmost pixel of a character to
112 the logical origin of that character. That is, it is the number of
113 pixels of the character which are to the left of its logical origin.
115 "rbearing" is the distance from the logical origin of a character to
116 the rightmost pixel of a character. That is, it is the number of
117 pixels of the character to the right of its logical origin.
119 "descent" is the distance from the bottommost pixel of a character to
120 the logical baseline. That is, it is the number of pixels of the
121 character which are below the baseline.
123 "ascent" is the distance from the logical baseline to the topmost pixel.
124 That is, it is the number of pixels of the character above the baseline.
126 Therefore, the bounding box of the "ink" of a character is
127 lbearing + rbearing by ascent + descent;
129 "width" is the distance from the logical origin of this character to
130 the position where the logical orgin of the next character should be
133 For our purposes, we're only interested in the part of the character
134 lying inside the "width" box. If the characters have ink outside of
135 that box (the "charcell" box) then we're going to lose it. Alas.
139 static void clear (p_state *);
140 static void set_cursor (p_state *, Bool on);
143 phosphor_init (Display *dpy, Window window)
147 p_state *state = (p_state *) calloc (sizeof(*state), 1);
148 char *fontname = get_string_resource (dpy, "font", "Font");
152 state->window = window;
154 XGetWindowAttributes (dpy, window, &state->xgwa);
155 /* XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask);*/
157 state->delay = get_integer_resource (dpy, "delay", "Integer");
158 state->pty_p = get_boolean_resource (dpy, "usePty", "UsePty");
160 if (!strcasecmp (fontname, "builtin") ||
161 !strcasecmp (fontname, "(builtin)"))
164 fprintf (stderr, "%s: no builtin font\n", progname);
165 state->font = XLoadQueryFont (dpy, "fixed");
166 #endif /* !BUILTIN_FONT */
170 state->font = XLoadQueryFont (dpy, fontname);
174 fprintf(stderr, "couldn't load font \"%s\"\n", fontname);
175 state->font = XLoadQueryFont (dpy, "fixed");
179 fprintf(stderr, "couldn't load font \"fixed\"");
185 state->scale = get_integer_resource (dpy, "scale", "Integer");
186 state->ticks = STATE_MAX + get_integer_resource (dpy, "ticks", "Integer");
190 state->cursor_blink = get_integer_resource (dpy, "cursor", "Time");
195 state->char_width = (font6x10_width / 256) - 1;
196 state->char_height = font6x10_height;
199 # endif /* BUILTIN_FONT */
201 state->char_width = font->max_bounds.width;
202 state->char_height = font->max_bounds.ascent + font->max_bounds.descent;
205 state->grid_width = state->xgwa.width / (state->char_width * state->scale);
206 state->grid_height = state->xgwa.height /(state->char_height * state->scale);
207 state->cells = (p_cell *) calloc (sizeof(p_cell),
208 state->grid_width * state->grid_height);
209 state->chars = (p_char **) calloc (sizeof(p_char *), 256);
211 state->gcs = (GC *) calloc (sizeof(GC), state->ticks + 1);
214 int ncolors = MAX (0, state->ticks - 3);
215 XColor *colors = (XColor *) calloc (ncolors, sizeof(XColor));
217 double s1, s2, v1, v2;
219 unsigned long fg = get_pixel_resource (state->dpy, state->xgwa.colormap,
220 "foreground", "Foreground");
221 unsigned long bg = get_pixel_resource (state->dpy, state->xgwa.colormap,
222 "background", "Background");
223 unsigned long flare = get_pixel_resource (state->dpy,state->xgwa.colormap,
224 "flareForeground", "Foreground");
225 unsigned long fade = get_pixel_resource (state->dpy,state->xgwa.colormap,
226 "fadeForeground", "Foreground");
231 XQueryColor (state->dpy, state->xgwa.colormap, &start);
234 XQueryColor (state->dpy, state->xgwa.colormap, &end);
236 /* Now allocate a ramp of colors from the main color to the background. */
237 rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1);
238 rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2);
239 make_color_ramp (state->dpy, state->xgwa.colormap,
245 /* Adjust to the number of colors we actually got. */
246 state->ticks = ncolors + STATE_MAX;
248 /* Now, GCs all around.
250 state->gcv.font = (font ? font->fid : 0);
251 state->gcv.cap_style = CapRound;
253 state->gcv.line_width = (int) (((long) state->scale) * 1.3);
254 if (state->gcv.line_width == state->scale)
255 state->gcv.line_width++;
256 #else /* !FUZZY_BORDER */
257 state->gcv.line_width = (int) (((long) state->scale) * 0.9);
258 if (state->gcv.line_width >= state->scale)
259 state->gcv.line_width = state->scale - 1;
260 if (state->gcv.line_width < 1)
261 state->gcv.line_width = 1;
262 #endif /* !FUZZY_BORDER */
264 flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth);
266 state->gcv.background = bg;
267 state->gcv.foreground = bg;
268 state->gcs[BLANK] = XCreateGC (state->dpy, state->window, flags,
271 state->gcv.foreground = flare;
272 state->gcs[FLARE] = XCreateGC (state->dpy, state->window, flags,
275 state->gcv.foreground = fg;
276 state->gcs[NORMAL] = XCreateGC (state->dpy, state->window, flags,
279 for (i = 0; i < ncolors; i++)
281 state->gcv.foreground = colors[i].pixel;
282 state->gcs[STATE_MAX + i] = XCreateGC (state->dpy, state->window,
287 capture_font_bits (state);
289 set_cursor (state, True);
293 state->tc = textclient_open (dpy);
294 textclient_reshape (state->tc,
297 state->grid_width - 1,
298 state->grid_height - 1);
304 /* Re-query the window size and update the internal character grid if changed.
307 resize_grid (p_state *state)
309 int ow = state->grid_width;
310 int oh = state->grid_height;
311 p_cell *ocells = state->cells;
314 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
316 state->grid_width = state->xgwa.width /(state->char_width * state->scale);
317 state->grid_height = state->xgwa.height /(state->char_height * state->scale);
319 if (ow == state->grid_width &&
320 oh == state->grid_height)
323 state->cells = (p_cell *) calloc (sizeof(p_cell),
324 state->grid_width * state->grid_height);
326 for (y = 0; y < state->grid_height; y++)
328 for (x = 0; x < state->grid_width; x++)
330 p_cell *ncell = &state->cells [state->grid_width * y + x];
331 if (x < ow && y < oh)
332 *ncell = ocells [ow * y + x];
333 ncell->changed = True;
337 if (state->cursor_x >= state->grid_width)
338 state->cursor_x = state->grid_width-1;
339 if (state->cursor_y >= state->grid_height)
340 state->cursor_y = state->grid_height-1;
348 capture_font_bits (p_state *state)
350 XFontStruct *font = state->font;
351 int safe_width, height;
352 unsigned char string[257];
361 safe_width = state->char_width + 1;
362 height = state->char_height;
363 p2 = XCreatePixmapFromBitmapData (state->dpy, state->window,
364 (char *) font6x10_bits,
370 # endif /* BUILTIN_FONT */
372 safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
373 height = state->char_height;
376 p = XCreatePixmap (state->dpy, state->window,
377 (safe_width * 256), height, 1);
379 for (i = 0; i < 256; i++)
380 string[i] = (unsigned char) i;
383 state->gcv.foreground = 0;
384 state->gcv.background = 0;
385 state->gc0 = XCreateGC (state->dpy, p,
386 (GCForeground | GCBackground),
389 state->gcv.foreground = 1;
390 state->gc1 = XCreateGC (state->dpy, p,
391 ((font ? GCFont : 0) |
392 GCForeground | GCBackground |
393 GCCapStyle | GCLineWidth),
397 jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False);
398 jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False);
403 state->gcv.line_width = (int) (((long) state->scale) * 0.8);
404 if (state->gcv.line_width >= state->scale)
405 state->gcv.line_width = state->scale - 1;
406 if (state->gcv.line_width < 1)
407 state->gcv.line_width = 1;
408 state->gc2 = XCreateGC (state->dpy, p,
409 ((font ? GCFont : 0) |
410 GCForeground | GCBackground |
411 GCCapStyle | GCLineWidth),
414 #endif /* FUZZY_BORDER */
416 XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height);
421 XCopyPlane (state->dpy, p2, p, state->gc1,
422 0, 0, font6x10_width, font6x10_height,
424 XFreePixmap (state->dpy, p2);
427 # endif /* BUILTIN_FONT */
429 for (i = 0; i < 256; i++)
431 if (string[i] < font->min_char_or_byte2 ||
432 string[i] > font->max_char_or_byte2)
434 XDrawString (state->dpy, p, state->gc1,
435 i * safe_width, font->ascent,
436 (char *) (string + i), 1);
440 /* Draw the cursor. */
441 XFillRectangle (state->dpy, p, state->gc1,
442 (CURSOR_INDEX * safe_width), 1,
445 ? font->per_char['n'-font->min_char_or_byte2].width
446 : font->max_bounds.width)
447 : state->char_width),
450 : state->char_height));
452 state->font_bits = XGetImage (state->dpy, p, 0, 0,
453 (safe_width * 256), height, ~0L, XYPixmap);
454 XFreePixmap (state->dpy, p);
456 for (i = 0; i < 256; i++)
457 state->chars[i] = make_character (state, i);
458 state->chars[CURSOR_INDEX] = make_character (state, CURSOR_INDEX);
463 make_character (p_state *state, int c)
465 p_char *pc = (p_char *) malloc (sizeof (*pc));
466 pc->name = (unsigned char) c;
467 pc->width = state->scale * state->char_width;
468 pc->height = state->scale * state->char_height;
469 char_to_pixmap (state, pc, c);
475 char_to_pixmap (p_state *state, p_char *pc, int c)
482 #endif /* FUZZY_BORDER */
486 XFontStruct *font = state->font;
487 int safe_width = (font
488 ? font->max_bounds.rbearing - font->min_bounds.lbearing
489 : state->char_width + 1);
491 int width = state->scale * state->char_width;
492 int height = state->scale * state->char_height;
494 if (font && (c < font->min_char_or_byte2 ||
495 c > font->max_char_or_byte2))
499 p = XCreatePixmap (state->dpy, state->window, width, height, 1);
500 XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height);
503 p2 = XCreatePixmap (state->dpy, state->window, width, height, 1);
504 XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height);
505 #endif /* FUZZY_BORDER */
507 from = safe_width * c;
508 to = safe_width * (c + 1);
511 if (c > 75 && c < 150)
513 printf ("\n=========== %d (%c)\n", c, c);
514 for (y = 0; y < state->char_height; y++)
516 for (x1 = from; x1 < to; x1++)
517 printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". ");
524 for (y = 0; y < state->char_height; y++)
525 for (x1 = from; x1 < to; x1++)
526 if (XGetPixel (state->font_bits, x1, y))
528 int xoff = state->scale / 2;
530 for (x2 = x1; x2 < to; x2++)
531 if (!XGetPixel (state->font_bits, x2, y))
534 XDrawLine (state->dpy, p, gc,
535 (x1 - from) * state->scale + xoff, y * state->scale,
536 (x2 - from) * state->scale + xoff, y * state->scale);
538 XDrawLine (state->dpy, p2, gc2,
539 (x1 - from) * state->scale + xoff, y * state->scale,
540 (x2 - from) * state->scale + xoff, y * state->scale);
541 #endif /* FUZZY_BORDER */
546 /* if (pc->blank_p && c == CURSOR_INDEX)
553 #endif /* FUZZY_BORDER */
557 /* Managing the display.
560 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
561 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
564 set_cursor_1 (p_state *state, Bool on)
566 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
568 p_char *cursor = state->chars[CURSOR_INDEX];
569 int new_state = (on ? NORMAL : FADE);
571 if (cell->p_char != cursor)
572 cell->changed = True;
574 if (cell->state != new_state)
575 cell->changed = True;
577 cell->p_char = cursor;
578 cell->state = new_state;
579 return cell->changed;
583 set_cursor (p_state *state, Bool on)
585 if (set_cursor_1 (state, on))
587 if (state->cursor_timer)
588 XtRemoveTimeOut (state->cursor_timer);
589 state->cursor_timer = 0;
590 cursor_on_timer (state, 0);
596 cursor_off_timer (XtPointer closure, XtIntervalId *id)
598 p_state *state = (p_state *) closure;
599 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
600 set_cursor_1 (state, False);
601 state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink,
602 cursor_on_timer, closure);
606 cursor_on_timer (XtPointer closure, XtIntervalId *id)
608 p_state *state = (p_state *) closure;
609 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
610 set_cursor_1 (state, True);
611 state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink,
612 cursor_off_timer, closure);
617 clear (p_state *state)
622 for (y = 0; y < state->grid_height; y++)
623 for (x = 0; x < state->grid_width; x++)
625 p_cell *cell = &state->cells[state->grid_width * y + x];
626 if (cell->state == FLARE || cell->state == NORMAL)
629 cell->changed = True;
632 set_cursor (state, True);
637 decay (p_state *state)
640 for (y = 0; y < state->grid_height; y++)
641 for (x = 0; x < state->grid_width; x++)
643 p_cell *cell = &state->cells[state->grid_width * y + x];
644 if (cell->state == FLARE)
646 cell->state = NORMAL;
647 cell->changed = True;
649 else if (cell->state >= FADE)
652 if (cell->state >= state->ticks)
654 cell->changed = True;
661 scroll (p_state *state)
665 for (x = 0; x < state->grid_width; x++)
667 p_cell *from = 0, *to = 0;
668 for (y = 1; y < state->grid_height; y++)
670 from = &state->cells[state->grid_width * y + x];
671 to = &state->cells[state->grid_width * (y-1) + x];
673 if ((from->state == FLARE || from->state == NORMAL) &&
674 !from->p_char->blank_p)
677 to->state = NORMAL; /* should be FLARE? Looks bad... */
681 if (to->state == FLARE || to->state == NORMAL)
689 if (to && (to->state == FLARE || to->state == NORMAL))
695 set_cursor (state, True);
700 print_char (p_state *state, int c)
702 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
705 /* Start the cursor fading (in case we don't end up overwriting it.) */
706 if (cell->state == FLARE || cell->state == NORMAL)
709 cell->changed = True;
713 if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
714 It would be nice if we could just interpret them all
715 the time, but that would require subprocesses to send
716 CRLF line endings instead of bare LF, so that's no good.
720 switch (state->escstate)
726 /* Dummy case - we don't want the screensaver to beep */
727 /* #### But maybe this should flash the screen? */
730 if (state->cursor_x > 0)
734 if (state->cursor_x < state->grid_width - 8)
736 state->cursor_x = (state->cursor_x & ~7) + 8;
741 if (state->cursor_y < state->grid_height - 1)
750 if(state->last_c == 13)
752 cell->state = NORMAL;
753 cell->p_char = state->chars[state->bk];
754 cell->changed = True;
756 if (state->cursor_y < state->grid_height - 1)
763 cell = &state->cells[state->grid_width * state->cursor_y];
764 if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
767 state->bk = cell->p_char->name;
771 /* Dummy case - I don't want to load several fonts for
772 the maybe two programs world-wide that use that */
776 /* Dummy case - these interrupt escape sequences, so
777 they don't do anything in this state */
783 /* Dummy case - this is supposed to be ignored */
787 for(i = 0; i < NPAR; i++)
788 state->csiparam[i] = 0;
793 cell->p_char = state->chars[c];
794 cell->changed = True;
797 if (c != ' ' && cell->p_char->blank_p)
798 cell->p_char = state->chars[CURSOR_INDEX];
800 if (state->cursor_x >= state->grid_width - 1)
803 if (state->cursor_y >= state->grid_height - 1)
818 case 'c': /* Reset */
822 case 'D': /* Linefeed */
823 if (state->cursor_y < state->grid_height - 1)
829 case 'E': /* Newline */
833 case 'M': /* Reverse newline */
834 if (state->cursor_y > 0)
838 case '7': /* Save state */
839 state->saved_x = state->cursor_x;
840 state->saved_y = state->cursor_y;
843 case '8': /* Restore state */
844 state->cursor_x = state->saved_x;
845 state->cursor_y = state->saved_y;
850 for(i = 0; i < NPAR; i++)
851 state->csiparam[i] = 0;
854 case '%': /* Select charset */
855 /* No, I don't support UTF-8, since the phosphor font
856 isn't even Unicode anyway. We must still catch the
857 last byte, though. */
860 /* I don't support different fonts either - see above
865 /* Escape sequences not supported:
868 * Z - Terminal identification
870 * = - Other keypad change
884 case '0': case '1': case '2': case '3': case '4':
885 case '5': case '6': case '7': case '8': case '9':
886 if (state->curparam < NPAR)
887 state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
890 state->csiparam[++state->curparam] = 0;
896 for (i = 0; i < state->csiparam[0]; i++)
898 if(++state->cursor_x > state->grid_width)
901 if (state->cursor_y < state->grid_height - 1)
906 cell = &state->cells[state->grid_width * state->cursor_y + state->cursor_x];
907 if (cell->state == FLARE || cell->state == NORMAL)
910 cell->changed = True;
918 if (state->csiparam[0] == 0)
919 state->csiparam[0] = 1;
920 if ((state->cursor_y -= state->csiparam[0]) < 0)
928 if (state->csiparam[0] == 0)
929 state->csiparam[0] = 1;
930 if ((state->cursor_y += state->csiparam[0]) >= state->grid_height - 1)
931 state->cursor_y = state->grid_height - 1;
936 if (state->csiparam[0] == 0)
937 state->csiparam[0] = 1;
938 if ((state->cursor_x += state->csiparam[0]) >= state->grid_width - 1)
939 state->cursor_x = state->grid_width - 1;
943 if (state->csiparam[0] == 0)
944 state->csiparam[0] = 1;
945 if ((state->cursor_x -= state->csiparam[0]) < 0)
950 if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1)
951 state->cursor_y = state->grid_height - 1;
956 if ((state->cursor_x = (state->csiparam[0] - 1)) >= state->grid_width - 1)
957 state->cursor_x = state->grid_width - 1;
962 if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1)
963 state->cursor_y = state->grid_height - 1;
964 if ((state->cursor_x = (state->csiparam[1] - 1)) >= state->grid_width - 1)
965 state->cursor_x = state->grid_width - 1;
966 if(state->cursor_y < 0)
968 if(state->cursor_x < 0)
974 end = state->grid_height * state->grid_width;
975 if (state->csiparam[0] == 0)
976 start = state->grid_width * state->cursor_y + state->cursor_x;
977 if (state->csiparam[0] == 1)
978 end = state->grid_width * state->cursor_y + state->cursor_x;
979 for (i = start; i < end; i++)
981 cell = &state->cells[i];
982 if (cell->state == FLARE || cell->state == NORMAL)
985 cell->changed = True;
988 set_cursor (state, True);
993 end = state->grid_width;
994 if (state->csiparam[0] == 0)
995 start = state->cursor_x;
996 if (state->csiparam[1] == 1)
997 end = state->cursor_x;
998 for (i = start; i < end; i++)
1000 if (cell->state == FLARE || cell->state == NORMAL)
1003 cell->changed = True;
1007 state->escstate = 0;
1009 case 's': /* Save position */
1010 state->saved_x = state->cursor_x;
1011 state->saved_y = state->cursor_y;
1012 state->escstate = 0;
1014 case 'u': /* Restore position */
1015 state->cursor_x = state->saved_x;
1016 state->cursor_y = state->saved_y;
1017 state->escstate = 0;
1019 case '?': /* DEC Private modes */
1020 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1021 state->escstate = 0;
1024 /* Known unsupported CSIs:
1026 * L - Insert blank lines
1027 * M - Delete lines (I don't know what this means...)
1028 * P - Delete characters
1029 * X - Erase characters (difference with P being...?)
1030 * c - Terminal identification
1031 * g - Clear tab stop(s)
1032 * h - Set mode (Mainly due to its complexity and lack of good
1035 * m - Set mode (Phosphor is, per defenition, green on black)
1037 * q - Set keyboard LEDs
1038 * r - Set scrolling region (too exhausting - noone uses this,
1041 state->escstate = 0;
1046 state->escstate = 0;
1049 set_cursor (state, True);
1052 #endif /* HAVE_FORKPTY */
1054 if (c == '\t') c = ' '; /* blah. */
1056 if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
1058 if (c == '\n' && state->last_c == '\r')
1059 ; /* CRLF -- do nothing */
1062 state->cursor_x = 0;
1063 if (state->cursor_y == state->grid_height - 1)
1069 else if (c == '\014')
1075 cell->state = FLARE;
1076 cell->p_char = state->chars[c];
1077 cell->changed = True;
1080 if (c != ' ' && cell->p_char->blank_p)
1081 cell->p_char = state->chars[CURSOR_INDEX];
1083 if (state->cursor_x >= state->grid_width - 1)
1085 state->cursor_x = 0;
1086 if (state->cursor_y >= state->grid_height - 1)
1092 set_cursor (state, True);
1100 update_display (p_state *state, Bool changed_only)
1104 for (y = 0; y < state->grid_height; y++)
1105 for (x = 0; x < state->grid_width; x++)
1107 p_cell *cell = &state->cells[state->grid_width * y + x];
1108 int width, height, tx, ty;
1110 if (changed_only && !cell->changed)
1113 width = state->char_width * state->scale;
1114 height = state->char_height * state->scale;
1118 if (cell->state == BLANK || cell->p_char->blank_p)
1120 XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
1121 tx, ty, width, height);
1126 GC gc1 = state->gcs[cell->state];
1127 GC gc2 = ((cell->state + 2) < state->ticks
1128 ? state->gcs[cell->state + 2]
1130 GC gc3 = (gc2 ? gc2 : gc1);
1132 XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
1133 0, 0, width, height, tx, ty, 1L);
1136 XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
1137 XSetClipOrigin (state->dpy, gc1, tx, ty);
1138 XFillRectangle (state->dpy, state->window, gc1,
1139 tx, ty, width, height);
1140 XSetClipMask (state->dpy, gc1, None);
1142 #else /* !FUZZY_BORDER */
1144 XCopyPlane (state->dpy,
1145 cell->p_char->pixmap, state->window,
1146 state->gcs[cell->state],
1147 0, 0, width, height, tx, ty, 1L);
1149 #endif /* !FUZZY_BORDER */
1152 cell->changed = False;
1157 static unsigned long
1158 phosphor_draw (Display *dpy, Window window, void *closure)
1160 p_state *state = (p_state *) closure;
1162 update_display (state, True);
1165 c = textclient_getc (state->tc);
1167 print_char (state, c);
1169 return state->delay;
1174 phosphor_reshape (Display *dpy, Window window, void *closure,
1175 unsigned int w, unsigned int h)
1177 p_state *state = (p_state *) closure;
1178 Bool changed_p = resize_grid (state);
1180 if (! changed_p) return;
1182 textclient_reshape (state->tc, w, h,
1183 state->grid_width - 1,
1184 state->grid_height - 1);
1189 phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
1191 p_state *state = (p_state *) closure;
1193 if (event->xany.type == Expose)
1194 update_display (state, False);
1195 else if (event->xany.type == KeyPress)
1196 return textclient_putc (state->tc, &event->xkey);
1201 phosphor_free (Display *dpy, Window window, void *closure)
1203 p_state *state = (p_state *) closure;
1205 textclient_close (state->tc);
1206 if (state->cursor_timer)
1207 XtRemoveTimeOut (state->cursor_timer);
1209 /* #### there's more to free here */
1216 static const char *phosphor_defaults [] = {
1217 ".background: Black",
1218 ".foreground: #00FF00",
1220 "*fadeForeground: #006400",
1221 "*flareForeground: #FFFFFF",
1222 #if defined(BUILTIN_FONT)
1224 #elif defined(HAVE_COCOA)
1233 "*program: xscreensaver-text",
1235 "*metaSendsESC: True",
1239 #else /* !HAVE_FORKPTY */
1241 #endif /* !HAVE_FORKPTY */
1245 static XrmOptionDescRec phosphor_options [] = {
1246 { "-font", ".font", XrmoptionSepArg, 0 },
1247 { "-scale", ".scale", XrmoptionSepArg, 0 },
1248 { "-ticks", ".ticks", XrmoptionSepArg, 0 },
1249 { "-delay", ".delay", XrmoptionSepArg, 0 },
1250 { "-program", ".program", XrmoptionSepArg, 0 },
1251 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
1252 { "-pty", ".usePty", XrmoptionNoArg, "True" },
1253 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
1254 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
1255 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
1256 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
1261 XSCREENSAVER_MODULE ("Phosphor", phosphor)