1 /* xscreensaver, Copyright (c) 1999-2017 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;
82 int unicruds; unsigned char unicrud[7];
91 #endif /* FUZZY_BORDER */
95 int cursor_x, cursor_y;
96 XtIntervalId cursor_timer;
109 static void capture_font_bits (p_state *state);
110 static p_char *make_character (p_state *state, int c);
111 static void char_to_pixmap (p_state *state, p_char *pc, int c);
114 /* About font metrics:
116 "lbearing" is the distance from the leftmost pixel of a character to
117 the logical origin of that character. That is, it is the number of
118 pixels of the character which are to the left of its logical origin.
120 "rbearing" is the distance from the logical origin of a character to
121 the rightmost pixel of a character. That is, it is the number of
122 pixels of the character to the right of its logical origin.
124 "descent" is the distance from the bottommost pixel of a character to
125 the logical baseline. That is, it is the number of pixels of the
126 character which are below the baseline.
128 "ascent" is the distance from the logical baseline to the topmost pixel.
129 That is, it is the number of pixels of the character above the baseline.
131 Therefore, the bounding box of the "ink" of a character is
132 lbearing + rbearing by ascent + descent;
134 "width" is the distance from the logical origin of this character to
135 the position where the logical orgin of the next character should be
138 For our purposes, we're only interested in the part of the character
139 lying inside the "width" box. If the characters have ink outside of
140 that box (the "charcell" box) then we're going to lose it. Alas.
144 static void clear (p_state *);
145 static void set_cursor (p_state *, Bool on);
147 static unsigned short scale_color_channel (unsigned short ch1, unsigned short ch2)
149 return (ch1 * 100 + ch2 * 156) >> 8;
153 phosphor_init (Display *dpy, Window window)
157 p_state *state = (p_state *) calloc (sizeof(*state), 1);
158 char *fontname = get_string_resource (dpy, "font", "Font");
162 state->window = window;
164 XGetWindowAttributes (dpy, window, &state->xgwa);
165 /* XSelectInput (dpy, window, state->xgwa.your_event_mask | ExposureMask);*/
167 state->delay = get_integer_resource (dpy, "delay", "Integer");
168 state->pty_p = get_boolean_resource (dpy, "usePty", "UsePty");
170 if (!strcasecmp (fontname, "builtin") ||
171 !strcasecmp (fontname, "(builtin)"))
174 fprintf (stderr, "%s: no builtin font\n", progname);
175 state->font = XLoadQueryFont (dpy, "fixed");
176 #endif /* !BUILTIN_FONT */
180 state->font = XLoadQueryFont (dpy, fontname);
184 fprintf(stderr, "couldn't load font \"%s\"\n", fontname);
185 state->font = XLoadQueryFont (dpy, "fixed");
189 fprintf(stderr, "couldn't load font \"fixed\"");
195 state->scale = get_integer_resource (dpy, "scale", "Integer");
196 state->ticks = STATE_MAX + get_integer_resource (dpy, "ticks", "Integer");
200 state->cursor_blink = get_integer_resource (dpy, "cursor", "Time");
205 state->char_width = (font6x10_width / 256) - 1;
206 state->char_height = font6x10_height;
209 # endif /* BUILTIN_FONT */
211 state->char_width = font->max_bounds.width;
212 state->char_height = font->max_bounds.ascent + font->max_bounds.descent;
216 /* Stupid iPhone X bezel.
217 #### This is the worst of all possible ways to do this! But how else?
219 if (state->xgwa.width == 2436 || state->xgwa.height == 2436) {
221 state->ymargin = state->xmargin;
225 state->grid_width = ((state->xgwa.width - state->xmargin * 2) /
226 (state->char_width * state->scale));
227 state->grid_height = ((state->xgwa.height - state->ymargin * 2) /
228 (state->char_height * state->scale));
229 state->cells = (p_cell *) calloc (sizeof(p_cell),
230 state->grid_width * state->grid_height);
231 state->chars = (p_char **) calloc (sizeof(p_char *), 256);
233 state->gcs = (GC *) calloc (sizeof(GC), state->ticks + 1);
236 int ncolors = MAX (1, state->ticks - 3);
237 XColor *colors = (XColor *) calloc (ncolors, sizeof(XColor));
239 double s1, s2, v1, v2;
241 unsigned long fg = get_pixel_resource (state->dpy, state->xgwa.colormap,
242 "foreground", "Foreground");
243 unsigned long bg = get_pixel_resource (state->dpy, state->xgwa.colormap,
244 "background", "Background");
245 unsigned long flare = fg;
247 XColor fg_color, bg_color;
250 XQueryColor (state->dpy, state->xgwa.colormap, &fg_color);
253 XQueryColor (state->dpy, state->xgwa.colormap, &bg_color);
255 /* Now allocate a ramp of colors from the main color to the background. */
256 rgb_to_hsv (scale_color_channel(fg_color.red, bg_color.red),
257 scale_color_channel(fg_color.green, bg_color.green),
258 scale_color_channel(fg_color.blue, bg_color.blue),
260 rgb_to_hsv (bg_color.red, bg_color.green, bg_color.blue, &h2, &s2, &v2);
262 /* Avoid rainbow effects when fading to black/grey/white. */
268 make_color_ramp (state->xgwa.screen, state->xgwa.visual,
269 state->xgwa.colormap,
275 /* Adjust to the number of colors we actually got. */
276 state->ticks = ncolors + STATE_MAX;
278 /* If the foreground is brighter than the background, the flare is white.
279 * Otherwise, the flare is left at the foreground color (i.e. no flare). */
280 rgb_to_hsv (fg_color.red, fg_color.green, fg_color.blue, &h1, &s1, &v1);
284 /* WhitePixel is only for the default visual, which can be overridden
285 * on the command line. */
287 white.green = 0xffff;
289 if (XAllocColor(state->dpy, state->xgwa.colormap, &white))
293 /* Now, GCs all around.
295 state->gcv.font = (font ? font->fid : 0);
296 state->gcv.cap_style = CapRound;
298 state->gcv.line_width = (int) (((long) state->scale) * 1.3);
299 if (state->gcv.line_width == state->scale)
300 state->gcv.line_width++;
301 #else /* !FUZZY_BORDER */
302 state->gcv.line_width = (int) (((long) state->scale) * 0.9);
303 if (state->gcv.line_width >= state->scale)
304 state->gcv.line_width = state->scale - 1;
305 if (state->gcv.line_width < 1)
306 state->gcv.line_width = 1;
307 #endif /* !FUZZY_BORDER */
309 flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth);
311 state->gcv.background = bg;
312 state->gcv.foreground = bg;
313 state->gcs[BLANK] = XCreateGC (state->dpy, state->window, flags,
316 state->gcv.foreground = flare;
317 state->gcs[FLARE] = XCreateGC (state->dpy, state->window, flags,
320 state->gcv.foreground = fg;
321 state->gcs[NORMAL] = XCreateGC (state->dpy, state->window, flags,
324 for (i = 0; i < ncolors; i++)
326 state->gcv.foreground = colors[i].pixel;
327 state->gcs[STATE_MAX + i] = XCreateGC (state->dpy, state->window,
332 capture_font_bits (state);
334 set_cursor (state, True);
338 state->tc = textclient_open (dpy);
339 textclient_reshape (state->tc,
340 state->xgwa.width - state->xmargin * 2,
341 state->xgwa.height - state->ymargin * 2,
342 state->grid_width - 1,
343 state->grid_height - 1,
350 /* Re-query the window size and update the internal character grid if changed.
353 resize_grid (p_state *state)
355 int ow = state->grid_width;
356 int oh = state->grid_height;
357 p_cell *ocells = state->cells;
360 XGetWindowAttributes (state->dpy, state->window, &state->xgwa);
362 /* Would like to ensure here that
363 state->char_height * state->scale <= state->xgwa.height
364 but changing scale requires regenerating the bitmaps. */
366 state->grid_width = ((state->xgwa.width - state->xmargin * 2) /
367 (state->char_width * state->scale));
368 state->grid_height = ((state->xgwa.height - state->ymargin * 2) /
369 (state->char_height * state->scale));
371 if (state->grid_width < 2) state->grid_width = 2;
372 if (state->grid_height < 2) state->grid_height = 2;
374 if (ow == state->grid_width &&
375 oh == state->grid_height)
378 state->cells = (p_cell *) calloc (sizeof(p_cell),
379 state->grid_width * state->grid_height);
381 for (y = 0; y < state->grid_height; y++)
383 for (x = 0; x < state->grid_width; x++)
385 p_cell *ncell = &state->cells [state->grid_width * y + x];
386 if (x < ow && y < oh)
387 *ncell = ocells [ow * y + x];
388 ncell->changed = True;
392 if (state->cursor_x >= state->grid_width)
393 state->cursor_x = state->grid_width-1;
394 if (state->cursor_y >= state->grid_height)
395 state->cursor_y = state->grid_height-1;
403 capture_font_bits (p_state *state)
405 XFontStruct *font = state->font;
406 int safe_width, height;
407 unsigned char string[257];
416 safe_width = state->char_width + 1;
417 height = state->char_height;
418 p2 = XCreatePixmapFromBitmapData (state->dpy, state->window,
419 (char *) font6x10_bits,
425 # endif /* BUILTIN_FONT */
427 safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing;
428 height = state->char_height;
431 p = XCreatePixmap (state->dpy, state->window,
432 (safe_width * 256), height, 1);
434 for (i = 0; i < 256; i++)
435 string[i] = (unsigned char) i;
438 state->gcv.foreground = 0;
439 state->gcv.background = 0;
440 state->gc0 = XCreateGC (state->dpy, p,
441 (GCForeground | GCBackground),
444 state->gcv.foreground = 1;
445 state->gc1 = XCreateGC (state->dpy, p,
446 ((font ? GCFont : 0) |
447 GCForeground | GCBackground |
448 GCCapStyle | GCLineWidth),
452 jwxyz_XSetAntiAliasing (state->dpy, state->gc0, False);
453 jwxyz_XSetAntiAliasing (state->dpy, state->gc1, False);
458 state->gcv.line_width = (int) (((long) state->scale) * 0.8);
459 if (state->gcv.line_width >= state->scale)
460 state->gcv.line_width = state->scale - 1;
461 if (state->gcv.line_width < 1)
462 state->gcv.line_width = 1;
463 state->gc2 = XCreateGC (state->dpy, p,
464 ((font ? GCFont : 0) |
465 GCForeground | GCBackground |
466 GCCapStyle | GCLineWidth),
469 #endif /* FUZZY_BORDER */
471 XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height);
476 XCopyPlane (state->dpy, p2, p, state->gc1,
477 0, 0, font6x10_width, font6x10_height,
479 XFreePixmap (state->dpy, p2);
482 # endif /* BUILTIN_FONT */
484 for (i = 0; i < 256; i++)
486 if (string[i] < font->min_char_or_byte2 ||
487 string[i] > font->max_char_or_byte2)
489 XDrawString (state->dpy, p, state->gc1,
490 i * safe_width, font->ascent,
491 (char *) (string + i), 1);
495 /* Draw the cursor. */
496 XFillRectangle (state->dpy, p, state->gc1,
497 (CURSOR_INDEX * safe_width), 1,
500 ? font->per_char['n'-font->min_char_or_byte2].width
501 : font->max_bounds.width)
502 : state->char_width),
505 : state->char_height));
507 state->font_bits = XGetImage (state->dpy, p, 0, 0,
508 (safe_width * 256), height, ~0L, XYPixmap);
509 XFreePixmap (state->dpy, p);
511 for (i = 0; i < 256; i++)
512 state->chars[i] = make_character (state, i);
513 state->chars[CURSOR_INDEX] = make_character (state, CURSOR_INDEX);
518 make_character (p_state *state, int c)
520 p_char *pc = (p_char *) malloc (sizeof (*pc));
521 pc->name = (unsigned char) c;
522 pc->width = state->scale * state->char_width;
523 pc->height = state->scale * state->char_height;
524 char_to_pixmap (state, pc, c);
530 char_to_pixmap (p_state *state, p_char *pc, int c)
537 #endif /* FUZZY_BORDER */
541 XFontStruct *font = state->font;
542 int safe_width = (font
543 ? font->max_bounds.rbearing - font->min_bounds.lbearing
544 : state->char_width + 1);
546 int width = state->scale * state->char_width;
547 int height = state->scale * state->char_height;
549 if (font && (c < font->min_char_or_byte2 ||
550 c > font->max_char_or_byte2))
554 p = XCreatePixmap (state->dpy, state->window, width, height, 1);
555 XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height);
558 p2 = XCreatePixmap (state->dpy, state->window, width, height, 1);
559 XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height);
560 #endif /* FUZZY_BORDER */
562 from = safe_width * c;
563 to = safe_width * (c + 1);
566 if (c > 75 && c < 150)
568 printf ("\n=========== %d (%c)\n", c, c);
569 for (y = 0; y < state->char_height; y++)
571 for (x1 = from; x1 < to; x1++)
572 printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". ");
579 for (y = 0; y < state->char_height; y++)
580 for (x1 = from; x1 < to; x1++)
581 if (XGetPixel (state->font_bits, x1, y))
583 int xoff = state->scale / 2;
585 for (x2 = x1; x2 < to; x2++)
586 if (!XGetPixel (state->font_bits, x2, y))
589 XDrawLine (state->dpy, p, gc,
590 (x1 - from) * state->scale + xoff, y * state->scale,
591 (x2 - from) * state->scale + xoff, y * state->scale);
593 XDrawLine (state->dpy, p2, gc2,
594 (x1 - from) * state->scale + xoff, y * state->scale,
595 (x2 - from) * state->scale + xoff, y * state->scale);
596 #endif /* FUZZY_BORDER */
601 /* if (pc->blank_p && c == CURSOR_INDEX)
608 #endif /* FUZZY_BORDER */
612 /* Managing the display.
615 static void cursor_on_timer (XtPointer closure, XtIntervalId *id);
616 static void cursor_off_timer (XtPointer closure, XtIntervalId *id);
619 set_cursor_1 (p_state *state, Bool on)
621 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
623 p_char *cursor = state->chars[CURSOR_INDEX];
624 int new_state = (on ? NORMAL : FADE);
626 if (cell->p_char != cursor)
627 cell->changed = True;
629 if (cell->state != new_state)
630 cell->changed = True;
632 cell->p_char = cursor;
633 cell->state = new_state;
634 return cell->changed;
638 set_cursor (p_state *state, Bool on)
640 if (set_cursor_1 (state, on))
642 if (state->cursor_timer)
643 XtRemoveTimeOut (state->cursor_timer);
644 state->cursor_timer = 0;
645 cursor_on_timer (state, 0);
651 cursor_off_timer (XtPointer closure, XtIntervalId *id)
653 p_state *state = (p_state *) closure;
654 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
655 set_cursor_1 (state, False);
656 state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink,
657 cursor_on_timer, closure);
661 cursor_on_timer (XtPointer closure, XtIntervalId *id)
663 p_state *state = (p_state *) closure;
664 XtAppContext app = XtDisplayToApplicationContext (state->dpy);
665 set_cursor_1 (state, True);
666 state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink,
667 cursor_off_timer, closure);
672 clear (p_state *state)
677 for (y = 0; y < state->grid_height; y++)
678 for (x = 0; x < state->grid_width; x++)
680 p_cell *cell = &state->cells[state->grid_width * y + x];
681 if (cell->state == FLARE || cell->state == NORMAL)
684 cell->changed = True;
687 set_cursor (state, True);
692 decay (p_state *state)
695 for (y = 0; y < state->grid_height; y++)
696 for (x = 0; x < state->grid_width; x++)
698 p_cell *cell = &state->cells[state->grid_width * y + x];
699 if (cell->state == FLARE)
701 cell->state = NORMAL;
702 cell->changed = True;
704 else if (cell->state >= FADE)
707 if (cell->state >= state->ticks)
709 cell->changed = True;
716 scroll (p_state *state)
720 for (x = 0; x < state->grid_width; x++)
722 p_cell *from = 0, *to = 0;
723 for (y = 1; y < state->grid_height; y++)
725 from = &state->cells[state->grid_width * y + x];
726 to = &state->cells[state->grid_width * (y-1) + x];
728 if ((from->state == FLARE || from->state == NORMAL) &&
729 !from->p_char->blank_p)
732 to->state = NORMAL; /* should be FLARE? Looks bad... */
736 if (to->state == FLARE || to->state == NORMAL)
744 if (to && (to->state == FLARE || to->state == NORMAL))
750 set_cursor (state, True);
755 process_unicrud (p_state *state, int c)
757 if ((c & 0xE0) == 0xC0) { /* 110xxxxx: 11 bits, 2 bytes */
759 state->unicrud[0] = c;
760 state->escstate = 102;
761 } else if ((c & 0xF0) == 0xE0) { /* 1110xxxx: 16 bits, 3 bytes */
763 state->unicrud[0] = c;
764 state->escstate = 103;
765 } else if ((c & 0xF8) == 0xF0) { /* 11110xxx: 21 bits, 4 bytes */
767 state->unicrud[0] = c;
768 state->escstate = 104;
769 } else if ((c & 0xFC) == 0xF8) { /* 111110xx: 26 bits, 5 bytes */
771 state->unicrud[0] = c;
772 state->escstate = 105;
773 } else if ((c & 0xFE) == 0xFC) { /* 1111110x: 31 bits, 6 bytes */
775 state->unicrud[0] = c;
776 state->escstate = 106;
777 } else if (state->unicruds == 0) {
780 int total = state->escstate - 100; /* see what I did there */
781 if (state->unicruds < total) {
782 /* Buffer more bytes of the UTF-8 sequence */
783 state->unicrud[state->unicruds++] = c;
786 if (state->unicruds >= total) {
787 /* Done! Convert it to Latin1 and print that. */
789 state->unicrud[state->unicruds] = 0;
790 s = utf8_to_latin1 ((const char *) state->unicrud, False);
794 c = (unsigned char) s[0];
805 print_char (p_state *state, int c)
807 int cols = state->grid_width;
808 int rows = state->grid_height;
809 p_cell *cell = &state->cells[state->grid_width * state->cursor_y
812 /* Start the cursor fading (in case we don't end up overwriting it.) */
813 if (cell->state == FLARE || cell->state == NORMAL)
816 cell->changed = True;
820 if (state->pty_p) /* Only interpret VT100 sequences if running in pty-mode.
821 It would be nice if we could just interpret them all
822 the time, but that would require subprocesses to send
823 CRLF line endings instead of bare LF, so that's no good.
829 /* Mostly duplicated in apple2-main.c */
831 switch (state->escstate)
837 /* Dummy case - we don't want the screensaver to beep */
838 /* #### But maybe this should flash the screen? */
841 if (state->cursor_x > 0)
845 if (state->cursor_x < cols - 8)
847 state->cursor_x = (state->cursor_x & ~7) + 8;
852 if (state->cursor_y < rows - 1)
859 # ifndef HAVE_FORKPTY
860 state->cursor_x = 0; /* No ptys on iPhone; assume CRLF. */
864 if(state->last_c == 13)
866 cell->state = NORMAL;
867 cell->p_char = state->chars[state->bk];
868 cell->changed = True;
870 if (state->cursor_y < rows - 1)
877 cell = &state->cells[cols * state->cursor_y];
878 if((cell->p_char == NULL) || (cell->p_char->name == CURSOR_INDEX))
881 state->bk = cell->p_char->name;
885 /* Dummy case - there is one and only one font. */
889 /* Dummy case - these interrupt escape sequences, so
890 they don't do anything in this state */
896 /* Dummy case - this is supposed to be ignored */
900 for(i = 0; i < NPAR; i++)
901 state->csiparam[i] = 0;
906 PRINT: /* Come from states 102-106 */
907 c = process_unicrud (state, c);
911 /* If the cursor is in column 39 and we print a character, then
912 that character shows up in column 39, and the cursor is no
913 longer visible on the screen (it's in "column 40".) If
914 another character is printed, then that character shows up in
915 column 0, and the cursor moves to column 1.
917 This is empirically what xterm and gnome-terminal do, so that
918 must be the right thing. (In xterm, the cursor vanishes,
919 whereas; in gnome-terminal, the cursor overprints the
920 character in col 39.)
923 cell->p_char = state->chars[c];
924 cell->changed = True;
927 if (c != ' ' && cell->p_char->blank_p)
928 cell->p_char = state->chars[CURSOR_INDEX];
930 if (state->cursor_x >= cols - 1 /*####*/)
933 if (state->cursor_y >= rows - 1)
948 case 'c': /* Reset */
952 case 'D': /* Linefeed */
953 if (state->cursor_y < rows - 1)
959 case 'E': /* Newline */
963 case 'M': /* Reverse newline */
964 if (state->cursor_y > 0)
968 case '7': /* Save state */
969 state->saved_x = state->cursor_x;
970 state->saved_y = state->cursor_y;
973 case '8': /* Restore state */
974 state->cursor_x = state->saved_x;
975 state->cursor_y = state->saved_y;
980 for(i = 0; i < NPAR; i++)
981 state->csiparam[i] = 0;
984 case '%': /* Select charset */
985 /* @: Select default (ISO 646 / ISO 8859-1)
987 8: Select UTF-8 (obsolete)
989 We can just ignore this and always process UTF-8, I think?
990 We must still catch the last byte, though.
994 /* I don't support different fonts either - see above
999 /* Escape sequences not supported:
1002 * Z - Terminal identification
1004 * = - Other keypad change
1007 state->escstate = 0;
1016 state->escstate = 0;
1018 case '0': case '1': case '2': case '3': case '4':
1019 case '5': case '6': case '7': case '8': case '9':
1020 if (state->curparam < NPAR)
1021 state->csiparam[state->curparam] = (state->csiparam[state->curparam] * 10) + (c - '0');
1024 state->csiparam[++state->curparam] = 0;
1027 state->escstate = 3;
1030 for (i = 0; i < state->csiparam[0]; i++)
1032 if(++state->cursor_x > cols)
1034 state->cursor_x = 0;
1035 if (state->cursor_y < rows - 1)
1040 cell = &state->cells[cols * state->cursor_y + state->cursor_x];
1041 if (cell->state == FLARE || cell->state == NORMAL)
1044 cell->changed = True;
1047 state->escstate = 0;
1050 state->cursor_x = 0;
1052 if (state->csiparam[0] == 0)
1053 state->csiparam[0] = 1;
1054 if ((state->cursor_y -= state->csiparam[0]) < 0)
1055 state->cursor_y = 0;
1056 state->escstate = 0;
1059 state->cursor_x = 0;
1062 if (state->csiparam[0] == 0)
1063 state->csiparam[0] = 1;
1064 if ((state->cursor_y += state->csiparam[0]) >= rows - 1 /*####*/)
1065 state->cursor_y = rows - 1;
1066 state->escstate = 0;
1070 if (state->csiparam[0] == 0)
1071 state->csiparam[0] = 1;
1072 if ((state->cursor_x += state->csiparam[0]) >= cols - 1 /*####*/)
1073 state->cursor_x = cols - 1;
1074 state->escstate = 0;
1077 if (state->csiparam[0] == 0)
1078 state->csiparam[0] = 1;
1079 if ((state->cursor_x -= state->csiparam[0]) < 0)
1080 state->cursor_x = 0;
1081 state->escstate = 0;
1084 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1085 state->cursor_y = rows - 1;
1086 state->escstate = 0;
1090 if ((state->cursor_x = (state->csiparam[0] - 1)) >= cols - 1 /*####*/)
1091 state->cursor_x = cols - 1;
1092 state->escstate = 0;
1096 if ((state->cursor_y = (state->csiparam[0] - 1)) >= rows - 1 /*####*/)
1097 state->cursor_y = rows - 1;
1098 if ((state->cursor_x = (state->csiparam[1] - 1)) >= cols - 1 /*####*/)
1099 state->cursor_x = cols - 1;
1100 if(state->cursor_y < 0)
1101 state->cursor_y = 0;
1102 if(state->cursor_x < 0)
1103 state->cursor_x = 0;
1104 state->escstate = 0;
1109 if (state->csiparam[0] == 0)
1110 start = cols * state->cursor_y + state->cursor_x;
1111 if (state->csiparam[0] == 1)
1112 end = cols * state->cursor_y + state->cursor_x;
1113 for (i = start; i < end; i++)
1115 cell = &state->cells[i];
1116 if (cell->state == FLARE || cell->state == NORMAL)
1119 cell->changed = True;
1122 set_cursor (state, True);
1123 state->escstate = 0;
1128 if (state->csiparam[0] == 0)
1129 start = state->cursor_x;
1130 if (state->csiparam[1] == 1)
1131 end = state->cursor_x;
1132 for (i = start; i < end; i++)
1134 if (cell->state == FLARE || cell->state == NORMAL)
1137 cell->changed = True;
1141 state->escstate = 0;
1143 case 'm': /* Set attributes unimplemented (bold, blink, rev) */
1144 state->escstate = 0;
1146 case 's': /* Save position */
1147 state->saved_x = state->cursor_x;
1148 state->saved_y = state->cursor_y;
1149 state->escstate = 0;
1151 case 'u': /* Restore position */
1152 state->cursor_x = state->saved_x;
1153 state->cursor_y = state->saved_y;
1154 state->escstate = 0;
1156 case '?': /* DEC Private modes */
1157 if ((state->curparam != 0) || (state->csiparam[0] != 0))
1158 state->escstate = 0;
1161 /* Known unsupported CSIs:
1163 * L - Insert blank lines
1164 * M - Delete lines (I don't know what this means...)
1165 * P - Delete characters
1166 * X - Erase characters (difference with P being...?)
1167 * c - Terminal identification
1168 * g - Clear tab stop(s)
1169 * h - Set mode (Mainly due to its complexity and lack of good
1172 * m - Set mode (Phosphor is, per defenition, green on black)
1174 * q - Set keyboard LEDs
1175 * r - Set scrolling region (too exhausting - noone uses this,
1178 state->escstate = 0;
1183 state->escstate = 0;
1186 case 102: /* states 102-106 are for UTF-8 decoding */
1196 set_cursor (state, True);
1199 #endif /* HAVE_FORKPTY */
1201 if (c == '\t') c = ' '; /* blah. */
1203 if (c == '\r' || c == '\n') /* handle CR, LF, or CRLF as "new line". */
1205 if (c == '\n' && state->last_c == '\r')
1206 ; /* CRLF -- do nothing */
1209 state->cursor_x = 0;
1210 if (state->cursor_y == rows - 1)
1216 else if (c == '\014')
1222 c = process_unicrud (state, c);
1225 cell->state = FLARE;
1226 cell->p_char = state->chars[c];
1227 cell->changed = True;
1230 if (c != ' ' && cell->p_char->blank_p)
1231 cell->p_char = state->chars[CURSOR_INDEX];
1233 if (state->cursor_x >= cols - 1)
1235 state->cursor_x = 0;
1236 if (state->cursor_y >= rows - 1)
1242 set_cursor (state, True);
1250 update_display (p_state *state, Bool changed_only)
1254 for (y = 0; y < state->grid_height; y++)
1255 for (x = 0; x < state->grid_width; x++)
1257 p_cell *cell = &state->cells[state->grid_width * y + x];
1258 int width, height, tx, ty;
1260 if (changed_only && !cell->changed)
1263 width = state->char_width * state->scale;
1264 height = state->char_height * state->scale;
1265 tx = x * width + state->xmargin;
1266 ty = y * height + state->ymargin;
1268 if (cell->state == BLANK || cell->p_char->blank_p)
1270 XFillRectangle (state->dpy, state->window, state->gcs[BLANK],
1271 tx, ty, width, height);
1276 GC gc1 = state->gcs[cell->state];
1277 GC gc2 = ((cell->state + 2) < state->ticks
1278 ? state->gcs[cell->state + 2]
1280 GC gc3 = (gc2 ? gc2 : gc1);
1282 XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3,
1283 0, 0, width, height, tx, ty, 1L);
1286 XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2);
1287 XSetClipOrigin (state->dpy, gc1, tx, ty);
1288 XFillRectangle (state->dpy, state->window, gc1,
1289 tx, ty, width, height);
1290 XSetClipMask (state->dpy, gc1, None);
1292 #else /* !FUZZY_BORDER */
1294 XCopyPlane (state->dpy,
1295 cell->p_char->pixmap, state->window,
1296 state->gcs[cell->state],
1297 0, 0, width, height, tx, ty, 1L);
1299 #endif /* !FUZZY_BORDER */
1302 cell->changed = False;
1307 static unsigned long
1308 phosphor_draw (Display *dpy, Window window, void *closure)
1310 p_state *state = (p_state *) closure;
1312 update_display (state, True);
1315 c = textclient_getc (state->tc);
1317 print_char (state, c);
1319 return state->delay;
1324 phosphor_reshape (Display *dpy, Window window, void *closure,
1325 unsigned int w, unsigned int h)
1327 p_state *state = (p_state *) closure;
1328 Bool changed_p = resize_grid (state);
1330 if (! changed_p) return;
1332 textclient_reshape (state->tc,
1333 w - state->xmargin * 2,
1334 h - state->ymargin * 2,
1335 state->grid_width - 1,
1336 state->grid_height - 1,
1342 phosphor_event (Display *dpy, Window window, void *closure, XEvent *event)
1344 p_state *state = (p_state *) closure;
1346 if (event->xany.type == Expose)
1347 update_display (state, False);
1348 else if (event->xany.type == KeyPress)
1349 return textclient_putc (state->tc, &event->xkey);
1354 phosphor_free (Display *dpy, Window window, void *closure)
1356 p_state *state = (p_state *) closure;
1358 textclient_close (state->tc);
1359 if (state->cursor_timer)
1360 XtRemoveTimeOut (state->cursor_timer);
1362 /* #### there's more to free here */
1369 static const char *phosphor_defaults [] = {
1370 /* ".lowrez: true",*/
1371 ".background: Black",
1372 ".foreground: #00FF00",
1374 #if defined(BUILTIN_FONT)
1376 #elif defined(HAVE_COCOA)
1385 "*program: xscreensaver-text",
1387 "*metaSendsESC: True",
1391 #else /* !HAVE_FORKPTY */
1393 #endif /* !HAVE_FORKPTY */
1397 static XrmOptionDescRec phosphor_options [] = {
1398 { "-font", ".font", XrmoptionSepArg, 0 },
1399 { "-scale", ".scale", XrmoptionSepArg, 0 },
1400 { "-ticks", ".ticks", XrmoptionSepArg, 0 },
1401 { "-delay", ".delay", XrmoptionSepArg, 0 },
1402 { "-program", ".program", XrmoptionSepArg, 0 },
1403 { "-pipe", ".usePty", XrmoptionNoArg, "False" },
1404 { "-pty", ".usePty", XrmoptionNoArg, "True" },
1405 { "-meta", ".metaSendsESC", XrmoptionNoArg, "False" },
1406 { "-esc", ".metaSendsESC", XrmoptionNoArg, "True" },
1407 { "-bs", ".swapBSDEL", XrmoptionNoArg, "False" },
1408 { "-del", ".swapBSDEL", XrmoptionNoArg, "True" },
1413 XSCREENSAVER_MODULE ("Phosphor", phosphor)