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;
59 if (si->prefs.debug_p)
60 fprintf ((real_stderr ? real_stderr : stderr),
61 "%s: resetting stderr\n", progname);
64 ssi->stderr_text_x = 0;
65 ssi->stderr_text_y = 0;
68 XFreeGC (si->dpy, ssi->stderr_gc);
71 if (ssi->stderr_overlay_window)
72 XDestroyWindow(si->dpy, ssi->stderr_overlay_window);
73 ssi->stderr_overlay_window = 0;
76 XFreeColormap(si->dpy, ssi->stderr_cmap);
81 clear_stderr (saver_screen_info *ssi)
83 saver_info *si = ssi->global;
84 ssi->stderr_text_x = 0;
85 ssi->stderr_text_y = 0;
86 if (ssi->stderr_overlay_window)
87 XClearWindow (si->dpy, ssi->stderr_overlay_window);
92 print_stderr_1 (saver_screen_info *ssi, char *string)
94 saver_info *si = ssi->global;
95 saver_preferences *p = &si->prefs;
96 Display *dpy = si->dpy;
97 Screen *screen = ssi->screen;
98 Window window = (ssi->stderr_overlay_window ?
99 ssi->stderr_overlay_window :
100 ssi->screensaver_window);
106 /* In verbose mode, copy it to stderr as well. */
108 fprintf (real_stderr, "%s", string);
110 if (! ssi->stderr_font)
112 char *font_name = get_string_resource ("font", "Font");
113 if (!font_name) font_name = "fixed";
114 ssi->stderr_font = XLoadQueryFont (dpy, font_name);
115 if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed");
116 ssi->stderr_line_height = (ssi->stderr_font->ascent +
117 ssi->stderr_font->descent);
120 if (! ssi->stderr_gc)
124 Colormap cmap = ssi->cmap;
126 if (!ssi->stderr_overlay_window &&
127 get_boolean_resource("overlayStderr", "Boolean"))
129 make_stderr_overlay_window (ssi);
130 if (ssi->stderr_overlay_window)
131 window = ssi->stderr_overlay_window;
132 if (ssi->stderr_cmap)
133 cmap = ssi->stderr_cmap;
136 fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
137 bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
138 gcv.font = ssi->stderr_font->fid;
141 ssi->stderr_gc = XCreateGC (dpy, window,
142 (GCFont | GCForeground | GCBackground),
147 if (ssi->stderr_cmap)
148 XInstallColormap(si->dpy, ssi->stderr_cmap);
150 for (tail = string; *tail; tail++)
152 if (*tail == '\n' || *tail == '\r')
154 int maxy = HeightOfScreen (screen) - v_border - v_border;
156 XDrawImageString (dpy, window, ssi->stderr_gc,
157 ssi->stderr_text_x + h_border,
158 ssi->stderr_text_y + v_border +
159 ssi->stderr_font->ascent,
161 ssi->stderr_text_x = 0;
162 ssi->stderr_text_y += ssi->stderr_line_height;
164 if (*tail == '\r' && *head == '\n')
167 if (ssi->stderr_text_y > maxy - ssi->stderr_line_height)
170 ssi->stderr_text_y = 0;
172 int offset = ssi->stderr_line_height * 5;
173 XCopyArea (dpy, window, window, ssi->stderr_gc,
174 0, v_border + offset,
175 WidthOfScreen (screen),
176 (HeightOfScreen (screen) - v_border - v_border
179 XClearArea (dpy, window,
180 0, HeightOfScreen (screen) - v_border - offset,
181 WidthOfScreen (screen), offset, False);
182 ssi->stderr_text_y -= offset;
189 int direction, ascent, descent;
191 XDrawImageString (dpy, window, ssi->stderr_gc,
192 ssi->stderr_text_x + h_border,
193 ssi->stderr_text_y + v_border
194 + ssi->stderr_font->ascent,
196 XTextExtents (ssi->stderr_font, tail, tail - head,
197 &direction, &ascent, &descent, &overall);
198 ssi->stderr_text_x += overall.width;
203 make_stderr_overlay_window (saver_screen_info *ssi)
205 saver_info *si = ssi->global;
206 unsigned long transparent_pixel = 0;
207 Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel);
210 int depth = visual_depth (ssi->screen, visual);
211 XSetWindowAttributes attrs;
212 unsigned long attrmask;
215 if (si->prefs.debug_p)
217 "%s: using overlay visual 0x%0x for stderr text layer.\n",
218 progname, (int) XVisualIDFromVisual (visual));
221 ssi->stderr_cmap = XCreateColormap(si->dpy,
222 RootWindowOfScreen(ssi->screen),
225 attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel |
226 CWBackingStore | CWSaveUnder);
227 attrs.colormap = ssi->stderr_cmap;
228 attrs.background_pixel = transparent_pixel;
229 attrs.backing_pixel = transparent_pixel;
230 attrs.border_pixel = transparent_pixel;
231 attrs.backing_store = NotUseful;
232 attrs.save_under = False;
234 ssi->stderr_overlay_window =
235 XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
236 WidthOfScreen(ssi->screen), HeightOfScreen(ssi->screen),
237 0, depth, InputOutput, visual, attrmask, &attrs);
238 XMapRaised(si->dpy, ssi->stderr_overlay_window);
244 print_stderr (saver_info *si, char *string)
246 saver_preferences *p = &si->prefs;
249 /* In verbose mode, copy it to stderr as well. */
251 fprintf (real_stderr, "%s", string);
253 for (i = 0; i < si->nscreens; i++)
254 print_stderr_1 (&si->screens[i], string);
259 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
261 saver_info *si = (saver_info *) closure;
262 char *s = stderr_buffer;
265 /* If too much data was printed, then something has gone haywire,
267 char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
268 int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
269 if (strlen (s) > max)
270 strcpy (s + max, trailer);
271 /* Now show the user. */
272 print_stderr (si, s);
275 stderr_tail = stderr_buffer;
276 si->stderr_popup_timer = 0;
281 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
283 saver_info *si = (saver_info *) closure;
287 int read_this_time = 0;
289 if (stderr_tail == 0)
290 stderr_tail = stderr_buffer;
292 left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
297 /* Read as much data from the fd as we can, up to our buffer size. */
300 while ((size = read (*fd, (void *) s, left)) > 0)
304 read_this_time += size;
311 /* The buffer is full; flush the rest of it. */
312 while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
317 stderr_last_read = time ((time_t *) 0);
319 /* Now we have read some data that we would like to put up in a dialog
320 box. But more data may still be coming in - so don't pop up the
321 dialog right now, but instead, start a timer that will pop it up
322 a second from now. Should more data come in in the meantime, we
323 will be called again, and will reset that timer again. So the
324 dialog will only pop up when a second has elapsed with no new data
325 being written to stderr.
327 However, if the buffer is full (meaning lots of data has been written)
328 then we don't reset the timer.
330 if (read_this_time > 0)
332 if (si->stderr_popup_timer)
333 XtRemoveTimeOut (si->stderr_popup_timer);
335 si->stderr_popup_timer =
336 XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn,
342 initialize_stderr (saver_info *si)
344 static Boolean done = False;
347 int new_stdout, new_stderr;
351 Boolean stderr_dialog_p, stdout_dialog_p;
356 stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
357 stdout_dialog_p = get_boolean_resource ("captureStdout", "Boolean");
359 if (!stderr_dialog_p && !stdout_dialog_p)
362 real_stderr = stderr;
363 real_stdout = stdout;
367 perror ("error creating pipe:");
376 # if defined(O_NONBLOCK)
378 # elif defined(O_NDELAY)
381 ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
384 /* Set both sides of the pipe to nonblocking - this is so that
385 our reads (in stderr_callback) will terminate, and so that
386 out writes (in the client programs) will silently fail when
387 the pipe is full, instead of hosing the program. */
388 if (fcntl (in, F_SETFL, flags) != 0)
393 if (fcntl (out, F_SETFL, flags) != 0)
399 # endif /* !HAVE_FCNTL */
403 FILE *new_stderr_file;
404 new_stderr = dup (stderr_fd);
407 perror ("could not dup() a stderr:");
410 if (! (new_stderr_file = fdopen (new_stderr, "w")))
412 perror ("could not fdopen() the new stderr:");
415 real_stderr = new_stderr_file;
418 if (dup2 (out, stderr_fd) < 0)
420 perror ("could not dup() a new stderr:");
427 FILE *new_stdout_file;
428 new_stdout = dup (stdout_fd);
431 perror ("could not dup() a stdout:");
434 if (! (new_stdout_file = fdopen (new_stdout, "w")))
436 perror ("could not fdopen() the new stdout:");
439 real_stdout = new_stdout_file;
442 if (dup2 (out, stdout_fd) < 0)
444 perror ("could not dup() a new stdout:");
449 XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,