http://www.ibiblio.org/pub/historic-linux/ftp-archives/sunsite.unc.edu/Sep-29-1996...
[xscreensaver] / driver / stderr.c
1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@netscape.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or 
9  * implied warranty.
10  */
11
12 /* stderr hackery - Why Unix Sucks, reason number 32767.
13  */
14
15 #if __STDC__
16 #include <stdlib.h>
17 #include <unistd.h>
18 #endif
19
20 #include <stdio.h>
21 #include <fcntl.h>
22 #include <time.h>
23
24 #include <X11/Intrinsic.h>
25
26 #include "xscreensaver.h"
27
28 extern XtAppContext app;
29 extern Colormap cmap;
30 extern Window screensaver_window;
31
32 extern char *get_string_resource P((char *, char *));
33 extern Bool get_boolean_resource P((char *, char *));
34 extern unsigned int get_pixel_resource P((char *, char *,
35                                           Display *, Colormap));
36
37 static char stderr_buffer [1024];
38 static char *stderr_tail = 0;
39 static time_t stderr_last_read = 0;
40 static XtIntervalId stderr_popup_timer = 0;
41
42 FILE *real_stderr = 0;
43 FILE *real_stdout = 0;
44
45 static int text_x = 0;
46 static int text_y = 0;
47
48 void
49 reset_stderr ()
50 {
51   text_x = text_y = 0;
52 }
53
54 static void
55 print_stderr (string)
56      char *string;
57 {
58   int h_border = 20;
59   int v_border = 20;
60   static int line_height;
61   static XFontStruct *font = 0;
62   static GC gc = 0;
63   char *head = string;
64   char *tail;
65
66   /* In verbose mode, copy it to stderr as well. */
67   if (verbose_p)
68     fprintf (real_stderr, "%s", string);
69
70   if (! gc)
71     {
72       XGCValues gcv;
73       Pixel fg, bg;
74       char *font_name = get_string_resource ("font", "Font");
75       if (!font_name) font_name = "fixed";
76       font = XLoadQueryFont (dpy, font_name);
77       if (! font) font = XLoadQueryFont (dpy, "fixed");
78       line_height = font->ascent + font->descent;
79       fg = get_pixel_resource ("textForeground", "Foreground", dpy, cmap);
80       bg = get_pixel_resource ("textBackground", "Background", dpy, cmap);
81       gcv.font = font->fid;
82       gcv.foreground = fg;
83       gcv.background = bg;
84       gc = XCreateGC (dpy, screensaver_window,
85                       (GCFont | GCForeground | GCBackground), &gcv);
86     }
87
88   for (tail = string; *tail; tail++)
89     {
90       if (*tail == '\n' || *tail == '\r')
91         {
92           int maxy = HeightOfScreen (screen) - v_border - v_border;
93           if (tail != head)
94             XDrawImageString (dpy, screensaver_window, gc,
95                               text_x + h_border,
96                               text_y + v_border + font->ascent,
97                               head, tail - head);
98           text_x = 0;
99           text_y += line_height;
100           head = tail + 1;
101           if (*tail == '\r' && *head == '\n')
102             head++, tail++;
103
104           if (text_y > maxy - line_height)
105             {
106 #if 0
107               text_y = 0;
108 #else
109               int offset = line_height * 5;
110               XCopyArea (dpy, screensaver_window, screensaver_window, gc,
111                          0, v_border + offset,
112                          WidthOfScreen (screen),
113                          (HeightOfScreen (screen) - v_border - v_border
114                           - offset),
115                          0, v_border);
116               XClearArea (dpy, screensaver_window,
117                           0, HeightOfScreen (screen) - v_border - offset,
118                           WidthOfScreen (screen), offset, False);
119               text_y -= offset;
120 #endif
121             }
122         }
123     }
124   if (tail != head)
125     {
126       int direction, ascent, descent;
127       XCharStruct overall;
128       XDrawImageString (dpy, screensaver_window, gc,
129                         text_x + h_border, text_y + v_border + font->ascent,
130                         head, tail - head);
131       XTextExtents (font, tail, tail - head,
132                     &direction, &ascent, &descent, &overall);
133       text_x += overall.width;
134     }
135 }
136
137
138 static void
139 stderr_popup_timer_fn (closure, id)
140      XtPointer closure;
141      XtIntervalId *id;
142 {
143   char *s = stderr_buffer;
144   if (*s)
145     {
146       /* If too much data was printed, then something has gone haywire,
147          so truncate it. */
148       char *trailer = "\n\n<< stderr diagnostics have been truncated >>\n\n";
149       int max = sizeof (stderr_buffer) - strlen (trailer) - 5;
150       if (strlen (s) > max)
151         strcpy (s + max, trailer);
152       /* Now show the user. */
153       print_stderr (s);
154     }
155
156   stderr_tail = stderr_buffer;
157   stderr_popup_timer = 0;
158 }
159
160
161 static void
162 stderr_callback (closure, fd, id)
163      XtPointer closure;
164      int *fd;
165      XtIntervalId *id;
166 {
167   char *s;
168   int left;
169   int size;
170   int read_this_time = 0;
171
172   if (stderr_tail == 0)
173     stderr_tail = stderr_buffer;
174
175   left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
176
177   s = stderr_tail;
178   *s = 0;
179
180   /* Read as much data from the fd as we can, up to our buffer size. */
181   if (left > 0)
182     {
183       while ((size = read (*fd, (void *) s, left)) > 0)
184         {
185           left -= size;
186           s += size;
187           read_this_time += size;
188         }
189       *s = 0;
190     }
191   else
192     {
193       char buf2 [1024];
194       /* The buffer is full; flush the rest of it. */
195       while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
196         ;
197     }
198
199   stderr_tail = s;
200   stderr_last_read = time ((time_t *) 0);
201
202   /* Now we have read some data that we would like to put up in a dialog
203      box.  But more data may still be coming in - so don't pop up the
204      dialog right now, but instead, start a timer that will pop it up
205      a second from now.  Should more data come in in the meantime, we
206      will be called again, and will reset that timer again.  So the
207      dialog will only pop up when a second has elapsed with no new data
208      being written to stderr.
209
210      However, if the buffer is full (meaning lots of data has been written)
211      then we don't reset the timer.
212    */
213   if (read_this_time > 0)
214     {
215       if (stderr_popup_timer)
216         XtRemoveTimeOut (stderr_popup_timer);
217
218       stderr_popup_timer =
219         XtAppAddTimeOut (app, 1 * 1000, stderr_popup_timer_fn, 0);
220     }
221 }
222
223 void
224 initialize_stderr ()
225 {
226   static Boolean done = False;
227   int fds [2];
228   int in, out;
229   int new_stdout, new_stderr;
230   int stdout_fd = 1;
231   int stderr_fd = 2;
232   int flags;
233   Boolean stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
234   Boolean stdout_dialog_p = get_boolean_resource ("captureStdout", "Boolean");
235
236   real_stderr = stderr;
237   real_stdout = stdout;
238
239   if (!stderr_dialog_p && !stdout_dialog_p)
240     return;
241
242   if (done) return;
243   done = True;
244
245   if (pipe (fds))
246     {
247       perror ("error creating pipe:");
248       return;
249     }
250
251   in = fds [0];
252   out = fds [1];
253
254 # ifdef O_NONBLOCK
255   flags = O_NONBLOCK;
256 # else
257 #  ifdef O_NDELAY
258   flags = O_NDELAY;
259 #  else
260   ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
261 #  endif
262 # endif
263
264     /* Set both sides of the pipe to nonblocking - this is so that
265        our reads (in stderr_callback) will terminate, and so that
266        out writes (in the client programs) will silently fail when
267        the pipe is full, instead of hosing the program. */
268   if (fcntl (in, F_SETFL, flags) != 0)
269     {
270       perror ("fcntl:");
271       return;
272     }
273   if (fcntl (out, F_SETFL, flags) != 0)
274     {
275       perror ("fcntl:");
276       return;
277     }
278
279   if (stderr_dialog_p)
280     {
281       FILE *new_stderr_file;
282       new_stderr = dup (stderr_fd);
283       if (new_stderr < 0)
284         {
285           perror ("could not dup() a stderr:");
286           return;
287         }
288       if (! (new_stderr_file = fdopen (new_stderr, "w")))
289         {
290           perror ("could not fdopen() the new stderr:");
291           return;
292         }
293       real_stderr = new_stderr_file;
294
295       close (stderr_fd);
296       if (dup2 (out, stderr_fd) < 0)
297         {
298           perror ("could not dup() a new stderr:");
299           return;
300         }
301     }
302
303   if (stdout_dialog_p)
304     {
305       FILE *new_stdout_file;
306       new_stdout = dup (stdout_fd);
307       if (new_stdout < 0)
308         {
309           perror ("could not dup() a stdout:");
310           return;
311         }
312       if (! (new_stdout_file = fdopen (new_stdout, "w")))
313         {
314           perror ("could not fdopen() the new stdout:");
315           return;
316         }
317       real_stdout = new_stdout_file;
318
319       close (stdout_fd);
320       if (dup2 (out, stdout_fd) < 0)
321         {
322           perror ("could not dup() a new stdout:");
323           return;
324         }
325     }
326
327   XtAppAddInput (app, in, (XtPointer) XtInputReadMask, stderr_callback, 0);
328 }