1 /* xscreensaver, Copyright (c) 1999-2016 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; unsigned 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 process_unicrud (p_state *state, int c)
735 if ((c & 0xE0) == 0xC0) { /* 110xxxxx: 11 bits, 2 bytes */
737 state->unicrud[0] = c;
738 state->escstate = 102;
739 } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx: 16 bits, 3 bytes */
741 state->unicrud[0] = c;
742 state->escstate = 103;
743 } else if ((c & 0xF8) == 0xF0) { /* 11110xxx: 21 bits, 4 bytes */
745 state->unicrud[0] = c;
746 state->escstate = 104;
747 } else if ((c & 0xFC) == 0xF8) { /* 111110xx: 26 bits, 5 bytes */
749 state->unicrud[0] = c;
750 state->escstate = 105;
751 } else if ((c & 0xFE) == 0xFC) { /* 1111110x: 31 bits, 6 bytes */
753 state->unicrud[0] = c;
754 state->escstate = 106;
755 } else if (state->unicruds == 0) {
758 int total = state->escstate - 100; /* see what I did there */
759 if (state->unicruds < total) {
760 /* Buffer more bytes of the UTF-8 sequence */
761 state->unicrud[state->unicruds++] = c;
764 if (state->unicruds >= total) {
765 /* Done! Convert it to Latin1 and print that. */
767 state->unicrud[state->unicruds] = 0;
768 s = utf8_to_latin1 ((const char *) state->unicrud, False);
772 c = (unsigned char) s[0];
783 print_char (p_state *state, int c)
785 int cols = state->grid_width;
786 int rows = state->grid_height;
787 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
790 /* Start the cursor fading (in case we don't end up overwriting it.) */
791 if (cell->state == FLARE || cell->state == NORMAL)
794 cell->changed = True;
798 if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
799 It would be nice if we could just interpret them all
800 the time, but that would require subprocesses to send
801 CRLF line endings instead of bare LF, so that's no good.
807 /* Mostly duplicated in apple2-main.c */
809 switch (state->escstate)
815 /* Dummy case - we don't want the screensaver to beep */
816 /* #### But maybe this should flash the screen? */
819 if (state->cursor_x > 0)
823 if (state->cursor_x < cols - 8)
825 state->cursor_x = (state->cursor_x & ~7) + 8;
830 if (state->cursor_y < rows - 1)
837 # ifndef HAVE_FORKPTY
838 state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
842 if(state->last_c == 13)
844 cell->state = NORMAL;
845 cell->p_char = state->chars[state->bk];
846 cell->changed = True;
848 if (state->cursor_y < rows - 1)
855 cell = &state->cells[cols * state->cursor_y];
856 if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
859 state->bk = cell->p_char->name;
863 /* Dummy case - there is one and only one font. */
867 /* Dummy case - these interrupt escape sequences, so
868 they don't do anything in this state */
874 /* Dummy case - this is supposed to be ignored */
878 for(i = 0; i < NPAR; i++)
879 state->csiparam[i] = 0;
884 PRINT: /* Come from states 102-106 */
885 c = process_unicrud (state, c);
889 /* If the cursor is in column 39 and we print a character, then
890 that character shows up in column 39, and the cursor is no
891 longer visible on the screen (it's in "column 40".) If
892 another character is printed, then that character shows up in
893 column 0, and the cursor moves to column 1.
895 This is empirically what xterm and gnome-terminal do, so that
896 must be the right thing. (In xterm, the cursor vanishes,
897 whereas; in gnome-terminal, the cursor overprints the
898 character in col 39.)
901 cell->p_char = state->chars[c];
902 cell->changed = True;
905 if (c != ' ' && cell->p_char->blank_p)
906 cell->p_char = state->chars[CURSOR_INDEX];
908 if (state->cursor_x >= cols - 1 /*####*/)
911 if (state->cursor_y >= rows - 1)
926 case 'c': /* Reset */
930 case 'D': /* Linefeed */
931 if (state->cursor_y < rows - 1)
937 case 'E': /* Newline */
941 case 'M': /* Reverse newline */
942 if (state->cursor_y > 0)
946 case '7': /* Save state */
947 state->saved_x = state->cursor_x;
948 state->saved_y = state->cursor_y;
951 case '8': /* Restore state */
952 state->cursor_x = state->saved_x;
953 state->cursor_y = state->saved_y;
958 for(i = 0; i < NPAR; i++)
959 state->csiparam[i] = 0;
962 case '%': /* Select charset */
963 /* @: Select default (ISO 646 / ISO 8859-1)
965 8: Select UTF-8 (obsolete)
967 We can just ignore this and always process UTF-8, I think?
968 We must still catch the last byte, though.
972 /* I don't support different fonts either - see above
977 /* Escape sequences not supported:
980 * Z - Terminal identification
982 * = - Other keypad change
996 case '0': case '1': case '2': case '3': case '4':
997 case '5': case '6': case '7': case '8': case '9':
998 if (state->curparam < NPAR)
999 state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
1002 state->csiparam[++state->curparam] = 0;
1005 state->escstate = 3;
1008 for (i = 0; i < state->csiparam[0]; i++)
1010 if(++state->cursor_x > cols)
1012 state->cursor_x = 0;
1013 if (state->cursor_y < rows - 1)
1018 cell = &state->cells[cols * state->cursor_y + state->cursor_x];
1019 if (cell->state == FLARE || cell->state == NORMAL)
1022 cell->changed = True;
1025 state->escstate = 0;
1028 state->cursor_x = 0;
1030 if (state->csiparam[0] == 0)
1031 state->csiparam[0] = 1;
1032 if ((state->cursor_y -= state->csiparam[0]) < 0)
1033 state->cursor_y = 0;
1034 state->escstate = 0;
1037 state->cursor_x = 0;
1040 if (state->csiparam[0] == 0)
1041 state->csiparam[0] = 1;
1042 if ((state->cursor_y += state->csiparam[0]) >= rows - 1 /*####*/)
1043 state->cursor_y = rows - 1;
1044 state->escstate = 0;
1048 if (state->csiparam[0] == 0)
1049 state->csiparam[0] = 1;
1050 if ((state->cursor_x += state->csiparam[0]) >= cols - 1 /*####*/)
1051 state->cursor_x = cols - 1;
1052 state->escstate = 0;
1055 if (state->csiparam[0] == 0)
1056 state->csiparam[0] = 1;
1057 if ((state->cursor_x -= state->csiparam[0]) < 0)
1058 state->cursor_x = 0;
1059 state->escstate = 0;
1062 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1063 state->cursor_y = rows - 1;
1064 state->escstate = 0;
1068 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols - 1 /*####*/)
1069 state->cursor_x = cols - 1;
1070 state->escstate = 0;
1074 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1075 state->cursor_y = rows - 1;
1076 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols - 1 /*####*/)
1077 state->cursor_x = cols - 1;
1078 if(state->cursor_y < 0)
1079 state->cursor_y = 0;
1080 if(state->cursor_x < 0)
1081 state->cursor_x = 0;
1082 state->escstate = 0;
1087 if (state->csiparam[0] == 0)
1088 start = cols * state->cursor_y + state->cursor_x;
1089 if (state->csiparam[0] == 1)
1090 end = cols * state->cursor_y + state->cursor_x;
1091 for (i = start; i < end; i++)
1093 cell = &state->cells[i];
1094 if (cell->state == FLARE || cell->state == NORMAL)
1097 cell->changed = True;
1100 set_cursor (state, True);
1101 state->escstate = 0;
1106 if (state->csiparam[0] == 0)
1107 start = state->cursor_x;
1108 if (state->csiparam[1] == 1)
1109 end = state->cursor_x;
1110 for (i = start; i < end; i++)
1112 if (cell->state == FLARE || cell->state == NORMAL)
1115 cell->changed = True;
1119 state->escstate = 0;
1121 case 'm': /* Set attributes unimplemented (bold, blink, rev) */
1122 state->escstate = 0;
1124 case 's': /* Save position */
1125 state->saved_x = state->cursor_x;
1126 state->saved_y = state->cursor_y;
1127 state->escstate = 0;
1129 case 'u': /* Restore position */
1130 state->cursor_x = state->saved_x;
1131 state->cursor_y = state->saved_y;
1132 state->escstate = 0;
1134 case '?': /* DEC Private modes */
1135 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1136 state->escstate = 0;
1139 /* Known unsupported CSIs:
1141 * L - Insert blank lines
1142 * M - Delete lines (I don't know what this means...)
1143 * P - Delete characters
1144 * X - Erase characters (difference with P being...?)
1145 * c - Terminal identification
1146 * g - Clear tab stop(s)
1147 * h - Set mode (Mainly due to its complexity and lack of good
1150 * m - Set mode (Phosphor is, per defenition, green on black)
1152 * q - Set keyboard LEDs
1153 * r - Set scrolling region (too exhausting - noone uses this,
1156 state->escstate = 0;
1161 state->escstate = 0;
1164 case 102: /* states 102-106 are for UTF-8 decoding */
1174 set_cursor (state, True);
1177 #endif /* HAVE_FORKPTY */
1179 if (c == '\t') c = ' '; /* blah. */
1181 if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
1183 if (c == '\n' && state->last_c == '\r')
1184 ; /* CRLF -- do nothing */
1187 state->cursor_x = 0;
1188 if (state->cursor_y == rows - 1)
1194 else if (c == '\014')
1200 c = process_unicrud (state, c);
1203 cell->state = FLARE;
1204 cell->p_char = state->chars[c];
1205 cell->changed = True;
1208 if (c != ' ' && cell->p_char->blank_p)
1209 cell->p_char = state->chars[CURSOR_INDEX];
1211 if (state->cursor_x >= cols - 1)
1213 state->cursor_x = 0;
1214 if (state->cursor_y >= rows - 1)
1220 set_cursor (state, True);
1228 update_display (p_state *state, Bool changed_only)
1232 for (y = 0; y < state->grid_height; y++)
1233 for (x = 0; x < state->grid_width; x++)
1235 p_cell *cell = &state->cells[state->grid_width * y + x];
1236 int width, height, tx, ty;
1238 if (changed_only && !cell->changed)
1241 width = state->char_width * state->scale;
1242 height = state->char_height * state->scale;
1246 if (cell->state == BLANK || cell->p_char->blank_p)
1248 XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
1249 tx, ty, width, height);
1254 GC gc1 = state->gcs[cell->state];
1255 GC gc2 = ((cell->state + 2) < state->ticks
1256 ? state->gcs[cell->state + 2]
1258 GC gc3 = (gc2 ? gc2 : gc1);
1260 XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
1261 0, 0, width, height, tx, ty, 1L);
1264 XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
1265 XSetClipOrigin (state->dpy, gc1, tx, ty);
1266 XFillRectangle (state->dpy, state->window, gc1,
1267 tx, ty, width, height);
1268 XSetClipMask (state->dpy, gc1, None);
1270 #else /* !FUZZY_BORDER */
1272 XCopyPlane (state->dpy,
1273 cell->p_char->pixmap, state->window,
1274 state->gcs[cell->state],
1275 0, 0, width, height, tx, ty, 1L);
1277 #endif /* !FUZZY_BORDER */
1280 cell->changed = False;
1285 static unsigned long
1286 phosphor_draw (Display *dpy, Window window, void *closure)
1288 p_state *state = (p_state *) closure;
1290 update_display (state, True);
1293 c = textclient_getc (state->tc);
1295 print_char (state, c);
1297 return state->delay;
1302 phosphor_reshape (Display *dpy, Window window, void *closure,
1303 unsigned int w, unsigned int h)
1305 p_state *state = (p_state *) closure;
1306 Bool changed_p = resize_grid (state);
1308 if (! changed_p) return;
1310 textclient_reshape (state->tc, w, h,
1311 state->grid_width - 1,
1312 state->grid_height - 1,
1318 phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
1320 p_state *state = (p_state *) closure;
1322 if (event->xany.type == Expose)
1323 update_display (state, False);
1324 else if (event->xany.type == KeyPress)
1325 return textclient_putc (state->tc, &event->xkey);
1330 phosphor_free (Display *dpy, Window window, void *closure)
1332 p_state *state = (p_state *) closure;
1334 textclient_close (state->tc);
1335 if (state->cursor_timer)
1336 XtRemoveTimeOut (state->cursor_timer);
1338 /* #### there's more to free here */
1345 static const char *phosphor_defaults [] = {
1346 ".background: Black",
1347 ".foreground: #00FF00",
1349 #if defined(BUILTIN_FONT)
1351 #elif defined(HAVE_COCOA)
1360 "*program: xscreensaver-text",
1362 "*metaSendsESC: True",
1366 #else /* !HAVE_FORKPTY */
1368 #endif /* !HAVE_FORKPTY */
1372 static XrmOptionDescRec phosphor_options [] = {
1373 { "-font", ".font", XrmoptionSepArg, 0 },
1374 { "-scale", ".scale", XrmoptionSepArg, 0 },
1375 { "-ticks", ".ticks", XrmoptionSepArg, 0 },
1376 { "-delay", ".delay", XrmoptionSepArg, 0 },
1377 { "-program", ".program", XrmoptionSepArg, 0 },
1378 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
1379 { "-pty", ".usePty", XrmoptionNoArg, "True" },
1380 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
1381 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
1382 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
1383 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
1388 XSCREENSAVER_MODULE ("Phosphor", phosphor)