don't work
[xscreensaver] / xscreensaver / hacks / screenhack.c
1 /* xscreensaver, Copyright (c) 1992-2010 Jamie Zawinski <jwz@jwz.org>
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  * And remember: X Windows is to graphics hacking as roman numerals are to
12  * the square root of pi.
13  */
14
15 /* This file contains simple code to open a window or draw on the root.
16    The idea being that, when writing a graphics hack, you can just link
17    with this .o to get all of the uninteresting junk out of the way.
18
19    Create a few static global procedures and variables:
20
21       static void *YOURNAME_init (Display *, Window);
22
23           Return an opaque structure representing your drawing state.
24
25       static unsigned long YOURNAME_draw (Display *, Window, void *closure);
26
27           Draw one frame.
28           The `closure' arg is your drawing state, that you created in `init'.
29           Return the number of microseconds to wait until the next frame.
30
31           This should return in some small fraction of a second. 
32           Do not call `usleep' or loop excessively.  For long loops, use a
33           finite state machine.
34
35       static void YOURNAME_reshape (Display *, Window, void *closure,
36                                     unsigned int width, unsigned int height);
37
38           Called when the size of the window changes with the new size.
39
40       static Bool YOURNAME_event (Display *, Window, void *closure,
41                                   XEvent *event);
42
43           Called when a keyboard or mouse event arrives.
44           Return True if you handle it in some way, False otherwise.
45
46       static void YOURNAME_free (Display *, Window, void *closure);
47
48            Called when you are done: free everything you've allocated,
49            including your private `state' structure.  
50
51            NOTE: this is called in windowed-mode when the user typed
52            'q' or clicks on the window's close box; but when
53            xscreensaver terminates this screenhack, it does so by
54            killing the process with SIGSTOP.  So this callback is
55            mostly useless.
56
57       static char YOURNAME_defaults [] = { "...", "...", ... , 0 };
58
59            This variable is an array of strings, your default resources.
60            Null-terminate the list.
61
62       static XrmOptionDescRec YOURNAME_options[] = { { ... }, ... { 0,0,0,0 } }
63
64            This variable describes your command-line options.
65            Null-terminate the list.
66
67       Finally , invoke the XSCREENSAVER_MODULE() macro to tie it all together.
68
69    Additional caveats:
70
71       - Make sure that all functions in your module are static (check this
72         by running "nm -g" on the .o file).
73
74       - Do not use global variables: all such info must be stored in the
75         private `state' structure.
76
77       - Do not use static function-local variables, either.  Put it in `state'.
78
79         Assume that there are N independent runs of this code going in the
80         same address space at the same time: they must not affect each other.
81
82       - Don't forget to write an XML file to describe the user interface
83         of your screen saver module.  See .../hacks/config/README for details.
84  */
85
86 #define DEBUG_PAIR
87
88 #include <stdio.h>
89 #include <X11/Intrinsic.h>
90 #include <X11/IntrinsicP.h>
91 #include <X11/CoreP.h>
92 #include <X11/Shell.h>
93 #include <X11/StringDefs.h>
94 #include <X11/keysym.h>
95 /* #include <libosso.h> */
96 #include <dbus/dbus.h>
97 #include <dbus/dbus-glib.h> 
98
99 #ifdef __sgi
100 # include <X11/SGIScheme.h>     /* for SgiUseSchemes() */
101 #endif /* __sgi */
102
103 #ifdef HAVE_XMU
104 # ifndef VMS
105 #  include <X11/Xmu/Error.h>
106 # else /* VMS */
107 #  include <Xmu/Error.h>
108 # endif
109 #else
110 # include "xmu.h"
111 #endif
112
113 #include "screenhackI.h"
114 #include "version.h"
115 #include "vroot.h"
116 #include "fps.h"
117
118 #ifndef _XSCREENSAVER_VROOT_H_
119 # error Error!  You have an old version of vroot.h!  Check -I args.
120 #endif /* _XSCREENSAVER_VROOT_H_ */
121
122 #ifndef isupper
123 # define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
124 #endif
125 #ifndef _tolower
126 # define _tolower(c)  ((c) - 'A' + 'a')
127 #endif
128
129
130 /* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
131  */
132 extern struct xscreensaver_function_table *xscreensaver_function_table;
133
134
135 const char *progname;   /* used by hacks in error messages */
136 const char *progclass;  /* used by ../utils/resources.c */
137 Bool mono_p;            /* used by hacks */
138
139
140 static XrmOptionDescRec default_options [] = {
141   { "-root",    ".root",                XrmoptionNoArg, "True" },
142   { "-window",  ".root",                XrmoptionNoArg, "False" },
143   { "-mono",    ".mono",                XrmoptionNoArg, "True" },
144   { "-install", ".installColormap",     XrmoptionNoArg, "True" },
145   { "-noinstall",".installColormap",    XrmoptionNoArg, "False" },
146   { "-visual",  ".visualID",            XrmoptionSepArg, 0 },
147   { "-window-id", ".windowID",          XrmoptionSepArg, 0 },
148   { "-fps",     ".doFPS",               XrmoptionNoArg, "True" },
149   { "-no-fps",  ".doFPS",               XrmoptionNoArg, "False" },
150
151 # ifdef DEBUG_PAIR
152   { "-pair",    ".pair",                XrmoptionNoArg, "True" },
153 # endif /* DEBUG_PAIR */
154   { 0, 0, 0, 0 }
155 };
156
157 static char *default_defaults[] = {
158   ".root:               false",
159   "*geometry:           600x480", /* this should be .geometry, but nooooo... */
160   "*mono:               false",
161   "*installColormap:    false",
162   "*doFPS:              false",
163   "*visualID:           default",
164   "*windowID:           ",
165   "*desktopGrabber:     xscreensaver-getimage %s",
166   0
167 };
168
169 static XrmOptionDescRec *merged_options;
170 static int merged_options_size;
171 static char **merged_defaults;
172
173
174 static void
175 merge_options (void)
176 {
177   struct xscreensaver_function_table *ft = xscreensaver_function_table;
178
179   const XrmOptionDescRec *options = ft->options;
180   const char * const *defaults    = ft->defaults;
181   const char *progclass           = ft->progclass;
182
183   int def_opts_size, opts_size;
184   int def_defaults_size, defaults_size;
185
186   for (def_opts_size = 0; default_options[def_opts_size].option;
187        def_opts_size++)
188     ;
189   for (opts_size = 0; options[opts_size].option; opts_size++)
190     ;
191
192   merged_options_size = def_opts_size + opts_size;
193   merged_options = (XrmOptionDescRec *)
194     malloc ((merged_options_size + 1) * sizeof(*default_options));
195   memcpy (merged_options, default_options,
196           (def_opts_size * sizeof(*default_options)));
197   memcpy (merged_options + def_opts_size, options,
198           ((opts_size + 1) * sizeof(*default_options)));
199
200   for (def_defaults_size = 0; default_defaults[def_defaults_size];
201        def_defaults_size++)
202     ;
203   for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
204     ;
205   merged_defaults = (char **)
206     malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
207   memcpy (merged_defaults, default_defaults,
208           def_defaults_size * sizeof(*defaults));
209   memcpy (merged_defaults + def_defaults_size, defaults,
210           (defaults_size + 1) * sizeof(*defaults));
211
212   /* This totally sucks.  Xt should behave like this by default.
213      If the string in `defaults' looks like ".foo", change that
214      to "Progclass.foo".
215    */
216   {
217     char **s;
218     for (s = merged_defaults; *s; s++)
219       if (**s == '.')
220         {
221           const char *oldr = *s;
222           char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
223           strcpy (newr, progclass);
224           strcat (newr, oldr);
225           *s = newr;
226         }
227       else
228         *s = strdup (*s);
229   }
230 }
231
232 \f
233 /* Make the X errors print out the name of this program, so we have some
234    clue which one has a bug when they die under the screensaver.
235  */
236
237 static int
238 screenhack_ehandler (Display *dpy, XErrorEvent *error)
239 {
240   fprintf (stderr, "\nX error in %s:\n", progname);
241   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
242     exit (-1);
243   else
244     fprintf (stderr, " (nonfatal.)\n");
245   return 0;
246 }
247
248 static Bool
249 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
250 {
251   return (event->xany.type == MapNotify &&
252           event->xvisibility.window == (Window) window);
253 }
254
255
256 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
257
258 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
259    Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
260    Returns False if the screen saver should now terminate.
261  */
262 static Bool
263 screenhack_handle_event_1 (Display *dpy, XEvent *event)
264 {
265   switch (event->xany.type)
266     {
267     case KeyPress:
268       {
269         KeySym keysym;
270         char c = 0;
271         XLookupString (&event->xkey, &c, 1, &keysym, 0);
272         if (c == 'q' ||
273             c == 'Q' ||
274             c == 3 ||   /* ^C */
275             c == 27)    /* ESC */
276           return False;  /* exit */
277         else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
278           XBell (dpy, 0);  /* beep for non-chord keys */
279       }
280       break;
281     case ButtonPress:
282       XBell (dpy, 0);
283       break;
284     case ClientMessage:
285       {
286         if (event->xclient.message_type != XA_WM_PROTOCOLS)
287           {
288             char *s = XGetAtomName(dpy, event->xclient.message_type);
289             if (!s) s = "(null)";
290             fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
291                      progname, s);
292           }
293         else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
294           {
295             char *s1 = XGetAtomName(dpy, event->xclient.message_type);
296             char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
297             if (!s1) s1 = "(null)";
298             if (!s2) s2 = "(null)";
299             fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
300                      progname, s1, s2);
301           }
302         else
303           {
304             return False;  /* exit */
305           }
306       }
307       break;
308     }
309   return True;
310 }
311
312
313 static Visual *
314 pick_visual (Screen *screen)
315 {
316   struct xscreensaver_function_table *ft = xscreensaver_function_table;
317
318   if (ft->pick_visual_hook)
319     {
320       Visual *v = ft->pick_visual_hook (screen);
321       if (v) return v;
322     }
323
324   return get_visual_resource (screen, "visualID", "VisualID", False);
325 }
326
327
328 /* Notice when the user has requested a different visual or colormap
329    on a pre-existing window (e.g., "-root -visual truecolor" or
330    "-window-id 0x2c00001 -install") and complain, since when drawing
331    on an existing window, we have no choice about these things.
332  */
333 static void
334 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
335                 Bool window_p)
336 {
337   struct xscreensaver_function_table *ft = xscreensaver_function_table;
338
339   char *visual_string = get_string_resource (DisplayOfScreen (screen),
340                                              "visualID", "VisualID");
341   Visual *desired_visual = pick_visual (screen);
342   char win[100];
343   char why[100];
344
345   if (window == RootWindowOfScreen (screen))
346     strcpy (win, "root window");
347   else
348     sprintf (win, "window 0x%lx", (unsigned long) window);
349
350   if (window_p)
351     sprintf (why, "-window-id 0x%lx", (unsigned long) window);
352   else
353     strcpy (why, "-root");
354
355   if (visual_string && *visual_string)
356     {
357       char *s;
358       for (s = visual_string; *s; s++)
359         if (isupper (*s)) *s = _tolower (*s);
360
361       if (!strcmp (visual_string, "default") ||
362           !strcmp (visual_string, "default") ||
363           !strcmp (visual_string, "best"))
364         /* don't warn about these, just silently DWIM. */
365         ;
366       else if (visual != desired_visual)
367         {
368           fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
369                    progname, visual_string, why);
370           fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
371                    progname, win, XVisualIDFromVisual (visual));
372         }
373       free (visual_string);
374     }
375
376   if (visual == DefaultVisualOfScreen (screen) &&
377       has_writable_cells (screen, visual) &&
378       get_boolean_resource (DisplayOfScreen (screen),
379                             "installColormap", "InstallColormap"))
380     {
381       fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
382                progname, why);
383       fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
384                progname, win, (unsigned long) cmap);
385     }
386
387   if (ft->validate_visual_hook)
388     {
389       if (! ft->validate_visual_hook (screen, win, visual))
390         exit (1);
391     }
392 }
393
394
395 static void
396 fix_fds (void)
397 {
398   /* Bad Things Happen if stdin, stdout, and stderr have been closed
399      (as by the `sh incantation "attraction >&- 2>&-").  When you do
400      that, the X connection gets allocated to one of these fds, and
401      then some random library writes to stderr, and random bits get
402      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
403      So, we cause the first three file descriptors to be open to
404      /dev/null if they aren't open to something else already.  This
405      must be done before any other files are opened (or the closing
406      of that other file will again free up one of the "magic" first
407      three FDs.)
408
409      We do this by opening /dev/null three times, and then closing
410      those fds, *unless* any of them got allocated as #0, #1, or #2,
411      in which case we leave them open.  Gag.
412
413      Really, this crap is technically required of *every* X program,
414      if you want it to be robust in the face of "2>&-".
415    */
416   int fd0 = open ("/dev/null", O_RDWR);
417   int fd1 = open ("/dev/null", O_RDWR);
418   int fd2 = open ("/dev/null", O_RDWR);
419   if (fd0 > 2) close (fd0);
420   if (fd1 > 2) close (fd1);
421   if (fd2 > 2) close (fd2);
422 }
423
424
425 static Boolean
426 screenhack_table_handle_events (Display *dpy,
427                                 const struct xscreensaver_function_table *ft,
428                                 Window window, void *closure
429 #ifdef DEBUG_PAIR
430                                 , Window window2, void *closure2
431 #endif
432                                 )
433 {
434   XtAppContext app = XtDisplayToApplicationContext (dpy);
435
436   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
437     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
438
439   while (XPending (dpy))
440     {
441       XEvent event;
442       XNextEvent (dpy, &event);
443
444       if (event.xany.type == ConfigureNotify)
445         {
446           if (event.xany.window == window)
447             ft->reshape_cb (dpy, window, closure,
448                             event.xconfigure.width, event.xconfigure.height);
449 #ifdef DEBUG_PAIR
450           if (event.xany.window == window2)
451             ft->reshape_cb (dpy, window2, closure2,
452                             event.xconfigure.width, event.xconfigure.height);
453 #endif
454         }
455       else if (event.xany.type == ClientMessage ||
456                (! (event.xany.window == window
457                    ? ft->event_cb (dpy, window, closure, &event)
458 #ifdef DEBUG_PAIR
459                    : event.xany.window == window2
460                    ? ft->event_cb (dpy, window2, closure2, &event)
461 #endif
462                    : 0)))
463         if (! screenhack_handle_event_1 (dpy, &event))
464           return False;
465
466       if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
467         XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
468     }
469   return True;
470 }
471
472
473 static Boolean
474 usleep_and_process_events (Display *dpy,
475                            const struct xscreensaver_function_table *ft,
476                            Window window, fps_state *fpst, void *closure,
477                            unsigned long delay
478 #ifdef DEBUG_PAIR
479                          , Window window2, fps_state *fpst2, void *closure2,
480                            unsigned long delay2
481 #endif
482                            )
483 {
484   do {
485     unsigned long quantum = 100000;  /* 1/10th second */
486     if (quantum > delay) 
487       quantum = delay;
488     delay -= quantum;
489
490     XSync (dpy, False);
491     if (quantum > 0)
492       {
493         usleep (quantum);
494         if (fpst) fps_slept (fpst, quantum);
495 #ifdef DEBUG_PAIR
496         if (fpst2) fps_slept (fpst2, quantum);
497 #endif
498       }
499
500     if (! screenhack_table_handle_events (dpy, ft, window, closure
501 #ifdef DEBUG_PAIR
502                                           , window2, closure2
503 #endif
504                                           ))
505       return False;
506   } while (delay > 0);
507
508   return True;
509 }
510
511
512 static void
513 screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
514 {
515   fps_compute (fpst, 0);
516   fps_draw (fpst);
517 }
518
519
520 static void
521 run_screenhack_table (Display *dpy, 
522                       Window window,
523 # ifdef DEBUG_PAIR
524                       Window window2,
525 # endif
526                       const struct xscreensaver_function_table *ft)
527 {
528
529   /* Kludge: even though the init_cb functions are declared to take 2 args,
530      actually call them with 3, for the benefit of xlockmore_init() and
531      xlockmore_setup().
532    */
533   void *(*init_cb) (Display *, Window, void *) = 
534     (void *(*) (Display *, Window, void *)) ft->init_cb;
535
536   void (*fps_cb) (Display *, Window, fps_state *, void *) = ft->fps_cb;
537
538   void *closure = init_cb (dpy, window, ft->setup_arg);
539   fps_state *fpst = fps_init (dpy, window);
540
541 #ifdef DEBUG_PAIR
542   void *closure2 = 0;
543   fps_state *fpst2 = 0;
544   if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
545   if (window2) fpst2 = fps_init (dpy, window2);
546 #endif
547
548   if (! closure)  /* if it returns nothing, it can't possibly be re-entrant. */
549     abort();
550
551   if (! fps_cb) fps_cb = screenhack_do_fps;
552
553   while (1)
554     {
555       unsigned long delay = ft->draw_cb (dpy, window, closure);
556 #ifdef DEBUG_PAIR
557       unsigned long delay2 = 0;
558       if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
559 #endif
560
561       if (fpst) fps_cb (dpy, window, fpst, closure);
562 #ifdef DEBUG_PAIR
563       if (fpst2) fps_cb (dpy, window, fpst2, closure);
564 #endif
565
566       if (! usleep_and_process_events (dpy, ft,
567                                        window, fpst, closure, delay
568 #ifdef DEBUG_PAIR
569                                        , window2, fpst2, closure2, delay2
570 #endif
571                                        ))
572         break;
573     }
574
575   ft->free_cb (dpy, window, closure);
576   if (fpst) fps_free (fpst);
577
578 #ifdef DEBUG_PAIR
579   if (window2) ft->free_cb (dpy, window2, closure2);
580   if (window2) fps_free (fpst2);
581 #endif
582 }
583
584
585 static Widget
586 make_shell (Screen *screen, Widget toplevel, int width, int height)
587 {
588   Display *dpy = DisplayOfScreen (screen);
589   Visual *visual = pick_visual (screen);
590   Boolean def_visual_p = (toplevel && 
591                           visual == DefaultVisualOfScreen (screen));
592
593   if (width  <= 0) width  = 600;
594   if (height <= 0) height = 480;
595
596   if (def_visual_p)
597     {
598       Window window;
599       XtVaSetValues (toplevel,
600                      XtNmappedWhenManaged, False,
601                      XtNwidth, width,
602                      XtNheight, height,
603                      XtNinput, True,  /* for WM_HINTS */
604                      NULL);
605       XtRealizeWidget (toplevel);
606       window = XtWindow (toplevel);
607
608       if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
609         {
610           Colormap cmap = 
611             XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
612                              AllocNone);
613           XSetWindowColormap (dpy, window, cmap);
614         }
615     }
616   else
617     {
618       unsigned int bg, bd;
619       Widget new;
620       Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
621                                        visual, AllocNone);
622       bg = get_pixel_resource (dpy, cmap, "background", "Background");
623       bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
624
625       new = XtVaAppCreateShell (progname, progclass,
626                                 topLevelShellWidgetClass, dpy,
627                                 XtNmappedWhenManaged, False,
628                                 XtNvisual, visual,
629                                 XtNdepth, visual_depth (screen, visual),
630                                 XtNwidth, width,
631                                 XtNheight, height,
632                                 XtNcolormap, cmap,
633                                 XtNbackground, (Pixel) bg,
634                                 XtNborderColor, (Pixel) bd,
635                                 XtNinput, True,  /* for WM_HINTS */
636                                 NULL);
637
638       if (!toplevel)  /* kludge for the second window in -pair mode... */
639         XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
640
641       XtRealizeWidget (new);
642       toplevel = new;
643     }
644
645   return toplevel;
646 }
647
648 static void
649 init_window (Display *dpy, Widget toplevel, const char *title)
650 {
651   Window window;
652   XWindowAttributes xgwa;
653   XtPopup (toplevel, XtGrabNone);
654   XtVaSetValues (toplevel, XtNtitle, title, NULL);
655
656   /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
657    */
658   window = XtWindow (toplevel);
659   XGetWindowAttributes (dpy, window, &xgwa);
660   XSelectInput (dpy, window,
661                 (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
662                  ButtonPressMask | ButtonReleaseMask));
663   XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
664                    PropModeReplace,
665                    (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
666 }
667
668
669 int
670 main (int argc, char **argv)
671 {
672   struct xscreensaver_function_table *ft = xscreensaver_function_table;
673
674 /*  osso_context_t  *osso; */
675
676   XWindowAttributes xgwa;
677   Widget toplevel;
678   Display *dpy;
679   Window window;
680 # ifdef DEBUG_PAIR
681   Window window2 = 0;
682   Widget toplevel2 = 0;
683 # endif
684   XtAppContext app;
685   Bool root_p;
686   Window on_window = 0;
687   XEvent event;
688   Boolean dont_clear;
689   char version[255];
690
691   fix_fds();
692
693   progname = argv[0];   /* reset later */
694   progclass = ft->progclass;
695
696   if (ft->setup_cb)
697     ft->setup_cb (ft, ft->setup_arg);
698
699   merge_options ();
700
701 #ifdef __sgi
702   /* We have to do this on SGI to prevent the background color from being
703      overridden by the current desktop color scheme (we'd like our backgrounds
704      to be black, thanks.)  This should be the same as setting the
705      "*useSchemes: none" resource, but it's not -- if that resource is
706      present in the `default_defaults' above, it doesn't work, though it
707      does work when passed as an -xrm arg on the command line.  So screw it,
708      turn them off from C instead.
709    */
710   SgiUseSchemes ("none"); 
711 #endif /* __sgi */
712
713   toplevel = XtAppInitialize (&app, progclass, merged_options,
714                               merged_options_size, &argc, argv,
715                               merged_defaults, 0, 0);
716
717   dpy = XtDisplay (toplevel);
718
719   XtGetApplicationNameAndClass (dpy,
720                                 (char **) &progname,
721                                 (char **) &progclass);
722
723   /* half-assed way of avoiding buffer-overrun attacks. */
724   if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
725
726   XSetErrorHandler (screenhack_ehandler);
727
728   XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
729   XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
730
731   {
732     char *v = (char *) strdup(strchr(screensaver_id, ' '));
733     char *s1, *s2, *s3, *s4;
734     s1 = (char *) strchr(v,  ' '); s1++;
735     s2 = (char *) strchr(s1, ' ');
736     s3 = (char *) strchr(v,  '('); s3++;
737     s4 = (char *) strchr(s3, ')');
738     *s2 = 0;
739     *s4 = 0;
740     sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
741              progclass, s1, s3);
742     free(v);
743   }
744
745   if (argc > 1)
746     {
747       const char *s;
748       int i;
749       int x = 18;
750       int end = 78;
751       Bool help_p = (!strcmp(argv[1], "-help") ||
752                      !strcmp(argv[1], "--help"));
753       fprintf (stderr, "%s\n", version);
754       for (s = progclass; *s; s++) fprintf(stderr, " ");
755       fprintf (stderr, "  http://www.jwz.org/xscreensaver/\n\n");
756
757       if (!help_p)
758         fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
759       fprintf (stderr, "Options include: ");
760       for (i = 0; i < merged_options_size; i++)
761         {
762           char *sw = merged_options [i].option;
763           Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
764           int size = strlen (sw) + (argp ? 6 : 0) + 2;
765           if (x + size >= end)
766             {
767               fprintf (stderr, "\n\t\t ");
768               x = 18;
769             }
770           x += size;
771           fprintf (stderr, "%s", sw);
772           if (argp) fprintf (stderr, " <arg>");
773           if (i != merged_options_size - 1) fprintf (stderr, ", ");
774         }
775
776       fprintf (stderr, ".\n");
777
778 #if 0
779       if (help_p)
780         {
781           fprintf (stderr, "\nResources:\n\n");
782           for (i = 0; i < merged_options_size; i++)
783             {
784               const char *opt = merged_options [i].option;
785               const char *res = merged_options [i].specifier + 1;
786               const char *val = merged_options [i].value;
787               char *s = get_string_resource (dpy, (char *) res, (char *) res);
788
789               if (s)
790                 {
791                   int L = strlen(s);
792                 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
793                   s[--L] = 0;
794                 }
795
796               fprintf (stderr, "    %-16s %-18s ", opt, res);
797               if (merged_options [i].argKind == XrmoptionSepArg)
798                 {
799                   fprintf (stderr, "[%s]", (s ? s : "?"));
800                 }
801               else
802                 {
803                   fprintf (stderr, "%s", (val ? val : "(null)"));
804                   if (val && s && !strcasecmp (val, s))
805                     fprintf (stderr, " [default]");
806                 }
807               fprintf (stderr, "\n");
808             }
809           fprintf (stderr, "\n");
810         }
811 #endif
812
813       exit (help_p ? 0 : 1);
814     }
815
816   {
817     char **s;
818     for (s = merged_defaults; *s; s++)
819       free(*s);
820   }
821
822   free (merged_options);
823   free (merged_defaults);
824   merged_options = 0;
825   merged_defaults = 0;
826
827   dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
828   mono_p = get_boolean_resource (dpy, "mono", "Boolean");
829   if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
830     mono_p = True;
831
832   root_p = get_boolean_resource (dpy, "root", "Boolean");
833
834   {
835     char *s = get_string_resource (dpy, "windowID", "WindowID");
836     if (s && *s)
837       on_window = get_integer_resource (dpy, "windowID", "WindowID");
838     if (s) free (s);
839   }
840   /* OSSO initialize */
841 /*  osso = osso_initialize("org.maemo.xscreensaver", "0.5.11", TRUE, NULL); */
842   
843    DBusGConnection *connection;
844    GError *error;
845    DBusGProxy *proxy;
846
847    g_type_init ();
848    error = NULL;
849    connection = dbus_g_bus_get (DBUS_BUS_SESSION,  &error);
850
851
852
853   if (on_window)
854     {
855       window = (Window) on_window;
856       XtDestroyWidget (toplevel);
857       XGetWindowAttributes (dpy, window, &xgwa);
858       visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
859
860       /* Select KeyPress and resize events on the external window.
861        */
862       xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
863       XSelectInput (dpy, window, xgwa.your_event_mask);
864
865       /* Select ButtonPress and ButtonRelease events on the external window,
866          if no other app has already selected them (only one app can select
867          ButtonPress at a time: BadAccess results.)
868        */
869       if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
870         XSelectInput (dpy, window,
871                       (xgwa.your_event_mask |
872                        ButtonPressMask | ButtonReleaseMask));
873     }
874   else if (root_p)
875     {
876       window = VirtualRootWindowOfScreen (XtScreen (toplevel));
877       XtDestroyWidget (toplevel);
878       XGetWindowAttributes (dpy, window, &xgwa);
879       /* With RANDR, the root window can resize! */
880       XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
881       visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
882     }
883   else
884     {
885       Widget new = make_shell (XtScreen (toplevel), toplevel,
886                                toplevel->core.width,
887                                toplevel->core.height);
888       if (new != toplevel)
889         {
890           XtDestroyWidget (toplevel);
891           toplevel = new;
892         }
893
894       init_window (dpy, toplevel, version);
895       window = XtWindow (toplevel);
896       XGetWindowAttributes (dpy, window, &xgwa);
897
898 # ifdef DEBUG_PAIR
899       if (get_boolean_resource (dpy, "pair", "Boolean"))
900         {
901           toplevel2 = make_shell (xgwa.screen, 0,
902                                   toplevel->core.width,
903                                   toplevel->core.height);
904           init_window (dpy, toplevel2, version);
905           window2 = XtWindow (toplevel2);
906         }
907 # endif /* DEBUG_PAIR */
908     }
909
910   if (!dont_clear)
911     {
912       unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
913                                             "background", "Background");
914       XSetWindowBackground (dpy, window, bg);
915       XClearWindow (dpy, window);
916 # ifdef DEBUG_PAIR
917       if (window2)
918         {
919           XSetWindowBackground (dpy, window2, bg);
920           XClearWindow (dpy, window2);
921         }
922 # endif
923     }
924
925   if (!root_p && !on_window)
926     /* wait for it to be mapped */
927     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
928
929   XSync (dpy, False);
930
931   /* This is the one and only place that the random-number generator is
932      seeded in any screenhack.  You do not need to seed the RNG again,
933      it is done for you before your code is invoked. */
934 # undef ya_rand_init
935   ya_rand_init (0);
936
937   run_screenhack_table (dpy, window, 
938 # ifdef DEBUG_PAIR
939                         window2,
940 # endif
941                         ft);
942
943   XtDestroyWidget (toplevel);
944   XtDestroyApplicationContext (app);
945
946   return 0;
947 }