1 /* stderr.c --- capturing stdout/stderr output onto the screensaver window.
2 * xscreensaver, Copyright (c) 1991-1998 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* stderr hackery - Why Unix Sucks, reason number 32767.
33 #include <X11/Intrinsic.h>
35 #include "xscreensaver.h"
36 #include "resources.h"
39 FILE *real_stderr = 0;
40 FILE *real_stdout = 0;
43 /* It's ok for these to be global, since they refer to the one and only
44 stderr stream, not to a particular screen or window or visual.
46 static char stderr_buffer [4096];
47 static char *stderr_tail = 0;
48 static time_t stderr_last_read = 0;
50 static int stderr_stdout_read_fd = -1;
52 static void make_stderr_overlay_window (saver_screen_info *);
55 /* Recreates the stderr window or GCs: do this when the xscreensaver window
56 on a screen has been re-created.
59 reset_stderr (saver_screen_info *ssi)
61 saver_info *si = ssi->global;
63 if (si->prefs.debug_p)
64 fprintf ((real_stderr ? real_stderr : stderr),
65 "%s: resetting stderr\n", blurb());
67 ssi->stderr_text_x = 0;
68 ssi->stderr_text_y = 0;
71 XFreeGC (si->dpy, ssi->stderr_gc);
74 if (ssi->stderr_overlay_window)
75 XDestroyWindow(si->dpy, ssi->stderr_overlay_window);
76 ssi->stderr_overlay_window = 0;
79 XFreeColormap(si->dpy, ssi->stderr_cmap);
83 /* Erases any stderr text overlaying the screen (if possible) and resets
84 the stderr output cursor to the upper left. Do this when the xscreensaver
88 clear_stderr (saver_screen_info *ssi)
90 saver_info *si = ssi->global;
91 ssi->stderr_text_x = 0;
92 ssi->stderr_text_y = 0;
93 if (ssi->stderr_overlay_window)
94 XClearWindow (si->dpy, ssi->stderr_overlay_window);
98 /* Draws the string on the screen's window.
101 print_stderr_1 (saver_screen_info *ssi, char *string)
103 saver_info *si = ssi->global;
104 Display *dpy = si->dpy;
105 Screen *screen = ssi->screen;
106 Window window = (ssi->stderr_overlay_window ?
107 ssi->stderr_overlay_window :
108 ssi->screensaver_window);
114 if (! ssi->stderr_font)
116 char *font_name = get_string_resource ("font", "Font");
117 if (!font_name) font_name = "fixed";
118 ssi->stderr_font = XLoadQueryFont (dpy, font_name);
119 if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed");
120 ssi->stderr_line_height = (ssi->stderr_font->ascent +
121 ssi->stderr_font->descent);
124 if (! ssi->stderr_gc)
128 Colormap cmap = ssi->cmap;
130 if (!ssi->stderr_overlay_window &&
131 get_boolean_resource("overlayStderr", "Boolean"))
133 make_stderr_overlay_window (ssi);
134 if (ssi->stderr_overlay_window)
135 window = ssi->stderr_overlay_window;
136 if (ssi->stderr_cmap)
137 cmap = ssi->stderr_cmap;
140 fg = get_pixel_resource ("overlayTextForeground","Foreground",dpy,cmap);
141 bg = get_pixel_resource ("overlayTextBackground","Background",dpy,cmap);
142 gcv.font = ssi->stderr_font->fid;
145 ssi->stderr_gc = XCreateGC (dpy, window,
146 (GCFont | GCForeground | GCBackground),
151 if (ssi->stderr_cmap)
152 XInstallColormap(si->dpy, ssi->stderr_cmap);
154 for (tail = string; *tail; tail++)
156 if (*tail == '\n' || *tail == '\r')
158 int maxy = HeightOfScreen (screen) - v_border - v_border;
160 XDrawImageString (dpy, window, ssi->stderr_gc,
161 ssi->stderr_text_x + h_border,
162 ssi->stderr_text_y + v_border +
163 ssi->stderr_font->ascent,
165 ssi->stderr_text_x = 0;
166 ssi->stderr_text_y += ssi->stderr_line_height;
168 if (*tail == '\r' && *head == '\n')
171 if (ssi->stderr_text_y > maxy - ssi->stderr_line_height)
174 ssi->stderr_text_y = 0;
176 int offset = ssi->stderr_line_height * 5;
177 XWindowAttributes xgwa;
178 XGetWindowAttributes (dpy, window, &xgwa);
180 XCopyArea (dpy, window, window, ssi->stderr_gc,
181 0, v_border + offset,
183 (xgwa.height - v_border - v_border - offset),
185 XClearArea (dpy, window,
186 0, xgwa.height - v_border - offset,
187 xgwa.width, offset, False);
188 ssi->stderr_text_y -= offset;
195 int direction, ascent, descent;
197 XDrawImageString (dpy, window, ssi->stderr_gc,
198 ssi->stderr_text_x + h_border,
199 ssi->stderr_text_y + v_border
200 + ssi->stderr_font->ascent,
202 XTextExtents (ssi->stderr_font, tail, tail - head,
203 &direction, &ascent, &descent, &overall);
204 ssi->stderr_text_x += overall.width;
209 make_stderr_overlay_window (saver_screen_info *ssi)
211 saver_info *si = ssi->global;
212 unsigned long transparent_pixel = 0;
213 Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel);
216 int depth = visual_depth (ssi->screen, visual);
217 XSetWindowAttributes attrs;
218 XWindowAttributes xgwa;
219 unsigned long attrmask;
220 XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
222 if (si->prefs.debug_p)
224 "%s: using overlay visual 0x%0x for stderr text layer.\n",
225 blurb(), (int) XVisualIDFromVisual (visual));
227 ssi->stderr_cmap = XCreateColormap(si->dpy,
228 RootWindowOfScreen(ssi->screen),
231 attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel |
232 CWBackingStore | CWSaveUnder);
233 attrs.colormap = ssi->stderr_cmap;
234 attrs.background_pixel = transparent_pixel;
235 attrs.backing_pixel = transparent_pixel;
236 attrs.border_pixel = transparent_pixel;
237 attrs.backing_store = NotUseful;
238 attrs.save_under = False;
240 ssi->stderr_overlay_window =
241 XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
242 xgwa.width, xgwa.height,
243 0, depth, InputOutput, visual, attrmask, &attrs);
244 XMapRaised(si->dpy, ssi->stderr_overlay_window);
249 /* Draws the string on each screen's window as error text.
252 print_stderr (saver_info *si, char *string)
254 saver_preferences *p = &si->prefs;
257 /* In verbose mode, copy it to stderr as well. */
259 fprintf (real_stderr, "%s", string);
261 for (i = 0; i < si->nscreens; i++)
262 print_stderr_1 (&si->screens[i], string);
266 /* Polls the stderr buffer every few seconds and if it finds any text,
267 writes it on all screens.
270 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
272 saver_info *si = (saver_info *) closure;
273 char *s = stderr_buffer;
276 /* If too much data was printed, then something has gone haywire,
278 char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
279 int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
280 if (strlen (s) > max)
281 strcpy (s + max, trailer);
282 /* Now show the user. */
283 print_stderr (si, s);
286 stderr_tail = stderr_buffer;
287 si->stderr_popup_timer = 0;
291 /* Called when data becomes available on the stderr pipe. Copies it into
292 stderr_buffer where stderr_popup_timer_fn() can find it later.
295 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
297 saver_info *si = (saver_info *) closure;
301 int read_this_time = 0;
303 if (!fd || *fd < 0 || *fd != stderr_stdout_read_fd)
306 if (stderr_tail == 0)
307 stderr_tail = stderr_buffer;
309 left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
314 /* Read as much data from the fd as we can, up to our buffer size. */
317 while ((size = read (*fd, (void *) s, left)) > 0)
321 read_this_time += size;
328 /* The buffer is full; flush the rest of it. */
329 while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
334 stderr_last_read = time ((time_t *) 0);
336 /* Now we have read some data that we would like to put up in a dialog
337 box. But more data may still be coming in - so don't pop up the
338 dialog right now, but instead, start a timer that will pop it up
339 a second from now. Should more data come in in the meantime, we
340 will be called again, and will reset that timer again. So the
341 dialog will only pop up when a second has elapsed with no new data
342 being written to stderr.
344 However, if the buffer is full (meaning lots of data has been written)
345 then we don't reset the timer.
347 if (read_this_time > 0)
349 if (si->stderr_popup_timer)
350 XtRemoveTimeOut (si->stderr_popup_timer);
352 si->stderr_popup_timer =
353 XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn,
358 /* If stderr capturing is desired, this replaces `stdout' and `stderr'
359 with a pipe, so that any output written to them will show up on the
360 screen as well as on the original value of those streams.
363 initialize_stderr (saver_info *si)
365 static Boolean done = False;
368 int new_stdout, new_stderr;
372 Boolean stderr_dialog_p;
377 real_stderr = stderr;
378 real_stdout = stdout;
380 stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
382 if (!stderr_dialog_p)
387 perror ("error creating pipe:");
396 # if defined(O_NONBLOCK)
398 # elif defined(O_NDELAY)
401 ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
404 /* Set both sides of the pipe to nonblocking - this is so that
405 our reads (in stderr_callback) will terminate, and so that
406 out writes (in the client programs) will silently fail when
407 the pipe is full, instead of hosing the program. */
408 if (fcntl (in, F_SETFL, flags) != 0)
413 if (fcntl (out, F_SETFL, flags) != 0)
419 # endif /* !HAVE_FCNTL */
423 FILE *new_stderr_file;
424 FILE *new_stdout_file;
426 new_stderr = dup (stderr_fd);
429 perror ("could not dup() a stderr:");
432 if (! (new_stderr_file = fdopen (new_stderr, "w")))
434 perror ("could not fdopen() the new stderr:");
437 real_stderr = new_stderr_file;
440 if (dup2 (out, stderr_fd) < 0)
442 perror ("could not dup() a new stderr:");
447 new_stdout = dup (stdout_fd);
450 perror ("could not dup() a stdout:");
453 if (! (new_stdout_file = fdopen (new_stdout, "w")))
455 perror ("could not fdopen() the new stdout:");
458 real_stdout = new_stdout_file;
461 if (dup2 (out, stdout_fd) < 0)
463 perror ("could not dup() a new stdout:");
468 stderr_stdout_read_fd = in;
469 XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,
474 /* If there is anything in the stderr buffer, flush it to the real stderr.
475 This does no X operations. Call this when exiting to make sure any
476 last words actually show up.
479 shutdown_stderr (saver_info *si)
481 if (!real_stderr || stderr_stdout_read_fd < 0)
484 stderr_callback ((XtPointer) si, &stderr_stdout_read_fd, 0);
488 stderr_buffer < stderr_tail)
491 fprintf (real_stderr, "%s", stderr_buffer);
492 fflush (real_stderr);
493 stderr_tail = stderr_buffer;