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 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", blurb());
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 Display *dpy = si->dpy;
94 Screen *screen = ssi->screen;
95 Window window = (ssi->stderr_overlay_window ?
96 ssi->stderr_overlay_window :
97 ssi->screensaver_window);
103 if (! ssi->stderr_font)
105 char *font_name = get_string_resource ("font", "Font");
106 if (!font_name) font_name = "fixed";
107 ssi->stderr_font = XLoadQueryFont (dpy, font_name);
108 if (! ssi->stderr_font) ssi->stderr_font = XLoadQueryFont (dpy, "fixed");
109 ssi->stderr_line_height = (ssi->stderr_font->ascent +
110 ssi->stderr_font->descent);
113 if (! ssi->stderr_gc)
117 Colormap cmap = ssi->cmap;
119 if (!ssi->stderr_overlay_window &&
120 get_boolean_resource("overlayStderr", "Boolean"))
122 make_stderr_overlay_window (ssi);
123 if (ssi->stderr_overlay_window)
124 window = ssi->stderr_overlay_window;
125 if (ssi->stderr_cmap)
126 cmap = ssi->stderr_cmap;
129 fg = get_pixel_resource ("overlayTextForeground","Foreground",dpy,cmap);
130 bg = get_pixel_resource ("overlayTextBackground","Background",dpy,cmap);
131 gcv.font = ssi->stderr_font->fid;
134 ssi->stderr_gc = XCreateGC (dpy, window,
135 (GCFont | GCForeground | GCBackground),
140 if (ssi->stderr_cmap)
141 XInstallColormap(si->dpy, ssi->stderr_cmap);
143 for (tail = string; *tail; tail++)
145 if (*tail == '\n' || *tail == '\r')
147 int maxy = HeightOfScreen (screen) - v_border - v_border;
149 XDrawImageString (dpy, window, ssi->stderr_gc,
150 ssi->stderr_text_x + h_border,
151 ssi->stderr_text_y + v_border +
152 ssi->stderr_font->ascent,
154 ssi->stderr_text_x = 0;
155 ssi->stderr_text_y += ssi->stderr_line_height;
157 if (*tail == '\r' && *head == '\n')
160 if (ssi->stderr_text_y > maxy - ssi->stderr_line_height)
163 ssi->stderr_text_y = 0;
165 int offset = ssi->stderr_line_height * 5;
166 XCopyArea (dpy, window, window, ssi->stderr_gc,
167 0, v_border + offset,
168 WidthOfScreen (screen),
169 (HeightOfScreen (screen) - v_border - v_border
172 XClearArea (dpy, window,
173 0, HeightOfScreen (screen) - v_border - offset,
174 WidthOfScreen (screen), offset, False);
175 ssi->stderr_text_y -= offset;
182 int direction, ascent, descent;
184 XDrawImageString (dpy, window, ssi->stderr_gc,
185 ssi->stderr_text_x + h_border,
186 ssi->stderr_text_y + v_border
187 + ssi->stderr_font->ascent,
189 XTextExtents (ssi->stderr_font, tail, tail - head,
190 &direction, &ascent, &descent, &overall);
191 ssi->stderr_text_x += overall.width;
196 make_stderr_overlay_window (saver_screen_info *ssi)
198 saver_info *si = ssi->global;
199 unsigned long transparent_pixel = 0;
200 Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel);
203 int depth = visual_depth (ssi->screen, visual);
204 XSetWindowAttributes attrs;
205 unsigned long attrmask;
207 if (si->prefs.debug_p)
209 "%s: using overlay visual 0x%0x for stderr text layer.\n",
210 blurb(), (int) XVisualIDFromVisual (visual));
212 ssi->stderr_cmap = XCreateColormap(si->dpy,
213 RootWindowOfScreen(ssi->screen),
216 attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel |
217 CWBackingStore | CWSaveUnder);
218 attrs.colormap = ssi->stderr_cmap;
219 attrs.background_pixel = transparent_pixel;
220 attrs.backing_pixel = transparent_pixel;
221 attrs.border_pixel = transparent_pixel;
222 attrs.backing_store = NotUseful;
223 attrs.save_under = False;
225 ssi->stderr_overlay_window =
226 XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
227 WidthOfScreen(ssi->screen), HeightOfScreen(ssi->screen),
228 0, depth, InputOutput, visual, attrmask, &attrs);
229 XMapRaised(si->dpy, ssi->stderr_overlay_window);
235 print_stderr (saver_info *si, char *string)
237 saver_preferences *p = &si->prefs;
240 /* In verbose mode, copy it to stderr as well. */
242 fprintf (real_stderr, "%s", string);
244 for (i = 0; i < si->nscreens; i++)
245 print_stderr_1 (&si->screens[i], string);
250 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
252 saver_info *si = (saver_info *) closure;
253 char *s = stderr_buffer;
256 /* If too much data was printed, then something has gone haywire,
258 char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
259 int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
260 if (strlen (s) > max)
261 strcpy (s + max, trailer);
262 /* Now show the user. */
263 print_stderr (si, s);
266 stderr_tail = stderr_buffer;
267 si->stderr_popup_timer = 0;
272 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
274 saver_info *si = (saver_info *) closure;
278 int read_this_time = 0;
280 if (stderr_tail == 0)
281 stderr_tail = stderr_buffer;
283 left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
288 /* Read as much data from the fd as we can, up to our buffer size. */
291 while ((size = read (*fd, (void *) s, left)) > 0)
295 read_this_time += size;
302 /* The buffer is full; flush the rest of it. */
303 while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
308 stderr_last_read = time ((time_t *) 0);
310 /* Now we have read some data that we would like to put up in a dialog
311 box. But more data may still be coming in - so don't pop up the
312 dialog right now, but instead, start a timer that will pop it up
313 a second from now. Should more data come in in the meantime, we
314 will be called again, and will reset that timer again. So the
315 dialog will only pop up when a second has elapsed with no new data
316 being written to stderr.
318 However, if the buffer is full (meaning lots of data has been written)
319 then we don't reset the timer.
321 if (read_this_time > 0)
323 if (si->stderr_popup_timer)
324 XtRemoveTimeOut (si->stderr_popup_timer);
326 si->stderr_popup_timer =
327 XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn,
333 initialize_stderr (saver_info *si)
335 static Boolean done = False;
338 int new_stdout, new_stderr;
342 Boolean stderr_dialog_p;
347 real_stderr = stderr;
348 real_stdout = stdout;
350 stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
352 if (!stderr_dialog_p)
357 perror ("error creating pipe:");
366 # if defined(O_NONBLOCK)
368 # elif defined(O_NDELAY)
371 ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
374 /* Set both sides of the pipe to nonblocking - this is so that
375 our reads (in stderr_callback) will terminate, and so that
376 out writes (in the client programs) will silently fail when
377 the pipe is full, instead of hosing the program. */
378 if (fcntl (in, F_SETFL, flags) != 0)
383 if (fcntl (out, F_SETFL, flags) != 0)
389 # endif /* !HAVE_FCNTL */
393 FILE *new_stderr_file;
394 FILE *new_stdout_file;
396 new_stderr = dup (stderr_fd);
399 perror ("could not dup() a stderr:");
402 if (! (new_stderr_file = fdopen (new_stderr, "w")))
404 perror ("could not fdopen() the new stderr:");
407 real_stderr = new_stderr_file;
410 if (dup2 (out, stderr_fd) < 0)
412 perror ("could not dup() a new stderr:");
417 new_stdout = dup (stdout_fd);
420 perror ("could not dup() a stdout:");
423 if (! (new_stdout_file = fdopen (new_stdout, "w")))
425 perror ("could not fdopen() the new stdout:");
428 real_stdout = new_stdout_file;
431 if (dup2 (out, stdout_fd) < 0)
433 perror ("could not dup() a new stdout:");
438 XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,