http://ftp.x.org/contrib/applications/xscreensaver-3.20.tar.gz
[xscreensaver] / hacks / screenhack.c
index ace18c57e88e58660854bed43c8c641a44e1504b..786d24b4cc5ddf95a3f02eb4186c5c5fecedb0e8 100644 (file)
@@ -1,5 +1,5 @@
 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
- *  Jamie Zawinski <jwz@netscape.com>
+ *  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
 #include <X11/CoreP.h>
 #include <X11/Shell.h>
 #include <X11/StringDefs.h>
+#include <X11/Xutil.h>
+#include <X11/keysym.h>
+
+#ifdef __sgi
+# include <X11/SGIScheme.h>    /* for SgiUseSchemes() */
+#endif /* __sgi */
+
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Error.h>
 #include "version.h"
 #include "vroot.h"
 
+#ifndef isupper
+# define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c)  ((c) - 'A' + 'a')
+#endif
+
+
 char *progname;
 XrmDatabase db;
+XtAppContext app;
 Bool mono_p;
 
 static XrmOptionDescRec default_options [] = {
@@ -66,7 +82,7 @@ static XrmOptionDescRec default_options [] = {
 };
 
 static char *default_defaults[] = {
-  "*root:              false",
+  ".root:              false",
   "*geometry:          600x480", /* this should be .geometry, but nooooo... */
   "*mono:              false",
   "*installColormap:   false",
@@ -110,6 +126,23 @@ merge_options (void)
          def_defaults_size * sizeof(*defaults));
   memcpy (merged_defaults + def_defaults_size, defaults,
          (defaults_size + 1) * sizeof(*defaults));
+
+  /* This totally sucks.  Xt should behave like this by default.
+     If the string in `defaults' looks like ".foo", change that
+     to "Progclass.foo".
+   */
+  {
+    char **s;
+    for (s = merged_defaults; *s; s++)
+      if (**s == '.')
+       {
+         const char *oldr = *s;
+         char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
+         strcpy (newr, progclass);
+         strcat (newr, oldr);
+         *s = newr;
+       }
+  }
 }
 
 \f
@@ -136,23 +169,167 @@ MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
 }
 
 
-#ifdef USE_GL
-extern Visual *get_gl_visual (Screen *, const char *, const char *);
-#endif
-
 #ifdef XLOCKMORE
 extern void pre_merge_options (void);
 #endif
 
 
+static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
+
+/* Dead-trivial event handling: exits if "q" or "ESC" are typed.
+   Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
+ */
+void
+screenhack_handle_event (Display *dpy, XEvent *event)
+{
+  switch (event->xany.type)
+    {
+    case KeyPress:
+      {
+        KeySym keysym;
+        char c = 0;
+        XLookupString (&event->xkey, &c, 1, &keysym, 0);
+        if (c == 'q' ||
+            c == 'Q' ||
+            c == 3 ||  /* ^C */
+            c == 27)   /* ESC */
+          exit (0);
+        else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
+          XBell (dpy, 0);  /* beep for non-chord keys */
+      }
+      break;
+    case ButtonPress:
+      XBell (dpy, 0);
+      break;
+    case ClientMessage:
+      {
+        if (event->xclient.message_type != XA_WM_PROTOCOLS)
+          {
+            char *s = XGetAtomName(dpy, event->xclient.message_type);
+            if (!s) s = "(null)";
+            fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
+                     progname, s);
+          }
+        else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
+          {
+            char *s1 = XGetAtomName(dpy, event->xclient.message_type);
+            char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
+            if (!s1) s1 = "(null)";
+            if (!s2) s2 = "(null)";
+            fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
+                     progname, s1, s2);
+          }
+        else
+          {
+            exit (0);
+          }
+      }
+      break;
+    }
+}
+
+
+void
+screenhack_handle_events (Display *dpy)
+{
+  while (XPending (dpy))
+    {
+      XEvent event;
+      XNextEvent (dpy, &event);
+      screenhack_handle_event (dpy, &event);
+    }
+}
+
+
+static Visual *
+pick_visual (Screen *screen)
+{
+#ifdef USE_GL
+  /* If we're linking against GL (that is, this is the version of screenhack.o
+     that the GL hacks will use, which is different from the one that the
+     non-GL hacks will use) then try to pick the "best" visual by interrogating
+     the GL library instead of by asking Xlib.  GL knows better.
+   */
+  Visual *v = 0;
+  char *string = get_string_resource ("visualID", "VisualID");
+  char *s;
+
+  if (string)
+    for (s = string; *s; s++)
+      if (isupper (*s)) *s = _tolower (*s);
+
+  if (!string || !*string ||
+      !strcmp (string, "gl") ||
+      !strcmp (string, "best") ||
+      !strcmp (string, "color") ||
+      !strcmp (string, "default"))
+    v = get_gl_visual (screen);                /* from ../utils/visual-gl.c */
+
+  if (string)
+    free (string);
+  if (v)
+    return v;
+#endif /* USE_GL */
+
+  return get_visual_resource (screen, "visualID", "VisualID", False);
+}
+
+
+/* Notice when the user has requested a different visual or colormap
+   on a pre-existing window (e.g., "-root -visual truecolor" or
+   "-window-id 0x2c00001 -install") and complain, since when drawing
+   on an existing window, we have no choice about these things.
+ */
+static void
+visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
+                Bool window_p)
+{
+  char *visual_string = get_string_resource ("visualID", "VisualID");
+  Visual *desired_visual = pick_visual (screen);
+  char win[100];
+  char why[100];
+
+  if (window == RootWindowOfScreen (screen))
+    strcpy (win, "root window");
+  else
+    sprintf (win, "window 0x%x", (unsigned long) window);
+
+  if (window_p)
+    sprintf (why, "-window-id 0x%x", (unsigned long) window);
+  else
+    strcpy (why, "-root");
+
+  if (visual_string && *visual_string)
+    {
+      if (visual != desired_visual)
+        {
+          fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
+                   progname, visual_string, why);
+          fprintf (stderr, "%s: using %s's visual 0x%x.\n",
+                   progname, win, XVisualIDFromVisual (visual));
+        }
+      free (visual_string);
+    }
+
+  if (visual == DefaultVisualOfScreen (screen) &&
+      has_writable_cells (screen, visual) &&
+      get_boolean_resource ("installColormap", "InstallColormap"))
+    {
+      fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
+               progname, why);
+      fprintf (stderr, "%s: using %s's colormap 0x%x.\n",
+               progname, win, (unsigned long) cmap);
+    }
+}
+
 
 int
 main (int argc, char **argv)
 {
-  XtAppContext app;
   Widget toplevel;
   Display *dpy;
   Window window;
+  Screen *screen;
   Visual *visual;
   Colormap cmap;
   Bool root_p;
@@ -165,20 +342,47 @@ main (int argc, char **argv)
   pre_merge_options ();
 #endif
   merge_options ();
+
+#ifdef __sgi
+  /* We have to do this on SGI to prevent the background color from being
+     overridden by the current desktop color scheme (we'd like our backgrounds
+     to be black, thanks.)  This should be the same as setting the
+     "*useSchemes: none" resource, but it's not -- if that resource is
+     present in the `default_defaults' above, it doesn't work, though it
+     does work when passed as an -xrm arg on the command line.  So screw it,
+     turn them off from C instead.
+   */
+  SgiUseSchemes ("none"); 
+#endif /* __sgi */
+
   toplevel = XtAppInitialize (&app, progclass, merged_options,
                              merged_options_size, &argc, argv,
                              merged_defaults, 0, 0);
   dpy = XtDisplay (toplevel);
+  screen = XtScreen (toplevel);
   db = XtDatabase (dpy);
+
   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
+
+  /* half-assed way of avoiding buffer-overrun attacks. */
+  if (strlen (progname) >= 100) progname[100] = 0;
+
   XSetErrorHandler (screenhack_ehandler);
 
+  XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
+  XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
+
   {
     char *v = (char *) strdup(strchr(screensaver_id, ' '));
-    char *s = (char *) strchr(v, ',');
-    *s = 0;
-    sprintf (version, "%s: from the XScreenSaver%s distribution.",
-            progclass, v);
+    char *s1, *s2, *s3, *s4;
+    s1 = (char *) strchr(v,  ' '); s1++;
+    s2 = (char *) strchr(s1, ' ');
+    s3 = (char *) strchr(v,  '('); s3++;
+    s4 = (char *) strchr(s3, ')');
+    *s2 = 0;
+    *s4 = 0;
+    sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
+            progclass, s1, s3);
     free(v);
   }
 
@@ -191,7 +395,7 @@ main (int argc, char **argv)
       Bool help_p = !strcmp(argv[1], "-help");
       fprintf (stderr, "%s\n", version);
       for (s = progclass; *s; s++) fprintf(stderr, " ");
-      fprintf (stderr, "  http://people.netscape.com/jwz/xscreensaver/\n\n");
+      fprintf (stderr, "  http://www.jwz.org/xscreensaver/\n\n");
 
       if (!help_p)
        fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
@@ -238,6 +442,7 @@ main (int argc, char **argv)
       XGetWindowAttributes (dpy, window, &xgwa);
       cmap = xgwa.colormap;
       visual = xgwa.visual;
+      visual_warning (screen, window, visual, cmap, True);
     }
   else if (root_p)
     {
@@ -247,17 +452,12 @@ main (int argc, char **argv)
       XGetWindowAttributes (dpy, window, &xgwa);
       cmap = xgwa.colormap;
       visual = xgwa.visual;
+      visual_warning (screen, window, visual, cmap, False);
     }
   else
     {
       Boolean def_visual_p;
-      Screen *screen = XtScreen (toplevel);
-
-#ifdef USE_GL
-      visual = get_gl_visual (screen, "visualID", "VisualID");
-#else
-      visual = get_visual_resource (screen, "visualID", "VisualID", False);
-#endif
+      visual = pick_visual (screen);
 
       if (toplevel->core.width <= 0)
        toplevel->core.width = 600;
@@ -324,6 +524,18 @@ main (int argc, char **argv)
        }
 
       XtVaSetValues(toplevel, XtNtitle, version, 0);
+
+      /* For screenhack_handle_events(): select KeyPress, and
+         announce that we accept WM_DELETE_WINDOW. */
+      {
+        XWindowAttributes xgwa;
+        XGetWindowAttributes (dpy, window, &xgwa);
+        XSelectInput (dpy, window,
+                      xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
+        XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
+                         PropModeReplace,
+                         (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
+      }
     }
 
   if (!dont_clear)