ace18c57e88e58660854bed43c8c641a44e1504b
[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 #ifdef HAVE_XMU
41 # ifndef VMS
42 #  include <X11/Xmu/Error.h>
43 # else /* VMS */
44 #  include <Xmu/Error.h>
45 # endif
46 #else
47 # include "xmu.h"
48 #endif
49 #include "screenhack.h"
50 #include "version.h"
51 #include "vroot.h"
52
53 char *progname;
54 XrmDatabase db;
55 Bool mono_p;
56
57 static XrmOptionDescRec default_options [] = {
58   { "-root",    ".root",                XrmoptionNoArg, "True" },
59   { "-window",  ".root",                XrmoptionNoArg, "False" },
60   { "-mono",    ".mono",                XrmoptionNoArg, "True" },
61   { "-install", ".installColormap",     XrmoptionNoArg, "True" },
62   { "-noinstall",".installColormap",    XrmoptionNoArg, "False" },
63   { "-visual",  ".visualID",            XrmoptionSepArg, 0 },
64   { "-window-id", ".windowID",          XrmoptionSepArg, 0 },
65   { 0, 0, 0, 0 }
66 };
67
68 static char *default_defaults[] = {
69   "*root:               false",
70   "*geometry:           600x480", /* this should be .geometry, but nooooo... */
71   "*mono:               false",
72   "*installColormap:    false",
73   "*visualID:           default",
74   "*windowID:           ",
75   0
76 };
77
78 static XrmOptionDescRec *merged_options;
79 static int merged_options_size;
80 static char **merged_defaults;
81
82 static void
83 merge_options (void)
84 {
85   int def_opts_size, opts_size;
86   int def_defaults_size, defaults_size;
87
88   for (def_opts_size = 0; default_options[def_opts_size].option;
89        def_opts_size++)
90     ;
91   for (opts_size = 0; options[opts_size].option; opts_size++)
92     ;
93
94   merged_options_size = def_opts_size + opts_size;
95   merged_options = (XrmOptionDescRec *)
96     malloc ((merged_options_size + 1) * sizeof(*default_options));
97   memcpy (merged_options, default_options,
98           (def_opts_size * sizeof(*default_options)));
99   memcpy (merged_options + def_opts_size, options,
100           ((opts_size + 1) * sizeof(*default_options)));
101
102   for (def_defaults_size = 0; default_defaults[def_defaults_size];
103        def_defaults_size++)
104     ;
105   for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
106     ;
107   merged_defaults = (char **)
108     malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
109   memcpy (merged_defaults, default_defaults,
110           def_defaults_size * sizeof(*defaults));
111   memcpy (merged_defaults + def_defaults_size, defaults,
112           (defaults_size + 1) * sizeof(*defaults));
113 }
114
115 \f
116 /* Make the X errors print out the name of this program, so we have some
117    clue which one has a bug when they die under the screensaver.
118  */
119
120 static int
121 screenhack_ehandler (Display *dpy, XErrorEvent *error)
122 {
123   fprintf (stderr, "\nX error in %s:\n", progname);
124   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
125     exit (-1);
126   else
127     fprintf (stderr, " (nonfatal.)\n");
128   return 0;
129 }
130
131 static Bool
132 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
133 {
134   return (event->xany.type == MapNotify &&
135           event->xvisibility.window == (Window) window);
136 }
137
138
139 #ifdef USE_GL
140 extern Visual *get_gl_visual (Screen *, const char *, const char *);
141 #endif
142
143 #ifdef XLOCKMORE
144 extern void pre_merge_options (void);
145 #endif
146
147
148
149 int
150 main (int argc, char **argv)
151 {
152   XtAppContext app;
153   Widget toplevel;
154   Display *dpy;
155   Window window;
156   Visual *visual;
157   Colormap cmap;
158   Bool root_p;
159   Window on_window = 0;
160   XEvent event;
161   Boolean dont_clear /*, dont_map */;
162   char version[255];
163
164 #ifdef XLOCKMORE
165   pre_merge_options ();
166 #endif
167   merge_options ();
168   toplevel = XtAppInitialize (&app, progclass, merged_options,
169                               merged_options_size, &argc, argv,
170                               merged_defaults, 0, 0);
171   dpy = XtDisplay (toplevel);
172   db = XtDatabase (dpy);
173   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
174   XSetErrorHandler (screenhack_ehandler);
175
176   {
177     char *v = (char *) strdup(strchr(screensaver_id, ' '));
178     char *s = (char *) strchr(v, ',');
179     *s = 0;
180     sprintf (version, "%s: from the XScreenSaver%s distribution.",
181              progclass, v);
182     free(v);
183   }
184
185   if (argc > 1)
186     {
187       const char *s;
188       int i;
189       int x = 18;
190       int end = 78;
191       Bool help_p = !strcmp(argv[1], "-help");
192       fprintf (stderr, "%s\n", version);
193       for (s = progclass; *s; s++) fprintf(stderr, " ");
194       fprintf (stderr, "  http://people.netscape.com/jwz/xscreensaver/\n\n");
195
196       if (!help_p)
197         fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
198       fprintf (stderr, "Options include: ");
199       for (i = 0; i < merged_options_size; i++)
200         {
201           char *sw = merged_options [i].option;
202           Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
203           int size = strlen (sw) + (argp ? 6 : 0) + 2;
204           if (x + size >= end)
205             {
206               fprintf (stderr, "\n\t\t ");
207               x = 18;
208             }
209           x += size;
210           fprintf (stderr, "%s", sw);
211           if (argp) fprintf (stderr, " <arg>");
212           if (i != merged_options_size - 1) fprintf (stderr, ", ");
213         }
214       fprintf (stderr, ".\n");
215       exit (help_p ? 0 : 1);
216     }
217
218   dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
219 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
220   mono_p = get_boolean_resource ("mono", "Boolean");
221   if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
222     mono_p = True;
223
224   root_p = get_boolean_resource ("root", "Boolean");
225
226   {
227     char *s = get_string_resource ("windowID", "WindowID");
228     if (s && *s)
229       on_window = get_integer_resource ("windowID", "WindowID");
230     if (s) free (s);
231   }
232
233   if (on_window)
234     {
235       XWindowAttributes xgwa;
236       window = (Window) on_window;
237       XtDestroyWidget (toplevel);
238       XGetWindowAttributes (dpy, window, &xgwa);
239       cmap = xgwa.colormap;
240       visual = xgwa.visual;
241     }
242   else if (root_p)
243     {
244       XWindowAttributes xgwa;
245       window = RootWindowOfScreen (XtScreen (toplevel));
246       XtDestroyWidget (toplevel);
247       XGetWindowAttributes (dpy, window, &xgwa);
248       cmap = xgwa.colormap;
249       visual = xgwa.visual;
250     }
251   else
252     {
253       Boolean def_visual_p;
254       Screen *screen = XtScreen (toplevel);
255
256 #ifdef USE_GL
257       visual = get_gl_visual (screen, "visualID", "VisualID");
258 #else
259       visual = get_visual_resource (screen, "visualID", "VisualID", False);
260 #endif
261
262       if (toplevel->core.width <= 0)
263         toplevel->core.width = 600;
264       if (toplevel->core.height <= 0)
265         toplevel->core.height = 480;
266
267       def_visual_p = (visual == DefaultVisualOfScreen (screen));
268
269       if (!def_visual_p)
270         {
271           unsigned int bg, bd;
272           Widget new;
273
274           cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
275                                   visual, AllocNone);
276           bg = get_pixel_resource ("background", "Background", dpy, cmap);
277           bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
278
279           new = XtVaAppCreateShell (progname, progclass,
280                                     topLevelShellWidgetClass, dpy,
281                                     XtNmappedWhenManaged, False,
282                                     XtNvisual, visual,
283                                     XtNdepth, visual_depth (screen, visual),
284                                     XtNwidth, toplevel->core.width,
285                                     XtNheight, toplevel->core.height,
286                                     XtNcolormap, cmap,
287                                     XtNbackground, (Pixel) bg,
288                                     XtNborderColor, (Pixel) bd,
289                                     0);
290           XtDestroyWidget (toplevel);
291           toplevel = new;
292           XtRealizeWidget (toplevel);
293           window = XtWindow (toplevel);
294         }
295       else
296         {
297           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
298           XtRealizeWidget (toplevel);
299           window = XtWindow (toplevel);
300
301           if (get_boolean_resource ("installColormap", "InstallColormap"))
302             {
303               cmap = XCreateColormap (dpy, window,
304                                    DefaultVisualOfScreen (XtScreen (toplevel)),
305                                       AllocNone);
306               XSetWindowColormap (dpy, window, cmap);
307             }
308           else
309             {
310               cmap = DefaultColormap (dpy, DefaultScreen (dpy));
311             }
312         }
313
314 /*
315       if (dont_map)
316         {
317           XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
318           XtRealizeWidget (toplevel);
319         }
320       else
321 */
322         {
323           XtPopup (toplevel, XtGrabNone);
324         }
325
326       XtVaSetValues(toplevel, XtNtitle, version, 0);
327     }
328
329   if (!dont_clear)
330     {
331       XSetWindowBackground (dpy, window,
332                             get_pixel_resource ("background", "Background",
333                                                 dpy, cmap));
334       XClearWindow (dpy, window);
335     }
336
337   if (!root_p && !on_window)
338     /* wait for it to be mapped */
339     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
340
341   XSync (dpy, False);
342   srandom ((int) time ((time_t *) 0));
343   screenhack (dpy, window); /* doesn't return */
344   return 0;
345 }