86789e4800a594caa72e63c0cd4dd1060f5ed7fc
[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
120 \f
121 /* Make the X errors print out the name of this program, so we have some
122    clue which one has a bug when they die under the screensaver.
123  */
124
125 static int
126 screenhack_ehandler (Display *dpy, XErrorEvent *error)
127 {
128   fprintf (stderr, "\nX error in %s:\n", progname);
129   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
130     exit (-1);
131   else
132     fprintf (stderr, " (nonfatal.)\n");
133   return 0;
134 }
135
136 static Bool
137 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
138 {
139   return (event->xany.type == MapNotify &&
140           event->xvisibility.window == (Window) window);
141 }
142
143
144 #ifdef USE_GL
145 extern Visual *get_gl_visual (Screen *, const char *, const char *);
146 #endif
147
148 #ifdef XLOCKMORE
149 extern void pre_merge_options (void);
150 #endif
151
152
153
154 int
155 main (int argc, char **argv)
156 {
157   XtAppContext app;
158   Widget toplevel;
159   Display *dpy;
160   Window window;
161   Visual *visual;
162   Colormap cmap;
163   Bool root_p;
164   Window on_window = 0;
165   XEvent event;
166   Boolean dont_clear /*, dont_map */;
167   char version[255];
168
169 #ifdef XLOCKMORE
170   pre_merge_options ();
171 #endif
172   merge_options ();
173
174 #ifdef __sgi
175   /* We have to do this on SGI to prevent the background color from being
176      overridden by the current desktop color scheme (we'd like our backgrounds
177      to be black, thanks.)  This should be the same as setting the
178      "*useSchemes: none" resource, but it's not -- if that resource is
179      present in the `default_defaults' above, it doesn't work, though it
180      does work when passed as an -xrm arg on the command line.  So screw it,
181      turn them off from C instead.
182    */
183   SgiUseSchemes ("none"); 
184 #endif /* __sgi */
185
186   toplevel = XtAppInitialize (&app, progclass, merged_options,
187                               merged_options_size, &argc, argv,
188                               merged_defaults, 0, 0);
189   dpy = XtDisplay (toplevel);
190   db = XtDatabase (dpy);
191   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
192   XSetErrorHandler (screenhack_ehandler);
193
194   {
195     char *v = (char *) strdup(strchr(screensaver_id, ' '));
196     char *s = (char *) strchr(v, ',');
197     *s = 0;
198     sprintf (version, "%s: from the XScreenSaver%s distribution.",
199              progclass, v);
200     free(v);
201   }
202
203   if (argc > 1)
204     {
205       const char *s;
206       int i;
207       int x = 18;
208       int end = 78;
209       Bool help_p = !strcmp(argv[1], "-help");
210       fprintf (stderr, "%s\n", version);
211       for (s = progclass; *s; s++) fprintf(stderr, " ");
212       fprintf (stderr, "  http://people.netscape.com/jwz/xscreensaver/\n\n");
213
214       if (!help_p)
215         fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
216       fprintf (stderr, "Options include: ");
217       for (i = 0; i < merged_options_size; i++)
218         {
219           char *sw = merged_options [i].option;
220           Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
221           int size = strlen (sw) + (argp ? 6 : 0) + 2;
222           if (x + size >= end)
223             {
224               fprintf (stderr, "\n\t\t ");
225               x = 18;
226             }
227           x += size;
228           fprintf (stderr, "%s", sw);
229           if (argp) fprintf (stderr, " <arg>");
230           if (i != merged_options_size - 1) fprintf (stderr, ", ");
231         }
232       fprintf (stderr, ".\n");
233       exit (help_p ? 0 : 1);
234     }
235
236   dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
237 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
238   mono_p = get_boolean_resource ("mono", "Boolean");
239   if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
240     mono_p = True;
241
242   root_p = get_boolean_resource ("root", "Boolean");
243
244   {
245     char *s = get_string_resource ("windowID", "WindowID");
246     if (s && *s)
247       on_window = get_integer_resource ("windowID", "WindowID");
248     if (s) free (s);
249   }
250
251   if (on_window)
252     {
253       XWindowAttributes xgwa;
254       window = (Window) on_window;
255       XtDestroyWidget (toplevel);
256       XGetWindowAttributes (dpy, window, &xgwa);
257       cmap = xgwa.colormap;
258       visual = xgwa.visual;
259     }
260   else if (root_p)
261     {
262       XWindowAttributes xgwa;
263       window = RootWindowOfScreen (XtScreen (toplevel));
264       XtDestroyWidget (toplevel);
265       XGetWindowAttributes (dpy, window, &xgwa);
266       cmap = xgwa.colormap;
267       visual = xgwa.visual;
268     }
269   else
270     {
271       Boolean def_visual_p;
272       Screen *screen = XtScreen (toplevel);
273
274 #ifdef USE_GL
275       visual = get_gl_visual (screen, "visualID", "VisualID");
276 #else
277       visual = get_visual_resource (screen, "visualID", "VisualID", False);
278 #endif
279
280       if (toplevel->core.width <= 0)
281         toplevel->core.width = 600;
282       if (toplevel->core.height <= 0)
283         toplevel->core.height = 480;
284
285       def_visual_p = (visual == DefaultVisualOfScreen (screen));
286
287       if (!def_visual_p)
288         {
289           unsigned int bg, bd;
290           Widget new;
291
292           cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
293                                   visual, AllocNone);
294           bg = get_pixel_resource ("background", "Background", dpy, cmap);
295           bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
296
297           new = XtVaAppCreateShell (progname, progclass,
298                                     topLevelShellWidgetClass, dpy,
299                                     XtNmappedWhenManaged, False,
300                                     XtNvisual, visual,
301                                     XtNdepth, visual_depth (screen, visual),
302                                     XtNwidth, toplevel->core.width,
303                                     XtNheight, toplevel->core.height,
304                                     XtNcolormap, cmap,
305                                     XtNbackground, (Pixel) bg,
306                                     XtNborderColor, (Pixel) bd,
307                                     0);
308           XtDestroyWidget (toplevel);
309           toplevel = new;
310           XtRealizeWidget (toplevel);
311           window = XtWindow (toplevel);
312         }
313       else
314         {
315           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
316           XtRealizeWidget (toplevel);
317           window = XtWindow (toplevel);
318
319           if (get_boolean_resource ("installColormap", "InstallColormap"))
320             {
321               cmap = XCreateColormap (dpy, window,
322                                    DefaultVisualOfScreen (XtScreen (toplevel)),
323                                       AllocNone);
324               XSetWindowColormap (dpy, window, cmap);
325             }
326           else
327             {
328               cmap = DefaultColormap (dpy, DefaultScreen (dpy));
329             }
330         }
331
332 /*
333       if (dont_map)
334         {
335           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
336           XtRealizeWidget (toplevel);
337         }
338       else
339 */
340         {
341           XtPopup (toplevel, XtGrabNone);
342         }
343
344       XtVaSetValues(toplevel, XtNtitle, version, 0);
345     }
346
347   if (!dont_clear)
348     {
349       XSetWindowBackground (dpy, window,
350                             get_pixel_resource ("background", "Background",
351                                                 dpy, cmap));
352       XClearWindow (dpy, window);
353     }
354
355   if (!root_p && !on_window)
356     /* wait for it to be mapped */
357     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
358
359   XSync (dpy, False);
360   srandom ((int) time ((time_t *) 0));
361   screenhack (dpy, window); /* doesn't return */
362   return 0;
363 }