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 XWindowAttributes xgwa;
167 XGetWindowAttributes (dpy, window, &xgwa);
169 XCopyArea (dpy, window, window, ssi->stderr_gc,
170 0, v_border + offset,
172 (xgwa.height - v_border - v_border - offset),
174 XClearArea (dpy, window,
175 0, xgwa.height - v_border - offset,
176 xgwa.width, offset, False);
177 ssi->stderr_text_y -= offset;
184 int direction, ascent, descent;
186 XDrawImageString (dpy, window, ssi->stderr_gc,
187 ssi->stderr_text_x + h_border,
188 ssi->stderr_text_y + v_border
189 + ssi->stderr_font->ascent,
191 XTextExtents (ssi->stderr_font, tail, tail - head,
192 &direction, &ascent, &descent, &overall);
193 ssi->stderr_text_x += overall.width;
198 make_stderr_overlay_window (saver_screen_info *ssi)
200 saver_info *si = ssi->global;
201 unsigned long transparent_pixel = 0;
202 Visual *visual = get_overlay_visual (ssi->screen, &transparent_pixel);
205 int depth = visual_depth (ssi->screen, visual);
206 XSetWindowAttributes attrs;
207 XWindowAttributes xgwa;
208 unsigned long attrmask;
209 XGetWindowAttributes (si->dpy, ssi->screensaver_window, &xgwa);
211 if (si->prefs.debug_p)
213 "%s: using overlay visual 0x%0x for stderr text layer.\n",
214 blurb(), (int) XVisualIDFromVisual (visual));
216 ssi->stderr_cmap = XCreateColormap(si->dpy,
217 RootWindowOfScreen(ssi->screen),
220 attrmask = (CWColormap | CWBackPixel | CWBackingPixel | CWBorderPixel |
221 CWBackingStore | CWSaveUnder);
222 attrs.colormap = ssi->stderr_cmap;
223 attrs.background_pixel = transparent_pixel;
224 attrs.backing_pixel = transparent_pixel;
225 attrs.border_pixel = transparent_pixel;
226 attrs.backing_store = NotUseful;
227 attrs.save_under = False;
229 ssi->stderr_overlay_window =
230 XCreateWindow(si->dpy, ssi->screensaver_window, 0, 0,
231 xgwa.width, xgwa.height,
232 0, depth, InputOutput, visual, attrmask, &attrs);
233 XMapRaised(si->dpy, ssi->stderr_overlay_window);
239 print_stderr (saver_info *si, char *string)
241 saver_preferences *p = &si->prefs;
244 /* In verbose mode, copy it to stderr as well. */
246 fprintf (real_stderr, "%s", string);
248 for (i = 0; i < si->nscreens; i++)
249 print_stderr_1 (&si->screens[i], string);
254 stderr_popup_timer_fn (XtPointer closure, XtIntervalId *id)
256 saver_info *si = (saver_info *) closure;
257 char *s = stderr_buffer;
260 /* If too much data was printed, then something has gone haywire,
262 char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
263 int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
264 if (strlen (s) > max)
265 strcpy (s + max, trailer);
266 /* Now show the user. */
267 print_stderr (si, s);
270 stderr_tail = stderr_buffer;
271 si->stderr_popup_timer = 0;
276 stderr_callback (XtPointer closure, int *fd, XtIntervalId *id)
278 saver_info *si = (saver_info *) closure;
282 int read_this_time = 0;
284 if (stderr_tail == 0)
285 stderr_tail = stderr_buffer;
287 left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
292 /* Read as much data from the fd as we can, up to our buffer size. */
295 while ((size = read (*fd, (void *) s, left)) > 0)
299 read_this_time += size;
306 /* The buffer is full; flush the rest of it. */
307 while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
312 stderr_last_read = time ((time_t *) 0);
314 /* Now we have read some data that we would like to put up in a dialog
315 box. But more data may still be coming in - so don't pop up the
316 dialog right now, but instead, start a timer that will pop it up
317 a second from now. Should more data come in in the meantime, we
318 will be called again, and will reset that timer again. So the
319 dialog will only pop up when a second has elapsed with no new data
320 being written to stderr.
322 However, if the buffer is full (meaning lots of data has been written)
323 then we don't reset the timer.
325 if (read_this_time > 0)
327 if (si->stderr_popup_timer)
328 XtRemoveTimeOut (si->stderr_popup_timer);
330 si->stderr_popup_timer =
331 XtAppAddTimeOut (si->app, 1 * 1000, stderr_popup_timer_fn,
337 initialize_stderr (saver_info *si)
339 static Boolean done = False;
342 int new_stdout, new_stderr;
346 Boolean stderr_dialog_p;
351 real_stderr = stderr;
352 real_stdout = stdout;
354 stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
356 if (!stderr_dialog_p)
361 perror ("error creating pipe:");
370 # if defined(O_NONBLOCK)
372 # elif defined(O_NDELAY)
375 ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
378 /* Set both sides of the pipe to nonblocking - this is so that
379 our reads (in stderr_callback) will terminate, and so that
380 out writes (in the client programs) will silently fail when
381 the pipe is full, instead of hosing the program. */
382 if (fcntl (in, F_SETFL, flags) != 0)
387 if (fcntl (out, F_SETFL, flags) != 0)
393 # endif /* !HAVE_FCNTL */
397 FILE *new_stderr_file;
398 FILE *new_stdout_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:");
421 new_stdout = dup (stdout_fd);
424 perror ("could not dup() a stdout:");
427 if (! (new_stdout_file = fdopen (new_stdout, "w")))
429 perror ("could not fdopen() the new stdout:");
432 real_stdout = new_stdout_file;
435 if (dup2 (out, stdout_fd) < 0)
437 perror ("could not dup() a new stdout:");
442 XtAppAddInput (si->app, in, (XtPointer) XtInputReadMask, stderr_callback,