1 | /* xscreensaver, Copyright (c) 1999, 2000 Jamie Zawinski <jwz@jwz.org> |
---|
2 | * |
---|
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 |
---|
9 | * implied warranty. |
---|
10 | * |
---|
11 | * Phosphor -- simulate a glass tty with long-sustain phosphor. |
---|
12 | */ |
---|
13 | |
---|
14 | #include "screenhack.h" |
---|
15 | #include <stdio.h> |
---|
16 | #include <X11/Xutil.h> |
---|
17 | #include <X11/Xatom.h> |
---|
18 | #include <X11/Intrinsic.h> |
---|
19 | |
---|
20 | extern XtAppContext app; |
---|
21 | |
---|
22 | #define FUZZY_BORDER |
---|
23 | |
---|
24 | #define MAX(a,b) ((a)>(b)?(a):(b)) |
---|
25 | #define MIN(a,b) ((a)<(b)?(a):(b)) |
---|
26 | |
---|
27 | #define BLANK 0 |
---|
28 | #define FLARE 1 |
---|
29 | #define NORMAL 2 |
---|
30 | #define FADE 3 |
---|
31 | #define STATE_MAX FADE |
---|
32 | |
---|
33 | #define CURSOR_INDEX 128 |
---|
34 | |
---|
35 | typedef struct { |
---|
36 | unsigned char name; |
---|
37 | int width, height; |
---|
38 | Pixmap pixmap; |
---|
39 | #ifdef FUZZY_BORDER |
---|
40 | Pixmap pixmap2; |
---|
41 | #endif /* FUZZY_BORDER */ |
---|
42 | Bool blank_p; |
---|
43 | } p_char; |
---|
44 | |
---|
45 | typedef struct { |
---|
46 | p_char *p_char; |
---|
47 | int state; |
---|
48 | Bool changed; |
---|
49 | } p_cell; |
---|
50 | |
---|
51 | typedef struct { |
---|
52 | Display *dpy; |
---|
53 | Window window; |
---|
54 | XWindowAttributes xgwa; |
---|
55 | XFontStruct *font; |
---|
56 | int grid_width, grid_height; |
---|
57 | int char_width, char_height; |
---|
58 | int scale; |
---|
59 | int ticks; |
---|
60 | p_char **chars; |
---|
61 | p_cell *cells; |
---|
62 | XGCValues gcv; |
---|
63 | GC gc0; |
---|
64 | GC gc1; |
---|
65 | #ifdef FUZZY_BORDER |
---|
66 | GC gc2; |
---|
67 | #endif /* FUZZY_BORDER */ |
---|
68 | GC *gcs; |
---|
69 | XImage *font_bits; |
---|
70 | |
---|
71 | int cursor_x, cursor_y; |
---|
72 | XtIntervalId cursor_timer; |
---|
73 | Time cursor_blink; |
---|
74 | |
---|
75 | FILE *pipe; |
---|
76 | XtInputId pipe_id; |
---|
77 | Bool input_available_p; |
---|
78 | Time subproc_relaunch_delay; |
---|
79 | |
---|
80 | } p_state; |
---|
81 | |
---|
82 | |
---|
83 | static void capture_font_bits (p_state *state); |
---|
84 | static p_char *make_character (p_state *state, int c); |
---|
85 | static void drain_input (p_state *state); |
---|
86 | static void char_to_pixmap (p_state *state, p_char *pc, int c); |
---|
87 | static void launch_text_generator (p_state *state); |
---|
88 | |
---|
89 | |
---|
90 | /* About font metrics: |
---|
91 | |
---|
92 | "lbearing" is the distance from the leftmost pixel of a character to |
---|
93 | the logical origin of that character. That is, it is the number of |
---|
94 | pixels of the character which are to the left of its logical origin. |
---|
95 | |
---|
96 | "rbearing" is the distance from the logical origin of a character to |
---|
97 | the rightmost pixel of a character. That is, it is the number of |
---|
98 | pixels of the character to the right of its logical origin. |
---|
99 | |
---|
100 | "descent" is the distance from the bottommost pixel of a character to |
---|
101 | the logical baseline. That is, it is the number of pixels of the |
---|
102 | character which are below the baseline. |
---|
103 | |
---|
104 | "ascent" is the distance from the logical baseline to the topmost pixel. |
---|
105 | That is, it is the number of pixels of the character above the baseline. |
---|
106 | |
---|
107 | Therefore, the bounding box of the "ink" of a character is |
---|
108 | lbearing + rbearing by ascent + descent; |
---|
109 | |
---|
110 | "width" is the distance from the logical origin of this character to |
---|
111 | the position where the logical orgin of the next character should be |
---|
112 | placed. |
---|
113 | |
---|
114 | For our purposes, we're only interested in the part of the character |
---|
115 | lying inside the "width" box. If the characters have ink outside of |
---|
116 | that box (the "charcell" box) then we're going to lose it. Alas. |
---|
117 | */ |
---|
118 | |
---|
119 | static p_state * |
---|
120 | init_phosphor (Display *dpy, Window window) |
---|
121 | { |
---|
122 | int i; |
---|
123 | unsigned long flags; |
---|
124 | p_state *state = (p_state *) calloc (sizeof(*state), 1); |
---|
125 | char *fontname = get_string_resource ("font", "Font"); |
---|
126 | XFontStruct *font; |
---|
127 | |
---|
128 | state->dpy = dpy; |
---|
129 | state->window = window; |
---|
130 | |
---|
131 | XGetWindowAttributes (dpy, window, &state->xgwa); |
---|
132 | |
---|
133 | state->font = XLoadQueryFont (dpy, fontname); |
---|
134 | |
---|
135 | if (!state->font) |
---|
136 | { |
---|
137 | fprintf(stderr, "couldn't load font \"%s\"\n", fontname); |
---|
138 | state->font = XLoadQueryFont (dpy, "fixed"); |
---|
139 | } |
---|
140 | if (!state->font) |
---|
141 | { |
---|
142 | fprintf(stderr, "couldn't load font \"fixed\""); |
---|
143 | exit(1); |
---|
144 | } |
---|
145 | |
---|
146 | font = state->font; |
---|
147 | state->scale = get_integer_resource ("scale", "Integer"); |
---|
148 | state->ticks = STATE_MAX + get_integer_resource ("ticks", "Integer"); |
---|
149 | |
---|
150 | #if 0 |
---|
151 | for (i = 0; i < font->n_properties; i++) |
---|
152 | if (font->properties[i].name == XA_FONT) |
---|
153 | printf ("font: %s\n", XGetAtomName(dpy, font->properties[i].card32)); |
---|
154 | #endif /* 0 */ |
---|
155 | |
---|
156 | state->cursor_blink = get_integer_resource ("cursor", "Time"); |
---|
157 | state->subproc_relaunch_delay = |
---|
158 | (1000 * get_integer_resource ("relaunch", "Time")); |
---|
159 | |
---|
160 | state->char_width = font->max_bounds.width; |
---|
161 | state->char_height = font->max_bounds.ascent + font->max_bounds.descent; |
---|
162 | |
---|
163 | state->grid_width = state->xgwa.width / (state->char_width * state->scale); |
---|
164 | state->grid_height = state->xgwa.height /(state->char_height * state->scale); |
---|
165 | state->cells = (p_cell *) calloc (sizeof(p_cell), |
---|
166 | state->grid_width * state->grid_height); |
---|
167 | state->chars = (p_char **) calloc (sizeof(p_char *), 256); |
---|
168 | |
---|
169 | state->gcs = (GC *) calloc (sizeof(GC), state->ticks + 1); |
---|
170 | |
---|
171 | { |
---|
172 | int ncolors = MAX (0, state->ticks - 3); |
---|
173 | XColor *colors = (XColor *) calloc (ncolors, sizeof(XColor)); |
---|
174 | int h1, h2; |
---|
175 | double s1, s2, v1, v2; |
---|
176 | |
---|
177 | unsigned long fg = get_pixel_resource ("foreground", "Foreground", |
---|
178 | state->dpy, state->xgwa.colormap); |
---|
179 | unsigned long bg = get_pixel_resource ("background", "Background", |
---|
180 | state->dpy, state->xgwa.colormap); |
---|
181 | unsigned long flare = get_pixel_resource ("flareForeground", "Foreground", |
---|
182 | state->dpy,state->xgwa.colormap); |
---|
183 | unsigned long fade = get_pixel_resource ("fadeForeground", "Foreground", |
---|
184 | state->dpy,state->xgwa.colormap); |
---|
185 | |
---|
186 | XColor start, end; |
---|
187 | |
---|
188 | start.pixel = fade; |
---|
189 | XQueryColor (state->dpy, state->xgwa.colormap, &start); |
---|
190 | |
---|
191 | end.pixel = bg; |
---|
192 | XQueryColor (state->dpy, state->xgwa.colormap, &end); |
---|
193 | |
---|
194 | /* Now allocate a ramp of colors from the main color to the background. */ |
---|
195 | rgb_to_hsv (start.red, start.green, start.blue, &h1, &s1, &v1); |
---|
196 | rgb_to_hsv (end.red, end.green, end.blue, &h2, &s2, &v2); |
---|
197 | make_color_ramp (state->dpy, state->xgwa.colormap, |
---|
198 | h1, s1, v1, |
---|
199 | h2, s2, v2, |
---|
200 | colors, &ncolors, |
---|
201 | False, True, False); |
---|
202 | |
---|
203 | /* Adjust to the number of colors we actually got. */ |
---|
204 | state->ticks = ncolors + STATE_MAX; |
---|
205 | |
---|
206 | /* Now, GCs all around. |
---|
207 | */ |
---|
208 | state->gcv.font = font->fid; |
---|
209 | state->gcv.cap_style = CapRound; |
---|
210 | #ifdef FUZZY_BORDER |
---|
211 | state->gcv.line_width = (int) (((long) state->scale) * 1.3); |
---|
212 | if (state->gcv.line_width == state->scale) |
---|
213 | state->gcv.line_width++; |
---|
214 | #else /* !FUZZY_BORDER */ |
---|
215 | state->gcv.line_width = (int) (((long) state->scale) * 0.9); |
---|
216 | if (state->gcv.line_width >= state->scale) |
---|
217 | state->gcv.line_width = state->scale - 1; |
---|
218 | if (state->gcv.line_width < 1) |
---|
219 | state->gcv.line_width = 1; |
---|
220 | #endif /* !FUZZY_BORDER */ |
---|
221 | |
---|
222 | flags = (GCForeground | GCBackground | GCCapStyle | GCLineWidth); |
---|
223 | |
---|
224 | state->gcv.background = bg; |
---|
225 | state->gcv.foreground = bg; |
---|
226 | state->gcs[BLANK] = XCreateGC (state->dpy, state->window, flags, |
---|
227 | &state->gcv); |
---|
228 | |
---|
229 | state->gcv.foreground = flare; |
---|
230 | state->gcs[FLARE] = XCreateGC (state->dpy, state->window, flags, |
---|
231 | &state->gcv); |
---|
232 | |
---|
233 | state->gcv.foreground = fg; |
---|
234 | state->gcs[NORMAL] = XCreateGC (state->dpy, state->window, flags, |
---|
235 | &state->gcv); |
---|
236 | |
---|
237 | for (i = 0; i < ncolors; i++) |
---|
238 | { |
---|
239 | state->gcv.foreground = colors[i].pixel; |
---|
240 | state->gcs[STATE_MAX + i] = XCreateGC (state->dpy, state->window, |
---|
241 | flags, &state->gcv); |
---|
242 | } |
---|
243 | } |
---|
244 | |
---|
245 | capture_font_bits (state); |
---|
246 | |
---|
247 | launch_text_generator (state); |
---|
248 | |
---|
249 | return state; |
---|
250 | } |
---|
251 | |
---|
252 | |
---|
253 | static void |
---|
254 | capture_font_bits (p_state *state) |
---|
255 | { |
---|
256 | XFontStruct *font = state->font; |
---|
257 | int safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing; |
---|
258 | int height = state->char_height; |
---|
259 | unsigned char string[257]; |
---|
260 | int i; |
---|
261 | Pixmap p = XCreatePixmap (state->dpy, state->window, |
---|
262 | (safe_width * 256), height, 1); |
---|
263 | |
---|
264 | for (i = 0; i < 256; i++) |
---|
265 | string[i] = (unsigned char) i; |
---|
266 | string[256] = 0; |
---|
267 | |
---|
268 | state->gcv.foreground = 0; |
---|
269 | state->gcv.background = 0; |
---|
270 | state->gc0 = XCreateGC (state->dpy, p, |
---|
271 | (GCForeground | GCBackground), |
---|
272 | &state->gcv); |
---|
273 | |
---|
274 | state->gcv.foreground = 1; |
---|
275 | state->gc1 = XCreateGC (state->dpy, p, |
---|
276 | (GCFont | GCForeground | GCBackground | |
---|
277 | GCCapStyle | GCLineWidth), |
---|
278 | &state->gcv); |
---|
279 | |
---|
280 | #ifdef FUZZY_BORDER |
---|
281 | { |
---|
282 | state->gcv.line_width = (int) (((long) state->scale) * 0.8); |
---|
283 | if (state->gcv.line_width >= state->scale) |
---|
284 | state->gcv.line_width = state->scale - 1; |
---|
285 | if (state->gcv.line_width < 1) |
---|
286 | state->gcv.line_width = 1; |
---|
287 | state->gc2 = XCreateGC (state->dpy, p, |
---|
288 | (GCFont | GCForeground | GCBackground | |
---|
289 | GCCapStyle | GCLineWidth), |
---|
290 | &state->gcv); |
---|
291 | } |
---|
292 | #endif /* FUZZY_BORDER */ |
---|
293 | |
---|
294 | XFillRectangle (state->dpy, p, state->gc0, 0, 0, (safe_width * 256), height); |
---|
295 | |
---|
296 | for (i = 0; i < 256; i++) |
---|
297 | { |
---|
298 | if (string[i] < font->min_char_or_byte2 || |
---|
299 | string[i] > font->max_char_or_byte2) |
---|
300 | continue; |
---|
301 | XDrawString (state->dpy, p, state->gc1, |
---|
302 | i * safe_width, font->ascent, |
---|
303 | (char *) (string + i), 1); |
---|
304 | } |
---|
305 | |
---|
306 | /* Draw the cursor. */ |
---|
307 | XFillRectangle (state->dpy, p, state->gc1, |
---|
308 | (CURSOR_INDEX * safe_width), 1, |
---|
309 | (font->per_char |
---|
310 | ? font->per_char['n'-font->min_char_or_byte2].width |
---|
311 | : font->max_bounds.width), |
---|
312 | font->ascent - 1); |
---|
313 | |
---|
314 | #if 0 |
---|
315 | XCopyPlane (state->dpy, p, state->window, state->gcs[FLARE], |
---|
316 | 0, 0, (safe_width * 256), height, 0, 0, 1L); |
---|
317 | XSync(state->dpy, False); |
---|
318 | #endif |
---|
319 | |
---|
320 | XSync (state->dpy, False); |
---|
321 | state->font_bits = XGetImage (state->dpy, p, 0, 0, |
---|
322 | (safe_width * 256), height, ~0L, XYPixmap); |
---|
323 | XFreePixmap (state->dpy, p); |
---|
324 | |
---|
325 | for (i = 0; i < 256; i++) |
---|
326 | state->chars[i] = make_character (state, i); |
---|
327 | state->chars[CURSOR_INDEX] = make_character (state, CURSOR_INDEX); |
---|
328 | } |
---|
329 | |
---|
330 | |
---|
331 | static p_char * |
---|
332 | make_character (p_state *state, int c) |
---|
333 | { |
---|
334 | p_char *pc = (p_char *) malloc (sizeof (*pc)); |
---|
335 | pc->name = (unsigned char) c; |
---|
336 | pc->width = state->scale * state->char_width; |
---|
337 | pc->height = state->scale * state->char_height; |
---|
338 | char_to_pixmap (state, pc, c); |
---|
339 | return pc; |
---|
340 | } |
---|
341 | |
---|
342 | |
---|
343 | static void |
---|
344 | char_to_pixmap (p_state *state, p_char *pc, int c) |
---|
345 | { |
---|
346 | Pixmap p = 0; |
---|
347 | GC gc; |
---|
348 | #ifdef FUZZY_BORDER |
---|
349 | Pixmap p2 = 0; |
---|
350 | GC gc2; |
---|
351 | #endif /* FUZZY_BORDER */ |
---|
352 | int from, to; |
---|
353 | int x1, y; |
---|
354 | |
---|
355 | XFontStruct *font = state->font; |
---|
356 | int safe_width = font->max_bounds.rbearing - font->min_bounds.lbearing; |
---|
357 | |
---|
358 | int width = state->scale * state->char_width; |
---|
359 | int height = state->scale * state->char_height; |
---|
360 | |
---|
361 | if (c < font->min_char_or_byte2 || |
---|
362 | c > font->max_char_or_byte2) |
---|
363 | goto DONE; |
---|
364 | |
---|
365 | gc = state->gc1; |
---|
366 | p = XCreatePixmap (state->dpy, state->window, width, height, 1); |
---|
367 | XFillRectangle (state->dpy, p, state->gc0, 0, 0, width, height); |
---|
368 | #ifdef FUZZY_BORDER |
---|
369 | gc2 = state->gc2; |
---|
370 | p2 = XCreatePixmap (state->dpy, state->window, width, height, 1); |
---|
371 | XFillRectangle (state->dpy, p2, state->gc0, 0, 0, width, height); |
---|
372 | #endif /* FUZZY_BORDER */ |
---|
373 | |
---|
374 | from = safe_width * c; |
---|
375 | to = safe_width * (c + 1); |
---|
376 | |
---|
377 | #if 0 |
---|
378 | if (c > 75 && c < 150) |
---|
379 | { |
---|
380 | printf ("\n=========== %d (%c)\n", c, c); |
---|
381 | for (y = 0; y < state->char_height; y++) |
---|
382 | { |
---|
383 | for (x1 = from; x1 < to; x1++) |
---|
384 | printf (XGetPixel (state->font_bits, x1, y) ? "* " : ". "); |
---|
385 | printf ("\n"); |
---|
386 | } |
---|
387 | } |
---|
388 | #endif |
---|
389 | |
---|
390 | pc->blank_p = True; |
---|
391 | for (y = 0; y < state->char_height; y++) |
---|
392 | for (x1 = from; x1 < to; x1++) |
---|
393 | if (XGetPixel (state->font_bits, x1, y)) |
---|
394 | { |
---|
395 | int xoff = state->scale / 2; |
---|
396 | int x2; |
---|
397 | for (x2 = x1; x2 < to; x2++) |
---|
398 | if (!XGetPixel (state->font_bits, x2, y)) |
---|
399 | break; |
---|
400 | x2--; |
---|
401 | XDrawLine (state->dpy, p, gc, |
---|
402 | (x1 - from) * state->scale + xoff, y * state->scale, |
---|
403 | (x2 - from) * state->scale + xoff, y * state->scale); |
---|
404 | #ifdef FUZZY_BORDER |
---|
405 | XDrawLine (state->dpy, p2, gc2, |
---|
406 | (x1 - from) * state->scale + xoff, y * state->scale, |
---|
407 | (x2 - from) * state->scale + xoff, y * state->scale); |
---|
408 | #endif /* FUZZY_BORDER */ |
---|
409 | x1 = x2; |
---|
410 | pc->blank_p = False; |
---|
411 | } |
---|
412 | |
---|
413 | /* if (pc->blank_p && c == CURSOR_INDEX) |
---|
414 | abort();*/ |
---|
415 | |
---|
416 | DONE: |
---|
417 | pc->pixmap = p; |
---|
418 | #ifdef FUZZY_BORDER |
---|
419 | pc->pixmap2 = p2; |
---|
420 | #endif /* FUZZY_BORDER */ |
---|
421 | } |
---|
422 | |
---|
423 | |
---|
424 | /* Managing the display. |
---|
425 | */ |
---|
426 | |
---|
427 | static void cursor_on_timer (XtPointer closure, XtIntervalId *id); |
---|
428 | static void cursor_off_timer (XtPointer closure, XtIntervalId *id); |
---|
429 | |
---|
430 | static Bool |
---|
431 | set_cursor_1 (p_state *state, Bool on) |
---|
432 | { |
---|
433 | p_cell *cell = &state->cells[state->grid_width * state->cursor_y |
---|
434 | + state->cursor_x]; |
---|
435 | p_char *cursor = state->chars[CURSOR_INDEX]; |
---|
436 | int new_state = (on ? NORMAL : FADE); |
---|
437 | |
---|
438 | if (cell->p_char != cursor) |
---|
439 | cell->changed = True; |
---|
440 | |
---|
441 | if (cell->state != new_state) |
---|
442 | cell->changed = True; |
---|
443 | |
---|
444 | cell->p_char = cursor; |
---|
445 | cell->state = new_state; |
---|
446 | return cell->changed; |
---|
447 | } |
---|
448 | |
---|
449 | static void |
---|
450 | set_cursor (p_state *state, Bool on) |
---|
451 | { |
---|
452 | if (set_cursor_1 (state, on)) |
---|
453 | { |
---|
454 | if (state->cursor_timer) |
---|
455 | XtRemoveTimeOut (state->cursor_timer); |
---|
456 | state->cursor_timer = 0; |
---|
457 | cursor_on_timer (state, 0); |
---|
458 | } |
---|
459 | } |
---|
460 | |
---|
461 | |
---|
462 | |
---|
463 | |
---|
464 | static void |
---|
465 | cursor_off_timer (XtPointer closure, XtIntervalId *id) |
---|
466 | { |
---|
467 | p_state *state = (p_state *) closure; |
---|
468 | set_cursor_1 (state, False); |
---|
469 | state->cursor_timer = XtAppAddTimeOut (app, state->cursor_blink, |
---|
470 | cursor_on_timer, closure); |
---|
471 | } |
---|
472 | |
---|
473 | static void |
---|
474 | cursor_on_timer (XtPointer closure, XtIntervalId *id) |
---|
475 | { |
---|
476 | p_state *state = (p_state *) closure; |
---|
477 | set_cursor_1 (state, True); |
---|
478 | state->cursor_timer = XtAppAddTimeOut (app, 2 * state->cursor_blink, |
---|
479 | cursor_off_timer, closure); |
---|
480 | } |
---|
481 | |
---|
482 | |
---|
483 | static void |
---|
484 | clear (p_state *state) |
---|
485 | { |
---|
486 | int x, y; |
---|
487 | state->cursor_x = 0; |
---|
488 | state->cursor_y = 0; |
---|
489 | for (y = 0; y < state->grid_height; y++) |
---|
490 | for (x = 0; x < state->grid_width; x++) |
---|
491 | { |
---|
492 | p_cell *cell = &state->cells[state->grid_width * y + x]; |
---|
493 | if (cell->state == FLARE || cell->state == NORMAL) |
---|
494 | { |
---|
495 | cell->state = FADE; |
---|
496 | cell->changed = True; |
---|
497 | } |
---|
498 | } |
---|
499 | set_cursor (state, True); |
---|
500 | } |
---|
501 | |
---|
502 | |
---|
503 | static void |
---|
504 | decay (p_state *state) |
---|
505 | { |
---|
506 | int x, y; |
---|
507 | for (y = 0; y < state->grid_height; y++) |
---|
508 | for (x = 0; x < state->grid_width; x++) |
---|
509 | { |
---|
510 | p_cell *cell = &state->cells[state->grid_width * y + x]; |
---|
511 | if (cell->state == FLARE) |
---|
512 | { |
---|
513 | cell->state = NORMAL; |
---|
514 | cell->changed = True; |
---|
515 | } |
---|
516 | else if (cell->state >= FADE) |
---|
517 | { |
---|
518 | cell->state++; |
---|
519 | if (cell->state >= state->ticks) |
---|
520 | cell->state = BLANK; |
---|
521 | cell->changed = True; |
---|
522 | } |
---|
523 | } |
---|
524 | } |
---|
525 | |
---|
526 | |
---|
527 | static void |
---|
528 | scroll (p_state *state) |
---|
529 | { |
---|
530 | int x, y; |
---|
531 | |
---|
532 | for (x = 0; x < state->grid_width; x++) |
---|
533 | { |
---|
534 | p_cell *from = 0, *to = 0; |
---|
535 | for (y = 1; y < state->grid_height; y++) |
---|
536 | { |
---|
537 | from = &state->cells[state->grid_width * y + x]; |
---|
538 | to = &state->cells[state->grid_width * (y-1) + x]; |
---|
539 | |
---|
540 | if ((from->state == FLARE || from->state == NORMAL) && |
---|
541 | !from->p_char->blank_p) |
---|
542 | { |
---|
543 | *to = *from; |
---|
544 | to->state = NORMAL; /* should be FLARE? Looks bad... */ |
---|
545 | } |
---|
546 | else |
---|
547 | { |
---|
548 | if (to->state == FLARE || to->state == NORMAL) |
---|
549 | to->state = FADE; |
---|
550 | } |
---|
551 | |
---|
552 | to->changed = True; |
---|
553 | } |
---|
554 | |
---|
555 | to = from; |
---|
556 | if (to && (to->state == FLARE || to->state == NORMAL)) |
---|
557 | { |
---|
558 | to->state = FADE; |
---|
559 | to->changed = True; |
---|
560 | } |
---|
561 | } |
---|
562 | set_cursor (state, True); |
---|
563 | } |
---|
564 | |
---|
565 | |
---|
566 | static void |
---|
567 | print_char (p_state *state, int c) |
---|
568 | { |
---|
569 | static char last_c = 0; |
---|
570 | |
---|
571 | p_cell *cell = &state->cells[state->grid_width * state->cursor_y |
---|
572 | + state->cursor_x]; |
---|
573 | |
---|
574 | /* Start the cursor fading (in case we don't end up overwriting it.) */ |
---|
575 | if (cell->state == FLARE || cell->state == NORMAL) |
---|
576 | { |
---|
577 | cell->state = FADE; |
---|
578 | cell->changed = True; |
---|
579 | } |
---|
580 | |
---|
581 | if (c == '\t') c = ' '; /* blah. */ |
---|
582 | |
---|
583 | if (c == '\r' || c == '\n') |
---|
584 | { |
---|
585 | if (c == '\n' && last_c == '\r') |
---|
586 | ; /* CRLF -- do nothing */ |
---|
587 | else |
---|
588 | { |
---|
589 | state->cursor_x = 0; |
---|
590 | if (state->cursor_y == state->grid_height - 1) |
---|
591 | scroll (state); |
---|
592 | else |
---|
593 | state->cursor_y++; |
---|
594 | } |
---|
595 | } |
---|
596 | else if (c == '\014') |
---|
597 | { |
---|
598 | clear (state); |
---|
599 | } |
---|
600 | else |
---|
601 | { |
---|
602 | cell->state = FLARE; |
---|
603 | cell->p_char = state->chars[c]; |
---|
604 | cell->changed = True; |
---|
605 | state->cursor_x++; |
---|
606 | |
---|
607 | if (c != ' ' && cell->p_char->blank_p) |
---|
608 | cell->p_char = state->chars[CURSOR_INDEX]; |
---|
609 | |
---|
610 | if (state->cursor_x >= state->grid_width - 1) |
---|
611 | { |
---|
612 | state->cursor_x = 0; |
---|
613 | if (state->cursor_y >= state->grid_height - 1) |
---|
614 | scroll (state); |
---|
615 | else |
---|
616 | state->cursor_y++; |
---|
617 | } |
---|
618 | } |
---|
619 | set_cursor (state, True); |
---|
620 | |
---|
621 | last_c = c; |
---|
622 | } |
---|
623 | |
---|
624 | |
---|
625 | static void |
---|
626 | update_display (p_state *state, Bool changed_only) |
---|
627 | { |
---|
628 | int x, y; |
---|
629 | |
---|
630 | for (y = 0; y < state->grid_height; y++) |
---|
631 | for (x = 0; x < state->grid_width; x++) |
---|
632 | { |
---|
633 | p_cell *cell = &state->cells[state->grid_width * y + x]; |
---|
634 | int width, height, tx, ty; |
---|
635 | |
---|
636 | if (changed_only && !cell->changed) |
---|
637 | continue; |
---|
638 | |
---|
639 | width = state->char_width * state->scale; |
---|
640 | height = state->char_height * state->scale; |
---|
641 | tx = x * width; |
---|
642 | ty = y * height; |
---|
643 | |
---|
644 | if (cell->state == BLANK || cell->p_char->blank_p) |
---|
645 | { |
---|
646 | XFillRectangle (state->dpy, state->window, state->gcs[BLANK], |
---|
647 | tx, ty, width, height); |
---|
648 | } |
---|
649 | else |
---|
650 | { |
---|
651 | #ifdef FUZZY_BORDER |
---|
652 | GC gc1 = state->gcs[cell->state]; |
---|
653 | GC gc2 = ((cell->state + 2) < state->ticks |
---|
654 | ? state->gcs[cell->state + 2] |
---|
655 | : 0); |
---|
656 | GC gc3 = (gc2 ? gc2 : gc1); |
---|
657 | if (gc3) |
---|
658 | XCopyPlane (state->dpy, cell->p_char->pixmap, state->window, gc3, |
---|
659 | 0, 0, width, height, tx, ty, 1L); |
---|
660 | if (gc2) |
---|
661 | { |
---|
662 | XSetClipMask (state->dpy, gc1, cell->p_char->pixmap2); |
---|
663 | XSetClipOrigin (state->dpy, gc1, tx, ty); |
---|
664 | XFillRectangle (state->dpy, state->window, gc1, |
---|
665 | tx, ty, width, height); |
---|
666 | XSetClipMask (state->dpy, gc1, None); |
---|
667 | } |
---|
668 | #else /* !FUZZY_BORDER */ |
---|
669 | |
---|
670 | XCopyPlane (state->dpy, |
---|
671 | cell->p_char->pixmap, state->window, |
---|
672 | state->gcs[cell->state], |
---|
673 | 0, 0, width, height, tx, ty, 1L); |
---|
674 | |
---|
675 | #endif /* !FUZZY_BORDER */ |
---|
676 | } |
---|
677 | |
---|
678 | cell->changed = False; |
---|
679 | } |
---|
680 | } |
---|
681 | |
---|
682 | |
---|
683 | static void |
---|
684 | run_phosphor (p_state *state) |
---|
685 | { |
---|
686 | update_display (state, True); |
---|
687 | decay (state); |
---|
688 | drain_input (state); |
---|
689 | } |
---|
690 | |
---|
691 | |
---|
692 | /* Subprocess. |
---|
693 | */ |
---|
694 | |
---|
695 | static void |
---|
696 | subproc_cb (XtPointer closure, int *source, XtInputId *id) |
---|
697 | { |
---|
698 | p_state *state = (p_state *) closure; |
---|
699 | state->input_available_p = True; |
---|
700 | } |
---|
701 | |
---|
702 | |
---|
703 | static void |
---|
704 | launch_text_generator (p_state *state) |
---|
705 | { |
---|
706 | char *oprogram = get_string_resource ("program", "Program"); |
---|
707 | char *program = (char *) malloc (strlen (oprogram) + 10); |
---|
708 | |
---|
709 | strcpy (program, "( "); |
---|
710 | strcat (program, oprogram); |
---|
711 | strcat (program, " ) 2>&1"); |
---|
712 | |
---|
713 | if ((state->pipe = popen (program, "r"))) |
---|
714 | { |
---|
715 | state->pipe_id = |
---|
716 | XtAppAddInput (app, fileno (state->pipe), |
---|
717 | (XtPointer) (XtInputReadMask | XtInputExceptMask), |
---|
718 | subproc_cb, (XtPointer) state); |
---|
719 | } |
---|
720 | else |
---|
721 | { |
---|
722 | perror (program); |
---|
723 | } |
---|
724 | } |
---|
725 | |
---|
726 | |
---|
727 | static void |
---|
728 | relaunch_generator_timer (XtPointer closure, XtIntervalId *id) |
---|
729 | { |
---|
730 | p_state *state = (p_state *) closure; |
---|
731 | launch_text_generator (state); |
---|
732 | } |
---|
733 | |
---|
734 | |
---|
735 | static void |
---|
736 | drain_input (p_state *state) |
---|
737 | { |
---|
738 | if (state->input_available_p) |
---|
739 | { |
---|
740 | unsigned char s[2]; |
---|
741 | int n = read (fileno (state->pipe), (void *) s, 1); |
---|
742 | if (n == 1) |
---|
743 | { |
---|
744 | print_char (state, s[0]); |
---|
745 | } |
---|
746 | else |
---|
747 | { |
---|
748 | XtRemoveInput (state->pipe_id); |
---|
749 | state->pipe_id = 0; |
---|
750 | pclose (state->pipe); |
---|
751 | state->pipe = 0; |
---|
752 | |
---|
753 | if (state->cursor_x != 0) /* break line if unbroken */ |
---|
754 | print_char (state, '\n'); /* blank line */ |
---|
755 | print_char (state, '\n'); |
---|
756 | |
---|
757 | /* Set up a timer to re-launch the subproc in a bit. */ |
---|
758 | XtAppAddTimeOut (app, state->subproc_relaunch_delay, |
---|
759 | relaunch_generator_timer, |
---|
760 | (XtPointer) state); |
---|
761 | } |
---|
762 | |
---|
763 | state->input_available_p = False; |
---|
764 | } |
---|
765 | } |
---|
766 | |
---|
767 | |
---|
768 | |
---|
769 | char *progclass = "Phosphor"; |
---|
770 | |
---|
771 | char *defaults [] = { |
---|
772 | ".background: Black", |
---|
773 | ".foreground: Green", |
---|
774 | "*fadeForeground: DarkGreen", |
---|
775 | "*flareForeground: White", |
---|
776 | "*font: fixed", |
---|
777 | "*scale: 6", |
---|
778 | "*ticks: 20", |
---|
779 | "*delay: 50000", |
---|
780 | "*cursor: 333", |
---|
781 | "*program: " FORTUNE_PROGRAM, |
---|
782 | "*relaunch: 5", |
---|
783 | 0 |
---|
784 | }; |
---|
785 | |
---|
786 | XrmOptionDescRec options [] = { |
---|
787 | { "-font", ".font", XrmoptionSepArg, 0 }, |
---|
788 | { "-scale", ".scale", XrmoptionSepArg, 0 }, |
---|
789 | { "-ticks", ".ticks", XrmoptionSepArg, 0 }, |
---|
790 | { "-delay", ".delay", XrmoptionSepArg, 0 }, |
---|
791 | { "-program", ".program", XrmoptionSepArg, 0 }, |
---|
792 | { 0, 0, 0, 0 } |
---|
793 | }; |
---|
794 | |
---|
795 | |
---|
796 | void |
---|
797 | screenhack (Display *dpy, Window window) |
---|
798 | { |
---|
799 | int delay = get_integer_resource ("delay", "Integer"); |
---|
800 | p_state *state = init_phosphor (dpy, window); |
---|
801 | |
---|
802 | clear (state); |
---|
803 | |
---|
804 | while (1) |
---|
805 | { |
---|
806 | run_phosphor (state); |
---|
807 | XSync (dpy, False); |
---|
808 | screenhack_handle_events (dpy); |
---|
809 | |
---|
810 | if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput)) |
---|
811 | XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput); |
---|
812 | |
---|
813 | if (delay) usleep (delay); |
---|
814 | } |
---|
815 | } |
---|