1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2013 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
29 #include <sys/param.h> /* for PATH_MAX */
32 #include <X11/Xresource.h>
41 /* This file doesn't need the Xt headers, so stub these types out... */
43 #define XtAppContext void*
44 #define XtIntervalId void*
45 #define XtPointer void*
49 /* Just in case there's something pathological about stat.h... */
51 # define S_IRUSR 00400
54 # define S_IWUSR 00200
57 # define S_IXUSR 00100
60 # define S_IXGRP 00010
63 # define S_IXOTH 00001
69 #include "resources.h"
71 /* don't use realpath() on fedora system */
72 #ifdef _FORTIFY_SOURCE
77 extern char *progname;
78 extern char *progclass;
79 extern const char *blurb (void);
83 static void get_screenhacks (Display *, saver_preferences *);
84 static char *format_command (const char *cmd, Bool wrap_p);
85 static void merge_system_screenhacks (Display *, saver_preferences *,
86 screenhack **system_list, int count);
87 static void stop_the_insanity (saver_preferences *p);
91 chase_symlinks (const char *file)
98 # define PATH_MAX MAXPATHLEN
100 # define PATH_MAX 2048
104 if (realpath (file, buf))
107 /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
110 # endif /* HAVE_REALPATH */
116 i_am_a_nobody (uid_t uid)
120 p = getpwnam ("nobody");
121 if (! p) p = getpwnam ("noaccess");
122 if (! p) p = getpwnam ("daemon");
124 if (! p) /* There is no nobody? */
127 return (uid == p->pw_uid);
132 init_file_name (void)
134 static char *file = 0;
138 uid_t uid = getuid ();
139 struct passwd *p = getpwuid (uid);
141 if (i_am_a_nobody (uid))
142 /* If we're running as nobody, then use root's .xscreensaver file
143 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
144 to be different -- if we didn't do this, then xscreensaver-demo
145 would appear to have no effect when the luser is running as root.)
151 if (!p || !p->pw_name || !*p->pw_name)
153 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
157 else if (!p->pw_dir || !*p->pw_dir)
159 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
160 blurb(), (p->pw_name ? p->pw_name : "???"));
165 const char *home = p->pw_dir;
166 const char *name = ".xscreensaver";
167 file = (char *) malloc(strlen(home) + strlen(name) + 2);
169 if (!*home || home[strlen(home)-1] != '/')
183 init_file_tmp_name (void)
185 static char *file = 0;
188 const char *name = init_file_name();
189 const char *suffix = ".tmp";
191 char *n2 = chase_symlinks (name);
198 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
200 strcat(file, suffix);
213 get_byte_resource (Display *dpy, char *name, char *class)
215 char *s = get_string_resource (dpy, name, class);
220 while (isspace(*s2)) s2++;
221 while (*s2 >= '0' && *s2 <= '9')
223 n = (n * 10) + (*s2 - '0');
226 while (isspace(*s2)) s2++;
227 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
228 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
229 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
233 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
239 if (*s2 == 'b' || *s2 == 'B') s2++;
240 while (isspace(*s2)) s2++;
248 static const char * const prefs[] = {
252 "lockVTs", /* not saved */
265 "helpURL", /* not saved */
266 "loadURL", /* not saved */
267 "newLoginCommand", /* not saved */
275 "captureStdout", /* not saved -- obsolete */
276 "logFile", /* not saved */
277 "ignoreUninstalledPrograms",
286 "chooseRandomImages",
300 "windowCreationTimeout",
302 "sgiSaverExtension", /* not saved -- obsolete */
303 "mitSaverExtension", /* not saved -- obsolete */
304 "xidleExtension", /* not saved -- obsolete */
305 "GetViewPortIsFullOfLies",
307 "xinputExtensionDev",
309 "overlayTextBackground", /* not saved -- X resources only */
310 "overlayTextForeground", /* not saved -- X resources only */
311 "bourneShell", /* not saved -- X resources only */
319 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
321 for (s2 = s; *s2; s2++)
323 for (s2--; s2 >= s; s2--)
324 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
336 handle_entry (XrmDatabase *db, const char *key, const char *value,
337 const char *filename, int line)
340 for (i = 0; prefs[i]; i++)
341 if (*prefs[i] && !strcasecmp(key, prefs[i]))
343 char *val = strdup(value);
344 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
345 strcpy(spec, progclass);
347 strcat(spec, prefs[i]);
349 XrmPutStringResource (db, spec, val);
356 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
357 blurb(), filename, line, key);
363 parse_init_file (saver_preferences *p)
365 time_t write_date = 0;
366 const char *name = init_file_name();
375 if (stat(name, &st) != 0)
377 p->init_file_date = 0;
381 in = fopen(name, "r");
384 char *buf = (char *) malloc(1024 + strlen(name));
385 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
391 if (fstat (fileno(in), &st) == 0)
393 write_date = st.st_mtime;
397 char *buf = (char *) malloc(1024 + strlen(name));
398 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
404 buf = (char *) malloc(buf_size);
406 while (fgets (buf, buf_size-1, in))
413 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
414 buf[L-2] == '\\')) /* or line ended with backslash */
416 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
422 buf = (char *) realloc(buf, buf_size);
426 if (!fgets (buf + L, buf_size-L-1, in))
431 /* Now handle other backslash escapes. */
434 for (i = 0; buf[i]; i++)
439 case 'n': buf[i] = '\n'; break;
440 case 'r': buf[i] = '\r'; break;
441 case 't': buf[i] = '\t'; break;
442 default: buf[i] = buf[i+1]; break;
444 for (j = i+2; buf[j]; j++)
452 if (*key == '#' || *key == '!' || *key == ';' ||
453 *key == '\n' || *key == 0)
456 value = strchr (key, ':');
459 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
466 value = strip(value);
470 handle_entry (&p->db, key, value, name, line);
475 p->init_file_date = write_date;
481 init_file_changed_p (saver_preferences *p)
483 const char *name = init_file_name();
486 if (!name) return False;
488 if (stat(name, &st) != 0)
491 if (p->init_file_date == st.st_mtime)
502 tab_to (FILE *out, int from, int to)
505 int to_mod = (to / tab_width) * tab_width;
506 while (from < to_mod)
509 from = (((from / tab_width) + 1) * tab_width);
520 stab_to (char *out, int from, int to)
523 int to_mod = (to / tab_width) * tab_width;
524 while (from < to_mod)
527 from = (((from / tab_width) + 1) * tab_width);
538 string_columns (const char *string, int length, int start)
542 const char *end = string + length;
547 else if (*string == '\t')
548 col = (((col / tab_width) + 1) * tab_width);
558 write_entry (FILE *out, const char *key, const char *value)
560 char *v = strdup(value ? value : "");
564 Bool programs_p = (!strcmp(key, "programs"));
565 int tab = (programs_p ? 32 : 16);
568 fprintf(out, "%s:", key);
569 col = strlen(key) + 1;
571 if (strlen(key) > 14)
572 col = tab_to (out, col, 20);
578 nl = strchr(v2, '\n');
582 if (first && programs_p)
584 col = tab_to (out, col, 77);
585 fprintf (out, " \\\n");
593 col = tab_to (out, col, 75);
594 fprintf (out, " \\n\\\n");
599 col = tab_to (out, col, tab);
602 string_columns(v2, strlen (v2), col) + col > 75)
609 while (v2[end] == ' ' || v2[end] == '\t')
611 while (v2[end] != ' ' && v2[end] != '\t' &&
612 v2[end] != '\n' && v2[end] != 0)
614 if (string_columns (v2 + start, (end - start), col) >= 74)
616 col = tab_to (out, col, 75);
617 fprintf(out, " \\\n");
618 col = tab_to (out, 0, tab + 2);
619 while (v2[start] == ' ' || v2[start] == '\t')
623 col = string_columns (v2 + start, (end - start), col);
625 fputc(v2[start++], out);
630 fprintf (out, "%s", v2);
631 col += string_columns(v2, strlen (v2), col);
645 write_init_file (Display *dpy,
646 saver_preferences *p, const char *version_string,
650 const char *name = init_file_name();
651 const char *tmp_name = init_file_tmp_name();
652 char *n2 = chase_symlinks (name);
656 /* Kludge, since these aren't in the saver_preferences struct as strings...
660 Bool overlay_stderr_p;
668 /* Throttle the various timeouts to reasonable values before writing
670 stop_the_insanity (p);
674 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
677 out = fopen(tmp_name, "w");
680 char *buf = (char *) malloc(1024 + strlen(name));
681 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
687 /* Give the new .xscreensaver file the same permissions as the old one;
688 except ensure that it is readable and writable by owner, and not
689 executable. Extra hack: if we're running as root, make the file
690 be world-readable (so that the daemon, running as "nobody", will
691 still be able to read it.)
693 if (stat(name, &st) == 0)
695 mode_t mode = st.st_mode;
696 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
697 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
699 if (getuid() == (uid_t) 0) /* read by group/other */
700 mode |= S_IRGRP | S_IROTH;
702 if (fchmod (fileno(out), mode) != 0)
704 char *buf = (char *) malloc(1024 + strlen(name));
705 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
706 tmp_name, (unsigned int) mode);
713 /* Kludge, since these aren't in the saver_preferences struct... */
714 visual_name = get_string_resource (dpy, "visualID", "VisualID");
716 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
717 stderr_font = get_string_resource (dpy, "font", "Font");
722 char **hack_strings = (char **)
723 calloc (p->screenhacks_count, sizeof(char *));
725 for (j = 0; j < p->screenhacks_count; j++)
727 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
728 i += strlen (hack_strings[j]);
732 ss = programs = (char *) malloc(i + 10);
734 for (j = 0; j < p->screenhacks_count; j++)
736 strcat (ss, hack_strings[j]);
737 free (hack_strings[j]);
746 struct passwd *pw = getpwuid (getuid ());
747 char *whoami = (pw && pw->pw_name && *pw->pw_name
750 time_t now = time ((time_t *) 0);
751 char *timestr = (char *) ctime (&now);
752 char *nl = (char *) strchr (timestr, '\n');
755 "# %s Preferences File\n"
756 "# Written by %s %s for %s on %s.\n"
757 "# http://www.jwz.org/xscreensaver/\n"
759 progclass, progname, version_string, whoami, timestr);
762 for (j = 0; prefs[j]; j++)
765 const char *pr = prefs[j];
766 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
780 # define CHECK(X) else if (!strcmp(pr, X))
782 CHECK("timeout") type = pref_time, t = p->timeout;
783 CHECK("cycle") type = pref_time, t = p->cycle;
784 CHECK("lock") type = pref_bool, b = p->lock_p;
785 CHECK("lockVTs") continue; /* don't save, unused */
786 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
787 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
788 CHECK("visualID") type = pref_str, s = visual_name;
789 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
790 CHECK("verbose") type = pref_bool, b = p->verbose_p;
791 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
792 CHECK("splash") type = pref_bool, b = p->splash_p;
793 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
795 CHECK("quad") type = pref_bool, b = p->quad_p;
796 # else /* !QUAD_MODE */
797 CHECK("quad") continue; /* don't save */
798 # endif /* !QUAD_MODE */
799 CHECK("demoCommand") type = pref_str, s = p->demo_command;
800 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
801 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
802 CHECK("helpURL") continue; /* don't save */
803 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
804 CHECK("loadURL") continue; /* don't save */
805 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
806 CHECK("newLoginCommand") continue; /* don't save */
807 CHECK("nice") type = pref_int, i = p->nice_inferior;
808 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
809 CHECK("fade") type = pref_bool, b = p->fade_p;
810 CHECK("unfade") type = pref_bool, b = p->unfade_p;
811 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
812 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
813 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
814 CHECK("captureStdout") continue; /* don't save */
815 CHECK("logFile") continue; /* don't save */
816 CHECK("ignoreUninstalledPrograms")
817 type = pref_bool, b = p->ignore_uninstalled_p;
819 CHECK("font") type = pref_str, s = stderr_font;
821 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
822 CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p;
823 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
824 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
825 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
827 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
828 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
829 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
830 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
832 CHECK("mode") type = pref_str,
833 s = (p->mode == ONE_HACK ? "one" :
834 p->mode == BLANK_ONLY ? "blank" :
835 p->mode == DONT_BLANK ? "off" :
836 p->mode == RANDOM_HACKS_SAME
839 CHECK("selected") type = pref_int, i = p->selected_hack;
841 CHECK("textMode") type = pref_str,
842 s = (p->tmode == TEXT_URL ? "url" :
843 p->tmode == TEXT_LITERAL ? "literal" :
844 p->tmode == TEXT_FILE ? "file" :
845 p->tmode == TEXT_PROGRAM ? "program" :
847 CHECK("textLiteral") type = pref_str, s = p->text_literal;
848 CHECK("textFile") type = pref_str, s = p->text_file;
849 CHECK("textProgram") type = pref_str, s = p->text_program;
850 CHECK("textURL") type = pref_str, s = p->text_url;
852 CHECK("programs") type = pref_str, s = programs;
853 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
854 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
855 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
856 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
857 CHECK("sgiSaverExtension") continue; /* don't save */
858 CHECK("mitSaverExtension") continue; /* don't save */
859 CHECK("xidleExtension") continue; /* don't save */
860 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
861 CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension;
862 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
863 b = p->getviewport_full_of_lies_p;
864 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
865 CHECK("overlayTextBackground") continue; /* don't save */
866 CHECK("overlayTextForeground") continue; /* don't save */
867 CHECK("bourneShell") continue; /* don't save */
876 sprintf(buf, "%d", i);
880 s = b ? "True" : "False";
884 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
895 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
901 if (i >= (1<<30) && i == ((i >> 30) << 30))
902 sprintf(buf, "%dG", i >> 30);
903 else if (i >= (1<<20) && i == ((i >> 20) << 20))
904 sprintf(buf, "%dM", i >> 20);
905 else if (i >= (1<<10) && i == ((i >> 10) << 10))
906 sprintf(buf, "%dK", i >> 10);
908 sprintf(buf, "%d", i);
917 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
920 write_entry (out, pr, s);
925 if (visual_name) free(visual_name);
926 if (stderr_font) free(stderr_font);
927 if (programs) free(programs);
929 if (fclose(out) == 0)
931 time_t write_date = 0;
933 if (stat(tmp_name, &st) == 0)
935 write_date = st.st_mtime;
939 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
940 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
947 if (rename (tmp_name, name) != 0)
949 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
950 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
951 blurb(), tmp_name, name);
959 p->init_file_date = write_date;
961 /* Since the .xscreensaver file is used for IPC, let's try and make
962 sure that the bits actually land on the disk right away. */
965 status = 0; /* wrote and renamed successfully! */
970 char *buf = (char *) malloc(1024 + strlen(name));
971 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
984 /* Parsing the resource database
988 free_screenhack (screenhack *hack)
990 if (hack->visual) free (hack->visual);
991 if (hack->name) free (hack->name);
992 free (hack->command);
993 memset (hack, 0, sizeof(*hack));
998 free_screenhack_list (screenhack **list, int count)
1002 for (i = 0; i < count; i++)
1004 free_screenhack (list[i]);
1010 /* Populate `saver_preferences' with the contents of the resource database.
1011 Note that this may be called multiple times -- it is re-run each time
1012 the ~/.xscreensaver file is reloaded.
1014 This function can be very noisy, since it issues resource syntax errors
1018 load_init_file (Display *dpy, saver_preferences *p)
1020 static Bool first_time = True;
1022 screenhack **system_default_screenhacks = 0;
1023 int system_default_screenhack_count = 0;
1027 /* Get the programs resource before the .xscreensaver file has been
1028 parsed and merged into the resource database for the first time:
1029 this is the value of *programs from the app-defaults file.
1030 Then clear it out so that it will be parsed again later, after
1031 the init file has been read.
1033 get_screenhacks (dpy, p);
1034 system_default_screenhacks = p->screenhacks;
1035 system_default_screenhack_count = p->screenhacks_count;
1037 p->screenhacks_count = 0;
1040 if (parse_init_file (p) != 0) /* file might have gone away */
1041 if (!first_time) return;
1045 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1046 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1047 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1048 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1049 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1050 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1051 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1052 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1053 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1054 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1055 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1057 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1059 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1061 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1062 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1063 "ignoreUninstalledPrograms",
1066 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1067 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1068 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1069 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1070 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1071 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1072 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1073 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1074 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1075 "windowCreationTimeout",
1078 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1079 p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean");
1080 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1081 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1082 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1084 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1085 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1086 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1087 p->image_directory = get_string_resource (dpy,
1091 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1092 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1093 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1094 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1096 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1098 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1099 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1100 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1101 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1102 p->new_login_command = get_string_resource(dpy,
1106 /* If "*splash" is unset, default to true. */
1108 char *s = get_string_resource (dpy, "splash", "Boolean");
1115 /* If "*grabDesktopImages" is unset, default to true. */
1117 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1121 p->grab_desktop_p = True;
1124 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1125 #if 0 /* obsolete. */
1126 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1127 "sgiSaverExtension",
1131 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1134 #if 0 /* broken and evil. */
1135 p->use_mit_saver_extension = get_boolean_resource (dpy,
1136 "mitSaverExtension",
1140 p->use_proc_interrupts = get_boolean_resource (dpy,
1141 "procInterrupts", "Boolean");
1143 p->getviewport_full_of_lies_p =
1144 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1146 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1149 char *s = get_string_resource (dpy, "selected", "Integer");
1151 p->selected_hack = -1;
1153 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1155 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1156 p->selected_hack = -1;
1160 char *s = get_string_resource (dpy, "mode", "Mode");
1161 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1162 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1163 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1164 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1165 else p->mode = RANDOM_HACKS;
1170 char *s = get_string_resource (dpy, "textMode", "TextMode");
1171 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1172 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1173 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1174 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1175 else p->tmode = TEXT_DATE;
1179 if (system_default_screenhack_count) /* note: first_time is also true */
1181 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1182 system_default_screenhack_count);
1183 free_screenhack_list (system_default_screenhacks,
1184 system_default_screenhack_count);
1185 system_default_screenhacks = 0;
1186 system_default_screenhack_count = 0;
1192 p->verbose_p = True;
1193 p->timestamp_p = True;
1194 p->initial_delay = 0;
1197 /* Throttle the various timeouts to reasonable values after reading the
1199 stop_the_insanity (p);
1203 /* If there are any hacks in the system-wide defaults that are not in
1204 the ~/.xscreensaver file, add the new ones to the end of the list.
1205 This does *not* actually save the file.
1208 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1209 screenhack **system_list, int system_count)
1211 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1216 for (i = 0; i < system_count; i++)
1219 Bool matched_p = False;
1221 for (j = 0; j < p->screenhacks_count; j++)
1224 if (!system_list[i]->name)
1225 system_list[i]->name = make_hack_name (dpy,
1226 system_list[i]->command);
1228 name = p->screenhacks[j]->name;
1230 name = make_hack_name (dpy, p->screenhacks[j]->command);
1232 matched_p = !strcasecmp (name, system_list[i]->name);
1234 if (name != p->screenhacks[j]->name)
1243 /* We have an entry in the system-wide list that is not in the
1244 user's .xscreensaver file. Add it to the end.
1245 Note that p->screenhacks is a single malloc block, not a
1246 linked list, so we have to realloc it.
1248 screenhack *oh = system_list[i];
1249 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1251 if (made_space == 0)
1254 p->screenhacks = (screenhack **)
1255 realloc (p->screenhacks,
1256 (p->screenhacks_count + made_space + 1)
1257 * sizeof(screenhack));
1258 if (!p->screenhacks) abort();
1261 nh->enabled_p = oh->enabled_p;
1262 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1263 nh->name = oh->name ? strdup(oh->name) : 0;
1264 nh->command = oh->command ? strdup(oh->command) : 0;
1266 p->screenhacks[p->screenhacks_count++] = nh;
1267 p->screenhacks[p->screenhacks_count] = 0;
1271 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1272 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1280 /* Parsing the programs resource.
1284 parse_screenhack (const char *line)
1286 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1289 h->enabled_p = True;
1291 while (isspace(*line)) line++; /* skip whitespace */
1292 if (*line == '-') /* handle "-" */
1294 h->enabled_p = False;
1296 while (isspace(*line)) line++; /* skip whitespace */
1299 s = line; /* handle "visual:" */
1300 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1306 h->visual = (char *) malloc (line-s+1);
1307 strncpy (h->visual, s, line-s);
1308 h->visual[line-s] = 0;
1309 if (*line == ':') line++; /* skip ":" */
1310 while (isspace(*line)) line++; /* skip whitespace */
1313 if (*line == '"') /* handle "name" */
1317 while (*line && *line != '"')
1319 h->name = (char *) malloc (line-s+1);
1320 strncpy (h->name, s, line-s);
1321 h->name[line-s] = 0;
1322 if (*line == '"') line++; /* skip "\"" */
1323 while (isspace(*line)) line++; /* skip whitespace */
1326 h->command = format_command (line, False); /* handle command */
1332 format_command (const char *cmd, Bool wrap_p)
1336 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1337 const char *in = cmd;
1341 /* shrink all whitespace to one space, for the benefit of the "demo"
1342 mode display. We only do this when we can easily tell that the
1343 whitespace is not significant (no shell metachars).
1347 case '\'': case '"': case '`': case '\\':
1348 /* Metachars are scary. Copy the rest of the line unchanged. */
1350 *out++ = *in++, col++;
1353 case ' ': case '\t':
1354 /* Squeeze all other whitespace down to one space. */
1355 while (*in == ' ' || *in == '\t')
1357 *out++ = ' ', col++;
1361 /* Copy other chars unchanged. */
1362 *out++ = *in++, col++;
1369 /* Strip trailing whitespace */
1370 while (out > cmd2 && isspace (out[-1]))
1377 /* Returns a new string describing the shell command.
1378 This may be just the name of the program, capitalized.
1379 It also may be something from the resource database (gotten
1380 by looking for "hacks.XYZ.name", where XYZ is the program.)
1383 make_hack_name (Display *dpy, const char *shell_command)
1385 char *s = strdup (shell_command);
1389 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1396 s2 = strrchr (s, '/'); /* if pathname, take last component */
1404 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1407 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1408 s2 = get_string_resource (dpy, res_name, res_name);
1415 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1416 if (*s2 >= 'A' && *s2 <= 'Z')
1419 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1421 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1423 if (s[0] == 'G' && s[1] == 'l' &&
1424 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1432 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1439 char *def_name = make_hack_name (dpy, hack->command);
1441 /* Don't ever write out a name for a hack if it's the same as the default.
1443 if (hack->name && !strcmp (hack->name, def_name))
1450 size = (2 * (strlen(hack->command) +
1451 (hack->visual ? strlen(hack->visual) : 0) +
1452 (hack->name ? strlen(hack->name) : 0) +
1454 h2 = (char *) malloc (size);
1457 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1459 if (hack->visual && *hack->visual) /* write visual name */
1461 if (hack->enabled_p) *out++ = ' ';
1463 strcpy (out, hack->visual);
1464 out += strlen (hack->visual);
1470 col = string_columns (h2, strlen (h2), 0);
1472 if (hack->name && *hack->name) /* write pretty name */
1474 int L = (strlen (hack->name) + 2);
1476 out = stab_to (out, col, tab - L - 2);
1480 strcpy (out, hack->name);
1481 out += strlen (hack->name);
1485 col = string_columns (h2, strlen (h2), 0);
1486 if (wrap_p && col >= tab)
1487 out = stab_to (out, col, 77);
1491 if (out >= h2+size) abort();
1495 col = string_columns (h2, strlen (h2), 0);
1496 out = stab_to (out, col, tab); /* indent */
1498 if (out >= h2+size) abort();
1499 s = format_command (hack->command, wrap_p);
1510 get_screenhacks (Display *dpy, saver_preferences *p)
1518 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1519 if (d && !*d) { free(d); d = 0; }
1521 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1522 if (d && !*d) { free(d); d = 0; }
1527 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1528 see the manual for details.\n", blurb());
1532 d = get_string_resource (dpy, "programs", "Programs");
1534 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1536 p->screenhacks_count = 0;
1544 /* Count up the number of newlines (which will be equal to or larger than
1545 one less than the number of hacks.)
1547 for (i = j = 0; d[i]; i++)
1552 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1554 /* Iterate over the lines in `d' (the string with newlines)
1555 and make new strings to stuff into the `screenhacks' array.
1557 p->screenhacks_count = 0;
1558 while (start < size)
1560 /* skip forward over whitespace. */
1561 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1564 /* skip forward to newline or end of string. */
1566 while (d[end] != 0 && d[end] != '\n')
1569 /* null terminate. */
1572 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1573 if (p->screenhacks_count >= i)
1581 if (p->screenhacks_count == 0)
1583 free (p->screenhacks);
1589 /* Make sure all the values in the preferences struct are sane.
1592 stop_the_insanity (saver_preferences *p)
1594 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1595 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1596 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1597 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1598 if (p->notice_events_timeout <= 0)
1599 p->notice_events_timeout = 10000; /* 10 secs */
1600 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1602 if (! p->fade_p) p->unfade_p = False;
1604 /* The DPMS settings may have the value 0.
1605 But if they are negative, or are a range less than 10 seconds,
1606 reset them to sensible defaults. (Since that must be a mistake.)
1608 if (p->dpms_standby != 0 &&
1609 p->dpms_standby < 10 * 1000)
1610 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1611 if (p->dpms_suspend != 0 &&
1612 p->dpms_suspend < 10 * 1000)
1613 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1614 if (p->dpms_off != 0 &&
1615 p->dpms_off < 10 * 1000)
1616 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1618 /* suspend may not be greater than off, unless off is 0.
1619 standby may not be greater than suspend, unless suspend is 0.
1621 if (p->dpms_off != 0 &&
1622 p->dpms_suspend > p->dpms_off)
1623 p->dpms_suspend = p->dpms_off;
1624 if (p->dpms_suspend != 0 &&
1625 p->dpms_standby > p->dpms_suspend)
1626 p->dpms_standby = p->dpms_suspend;
1628 /* These fixes above ignores the case
1629 suspend = 0 and standby > off ...
1631 if (p->dpms_off != 0 &&
1632 p->dpms_standby > p->dpms_off)
1633 p->dpms_standby = p->dpms_off;
1636 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1637 p->dpms_suspend == 0 &&
1639 !(p->dpms_quickoff_p) /* ... but we want to do DPMS quick off */
1641 p->dpms_enabled_p = False;
1644 /* Set watchdog timeout to about half of the cycle timeout, but
1645 don't let it be faster than 1/2 minute or slower than 1 minute.
1647 p->watchdog_timeout = p->cycle * 0.6;
1648 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1649 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1651 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1652 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;
1656 /* Getting very tired of bug reports of already-fixed bugs due to
1657 Linux distros shipping multi-year-old versions.
1662 time_t now = time ((time_t *) 0);
1663 struct tm *tm = localtime (&now);
1664 const char *s = screensaver_id;
1665 char mon[4], year[5];
1667 s = strchr (s, ' '); if (!s) abort(); s++;
1668 s = strchr (s, '('); if (!s) abort(); s++;
1669 s = strchr (s, '-'); if (!s) abort(); s++;
1670 strncpy (mon, s, 3);
1672 s = strchr (s, '-'); if (!s) abort(); s++;
1673 strncpy (year, s, 4);
1676 if (!strcmp(mon, "Jan")) m = 0;
1677 else if (!strcmp(mon, "Feb")) m = 1;
1678 else if (!strcmp(mon, "Mar")) m = 2;
1679 else if (!strcmp(mon, "Apr")) m = 3;
1680 else if (!strcmp(mon, "May")) m = 4;
1681 else if (!strcmp(mon, "Jun")) m = 5;
1682 else if (!strcmp(mon, "Jul")) m = 6;
1683 else if (!strcmp(mon, "Aug")) m = 7;
1684 else if (!strcmp(mon, "Sep")) m = 8;
1685 else if (!strcmp(mon, "Oct")) m = 9;
1686 else if (!strcmp(mon, "Nov")) m = 10;
1687 else if (!strcmp(mon, "Dec")) m = 11;
1689 months = ((((tm->tm_year + 1900) * 12) + tm->tm_mon) -
1692 return (months > 12);