1 /* xscreensaver, Copyright (c) 1999-2013 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->xgwa.screen, state->xgwa.visual,
240 state->xgwa.colormap,
246 /* Adjust to the number of colors we actually got. */
247 state->ticks = ncolors + STATE_MAX;
249 /* Now, GCs all around.
251 state->gcv.font = (font ? font->fid : 0);
252 state->gcv.cap_style = CapRound;
254 state->gcv.line_width = (int) (((long) state->scale) * 1.3);
255 if (state->gcv.line_width == state->scale)
256 state->gcv.line_width++;
257 #else /* !FUZZY_BORDER */
258 state->gcv.line_width = (int) (((long) state->scale) * 0.9);
259 if (state->gcv.line_width >= state->scale)
260 state->gcv.line_width = state->scale - 1;
261 if (state->gcv.line_width < 1)
262 state->gcv.line_width = 1;
263 #endif /* !FUZZY_BORDER */
265 flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth);
267 state->gcv.background = bg;
268 state->gcv.foreground = bg;
269 state->gcs[BLANK] = XCreateGC (state->dpy, state->window, flags,
272 state->gcv.foreground = flare;
273 state->gcs[FLARE] = XCreateGC (state->dpy, state->window, flags,
276 state->gcv.foreground = fg;
277 state->gcs[NORMAL] = XCreateGC (state->dpy, state->window, flags,
280 for (i = 0; i < ncolors; i++)
282 state->gcv.foreground = colors[i].pixel;
283 state->gcs[STATE_MAX + i] = XCreateGC (state->dpy, state->window,
288 capture_font_bits (state);
290 set_cursor (state, True);
294 state->tc = textclient_open (dpy);
295 textclient_reshape (state->tc,
298 state->grid_width - 1,
299 state->grid_height - 1);
305 /* Re-query the window size and update the internal character grid if changed.
308 resize_grid (p_state *state)
310 int ow = state->grid_width;
311 int oh = state->grid_height;
312 p_cell *ocells = state->cells;
315 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
317 state->grid_width = state->xgwa.width /(state->char_width * state->scale);
318 state->grid_height = state->xgwa.height /(state->char_height * state->scale);
320 if (ow == state->grid_width &&
321 oh == state->grid_height)
324 state->cells = (p_cell *) calloc (sizeof(p_cell),
325 state->grid_width * state->grid_height);
327 for (y = 0; y < state->grid_height; y++)
329 for (x = 0; x < state->grid_width; x++)
331 p_cell *ncell = &state->cells [state->grid_width * y + x];
332 if (x < ow && y < oh)
333 *ncell = ocells [ow * y + x];
334 ncell->changed = True;
338 if (state->cursor_x >= state->grid_width)
339 state->cursor_x = state->grid_width-1;
340 if (state->cursor_y >= state->grid_height)
341 state->cursor_y = state->grid_height-1;
349 capture_font_bits (p_state *state)
351 XFontStruct *font = state->font;
352 int safe_width, height;
353 unsigned char string[257];
362 safe_width = state->char_width + 1;
363 height = state->char_height;
364 p2 = XCreatePixmapFromBitmapData (state->dpy, state->window,
365 (char *) font6x10_bits,
371 # endif /* BUILTIN_FONT */
373 safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
374 height = state->char_height;
377 p = XCreatePixmap (state->dpy, state->window,
378 (safe_width * 256), height, 1);
380 for (i = 0; i < 256; i++)
381 string[i] = (unsigned char) i;
384 state->gcv.foreground = 0;
385 state->gcv.background = 0;
386 state->gc0 = XCreateGC (state->dpy, p,
387 (GCForeground | GCBackground),
390 state->gcv.foreground = 1;
391 state->gc1 = XCreateGC (state->dpy, p,
392 ((font ? GCFont : 0) |
393 GCForeground | GCBackground |
394 GCCapStyle | GCLineWidth),
398 jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False);
399 jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False);
404 state->gcv.line_width = (int) (((long) state->scale) * 0.8);
405 if (state->gcv.line_width >= state->scale)
406 state->gcv.line_width = state->scale - 1;
407 if (state->gcv.line_width < 1)
408 state->gcv.line_width = 1;
409 state->gc2 = XCreateGC (state->dpy, p,
410 ((font ? GCFont : 0) |
411 GCForeground | GCBackground |
412 GCCapStyle | GCLineWidth),
415 #endif /* FUZZY_BORDER */
417 XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height);
422 XCopyPlane (state->dpy, p2, p, state->gc1,
423 0, 0, font6x10_width, font6x10_height,
425 XFreePixmap (state->dpy, p2);
428 # endif /* BUILTIN_FONT */
430 for (i = 0; i < 256; i++)
432 if (string[i] < font->min_char_or_byte2 ||
433 string[i] > font->max_char_or_byte2)
435 XDrawString (state->dpy, p, state->gc1,
436 i * safe_width, font->ascent,
437 (char *) (string + i), 1);
441 /* Draw the cursor. */
442 XFillRectangle (state->dpy, p, state->gc1,
443 (CURSOR_INDEX * safe_width), 1,
446 ? font->per_char['n'-font->min_char_or_byte2].width
447 : font->max_bounds.width)
448 : state->char_width),
451 : state->char_height));
453 state->font_bits = XGetImage (state->dpy, p, 0, 0,
454 (safe_width * 256), height, ~0L, XYPixmap);
455 XFreePixmap (state->dpy, p);
457 for (i = 0; i < 256; i++)
458 state->chars[i] = make_character (state, i);
459 state->chars[CURSOR_INDEX] = make_character (state, CURSOR_INDEX);
464 make_character (p_state *state, int c)
466 p_char *pc = (p_char *) malloc (sizeof (*pc));
467 pc->name = (unsigned char) c;
468 pc->width = state->scale * state->char_width;
469 pc->height = state->scale * state->char_height;
470 char_to_pixmap (state, pc, c);
476 char_to_pixmap (p_state *state, p_char *pc, int c)
483 #endif /* FUZZY_BORDER */
487 XFontStruct *font = state->font;
488 int safe_width = (font
489 ? font->max_bounds.rbearing - font->min_bounds.lbearing
490 : state->char_width + 1);
492 int width = state->scale * state->char_width;
493 int height = state->scale * state->char_height;
495 if (font && (c < font->min_char_or_byte2 ||
496 c > font->max_char_or_byte2))
500 p = XCreatePixmap (state->dpy, state->window, width, height, 1);
501 XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height);
504 p2 = XCreatePixmap (state->dpy, state->window, width, height, 1);
505 XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height);
506 #endif /* FUZZY_BORDER */
508 from = safe_width * c;
509 to = safe_width * (c + 1);
512 if (c > 75 && c < 150)
514 printf ("\n=========== %d (%c)\n", c, c);
515 for (y = 0; y < state->char_height; y++)
517 for (x1 = from; x1 < to; x1++)
518 printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". ");
525 for (y = 0; y < state->char_height; y++)
526 for (x1 = from; x1 < to; x1++)
527 if (XGetPixel (state->font_bits, x1, y))
529 int xoff = state->scale / 2;
531 for (x2 = x1; x2 < to; x2++)
532 if (!XGetPixel (state->font_bits, x2, y))
535 XDrawLine (state->dpy, p, gc,
536 (x1 - from) * state->scale + xoff, y * state->scale,
537 (x2 - from) * state->scale + xoff, y * state->scale);
539 XDrawLine (state->dpy, p2, gc2,
540 (x1 - from) * state->scale + xoff, y * state->scale,
541 (x2 - from) * state->scale + xoff, y * state->scale);
542 #endif /* FUZZY_BORDER */
547 /* if (pc->blank_p && c == CURSOR_INDEX)
554 #endif /* FUZZY_BORDER */
558 /* Managing the display.
561 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
562 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
565 set_cursor_1 (p_state *state, Bool on)
567 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
569 p_char *cursor = state->chars[CURSOR_INDEX];
570 int new_state = (on ? NORMAL : FADE);
572 if (cell->p_char != cursor)
573 cell->changed = True;
575 if (cell->state != new_state)
576 cell->changed = True;
578 cell->p_char = cursor;
579 cell->state = new_state;
580 return cell->changed;
584 set_cursor (p_state *state, Bool on)
586 if (set_cursor_1 (state, on))
588 if (state->cursor_timer)
589 XtRemoveTimeOut (state->cursor_timer);
590 state->cursor_timer = 0;
591 cursor_on_timer (state, 0);
597 cursor_off_timer (XtPointer closure, XtIntervalId *id)
599 p_state *state = (p_state *) closure;
600 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
601 set_cursor_1 (state, False);
602 state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink,
603 cursor_on_timer, closure);
607 cursor_on_timer (XtPointer closure, XtIntervalId *id)
609 p_state *state = (p_state *) closure;
610 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
611 set_cursor_1 (state, True);
612 state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink,
613 cursor_off_timer, closure);
618 clear (p_state *state)
623 for (y = 0; y < state->grid_height; y++)
624 for (x = 0; x < state->grid_width; x++)
626 p_cell *cell = &state->cells[state->grid_width * y + x];
627 if (cell->state == FLARE || cell->state == NORMAL)
630 cell->changed = True;
633 set_cursor (state, True);
638 decay (p_state *state)
641 for (y = 0; y < state->grid_height; y++)
642 for (x = 0; x < state->grid_width; x++)
644 p_cell *cell = &state->cells[state->grid_width * y + x];
645 if (cell->state == FLARE)
647 cell->state = NORMAL;
648 cell->changed = True;
650 else if (cell->state >= FADE)
653 if (cell->state >= state->ticks)
655 cell->changed = True;
662 scroll (p_state *state)
666 for (x = 0; x < state->grid_width; x++)
668 p_cell *from = 0, *to = 0;
669 for (y = 1; y < state->grid_height; y++)
671 from = &state->cells[state->grid_width * y + x];
672 to = &state->cells[state->grid_width * (y-1) + x];
674 if ((from->state == FLARE || from->state == NORMAL) &&
675 !from->p_char->blank_p)
678 to->state = NORMAL; /* should be FLARE? Looks bad... */
682 if (to->state == FLARE || to->state == NORMAL)
690 if (to && (to->state == FLARE || to->state == NORMAL))
696 set_cursor (state, True);
701 print_char (p_state *state, int c)
703 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
706 /* Start the cursor fading (in case we don't end up overwriting it.) */
707 if (cell->state == FLARE || cell->state == NORMAL)
710 cell->changed = True;
714 if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
715 It would be nice if we could just interpret them all
716 the time, but that would require subprocesses to send
717 CRLF line endings instead of bare LF, so that's no good.
721 switch (state->escstate)
727 /* Dummy case - we don't want the screensaver to beep */
728 /* #### But maybe this should flash the screen? */
731 if (state->cursor_x > 0)
735 if (state->cursor_x < state->grid_width - 8)
737 state->cursor_x = (state->cursor_x & ~7) + 8;
742 if (state->cursor_y < state->grid_height - 1)
751 if(state->last_c == 13)
753 cell->state = NORMAL;
754 cell->p_char = state->chars[state->bk];
755 cell->changed = True;
757 if (state->cursor_y < state->grid_height - 1)
764 cell = &state->cells[state->grid_width * state->cursor_y];
765 if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
768 state->bk = cell->p_char->name;
772 /* Dummy case - I don't want to load several fonts for
773 the maybe two programs world-wide that use that */
777 /* Dummy case - these interrupt escape sequences, so
778 they don't do anything in this state */
784 /* Dummy case - this is supposed to be ignored */
788 for(i = 0; i < NPAR; i++)
789 state->csiparam[i] = 0;
794 cell->p_char = state->chars[c];
795 cell->changed = True;
798 if (c != ' ' && cell->p_char->blank_p)
799 cell->p_char = state->chars[CURSOR_INDEX];
801 if (state->cursor_x >= state->grid_width - 1)
804 if (state->cursor_y >= state->grid_height - 1)
819 case 'c': /* Reset */
823 case 'D': /* Linefeed */
824 if (state->cursor_y < state->grid_height - 1)
830 case 'E': /* Newline */
834 case 'M': /* Reverse newline */
835 if (state->cursor_y > 0)
839 case '7': /* Save state */
840 state->saved_x = state->cursor_x;
841 state->saved_y = state->cursor_y;
844 case '8': /* Restore state */
845 state->cursor_x = state->saved_x;
846 state->cursor_y = state->saved_y;
851 for(i = 0; i < NPAR; i++)
852 state->csiparam[i] = 0;
855 case '%': /* Select charset */
856 /* No, I don't support UTF-8, since the phosphor font
857 isn't even Unicode anyway. We must still catch the
858 last byte, though. */
861 /* I don't support different fonts either - see above
866 /* Escape sequences not supported:
869 * Z - Terminal identification
871 * = - Other keypad change
885 case '0': case '1': case '2': case '3': case '4':
886 case '5': case '6': case '7': case '8': case '9':
887 if (state->curparam < NPAR)
888 state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
891 state->csiparam[++state->curparam] = 0;
897 for (i = 0; i < state->csiparam[0]; i++)
899 if(++state->cursor_x > state->grid_width)
902 if (state->cursor_y < state->grid_height - 1)
907 cell = &state->cells[state->grid_width * state->cursor_y + state->cursor_x];
908 if (cell->state == FLARE || cell->state == NORMAL)
911 cell->changed = True;
919 if (state->csiparam[0] == 0)
920 state->csiparam[0] = 1;
921 if ((state->cursor_y -= state->csiparam[0]) < 0)
929 if (state->csiparam[0] == 0)
930 state->csiparam[0] = 1;
931 if ((state->cursor_y += state->csiparam[0]) >= state->grid_height - 1)
932 state->cursor_y = state->grid_height - 1;
937 if (state->csiparam[0] == 0)
938 state->csiparam[0] = 1;
939 if ((state->cursor_x += state->csiparam[0]) >= state->grid_width - 1)
940 state->cursor_x = state->grid_width - 1;
944 if (state->csiparam[0] == 0)
945 state->csiparam[0] = 1;
946 if ((state->cursor_x -= state->csiparam[0]) < 0)
951 if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1)
952 state->cursor_y = state->grid_height - 1;
957 if ((state->cursor_x = (state->csiparam[0] - 1)) >= state->grid_width - 1)
958 state->cursor_x = state->grid_width - 1;
963 if ((state->cursor_y = (state->csiparam[0] - 1)) >= state->grid_height - 1)
964 state->cursor_y = state->grid_height - 1;
965 if ((state->cursor_x = (state->csiparam[1] - 1)) >= state->grid_width - 1)
966 state->cursor_x = state->grid_width - 1;
967 if(state->cursor_y < 0)
969 if(state->cursor_x < 0)
975 end = state->grid_height * state->grid_width;
976 if (state->csiparam[0] == 0)
977 start = state->grid_width * state->cursor_y + state->cursor_x;
978 if (state->csiparam[0] == 1)
979 end = state->grid_width * state->cursor_y + state->cursor_x;
980 for (i = start; i < end; i++)
982 cell = &state->cells[i];
983 if (cell->state == FLARE || cell->state == NORMAL)
986 cell->changed = True;
989 set_cursor (state, True);
994 end = state->grid_width;
995 if (state->csiparam[0] == 0)
996 start = state->cursor_x;
997 if (state->csiparam[1] == 1)
998 end = state->cursor_x;
999 for (i = start; i < end; i++)
1001 if (cell->state == FLARE || cell->state == NORMAL)
1004 cell->changed = True;
1008 state->escstate = 0;
1010 case 's': /* Save position */
1011 state->saved_x = state->cursor_x;
1012 state->saved_y = state->cursor_y;
1013 state->escstate = 0;
1015 case 'u': /* Restore position */
1016 state->cursor_x = state->saved_x;
1017 state->cursor_y = state->saved_y;
1018 state->escstate = 0;
1020 case '?': /* DEC Private modes */
1021 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1022 state->escstate = 0;
1025 /* Known unsupported CSIs:
1027 * L - Insert blank lines
1028 * M - Delete lines (I don't know what this means...)
1029 * P - Delete characters
1030 * X - Erase characters (difference with P being...?)
1031 * c - Terminal identification
1032 * g - Clear tab stop(s)
1033 * h - Set mode (Mainly due to its complexity and lack of good
1036 * m - Set mode (Phosphor is, per defenition, green on black)
1038 * q - Set keyboard LEDs
1039 * r - Set scrolling region (too exhausting - noone uses this,
1042 state->escstate = 0;
1047 state->escstate = 0;
1050 set_cursor (state, True);
1053 #endif /* HAVE_FORKPTY */
1055 if (c == '\t') c = ' '; /* blah. */
1057 if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
1059 if (c == '\n' && state->last_c == '\r')
1060 ; /* CRLF -- do nothing */
1063 state->cursor_x = 0;
1064 if (state->cursor_y == state->grid_height - 1)
1070 else if (c == '\014')
1076 cell->state = FLARE;
1077 cell->p_char = state->chars[c];
1078 cell->changed = True;
1081 if (c != ' ' && cell->p_char->blank_p)
1082 cell->p_char = state->chars[CURSOR_INDEX];
1084 if (state->cursor_x >= state->grid_width - 1)
1086 state->cursor_x = 0;
1087 if (state->cursor_y >= state->grid_height - 1)
1093 set_cursor (state, True);
1101 update_display (p_state *state, Bool changed_only)
1105 for (y = 0; y < state->grid_height; y++)
1106 for (x = 0; x < state->grid_width; x++)
1108 p_cell *cell = &state->cells[state->grid_width * y + x];
1109 int width, height, tx, ty;
1111 if (changed_only && !cell->changed)
1114 width = state->char_width * state->scale;
1115 height = state->char_height * state->scale;
1119 if (cell->state == BLANK || cell->p_char->blank_p)
1121 XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
1122 tx, ty, width, height);
1127 GC gc1 = state->gcs[cell->state];
1128 GC gc2 = ((cell->state + 2) < state->ticks
1129 ? state->gcs[cell->state + 2]
1131 GC gc3 = (gc2 ? gc2 : gc1);
1133 XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
1134 0, 0, width, height, tx, ty, 1L);
1137 XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
1138 XSetClipOrigin (state->dpy, gc1, tx, ty);
1139 XFillRectangle (state->dpy, state->window, gc1,
1140 tx, ty, width, height);
1141 XSetClipMask (state->dpy, gc1, None);
1143 #else /* !FUZZY_BORDER */
1145 XCopyPlane (state->dpy,
1146 cell->p_char->pixmap, state->window,
1147 state->gcs[cell->state],
1148 0, 0, width, height, tx, ty, 1L);
1150 #endif /* !FUZZY_BORDER */
1153 cell->changed = False;
1158 static unsigned long
1159 phosphor_draw (Display *dpy, Window window, void *closure)
1161 p_state *state = (p_state *) closure;
1163 update_display (state, True);
1166 c = textclient_getc (state->tc);
1168 print_char (state, c);
1170 return state->delay;
1175 phosphor_reshape (Display *dpy, Window window, void *closure,
1176 unsigned int w, unsigned int h)
1178 p_state *state = (p_state *) closure;
1179 Bool changed_p = resize_grid (state);
1181 if (! changed_p) return;
1183 textclient_reshape (state->tc, w, h,
1184 state->grid_width - 1,
1185 state->grid_height - 1);
1190 phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
1192 p_state *state = (p_state *) closure;
1194 if (event->xany.type == Expose)
1195 update_display (state, False);
1196 else if (event->xany.type == KeyPress)
1197 return textclient_putc (state->tc, &event->xkey);
1202 phosphor_free (Display *dpy, Window window, void *closure)
1204 p_state *state = (p_state *) closure;
1206 textclient_close (state->tc);
1207 if (state->cursor_timer)
1208 XtRemoveTimeOut (state->cursor_timer);
1210 /* #### there's more to free here */
1217 static const char *phosphor_defaults [] = {
1218 ".background: Black",
1219 ".foreground: #00FF00",
1221 "*fadeForeground: #006400",
1222 "*flareForeground: #FFFFFF",
1223 #if defined(BUILTIN_FONT)
1225 #elif defined(HAVE_COCOA)
1234 "*program: xscreensaver-text",
1236 "*metaSendsESC: True",
1240 #else /* !HAVE_FORKPTY */
1242 #endif /* !HAVE_FORKPTY */
1246 static XrmOptionDescRec phosphor_options [] = {
1247 { "-font", ".font", XrmoptionSepArg, 0 },
1248 { "-scale", ".scale", XrmoptionSepArg, 0 },
1249 { "-ticks", ".ticks", XrmoptionSepArg, 0 },
1250 { "-delay", ".delay", XrmoptionSepArg, 0 },
1251 { "-program", ".program", XrmoptionSepArg, 0 },
1252 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
1253 { "-pty", ".usePty", XrmoptionNoArg, "True" },
1254 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
1255 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
1256 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
1257 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
1262 XSCREENSAVER_MODULE ("Phosphor", phosphor)