1 /* stderr.c --- capturing stdout/stderr output onto the screensaver window.
2 * xscreensaver, Copyright (c) 1991-1997 Jamie Zawinski <jwz@netscape.com>
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 void make_stderr_overlay_window (saver_screen_info *);
54 reset_stderr (saver_screen_info *ssi)
56 saver_info *si = ssi->global;
58 if (si->prefs.debug_p)
59 fprintf ((real_stderr ? real_stderr : stderr),
60 "%s: resetting stderr\n", progname);
62 ssi->stderr_text_x = 0;
63 ssi->stderr_text_y = 0;
66 XFreeGC (si->dpy, ssi->stderr_gc);
69 if (ssi->stderr_overlay_window)
70 XDestroyWindow(si->dpy, ssi->stderr_overlay_window);
71 ssi->stderr_overlay_window = 0;
74 XFreeColormap(si->dpy, ssi->stderr_cmap);
79 clear_stderr (saver_screen_info *ssi)
81 saver_info *si = ssi->global;
82 ssi->stderr_text_x = 0;
83 ssi->stderr_text_y = 0;
84 if (ssi->stderr_overlay_window)
85 XClearWindow (si->dpy, ssi->stderr_overlay_window);
90 print_stderr_1 (saver_screen_info *ssi, char *string)
92 saver_info *si = ssi->global;
93 saver_preferences *p = &si->prefs;
94 Display *dpy = si->dpy;
95 Screen *screen = ssi->screen;
96 Window window = (ssi->stderr_overlay_window ?
97 ssi->stderr_overlay_window :
98 ssi->screensaver_window);
104 /* In verbose mode, copy it to stderr as well. */
106 fprintf (real_stderr, "%s", string);
108 if (! ssi->stderr_font)
110 char *font_name = get_string_resource ("font", "Font");
111 if (!font_name) font_name = "fixed";
112 ssi->stderr_font = XLoadQueryFont (dpy, font_name);
113 if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed");
114 ssi->stderr_line_height = (ssi->stderr_font->ascent +
115 ssi->stderr_font->descent);
118 if (! ssi->stderr_gc)
122 Colormap cmap = ssi->cmap;
124 if (!ssi->stderr_overlay_window &&
125 get_boolean_resource("overlayStderr", "Boolean"))
127 make_stderr_overlay_window (ssi);
128 if (ssi->stderr_overlay_window)
129 window = ssi->stderr_overlay_window;
130 if (ssi->stderr_cmap)
131 cmap = ssi->stderr_cmap;
134 fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
135 bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
136 gcv.font = ssi->stderr_font->fid;
139 ssi->stderr_gc = XCreateGC (dpy, window,
140 (GCFont | GCForeground | GCBackground),
145 if (ssi->stderr_cmap)
146 XInstallColormap(si->dpy, ssi->stderr_cmap);
148 for (tail = string; *tail; tail++)
150 if (*tail == '\n' || *tail == '\r')
152 int maxy = HeightOfScreen (screen) - v_border - v_border;
154 XDrawImageString (dpy, window, ssi->stderr_gc,
155 ssi->stderr_text_x + h_border,
156 ssi->stderr_text_y + v_border +
157 ssi->stderr_font->ascent,
159 ssi->stderr_text_x = 0;
160 ssi->stderr_text_y += ssi->stderr_line_height;
162 if (*tail == '\r' && *head == '\n')
165 if (ssi->stderr_text_y > maxy - ssi->stderr_line_height)
168 ssi->stderr_text_y = 0;
170 int offset = ssi->stderr_line_height * 5;
171 XCopyArea (dpy, window, window, ssi->stderr_gc,
172 0, v_border + offset,
173 WidthOfScreen (screen),
174 (HeightOfScreen (screen) - v_border - v_border
177 XClearArea (dpy, window,
178 0, HeightOfScreen (screen) - v_border - offset,
179 WidthOfScreen (screen), offset, False);
180 ssi->stderr_text_y -= offset;
187 int direction, ascent, descent;
189 XDrawImageString (dpy, window, ssi->stderr_gc,
190 ssi->stderr_text_x + h_border,
191 ssi->stderr_text_y + v_border
192 + ssi->stderr_font->ascent,
194 XTextExtents (ssi->stderr_font, tail, tail - head,
195 &direction, &ascent, &descent, &overall);
196 ssi->stderr_text_x += overall.width;
201 make_stderr_overlay_window (saver_screen_info *ssi)
203 saver_info *si = ssi->global;
204 unsigned long transparent_pixel = 0;
205 Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel);
208 int depth = visual_depth (ssi->screen, visual);
209 XSetWindowAttributes attrs;
210 unsigned long attrmask;
212 if (si->prefs.debug_p)
214 "%s: using overlay visual 0x%0x for stderr text layer.\n",
215 progname, (int) XVisualIDFromVisual (visual));
217 ssi->stderr_cmap = XCreateColormap(si->dpy,
218 RootWindowOfScreen(ssi->screen),
221 attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel |
222 CWBackingStore | CWSaveUnder);
223 attrs.colormap = ssi->stderr_cmap;
224 attrs.background_pixel = transparent_pixel;
225 attrs.backing_pixel = transparent_pixel;
226 attrs.border_pixel = transparent_pixel;
227 attrs.backing_store = NotUseful;
228 attrs.save_under = False;
230 ssi->stderr_overlay_window =
231 XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
232 WidthOfScreen(ssi->screen), HeightOfScreen(ssi->screen),
233 0, depth, InputOutput, visual, attrmask, &attrs);
234 XMapRaised(si->dpy, ssi->stderr_overlay_window);
240 print_stderr (saver_info *si, char *string)
242 saver_preferences *p = &si->prefs;
245 /* In verbose mode, copy it to stderr as well. */
247 fprintf (real_stderr, "%s", string);
249 for (i = 0; i < si->nscreens; i++)
250 print_stderr_1 (&si->screens[i], string);
255 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
257 saver_info *si = (saver_info *) closure;
258 char *s = stderr_buffer;
261 /* If too much data was printed, then something has gone haywire,
263 char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
264 int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
265 if (strlen (s) > max)
266 strcpy (s + max, trailer);
267 /* Now show the user. */
268 print_stderr (si, s);
271 stderr_tail = stderr_buffer;
272 si->stderr_popup_timer = 0;
277 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
279 saver_info *si = (saver_info *) closure;
283 int read_this_time = 0;
285 if (stderr_tail == 0)
286 stderr_tail = stderr_buffer;
288 left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
293 /* Read as much data from the fd as we can, up to our buffer size. */
296 while ((size = read (*fd, (void *) s, left)) > 0)
300 read_this_time += size;
307 /* The buffer is full; flush the rest of it. */
308 while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
313 stderr_last_read = time ((time_t *) 0);
315 /* Now we have read some data that we would like to put up in a dialog
316 box. But more data may still be coming in - so don't pop up the
317 dialog right now, but instead, start a timer that will pop it up
318 a second from now. Should more data come in in the meantime, we
319 will be called again, and will reset that timer again. So the
320 dialog will only pop up when a second has elapsed with no new data
321 being written to stderr.
323 However, if the buffer is full (meaning lots of data has been written)
324 then we don't reset the timer.
326 if (read_this_time > 0)
328 if (si->stderr_popup_timer)
329 XtRemoveTimeOut (si->stderr_popup_timer);
331 si->stderr_popup_timer =
332 XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn,
338 initialize_stderr (saver_info *si)
340 static Boolean done = False;
343 int new_stdout, new_stderr;
347 Boolean stderr_dialog_p, stdout_dialog_p;
352 real_stderr = stderr;
353 real_stdout = stdout;
355 stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
356 stdout_dialog_p = get_boolean_resource ("captureStdout", "Boolean");
358 if (!stderr_dialog_p && !stdout_dialog_p)
363 perror ("error creating pipe:");
372 # if defined(O_NONBLOCK)
374 # elif defined(O_NDELAY)
377 ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
380 /* Set both sides of the pipe to nonblocking - this is so that
381 our reads (in stderr_callback) will terminate, and so that
382 out writes (in the client programs) will silently fail when
383 the pipe is full, instead of hosing the program. */
384 if (fcntl (in, F_SETFL, flags) != 0)
389 if (fcntl (out, F_SETFL, flags) != 0)
395 # endif /* !HAVE_FCNTL */
399 FILE *new_stderr_file;
400 new_stderr = dup (stderr_fd);
403 perror ("could not dup() a stderr:");
406 if (! (new_stderr_file = fdopen (new_stderr, "w")))
408 perror ("could not fdopen() the new stderr:");
411 real_stderr = new_stderr_file;
414 if (dup2 (out, stderr_fd) < 0)
416 perror ("could not dup() a new stderr:");
423 FILE *new_stdout_file;
424 new_stdout = dup (stdout_fd);
427 perror ("could not dup() a stdout:");
430 if (! (new_stdout_file = fdopen (new_stdout, "w")))
432 perror ("could not fdopen() the new stdout:");
435 real_stdout = new_stdout_file;
438 if (dup2 (out, stdout_fd) < 0)
440 perror ("could not dup() a new stdout:");
445 XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,