added defines
[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 <dbus/dbus.h>
96
97 #ifdef __sgi
98 # include <X11/SGIScheme.h>     /* for SgiUseSchemes() */
99 #endif /* __sgi */
100
101 #ifdef HAVE_XMU
102 # ifndef VMS
103 #  include <X11/Xmu/Error.h>
104 # else /* VMS */
105 #  include <Xmu/Error.h>
106 # endif
107 #else
108 # include "xmu.h"
109 #endif
110
111 #include "screenhackI.h"
112 #include "version.h"
113 #include "vroot.h"
114 #include "fps.h"
115
116 #ifndef _XSCREENSAVER_VROOT_H_
117 # error Error!  You have an old version of vroot.h!  Check -I args.
118 #endif /* _XSCREENSAVER_VROOT_H_ */
119
120 #ifndef isupper
121 # define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
122 #endif
123 #ifndef _tolower
124 # define _tolower(c)  ((c) - 'A' + 'a')
125 #endif
126
127
128 /* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
129  */
130 extern struct xscreensaver_function_table *xscreensaver_function_table;
131
132
133 const char *progname;   /* used by hacks in error messages */
134 const char *progclass;  /* used by ../utils/resources.c */
135 Bool mono_p;            /* used by hacks */
136
137
138 static XrmOptionDescRec default_options [] = {
139   { "-root",    ".root",                XrmoptionNoArg, "True" },
140   { "-window",  ".root",                XrmoptionNoArg, "False" },
141   { "-mono",    ".mono",                XrmoptionNoArg, "True" },
142   { "-install", ".installColormap",     XrmoptionNoArg, "True" },
143   { "-noinstall",".installColormap",    XrmoptionNoArg, "False" },
144   { "-visual",  ".visualID",            XrmoptionSepArg, 0 },
145   { "-window-id", ".windowID",          XrmoptionSepArg, 0 },
146   { "-fps",     ".doFPS",               XrmoptionNoArg, "True" },
147   { "-no-fps",  ".doFPS",               XrmoptionNoArg, "False" },
148   { "-view",  ".view",                  XrmoptionSepArg, 1 },
149
150 # ifdef DEBUG_PAIR
151   { "-pair",    ".pair",                XrmoptionNoArg, "True" },
152 # endif /* DEBUG_PAIR */
153   { 0, 0, 0, 0 }
154 };
155
156 static char *default_defaults[] = {
157   ".root:               false",
158   "*geometry:           600x480", /* this should be .geometry, but nooooo... */
159   "*mono:               false",
160   "*installColormap:    false",
161   "*doFPS:              false",
162   "*visualID:           default",
163   "*windowID:           ",
164   "*desktopGrabber:     xscreensaver-getimage %s",
165   0
166 };
167
168 static XrmOptionDescRec *merged_options;
169 static int merged_options_size;
170 static char **merged_defaults;
171
172
173 static void
174 merge_options (void)
175 {
176   struct xscreensaver_function_table *ft = xscreensaver_function_table;
177
178   const XrmOptionDescRec *options = ft->options;
179   const char * const *defaults    = ft->defaults;
180   const char *progclass           = ft->progclass;
181
182   int def_opts_size, opts_size;
183   int def_defaults_size, defaults_size;
184
185   for (def_opts_size = 0; default_options[def_opts_size].option;
186        def_opts_size++)
187     ;
188   for (opts_size = 0; options[opts_size].option; opts_size++)
189     ;
190
191   merged_options_size = def_opts_size + opts_size;
192   merged_options = (XrmOptionDescRec *)
193     malloc ((merged_options_size + 1) * sizeof(*default_options));
194   memcpy (merged_options, default_options,
195           (def_opts_size * sizeof(*default_options)));
196   memcpy (merged_options + def_opts_size, options,
197           ((opts_size + 1) * sizeof(*default_options)));
198
199   for (def_defaults_size = 0; default_defaults[def_defaults_size];
200        def_defaults_size++)
201     ;
202   for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
203     ;
204   merged_defaults = (char **)
205     malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
206   memcpy (merged_defaults, default_defaults,
207           def_defaults_size * sizeof(*defaults));
208   memcpy (merged_defaults + def_defaults_size, defaults,
209           (defaults_size + 1) * sizeof(*defaults));
210
211   /* This totally sucks.  Xt should behave like this by default.
212      If the string in `defaults' looks like ".foo", change that
213      to "Progclass.foo".
214    */
215   {
216     char **s;
217     for (s = merged_defaults; *s; s++)
218       if (**s == '.')
219         {
220           const char *oldr = *s;
221           char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
222           strcpy (newr, progclass);
223           strcat (newr, oldr);
224           *s = newr;
225         }
226       else
227         *s = strdup (*s);
228   }
229 }
230
231 \f
232 /* Make the X errors print out the name of this program, so we have some
233    clue which one has a bug when they die under the screensaver.
234  */
235
236 static int
237 screenhack_ehandler (Display *dpy, XErrorEvent *error)
238 {
239   fprintf (stderr, "\nX error in %s:\n", progname);
240   if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
241     exit (-1);
242   else
243     fprintf (stderr, " (nonfatal.)\n");
244   return 0;
245 }
246
247 static Bool
248 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
249 {
250   return (event->xany.type == MapNotify &&
251           event->xvisibility.window == (Window) window);
252 }
253
254
255 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
256
257 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
258    Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
259    Returns False if the screen saver should now terminate.
260  */
261 static Bool
262 screenhack_handle_event_1 (Display *dpy, XEvent *event)
263 {
264   switch (event->xany.type)
265     {
266     case KeyPress:
267       {
268         KeySym keysym;
269         char c = 0;
270         XLookupString (&event->xkey, &c, 1, &keysym, 0);
271         if (c == 'q' ||
272             c == 'Q' ||
273             c == 3 ||   /* ^C */
274             c == 27)    /* ESC */
275           return False;  /* exit */
276         else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
277           XBell (dpy, 0);  /* beep for non-chord keys */
278       }
279       break;
280     case ButtonPress:
281       XBell (dpy, 0);
282       break;
283     case ClientMessage:
284       {
285         if (event->xclient.message_type != XA_WM_PROTOCOLS)
286           {
287             char *s = XGetAtomName(dpy, event->xclient.message_type);
288             if (!s) s = "(null)";
289             fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
290                      progname, s);
291           }
292         else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
293           {
294             char *s1 = XGetAtomName(dpy, event->xclient.message_type);
295             char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
296             if (!s1) s1 = "(null)";
297             if (!s2) s2 = "(null)";
298             fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
299                      progname, s1, s2);
300           }
301         else
302           {
303             return False;  /* exit */
304           }
305       }
306       break;
307     }
308   return True;
309 }
310
311
312 static Visual *
313 pick_visual (Screen *screen)
314 {
315   struct xscreensaver_function_table *ft = xscreensaver_function_table;
316
317   if (ft->pick_visual_hook)
318     {
319       Visual *v = ft->pick_visual_hook (screen);
320       if (v) return v;
321     }
322
323   return get_visual_resource (screen, "visualID", "VisualID", False);
324 }
325
326
327 /* Notice when the user has requested a different visual or colormap
328    on a pre-existing window (e.g., "-root -visual truecolor" or
329    "-window-id 0x2c00001 -install") and complain, since when drawing
330    on an existing window, we have no choice about these things.
331  */
332 static void
333 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
334                 Bool window_p)
335 {
336   struct xscreensaver_function_table *ft = xscreensaver_function_table;
337
338   char *visual_string = get_string_resource (DisplayOfScreen (screen),
339                                              "visualID", "VisualID");
340   Visual *desired_visual = pick_visual (screen);
341   char win[100];
342   char why[100];
343
344   if (window == RootWindowOfScreen (screen))
345     strcpy (win, "root window");
346   else
347     sprintf (win, "window 0x%lx", (unsigned long) window);
348
349   if (window_p)
350     sprintf (why, "-window-id 0x%lx", (unsigned long) window);
351   else
352     strcpy (why, "-root");
353
354   if (visual_string && *visual_string)
355     {
356       char *s;
357       for (s = visual_string; *s; s++)
358         if (isupper (*s)) *s = _tolower (*s);
359
360       if (!strcmp (visual_string, "default") ||
361           !strcmp (visual_string, "default") ||
362           !strcmp (visual_string, "best"))
363         /* don't warn about these, just silently DWIM. */
364         ;
365       else if (visual != desired_visual)
366         {
367           fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
368                    progname, visual_string, why);
369           fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
370                    progname, win, XVisualIDFromVisual (visual));
371         }
372       free (visual_string);
373     }
374
375   if (visual == DefaultVisualOfScreen (screen) &&
376       has_writable_cells (screen, visual) &&
377       get_boolean_resource (DisplayOfScreen (screen),
378                             "installColormap", "InstallColormap"))
379     {
380       fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
381                progname, why);
382       fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
383                progname, win, (unsigned long) cmap);
384     }
385
386   if (ft->validate_visual_hook)
387     {
388       if (! ft->validate_visual_hook (screen, win, visual))
389         exit (1);
390     }
391 }
392
393
394 static void
395 fix_fds (void)
396 {
397   /* Bad Things Happen if stdin, stdout, and stderr have been closed
398      (as by the `sh incantation "attraction >&- 2>&-").  When you do
399      that, the X connection gets allocated to one of these fds, and
400      then some random library writes to stderr, and random bits get
401      stuffed down the X pipe, causing "Xlib: sequence lost" errors.
402      So, we cause the first three file descriptors to be open to
403      /dev/null if they aren't open to something else already.  This
404      must be done before any other files are opened (or the closing
405      of that other file will again free up one of the "magic" first
406      three FDs.)
407
408      We do this by opening /dev/null three times, and then closing
409      those fds, *unless* any of them got allocated as #0, #1, or #2,
410      in which case we leave them open.  Gag.
411
412      Really, this crap is technically required of *every* X program,
413      if you want it to be robust in the face of "2>&-".
414    */
415   int fd0 = open ("/dev/null", O_RDWR);
416   int fd1 = open ("/dev/null", O_RDWR);
417   int fd2 = open ("/dev/null", O_RDWR);
418   if (fd0 > 2) close (fd0);
419   if (fd1 > 2) close (fd1);
420   if (fd2 > 2) close (fd2);
421 }
422
423
424 static Boolean
425 screenhack_table_handle_events (Display *dpy,
426                                 const struct xscreensaver_function_table *ft,
427                                 Window window, void *closure
428 #ifdef DEBUG_PAIR
429                                 , Window window2, void *closure2
430 #endif
431                                 )
432 {
433   XtAppContext app = XtDisplayToApplicationContext (dpy);
434
435   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
436     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
437
438   while (XPending (dpy))
439     {
440       XEvent event;
441       XNextEvent (dpy, &event);
442
443       if (event.xany.type == ConfigureNotify)
444         {
445           if (event.xany.window == window)
446             ft->reshape_cb (dpy, window, closure,
447                             event.xconfigure.width, event.xconfigure.height);
448 #ifdef DEBUG_PAIR
449           if (event.xany.window == window2)
450             ft->reshape_cb (dpy, window2, closure2,
451                             event.xconfigure.width, event.xconfigure.height);
452 #endif
453         }
454       else if (event.xany.type == ClientMessage ||
455                (! (event.xany.window == window
456                    ? ft->event_cb (dpy, window, closure, &event)
457 #ifdef DEBUG_PAIR
458                    : event.xany.window == window2
459                    ? ft->event_cb (dpy, window2, closure2, &event)
460 #endif
461                    : 0)))
462         if (! screenhack_handle_event_1 (dpy, &event))
463           return False;
464
465       if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
466         XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
467     }
468   return True;
469 }
470
471
472 static Boolean
473 usleep_and_process_events (Display *dpy,
474                            const struct xscreensaver_function_table *ft,
475                            Window window, fps_state *fpst, void *closure,
476                            unsigned long delay
477 #ifdef DEBUG_PAIR
478                          , Window window2, fps_state *fpst2, void *closure2,
479                            unsigned long delay2
480 #endif
481                            )
482 {
483   do {
484     unsigned long quantum = 100000;  /* 1/10th second */
485     if (quantum > delay) 
486       quantum = delay;
487     delay -= quantum;
488
489     XSync (dpy, False);
490     if (quantum > 0)
491       {
492         usleep (quantum);
493         if (fpst) fps_slept (fpst, quantum);
494 #ifdef DEBUG_PAIR
495         if (fpst2) fps_slept (fpst2, quantum);
496 #endif
497       }
498
499     if (! screenhack_table_handle_events (dpy, ft, window, closure
500 #ifdef DEBUG_PAIR
501                                           , window2, closure2
502 #endif
503                                           ))
504       return False;
505   } while (delay > 0);
506
507   return True;
508 }
509
510
511 static void
512 screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
513 {
514   fps_compute (fpst, 0);
515   fps_draw (fpst);
516 }
517
518
519 static void
520 run_screenhack_table (Display *dpy, 
521                       Window window,
522 # ifdef DEBUG_PAIR
523                       Window window2,
524 # endif
525                       const struct xscreensaver_function_table *ft,
526                       int view)
527 {
528 #define LIVEWP_SIGNAL_INTERFACE "org.maemo.livewp"
529 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW1 "pause_livebg_on_view1"                                                                                                 
530 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW2 "pause_livebg_on_view2"                                                                                                 
531 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW3 "pause_livebg_on_view3"                                                                                                 
532 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW4 "pause_livebg_on_view4"
533 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW5 "pause_livebg_on_view5"
534 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW6 "pause_livebg_on_view6"
535 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW7 "pause_livebg_on_view7"
536 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW8 "pause_livebg_on_view8"
537 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW9 "pause_livebg_on_view9"
538 #define LIVEWP_PAUSE_LIVEBG_ON_VIEW "pause_livebg_on_view"
539 #define LIVEWP_PLAY_LIVEBG_ON_VIEW1 "play_livebg_on_view1"                                                                                                   
540 #define LIVEWP_PLAY_LIVEBG_ON_VIEW2 "play_livebg_on_view2"                                                                                                   
541 #define LIVEWP_PLAY_LIVEBG_ON_VIEW3 "play_livebg_on_view3"                                                                                                   
542 #define LIVEWP_PLAY_LIVEBG_ON_VIEW4 "play_livebg_on_view4" 
543 #define LIVEWP_PLAY_LIVEBG_ON_VIEW5 "play_livebg_on_view5" 
544 #define LIVEWP_PLAY_LIVEBG_ON_VIEW6 "play_livebg_on_view6" 
545 #define LIVEWP_PLAY_LIVEBG_ON_VIEW7 "play_livebg_on_view7" 
546 #define LIVEWP_PLAY_LIVEBG_ON_VIEW8 "play_livebg_on_view8" 
547 #define LIVEWP_PLAY_LIVEBG_ON_VIEW9 "play_livebg_on_view9" 
548 #define LIVEWP_PLAY_LIVEBG_ON_VIEW "play_livebg_on_view" 
549    DBusMessage* msg;
550    DBusConnection* conn;
551    DBusError err;
552    DBusMessageIter args;
553    int ret;
554    char pause = 0;
555    int param = 0;
556    int start_frame = 200;
557
558   /* Kludge: even though the init_cb functions are declared to take 2 args,
559      actually call them with 3, for the benefit of xlockmore_init() and
560      xlockmore_setup().
561    */
562   void *(*init_cb) (Display *, Window, void *) = 
563     (void *(*) (Display *, Window, void *)) ft->init_cb;
564
565   void (*fps_cb) (Display *, Window, fps_state *, void *) = ft->fps_cb;
566
567   void *closure = init_cb (dpy, window, ft->setup_arg);
568   fps_state *fpst = fps_init (dpy, window);
569
570 #ifdef DEBUG_PAIR
571   void *closure2 = 0;
572   fps_state *fpst2 = 0;
573   if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
574   if (window2) fpst2 = fps_init (dpy, window2);
575 #endif
576
577   if (! closure)  /* if it returns nothing, it can't possibly be re-entrant. */
578     abort();
579
580   if (! fps_cb) fps_cb = screenhack_do_fps;
581   /* DBUS */
582   /* initialise the error */
583   dbus_error_init(&err);
584   conn = dbus_bus_get(DBUS_BUS_SESSION, &err);
585   if (dbus_error_is_set(&err)) { 
586          fprintf(stderr, "Connection Error (%s)\n", err.message); 
587          dbus_error_free(&err); 
588   }
589   if (NULL == conn) {
590          fprintf(stderr, "Connection Null\n"); 
591          exit(1); 
592   }
593   if (view == 1){
594         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view1'", NULL);
595         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view1'", NULL);
596   }
597   if (view == 2){
598         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view2'", NULL);
599         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view2'", NULL);
600   }
601   if (view == 3){
602         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view3'", NULL);
603         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view3'", NULL);
604   }
605   if (view == 4){
606         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view4'", NULL);
607         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view4'", NULL);
608   }
609   if (view == 5){
610         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view5'", NULL);
611         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view5'", NULL);
612   }
613   if (view == 6){
614         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view6'", NULL);
615         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view6'", NULL);
616   }
617   if (view == 7){
618         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view7'", NULL);
619         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view7'", NULL);
620   }
621   if (view == 8){
622         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view8'", NULL);
623         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view8'", NULL);
624   }
625   if (view == 9){
626         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='pause_livebg_on_view9'", NULL);
627         dbus_bus_add_match (conn, "type='signal', interface='org.maemo.livewp', member='play_livebg_on_view9'", NULL);
628   }
629
630   dbus_connection_flush(conn);
631
632   while (1)
633     {
634       if (pause == 0){
635         unsigned long delay = ft->draw_cb (dpy, window, closure);
636 #ifdef DEBUG_PAIR
637         unsigned long delay2 = 0;
638         if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
639 #endif
640
641      
642         if (fpst) fps_cb (dpy, window, fpst, closure);
643 #ifdef DEBUG_PAIR
644         if (fpst2) fps_cb (dpy, window, fpst2, closure);
645 #endif
646       if (! usleep_and_process_events (dpy, ft,
647                                        window, fpst, closure, delay
648 #ifdef DEBUG_PAIR
649                                        , window2, fpst2, closure2, delay2
650 #endif
651                                        ))
652         break;
653
654       }
655       if (start_frame > 0) {
656           start_frame--;
657           continue;
658       }
659         
660       if (pause == 0)
661       /* non blocking read of the next available message */
662         dbus_connection_read_write(conn, 0);
663       else
664       /* blocking read of the next available message */
665         dbus_connection_read_write(conn, 20000);
666
667       msg = dbus_connection_pop_message(conn);
668
669       if (NULL == msg){  
670           continue; 
671       }
672       /* check this is a method call for the right interface & method */
673       if ((view == 1 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW1))||
674           (view == 2 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW2))||
675           (view == 3 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW3))||
676           (view == 4 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW4))||
677           (view == 5 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW5))||
678           (view == 6 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW6))||
679           (view == 7 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW7))||
680           (view == 8 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW8))||
681           (view == 9 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW9))){
682           /* fprintf(stderr, "Pause scene visible %i\n", view); */ 
683            pause = 1;
684            dbus_message_unref (msg);
685            continue;
686       }
687       if ((view == 1 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW1))||
688           (view == 2 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW2))||
689           (view == 3 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW3))||
690           (view == 4 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW4))||
691           (view == 5 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW5))||
692           (view == 6 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW6))||
693           (view == 7 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW7))||
694           (view == 8 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW8))||
695           (view == 9 && dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW9))){
696            /* fprintf(stderr, "Play scene visible %i\n", view); */
697            pause = 0;
698            dbus_message_unref (msg);
699            continue;
700       }
701 #if 0  
702       /*  dbus_connection_steal_borrowed_message(conn, msg); */
703      /* fprintf (stderr, "APPLICATION PATH11111111111111111111 %s %s %s\n",   dbus_message_get_path(msg),   dbus_message_get_interface (msg), dbus_message_get_member (msg));*/
704         /*dbus_connection_return_message(conn, msg);*/
705       if (dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW) || 
706           dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW)){
707           if (!dbus_message_iter_init(msg, &args))
708               fprintf(stderr, "dbus message has no param\n");
709           else if (DBUS_TYPE_INT32 != dbus_message_iter_get_arg_type(&args))             
710               fprintf(stderr, "dbus message param is not int \n");
711           else{ 
712               dbus_message_iter_get_basic(&args, &param);
713               fprintf(stderr, "dbus param = %i\n", param);
714           }
715           if (param == view){
716               if (dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PAUSE_LIVEBG_ON_VIEW)){
717                  fprintf(stderr, "dbus  Pause scene visible %i\n", param); 
718                      pause = 1;
719               }
720               if (dbus_message_is_signal(msg, LIVEWP_SIGNAL_INTERFACE, LIVEWP_PLAY_LIVEBG_ON_VIEW)){
721                  fprintf(stderr, "dbus   Play scene visible %i\n", param); 
722                      pause = 0;
723               }
724               /*msg = dbus_connection_pop_message(conn);*/
725               fprintf(stderr, "dbus  steal message serial = %i\n", dbus_message_get_serial(msg)); 
726               /*dbus_connection_steal_borrowed_message(conn, msg);*/
727           }else{ 
728               fprintf(stderr, "dbus  return message\n"); 
729               /*dbus_connection_return_message(conn, msg);*/
730           }
731       }    
732 #endif      
733 /*      dbus_message_unref (msg); */
734     }
735
736   ft->free_cb (dpy, window, closure);
737   if (fpst) fps_free (fpst);
738
739 #ifdef DEBUG_PAIR
740   if (window2) ft->free_cb (dpy, window2, closure2);
741   if (window2) fps_free (fpst2);
742 #endif
743   /* close the connection */
744   dbus_connection_close(conn);
745 }
746
747
748 static Widget
749 make_shell (Screen *screen, Widget toplevel, int width, int height)
750 {
751   Display *dpy = DisplayOfScreen (screen);
752   Visual *visual = pick_visual (screen);
753   Boolean def_visual_p = (toplevel && 
754                           visual == DefaultVisualOfScreen (screen));
755
756   if (width  <= 0) width  = 600;
757   if (height <= 0) height = 480;
758
759   if (def_visual_p)
760     {
761       Window window;
762       XtVaSetValues (toplevel,
763                      XtNmappedWhenManaged, False,
764                      XtNwidth, width,
765                      XtNheight, height,
766                      XtNinput, True,  /* for WM_HINTS */
767                      NULL);
768       XtRealizeWidget (toplevel);
769       window = XtWindow (toplevel);
770
771       if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
772         {
773           Colormap cmap = 
774             XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
775                              AllocNone);
776           XSetWindowColormap (dpy, window, cmap);
777         }
778     }
779   else
780     {
781       unsigned int bg, bd;
782       Widget new;
783       Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
784                                        visual, AllocNone);
785       bg = get_pixel_resource (dpy, cmap, "background", "Background");
786       bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
787
788       new = XtVaAppCreateShell (progname, progclass,
789                                 topLevelShellWidgetClass, dpy,
790                                 XtNmappedWhenManaged, False,
791                                 XtNvisual, visual,
792                                 XtNdepth, visual_depth (screen, visual),
793                                 XtNwidth, width,
794                                 XtNheight, height,
795                                 XtNcolormap, cmap,
796                                 XtNbackground, (Pixel) bg,
797                                 XtNborderColor, (Pixel) bd,
798                                 XtNinput, True,  /* for WM_HINTS */
799                                 NULL);
800
801       if (!toplevel)  /* kludge for the second window in -pair mode... */
802         XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
803
804       XtRealizeWidget (new);
805       toplevel = new;
806     }
807
808   return toplevel;
809 }
810
811 static void
812 init_window (Display *dpy, Widget toplevel, const char *title)
813 {
814   Window window;
815   XWindowAttributes xgwa;
816   XtPopup (toplevel, XtGrabNone);
817   XtVaSetValues (toplevel, XtNtitle, title, NULL);
818
819   /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
820    */
821   window = XtWindow (toplevel);
822   XGetWindowAttributes (dpy, window, &xgwa);
823   XSelectInput (dpy, window,
824                 (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
825                  ButtonPressMask | ButtonReleaseMask));
826   XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
827                    PropModeReplace,
828                    (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
829 }
830
831
832 int
833 main (int argc, char **argv)
834 {
835   struct xscreensaver_function_table *ft = xscreensaver_function_table;
836
837 /*  osso_context_t  *osso; */
838
839   XWindowAttributes xgwa;
840   Widget toplevel;
841   Display *dpy;
842   Window window;
843 # ifdef DEBUG_PAIR
844   Window window2 = 0;
845   Widget toplevel2 = 0;
846 # endif
847   XtAppContext app;
848   Bool root_p;
849   Window on_window = 0;
850   XEvent event;
851   Boolean dont_clear;
852   char version[255];
853   int view;
854
855   fix_fds();
856
857   progname = argv[0];   /* reset later */
858   progclass = ft->progclass;
859
860   if (ft->setup_cb)
861     ft->setup_cb (ft, ft->setup_arg);
862
863   merge_options ();
864
865 #ifdef __sgi
866   /* We have to do this on SGI to prevent the background color from being
867      overridden by the current desktop color scheme (we'd like our backgrounds
868      to be black, thanks.)  This should be the same as setting the
869      "*useSchemes: none" resource, but it's not -- if that resource is
870      present in the `default_defaults' above, it doesn't work, though it
871      does work when passed as an -xrm arg on the command line.  So screw it,
872      turn them off from C instead.
873    */
874   SgiUseSchemes ("none"); 
875 #endif /* __sgi */
876
877   toplevel = XtAppInitialize (&app, progclass, merged_options,
878                               merged_options_size, &argc, argv,
879                               merged_defaults, 0, 0);
880
881   dpy = XtDisplay (toplevel);
882
883   XtGetApplicationNameAndClass (dpy,
884                                 (char **) &progname,
885                                 (char **) &progclass);
886
887   /* half-assed way of avoiding buffer-overrun attacks. */
888   if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
889
890   XSetErrorHandler (screenhack_ehandler);
891
892   XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
893   XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
894
895   {
896     char *v = (char *) strdup(strchr(screensaver_id, ' '));
897     char *s1, *s2, *s3, *s4;
898     s1 = (char *) strchr(v,  ' '); s1++;
899     s2 = (char *) strchr(s1, ' ');
900     s3 = (char *) strchr(v,  '('); s3++;
901     s4 = (char *) strchr(s3, ')');
902     *s2 = 0;
903     *s4 = 0;
904     sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
905              progclass, s1, s3);
906     free(v);
907   }
908
909   if (argc > 1)
910     {
911       const char *s;
912       int i;
913       int x = 18;
914       int end = 78;
915       Bool help_p = (!strcmp(argv[1], "-help") ||
916                      !strcmp(argv[1], "--help"));
917       fprintf (stderr, "%s\n", version);
918       for (s = progclass; *s; s++) fprintf(stderr, " ");
919       fprintf (stderr, "  http://www.jwz.org/xscreensaver/\n\n");
920
921       if (!help_p)
922         fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
923       fprintf (stderr, "Options include: ");
924       for (i = 0; i < merged_options_size; i++)
925         {
926           char *sw = merged_options [i].option;
927           Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
928           int size = strlen (sw) + (argp ? 6 : 0) + 2;
929           if (x + size >= end)
930             {
931               fprintf (stderr, "\n\t\t ");
932               x = 18;
933             }
934           x += size;
935           fprintf (stderr, "%s", sw);
936           if (argp) fprintf (stderr, " <arg>");
937           if (i != merged_options_size - 1) fprintf (stderr, ", ");
938         }
939
940       fprintf (stderr, ".\n");
941
942 #if 0
943       if (help_p)
944         {
945           fprintf (stderr, "\nResources:\n\n");
946           for (i = 0; i < merged_options_size; i++)
947             {
948               const char *opt = merged_options [i].option;
949               const char *res = merged_options [i].specifier + 1;
950               const char *val = merged_options [i].value;
951               char *s = get_string_resource (dpy, (char *) res, (char *) res);
952
953               if (s)
954                 {
955                   int L = strlen(s);
956                 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
957                   s[--L] = 0;
958                 }
959
960               fprintf (stderr, "    %-16s %-18s ", opt, res);
961               if (merged_options [i].argKind == XrmoptionSepArg)
962                 {
963                   fprintf (stderr, "[%s]", (s ? s : "?"));
964                 }
965               else
966                 {
967                   fprintf (stderr, "%s", (val ? val : "(null)"));
968                   if (val && s && !strcasecmp (val, s))
969                     fprintf (stderr, " [default]");
970                 }
971               fprintf (stderr, "\n");
972             }
973           fprintf (stderr, "\n");
974         }
975 #endif
976
977       exit (help_p ? 0 : 1);
978     }
979
980   {
981     char **s;
982     for (s = merged_defaults; *s; s++)
983       free(*s);
984   }
985
986   free (merged_options);
987   free (merged_defaults);
988   merged_options = 0;
989   merged_defaults = 0;
990
991   dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
992   mono_p = get_boolean_resource (dpy, "mono", "Boolean");
993   if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
994     mono_p = True;
995
996   root_p = get_boolean_resource (dpy, "root", "Boolean");
997
998   {
999     char *s = get_string_resource (dpy, "windowID", "WindowID");
1000     if (s && *s)
1001       on_window = get_integer_resource (dpy, "windowID", "WindowID");
1002     if (s) free (s);
1003   }
1004
1005   view = get_integer_resource (dpy, "view", "view");
1006   fprintf(stderr, "View %i\n", view);
1007   if (on_window)
1008     {
1009       window = (Window) on_window;
1010       XtDestroyWidget (toplevel);
1011       XGetWindowAttributes (dpy, window, &xgwa);
1012       visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
1013
1014       /* Select KeyPress and resize events on the external window.
1015        */
1016       xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
1017       XSelectInput (dpy, window, xgwa.your_event_mask);
1018
1019       /* Select ButtonPress and ButtonRelease events on the external window,
1020          if no other app has already selected them (only one app can select
1021          ButtonPress at a time: BadAccess results.)
1022        */
1023       if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
1024         XSelectInput (dpy, window,
1025                       (xgwa.your_event_mask |
1026                        ButtonPressMask | ButtonReleaseMask));
1027     }
1028   else if (root_p)
1029     {
1030       window = VirtualRootWindowOfScreen (XtScreen (toplevel));
1031       XtDestroyWidget (toplevel);
1032       XGetWindowAttributes (dpy, window, &xgwa);
1033       /* With RANDR, the root window can resize! */
1034       XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
1035       visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
1036     }
1037   else
1038     {
1039       Widget new = make_shell (XtScreen (toplevel), toplevel,
1040                                toplevel->core.width,
1041                                toplevel->core.height);
1042       if (new != toplevel)
1043         {
1044           XtDestroyWidget (toplevel);
1045           toplevel = new;
1046         }
1047
1048       init_window (dpy, toplevel, version);
1049       window = XtWindow (toplevel);
1050       XGetWindowAttributes (dpy, window, &xgwa);
1051
1052 # ifdef DEBUG_PAIR
1053       if (get_boolean_resource (dpy, "pair", "Boolean"))
1054         {
1055           toplevel2 = make_shell (xgwa.screen, 0,
1056                                   toplevel->core.width,
1057                                   toplevel->core.height);
1058           init_window (dpy, toplevel2, version);
1059           window2 = XtWindow (toplevel2);
1060         }
1061 # endif /* DEBUG_PAIR */
1062     }
1063
1064   if (!dont_clear)
1065     {
1066       unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
1067                                             "background", "Background");
1068       XSetWindowBackground (dpy, window, bg);
1069       XClearWindow (dpy, window);
1070 # ifdef DEBUG_PAIR
1071       if (window2)
1072         {
1073           XSetWindowBackground (dpy, window2, bg);
1074           XClearWindow (dpy, window2);
1075         }
1076 # endif
1077     }
1078
1079   if (!root_p && !on_window)
1080     /* wait for it to be mapped */
1081     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
1082
1083   XSync (dpy, False);
1084
1085   /* This is the one and only place that the random-number generator is
1086      seeded in any screenhack.  You do not need to seed the RNG again,
1087      it is done for you before your code is invoked. */
1088 # undef ya_rand_init
1089   ya_rand_init (0);
1090
1091   run_screenhack_table (dpy, window, 
1092 # ifdef DEBUG_PAIR
1093                         window2,
1094 # endif
1095                         ft, view);
1096
1097   XtDestroyWidget (toplevel);
1098   XtDestroyApplicationContext (app);
1099
1100   return 0;
1101 }