http://ftp.x.org/contrib/applications/xscreensaver-2.23.tar.gz
[xscreensaver] / hacks / screenhack.c
1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
2  *  Jamie Zawinski <jwz@netscape.com>
3  *
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 
10  * implied warranty.
11  *
12  * And remember: X Windows is to graphics hacking as roman numerals are to
13  * the square root of pi.
14  */
15
16 /* This file contains simple code to open a window or draw on the root.
17    The idea being that, when writing a graphics hack, you can just link
18    with this .o to get all of the uninteresting junk out of the way.
19
20    -  create a procedure `screenhack(dpy, window)'
21
22    -  create a variable `char *progclass' which names this program's
23       resource class.
24
25    -  create a variable `char defaults []' for the default resources, and
26       null-terminate it.
27
28    -  create a variable `XrmOptionDescRec options[]' for the command-line,
29       and null-terminate it.
30
31    And that's it...
32  */
33
34 #include <stdio.h>
35 #include <X11/Intrinsic.h>
36 #include <X11/IntrinsicP.h>
37 #include <X11/CoreP.h>
38 #include <X11/Shell.h>
39 #include <X11/StringDefs.h>
40
41 #ifdef __sgi
42 # include <X11/SGIScheme.h>     /* for SgiUseSchemes() */
43 #endif /* __sgi */
44
45 #ifdef HAVE_XMU
46 # ifndef VMS
47 #  include <X11/Xmu/Error.h>
48 # else /* VMS */
49 #  include <Xmu/Error.h>
50 # endif
51 #else
52 # include "xmu.h"
53 #endif
54 #include "screenhack.h"
55 #include "version.h"
56 #include "vroot.h"
57
58 char *progname;
59 XrmDatabase db;
60 Bool mono_p;
61
62 static XrmOptionDescRec default_options [] = {
63   { "-root",    ".root",                XrmoptionNoArg, "True" },
64   { "-window",  ".root",                XrmoptionNoArg, "False" },
65   { "-mono",    ".mono",                XrmoptionNoArg, "True" },
66   { "-install", ".installColormap",     XrmoptionNoArg, "True" },
67   { "-noinstall",".installColormap",    XrmoptionNoArg, "False" },
68   { "-visual",  ".visualID",            XrmoptionSepArg, 0 },
69   { "-window-id", ".windowID",          XrmoptionSepArg, 0 },
70   { 0, 0, 0, 0 }
71 };
72
73 static char *default_defaults[] = {
74   ".root:               false",
75   "*geometry:           600x480", /* this should be .geometry, but nooooo... */
76   "*mono:               false",
77   "*installColormap:    false",
78   "*visualID:           default",
79   "*windowID:           ",
80   0
81 };
82
83 static XrmOptionDescRec *merged_options;
84 static int merged_options_size;
85 static char **merged_defaults;
86
87 static void
88 merge_options (void)
89 {
90   int def_opts_size, opts_size;
91   int def_defaults_size, defaults_size;
92
93   for (def_opts_size = 0; default_options[def_opts_size].option;
94        def_opts_size++)
95     ;
96   for (opts_size = 0; options[opts_size].option; opts_size++)
97     ;
98
99   merged_options_size = def_opts_size + opts_size;
100   merged_options = (XrmOptionDescRec *)
101     malloc ((merged_options_size + 1) * sizeof(*default_options));
102   memcpy (merged_options, default_options,
103           (def_opts_size * sizeof(*default_options)));
104   memcpy (merged_options + def_opts_size, options,
105           ((opts_size + 1) * sizeof(*default_options)));
106
107   for (def_defaults_size = 0; default_defaults[def_defaults_size];
108        def_defaults_size++)
109     ;
110   for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
111     ;
112   merged_defaults = (char **)
113     malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
114   memcpy (merged_defaults, default_defaults,
115           def_defaults_size * sizeof(*defaults));
116   memcpy (merged_defaults + def_defaults_size, defaults,
117           (defaults_size + 1) * sizeof(*defaults));
118
119   /* This totally sucks.  Xt should behave like this by default.
120      If the string in `defaults' looks like ".foo", change that
121      to "Progclass.foo".
122    */
123   {
124     char **s;
125     for (s = merged_defaults; *s; s++)
126       if (**s == '.')
127         {
128           const char *oldr = *s;
129           char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
130           strcpy (newr, progclass);
131           strcat (newr, oldr);
132           *s = newr;
133         }
134   }
135 }
136
137 \f
138 /* Make the X errors print out the name of this program, so we have some
139    clue which one has a bug when they die under the screensaver.
140  */
141
142 static int
143 screenhack_ehandler (Display *dpy, XErrorEvent *error)
144 {
145   fprintf (stderr, "\nX error in %s:\n", progname);
146   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
147     exit (-1);
148   else
149     fprintf (stderr, " (nonfatal.)\n");
150   return 0;
151 }
152
153 static Bool
154 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
155 {
156   return (event->xany.type == MapNotify &&
157           event->xvisibility.window == (Window) window);
158 }
159
160
161 #ifdef USE_GL
162 extern Visual *get_gl_visual (Screen *, const char *, const char *);
163 #endif
164
165 #ifdef XLOCKMORE
166 extern void pre_merge_options (void);
167 #endif
168
169
170
171 int
172 main (int argc, char **argv)
173 {
174   XtAppContext app;
175   Widget toplevel;
176   Display *dpy;
177   Window window;
178   Visual *visual;
179   Colormap cmap;
180   Bool root_p;
181   Window on_window = 0;
182   XEvent event;
183   Boolean dont_clear /*, dont_map */;
184   char version[255];
185
186 #ifdef XLOCKMORE
187   pre_merge_options ();
188 #endif
189   merge_options ();
190
191 #ifdef __sgi
192   /* We have to do this on SGI to prevent the background color from being
193      overridden by the current desktop color scheme (we'd like our backgrounds
194      to be black, thanks.)  This should be the same as setting the
195      "*useSchemes: none" resource, but it's not -- if that resource is
196      present in the `default_defaults' above, it doesn't work, though it
197      does work when passed as an -xrm arg on the command line.  So screw it,
198      turn them off from C instead.
199    */
200   SgiUseSchemes ("none"); 
201 #endif /* __sgi */
202
203   toplevel = XtAppInitialize (&app, progclass, merged_options,
204                               merged_options_size, &argc, argv,
205                               merged_defaults, 0, 0);
206   dpy = XtDisplay (toplevel);
207   db = XtDatabase (dpy);
208   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
209   XSetErrorHandler (screenhack_ehandler);
210
211   {
212     char *v = (char *) strdup(strchr(screensaver_id, ' '));
213     char *s = (char *) strchr(v, ',');
214     *s = 0;
215     sprintf (version, "%s: from the XScreenSaver%s distribution.",
216              progclass, v);
217     free(v);
218   }
219
220   if (argc > 1)
221     {
222       const char *s;
223       int i;
224       int x = 18;
225       int end = 78;
226       Bool help_p = !strcmp(argv[1], "-help");
227       fprintf (stderr, "%s\n", version);
228       for (s = progclass; *s; s++) fprintf(stderr, " ");
229       fprintf (stderr, "  http://people.netscape.com/jwz/xscreensaver/\n\n");
230
231       if (!help_p)
232         fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
233       fprintf (stderr, "Options include: ");
234       for (i = 0; i < merged_options_size; i++)
235         {
236           char *sw = merged_options [i].option;
237           Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
238           int size = strlen (sw) + (argp ? 6 : 0) + 2;
239           if (x + size >= end)
240             {
241               fprintf (stderr, "\n\t\t ");
242               x = 18;
243             }
244           x += size;
245           fprintf (stderr, "%s", sw);
246           if (argp) fprintf (stderr, " <arg>");
247           if (i != merged_options_size - 1) fprintf (stderr, ", ");
248         }
249       fprintf (stderr, ".\n");
250       exit (help_p ? 0 : 1);
251     }
252
253   dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
254 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
255   mono_p = get_boolean_resource ("mono", "Boolean");
256   if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
257     mono_p = True;
258
259   root_p = get_boolean_resource ("root", "Boolean");
260
261   {
262     char *s = get_string_resource ("windowID", "WindowID");
263     if (s && *s)
264       on_window = get_integer_resource ("windowID", "WindowID");
265     if (s) free (s);
266   }
267
268   if (on_window)
269     {
270       XWindowAttributes xgwa;
271       window = (Window) on_window;
272       XtDestroyWidget (toplevel);
273       XGetWindowAttributes (dpy, window, &xgwa);
274       cmap = xgwa.colormap;
275       visual = xgwa.visual;
276     }
277   else if (root_p)
278     {
279       XWindowAttributes xgwa;
280       window = RootWindowOfScreen (XtScreen (toplevel));
281       XtDestroyWidget (toplevel);
282       XGetWindowAttributes (dpy, window, &xgwa);
283       cmap = xgwa.colormap;
284       visual = xgwa.visual;
285     }
286   else
287     {
288       Boolean def_visual_p;
289       Screen *screen = XtScreen (toplevel);
290
291 #ifdef USE_GL
292       visual = get_gl_visual (screen, "visualID", "VisualID");
293 #else
294       visual = get_visual_resource (screen, "visualID", "VisualID", False);
295 #endif
296
297       if (toplevel->core.width <= 0)
298         toplevel->core.width = 600;
299       if (toplevel->core.height <= 0)
300         toplevel->core.height = 480;
301
302       def_visual_p = (visual == DefaultVisualOfScreen (screen));
303
304       if (!def_visual_p)
305         {
306           unsigned int bg, bd;
307           Widget new;
308
309           cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
310                                   visual, AllocNone);
311           bg = get_pixel_resource ("background", "Background", dpy, cmap);
312           bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
313
314           new = XtVaAppCreateShell (progname, progclass,
315                                     topLevelShellWidgetClass, dpy,
316                                     XtNmappedWhenManaged, False,
317                                     XtNvisual, visual,
318                                     XtNdepth, visual_depth (screen, visual),
319                                     XtNwidth, toplevel->core.width,
320                                     XtNheight, toplevel->core.height,
321                                     XtNcolormap, cmap,
322                                     XtNbackground, (Pixel) bg,
323                                     XtNborderColor, (Pixel) bd,
324                                     0);
325           XtDestroyWidget (toplevel);
326           toplevel = new;
327           XtRealizeWidget (toplevel);
328           window = XtWindow (toplevel);
329         }
330       else
331         {
332           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
333           XtRealizeWidget (toplevel);
334           window = XtWindow (toplevel);
335
336           if (get_boolean_resource ("installColormap", "InstallColormap"))
337             {
338               cmap = XCreateColormap (dpy, window,
339                                    DefaultVisualOfScreen (XtScreen (toplevel)),
340                                       AllocNone);
341               XSetWindowColormap (dpy, window, cmap);
342             }
343           else
344             {
345               cmap = DefaultColormap (dpy, DefaultScreen (dpy));
346             }
347         }
348
349 /*
350       if (dont_map)
351         {
352           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
353           XtRealizeWidget (toplevel);
354         }
355       else
356 */
357         {
358           XtPopup (toplevel, XtGrabNone);
359         }
360
361       XtVaSetValues(toplevel, XtNtitle, version, 0);
362     }
363
364   if (!dont_clear)
365     {
366       XSetWindowBackground (dpy, window,
367                             get_pixel_resource ("background", "Background",
368                                                 dpy, cmap));
369       XClearWindow (dpy, window);
370     }
371
372   if (!root_p && !on_window)
373     /* wait for it to be mapped */
374     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
375
376   XSync (dpy, False);
377   srandom ((int) time ((time_t *) 0));
378   screenhack (dpy, window); /* doesn't return */
379   return 0;
380 }