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