ftp://ftp.uni-heidelberg.de/pub/X11/contrib/applications/xscreensaver-1.25.tar.Z
[xscreensaver] / driver / stderr.c
1 /* xscreensaver, Copyright (c) 1991-1995 Jamie Zawinski <jwz@mcom.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 (XtPointer closure, int *fd, XtIntervalId *id)
163 {
164   char *s;
165   int left;
166   int size;
167   int read_this_time = 0;
168
169   if (stderr_tail == 0)
170     stderr_tail = stderr_buffer;
171
172   left = ((sizeof (stderr_buffer) - 2) - (stderr_tail - stderr_buffer));
173
174   s = stderr_tail;
175   *s = 0;
176
177   /* Read as much data from the fd as we can, up to our buffer size. */
178   if (left > 0)
179     {
180       while ((size = read (*fd, (void *) s, left)) > 0)
181         {
182           left -= size;
183           s += size;
184           read_this_time += size;
185         }
186       *s = 0;
187     }
188   else
189     {
190       char buf2 [1024];
191       /* The buffer is full; flush the rest of it. */
192       while (read (*fd, (void *) buf2, sizeof (buf2)) > 0)
193         ;
194     }
195
196   stderr_tail = s;
197   stderr_last_read = time ((time_t *) 0);
198
199   /* Now we have read some data that we would like to put up in a dialog
200      box.  But more data may still be coming in - so don't pop up the
201      dialog right now, but instead, start a timer that will pop it up
202      a second from now.  Should more data come in in the meantime, we
203      will be called again, and will reset that timer again.  So the
204      dialog will only pop up when a second has elapsed with no new data
205      being written to stderr.
206
207      However, if the buffer is full (meaning lots of data has been written)
208      then we don't reset the timer.
209    */
210   if (read_this_time > 0)
211     {
212       if (stderr_popup_timer)
213         XtRemoveTimeOut (stderr_popup_timer);
214
215       stderr_popup_timer =
216         XtAppAddTimeOut (app, 1 * 1000, stderr_popup_timer_fn, 0);
217     }
218 }
219
220 void
221 initialize_stderr ()
222 {
223   static Boolean done = False;
224   int fds [2];
225   int in, out;
226   int new_stdout, new_stderr;
227   int stdout_fd = 1;
228   int stderr_fd = 2;
229   int flags;
230   Boolean stderr_dialog_p = get_boolean_resource ("captureStderr", "Boolean");
231   Boolean stdout_dialog_p = get_boolean_resource ("captureStdout", "Boolean");
232
233   real_stderr = stderr;
234   real_stdout = stdout;
235
236   if (!stderr_dialog_p && !stdout_dialog_p)
237     return;
238
239   if (done) return;
240   done = True;
241
242   if (pipe (fds))
243     {
244       perror ("error creating pipe:");
245       return;
246     }
247
248   in = fds [0];
249   out = fds [1];
250
251 # ifdef O_NONBLOCK
252   flags = O_NONBLOCK;
253 # else
254 #  ifdef O_NDELAY
255   flags = O_NDELAY;
256 #  else
257   ERROR!! neither O_NONBLOCK nor O_NDELAY are defined.
258 #  endif
259 # endif
260
261     /* Set both sides of the pipe to nonblocking - this is so that
262        our reads (in stderr_callback) will terminate, and so that
263        out writes (in the client programs) will silently fail when
264        the pipe is full, instead of hosing the program. */
265   if (fcntl (in, F_SETFL, flags) != 0)
266     {
267       perror ("fcntl:");
268       return;
269     }
270   if (fcntl (out, F_SETFL, flags) != 0)
271     {
272       perror ("fcntl:");
273       return;
274     }
275
276   if (stderr_dialog_p)
277     {
278       FILE *new_stderr_file;
279       new_stderr = dup (stderr_fd);
280       if (new_stderr < 0)
281         {
282           perror ("could not dup() a stderr:");
283           return;
284         }
285       if (! (new_stderr_file = fdopen (new_stderr, "w")))
286         {
287           perror ("could not fdopen() the new stderr:");
288           return;
289         }
290       real_stderr = new_stderr_file;
291
292       close (stderr_fd);
293       if (dup2 (out, stderr_fd) < 0)
294         {
295           perror ("could not dup() a new stderr:");
296           return;
297         }
298     }
299
300   if (stdout_dialog_p)
301     {
302       FILE *new_stdout_file;
303       new_stdout = dup (stdout_fd);
304       if (new_stdout < 0)
305         {
306           perror ("could not dup() a stdout:");
307           return;
308         }
309       if (! (new_stdout_file = fdopen (new_stdout, "w")))
310         {
311           perror ("could not fdopen() the new stdout:");
312           return;
313         }
314       real_stdout = new_stdout_file;
315
316       close (stdout_fd);
317       if (dup2 (out, stdout_fd) < 0)
318         {
319           perror ("could not dup() a new stdout:");
320           return;
321         }
322     }
323
324   XtAppAddInput (app, in, (XtPointer) XtInputReadMask, stderr_callback, 0);
325 }