summaryrefslogtreecommitdiffstats
path: root/driver/xscreensaver-gfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver/xscreensaver-gfx.c')
-rw-r--r--driver/xscreensaver-gfx.c595
1 files changed, 595 insertions, 0 deletions
diff --git a/driver/xscreensaver-gfx.c b/driver/xscreensaver-gfx.c
new file mode 100644
index 0000000..5a543a4
--- /dev/null
+++ b/driver/xscreensaver-gfx.c
@@ -0,0 +1,595 @@
+/* xscreensaver, Copyright © 1991-2021 Jamie Zawinski <jwz@jwz.org>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation. No representations are made about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * XScreenSaver Daemon, version 6.
+ *
+ * This is "xscreensaver-gfx" -- When the time comes for the screen to blank,
+ * this process is launched by "xscreensaver" to fade out, black out every
+ * monitor on the system, launch graphics demos to render on those blanked
+ * screens, and cycle them from time to time. See the comment atop
+ * xscreensaver.c for details of the division of powers.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <X11/Xlib.h>
+#include <X11/Xlibint.h>
+#include <X11/Xatom.h>
+#include <X11/Intrinsic.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h>
+#include <X11/Xos.h>
+#include <time.h>
+#include <sys/time.h>
+#include <netdb.h> /* for gethostbyname() */
+#include <sys/types.h>
+#include <pwd.h>
+
+#include <X11/extensions/XInput2.h>
+
+#ifdef HAVE_RANDR
+# include <X11/extensions/Xrandr.h>
+#endif /* HAVE_RANDR */
+
+#ifdef ENABLE_NLS
+# include <locale.h>
+# include <libintl.h>
+#endif
+
+#include "xscreensaver.h"
+#include "version.h"
+#include "atoms.h"
+#include "yarandom.h"
+#include "resources.h"
+#include "visual.h"
+#include "screens.h"
+#include "clientmsg.h"
+#include "xmu.h"
+
+saver_info *global_si_kludge = 0; /* I hate C so much... */
+
+char *progclass = 0;
+XrmDatabase db = 0;
+Bool debug_p = False;
+
+
+#ifdef __GNUC__
+ __extension__ /* shut up about "string length is greater than the length
+ ISO C89 compilers are required to support" when including
+ the .ad file... */
+#endif
+
+static char *defaults[] = {
+#include "XScreenSaver_ad.h"
+ 0
+};
+
+#ifdef _VROOT_H_
+ERROR! You must not include vroot.h in this file.
+#endif
+
+
+void
+maybe_reload_init_file (saver_info *si)
+{
+ saver_preferences *p = &si->prefs;
+ if (init_file_changed_p (p))
+ {
+ if (p->verbose_p)
+ fprintf (stderr, "%s: file \"%s\" has changed, reloading\n",
+ blurb(), init_file_name());
+
+ load_init_file (si->dpy, p);
+
+ /* If the DPMS settings in the init file have changed, change the
+ settings on the server to match. This also would have happened at
+ the watchdog timer. */
+ sync_server_dpms_settings (si->dpy, p);
+ }
+}
+
+
+static int
+saver_ehandler (Display *dpy, XErrorEvent *error)
+{
+ saver_info *si = global_si_kludge; /* I hate C so much... */
+ int i;
+
+ fprintf (stderr, "\n"
+ "#######################################"
+ "#######################################\n\n"
+ "%s: X Error! PLEASE REPORT THIS BUG.\n",
+ blurb());
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ fprintf (stderr, "%s: screen %d/%d: 0x%x, 0x%x\n",
+ blurb(), ssi->real_screen_number, ssi->number,
+ (unsigned int) RootWindowOfScreen (si->screens[i].screen),
+ (unsigned int) si->screens[i].screensaver_window);
+ }
+
+ fprintf (stderr, "\n"
+ "#######################################"
+ "#######################################\n\n");
+
+ XmuPrintDefaultErrorMessage (dpy, error, stderr);
+ exit (1);
+}
+
+
+static void
+connect_to_server (saver_info *si)
+{
+ Widget toplevel_shell;
+ Window daemon_window;
+ XrmOptionDescRec options;
+ char *p;
+ int ac = 1;
+ char *av[] = { "xscreensaver" }; /* For Xt and Xrm purposes */
+
+ XSetErrorHandler (saver_ehandler);
+
+ toplevel_shell = XtAppInitialize (&si->app, progclass, &options, 0,
+ &ac, av, defaults, 0, 0);
+
+ si->dpy = XtDisplay (toplevel_shell);
+ si->prefs.db = XtDatabase (si->dpy);
+ XtGetApplicationNameAndClass (si->dpy, &p, &progclass);
+
+ db = si->prefs.db; /* resources.c needs this */
+
+ init_xscreensaver_atoms (si->dpy);
+
+ /* Select property changes on the window created by our parent xscreensaver
+ process: this is where ClientMessage events are sent, and response
+ properties are written.
+ */
+ daemon_window = find_screensaver_window (si->dpy, 0);
+ if (daemon_window)
+ {
+ XWindowAttributes xgwa;
+ XGetWindowAttributes (si->dpy, daemon_window, &xgwa);
+ XSelectInput (si->dpy, daemon_window,
+ xgwa.your_event_mask | PropertyChangeMask);
+ }
+ else
+ {
+ fprintf (stderr, "%s: xscreensaver does not seem to be running!\n",
+ blurb());
+ }
+}
+
+
+static void
+initialize_randr (saver_info *si)
+{
+ saver_preferences *p = &si->prefs;
+
+#ifdef HAVE_RANDR
+ if (XRRQueryExtension (si->dpy,
+ &si->randr_event_number, &si->randr_error_number))
+ {
+ int nscreens = ScreenCount (si->dpy); /* number of *real* screens */
+ int i;
+
+ si->using_randr_extension = TRUE;
+
+ if (p->verbose_p)
+ fprintf (stderr, "%s: selecting RANDR events\n", blurb());
+ for (i = 0; i < nscreens; i++)
+# ifdef RRScreenChangeNotifyMask /* randr.h 1.5, 2002/09/29 */
+ XRRSelectInput (si->dpy, RootWindow (si->dpy, i),
+ RRScreenChangeNotifyMask);
+# else /* !RRScreenChangeNotifyMask */ /* Xrandr.h 1.4, 2001/06/07 */
+ XRRScreenChangeSelectInput (si->dpy, RootWindow (si->dpy, i), True);
+# endif /* !RRScreenChangeNotifyMask */
+ }
+# endif /* HAVE_RANDR */
+}
+
+
+/* Read the XA_SCREENSAVER_STATUS propery from the root window of screen 0
+ to see what hacks were selected the last time we ran, so that -next and
+ -prev can work.
+ */
+static void
+read_status_prop (saver_info *si)
+{
+ Window w = RootWindow (si->dpy, 0); /* always screen 0 */
+ Atom type;
+ unsigned char *dataP = 0;
+ int format, i;
+ unsigned long nitems, bytesafter;
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ ssi->current_hack = -1;
+ }
+
+ XGetWindowProperty (si->dpy, w, XA_SCREENSAVER_STATUS,
+ 0, 999, False, XA_INTEGER, &type, &format, &nitems,
+ &bytesafter, &dataP);
+ if (dataP && type == XA_INTEGER && nitems >= 3)
+ {
+ for (i = 2; i < nitems; i++)
+ {
+ int j = i - 2;
+ if (j < si->nscreens)
+ {
+ saver_screen_info *ssi = &si->screens[j];
+ int n = ((PROP32 *) dataP)[i];
+ ssi->current_hack = n-1; /* 1-based */
+ }
+ }
+ }
+ if (dataP)
+ XFree (dataP);
+}
+
+
+/* Processing ClientMessage events.
+ Both xscreensaver and xscreensaver-gfx handle these; some are handled
+ exclusively by one program or another, and a couple (next, prev) are
+ handled by xscreensaver only if xscreensaver-gfx is not already running.
+ */
+static void
+handle_clientmessage (saver_info *si, XEvent *xev)
+{
+ Display *dpy = si->dpy;
+ saver_preferences *p = &si->prefs;
+ Atom type = 0;
+
+ if (xev->xclient.message_type != XA_SCREENSAVER ||
+ xev->xclient.format != 32)
+ return;
+
+ /* Preferences might affect our handling of client messages. */
+ maybe_reload_init_file (si);
+
+ type = xev->xclient.data.l[0];
+
+ if (type == XA_CYCLE)
+ {
+ int i;
+ clientmessage_response (dpy, xev, True, "cycling");
+ si->selection_mode = 0; /* 0 means randomize when its time. */
+ CYCLE:
+ si->demoing_p = False;
+ monitor_power_on (si, True);
+
+ for (i = 0; i < si->nscreens; i++)
+ {
+ saver_screen_info *ssi = &si->screens[i];
+ cycle_timer ((XtPointer) ssi, 0);
+ }
+ }
+ else if (type == XA_NEXT || type == XA_PREV)
+ {
+ /* If xscreensaver-gfx was not running, these were handled by
+ xscreensaver. If xscreensaver-gfx is running, then xscreensaver
+ ignored these and we reply instead.
+ */
+ clientmessage_response (dpy, xev, True, "cycling");
+ si->selection_mode = (type == XA_NEXT ? -1 : -2);
+ goto CYCLE;
+ }
+ else if (type == XA_SELECT)
+ {
+ /* Same xscreensaver/xscreensaver-gfx division of labor as next/prev. */
+ long which = xev->xclient.data.l[1];
+
+ if (p->mode == DONT_BLANK)
+ {
+ clientmessage_response (dpy, xev, False, "blanking disabled");
+ return;
+ }
+
+ clientmessage_response (dpy, xev, True, "selecting");
+
+ if (which < 0) which = 0; /* 0 == "random" */
+ si->selection_mode = which;
+ goto CYCLE;
+ }
+ else
+ {
+ /* All other ClientMessages are handled by xscreensaver rather than
+ xscreensaver-gfx, and it will return an error message for unknown
+ ones. */
+ }
+}
+
+
+#ifdef DEBUG_MULTISCREEN
+/* For monitor hot-swap stress-testing. See screens.c. */
+static void
+debug_multiscreen_timer (XtPointer closure, XtIntervalId *id)
+{
+ saver_info *si = (saver_info *) closure;
+ saver_preferences *p = &si->prefs;
+ if (update_screen_layout (si))
+ {
+ if (p->verbose_p)
+ {
+ fprintf (stderr, "%s: new layout:\n", blurb());
+ describe_monitor_layout (si->monitor_layout);
+ }
+ resize_screensaver_window (si);
+ }
+ XtAppAddTimeOut (si->app, 1000*4, debug_multiscreen_timer, (XtPointer) si);
+}
+#endif /* DEBUG_MULTISCREEN */
+
+
+static void
+main_loop (saver_info *si, Bool init_p)
+{
+ saver_preferences *p = &si->prefs;
+
+ if (init_p && p->verbose_p)
+ print_available_extensions (si);
+
+ initialize_randr (si);
+ update_screen_layout (si);
+ describe_monitor_layout (si->monitor_layout);
+
+ initialize_screensaver_window (si);
+ init_sigchld (si);
+ read_status_prop (si);
+
+ if (! p->verbose_p)
+ ;
+ else if (si->demoing_p)
+ fprintf (stderr, "%s: demoing %d\n", blurb(), si->selection_mode);
+ else
+ fprintf (stderr, "%s: blanking\n", blurb());
+
+ blank_screen (si);
+
+# ifdef DEBUG_MULTISCREEN
+ if (p->debug_p)
+ debug_multiscreen_timer ((XtPointer) si, 0);
+# endif
+
+ while (1)
+ {
+ /* We have to go through this union bullshit because gcc-4.4.0 has
+ stricter struct-aliasing rules. Without this, the optimizer
+ can fuck things up. */
+ union {
+ XEvent x_event;
+# ifdef HAVE_RANDR
+ XRRScreenChangeNotifyEvent xrr_event;
+# endif /* HAVE_RANDR */
+ } event;
+
+ /* Block waiting for the next X event, while running timers. */
+ XtAppNextEvent (si->app, &event.x_event);
+
+ if (event.x_event.xany.type == ClientMessage)
+ handle_clientmessage (si, &event.x_event);
+# ifdef HAVE_RANDR
+ else if (si->using_randr_extension &&
+ (event.x_event.type ==
+ (si->randr_event_number + RRScreenChangeNotify)))
+ {
+ /* The Resize and Rotate extension sends an event when the
+ size, rotation, or refresh rate of any screen has changed.
+ */
+ if (p->verbose_p)
+ {
+ int screen = XRRRootToScreen (si->dpy, event.xrr_event.window);
+ fprintf (stderr, "%s: %d: screen change event received\n",
+ blurb(), screen);
+ }
+
+ /* Inform Xlib that it's ok to update its data structures. */
+ XRRUpdateConfiguration (&event.x_event);
+
+ /* Resize the existing xscreensaver windows and cached ssi data. */
+ if (update_screen_layout (si))
+ {
+ if (p->verbose_p)
+ {
+ fprintf (stderr, "%s: new layout:\n", blurb());
+ describe_monitor_layout (si->monitor_layout);
+ }
+ resize_screensaver_window (si);
+ }
+ }
+# endif /* HAVE_RANDR */
+
+ XtDispatchEvent (&event.x_event);
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ char *dpy_str = getenv ("DISPLAY");
+ char *s;
+ saver_info the_si;
+ saver_info *si = &the_si;
+ saver_preferences *p = &si->prefs;
+ Bool init_p = False;
+ Bool cmdline_verbose_val = False, cmdline_verbose_p = False;
+ int i;
+
+ memset(si, 0, sizeof(*si));
+ global_si_kludge = si; /* I hate C so much... */
+
+# undef ya_rand_init
+ ya_rand_init (0);
+
+ /* For Xt and X resource database purposes, this program is
+ "xscreensaver", not "xscreensaver-gfx".
+ */
+ s = strrchr(argv[0], '/');
+ if (s) s++;
+ else s = argv[0];
+ if (strlen(s) > 20) /* keep it short. */
+ s[20] = 0;
+ progname = s;
+
+ progclass = "XScreenSaver";
+
+ si->version = strdup (screensaver_id + 17);
+ s = strchr (si->version, ' ');
+ *s = 0;
+
+ for (i = 1; i < argc; i++)
+ {
+ const char *oa = argv[i];
+ /* XScreenSaver predates the "--arg" convention. */
+ if (argv[i][0] == '-' && argv[i][1] == '-')
+ argv[i]++;
+
+ if (!strcmp (argv[i], "-debug"))
+ p->debug_p = True;
+ else if (!strcmp (argv[i], "-v") || !strcmp (argv[i], "-verbose"))
+ {
+ p->verbose_p++;
+ cmdline_verbose_p = True, cmdline_verbose_val = p->verbose_p;
+ }
+ else if (!strcmp (argv[i], "-vv"))
+ {
+ p->verbose_p += 2;
+ cmdline_verbose_p = True, cmdline_verbose_val = p->verbose_p;
+ }
+ else if (!strcmp (argv[i], "-vvv"))
+ {
+ p->verbose_p += 3;
+ cmdline_verbose_p = True, cmdline_verbose_val = p->verbose_p;
+ }
+ else if (!strcmp (argv[i], "-vvvv"))
+ {
+ p->verbose_p += 4;
+ cmdline_verbose_p = True, cmdline_verbose_val = p->verbose_p;
+ }
+ else if (!strcmp (argv[i], "-q") || !strcmp (argv[i], "-quiet"))
+ {
+ p->verbose_p = False;
+ cmdline_verbose_p = True, cmdline_verbose_val = p->verbose_p;
+ }
+ else if (!strcmp (argv[i], "-init"))
+ init_p = True;
+ else if (!strcmp (argv[i], "-d") ||
+ !strcmp (argv[i], "-dpy") ||
+ !strcmp (argv[i], "-disp") ||
+ !strcmp (argv[i], "-display"))
+ {
+ dpy_str = argv[++i];
+ if (!dpy_str) goto HELP;
+ }
+ else if (!strcmp (argv[i], "-sync") ||
+ !strcmp (argv[i], "-synch") ||
+ !strcmp (argv[i], "-synchronize") ||
+ !strcmp (argv[i], "-synchronise"))
+ p->xsync_p = True;
+ else if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "-help"))
+ {
+ HELP:
+ fprintf (stderr,
+ "\n"
+ "\txscreensaver-gfx is launched by the xscreensaver daemon\n"
+ "\tto manage the graphical display modes as subprocesses.\n"
+ "\tDo not run this directly.\n"
+ "\n"
+ "\tOptions:\n"
+ "\t\t--dpy host:display.screen\n"
+ "\t\t--verbose --debug --sync\n"
+ "\t\t--init --emergency\n"
+ "\t\t--next --prev --select N --demo N\n"
+ "\n"
+ "\tRun 'xscreensaver-settings' to configure.\n"
+ "\n");
+ exit (1);
+ }
+ else if (!strcmp (argv[i], "-emergency"))
+ {
+ /* This means we should start up without a fade-out, blanking the
+ screen as quickly as possible. This is used when suspending,
+ and also if xscreensaver-gfx has crashed and is being restarted.
+ */
+ si->emergency_p = True;
+ }
+ else if (!strcmp (argv[i], "-next"))
+ {
+ /* "xscreensaver-command -next" sent a ClientMessage to the daemon
+ while it was idle, so we should start with this hack. */
+ si->selection_mode = -1; /* -1 means next */
+ }
+ else if (!strcmp (argv[i], "-prev"))
+ {
+ si->selection_mode = -2; /* -2 means prev */
+ }
+ else if (!strcmp (argv[i], "-select") ||
+ !strcmp (argv[i], "-demo"))
+ {
+ /* "xscreensaver-command -select N" sent a ClientMessage to the
+ daemon while it was idle, so we should start with this hack. */
+ int n;
+ char c;
+ if (!strcmp (argv[i], "-demo"))
+ si->demoing_p = True; /* Just means "don't auto-cycle" */
+
+ if (! argv[++i]) goto FAIL;
+ if (1 != sscanf (argv[i], " %d %c", &n, &c)) goto HELP;
+ if (n <= 0) goto FAIL;
+ si->selection_mode = n; /* hack number is 1-based */
+ }
+ else
+ {
+ FAIL:
+ fprintf (stderr, "\n%s: unknown option: %s\n", blurb(), oa);
+ goto HELP;
+ }
+ }
+
+ /* Copy the -dpy arg to $DISPLAY for subprocesses. */
+ s = (char *) malloc (strlen(dpy_str) + 20);
+ sprintf (s, "DISPLAY=%s", dpy_str);
+ putenv (s);
+ /* free (s); */ /* some versions of putenv do not copy */
+
+# ifdef ENABLE_NLS
+ {
+ bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
+ textdomain (GETTEXT_PACKAGE);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ if (!setlocale (LC_ALL, ""))
+ fprintf (stderr, "%s: warning: could not set default locale\n",
+ progname);
+ }
+# endif /* ENABLE_NLS */
+
+ connect_to_server (si);
+
+ if (p->xsync_p)
+ XSynchronize (si->dpy, True);
+
+ load_init_file (si->dpy, p);
+
+ /* Command line overrides init file */
+ if (cmdline_verbose_p)
+ p->verbose_p = cmdline_verbose_val;
+ verbose_p = p->verbose_p;
+ debug_p = p->debug_p;
+
+ main_loop (si, init_p); /* doesn't return */
+ exit (-1);
+}