1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2011 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
68 #include "resources.h"
70 /* don't use realpath() on fedora system */
71 #ifdef _FORTIFY_SOURCE
76 extern char *progname;
77 extern char *progclass;
78 extern const char *blurb (void);
82 static void get_screenhacks (Display *, saver_preferences *);
83 static char *format_command (const char *cmd, Bool wrap_p);
84 static void merge_system_screenhacks (Display *, saver_preferences *,
85 screenhack **system_list, int count);
86 static void stop_the_insanity (saver_preferences *p);
90 chase_symlinks (const char *file)
97 # define PATH_MAX MAXPATHLEN
99 # define PATH_MAX 2048
103 if (realpath (file, buf))
106 /* sprintf (buf, "%.100s: realpath %.200s", blurb(), file);
109 # endif /* HAVE_REALPATH */
115 i_am_a_nobody (uid_t uid)
119 p = getpwnam ("nobody");
120 if (! p) p = getpwnam ("noaccess");
121 if (! p) p = getpwnam ("daemon");
123 if (! p) /* There is no nobody? */
126 return (uid == p->pw_uid);
131 init_file_name (void)
133 static char *file = 0;
137 uid_t uid = getuid ();
138 struct passwd *p = getpwuid (uid);
140 if (i_am_a_nobody (uid))
141 /* If we're running as nobody, then use root's .xscreensaver file
142 (since ~root/.xscreensaver and ~nobody/.xscreensaver are likely
143 to be different -- if we didn't do this, then xscreensaver-demo
144 would appear to have no effect when the luser is running as root.)
150 if (!p || !p->pw_name || !*p->pw_name)
152 fprintf (stderr, "%s: couldn't get user info of uid %d\n",
156 else if (!p->pw_dir || !*p->pw_dir)
158 fprintf (stderr, "%s: couldn't get home directory of \"%s\"\n",
159 blurb(), (p->pw_name ? p->pw_name : "???"));
164 const char *home = p->pw_dir;
165 const char *name = ".xscreensaver";
166 file = (char *) malloc(strlen(home) + strlen(name) + 2);
168 if (!*home || home[strlen(home)-1] != '/')
182 init_file_tmp_name (void)
184 static char *file = 0;
187 const char *name = init_file_name();
188 const char *suffix = ".tmp";
190 char *n2 = chase_symlinks (name);
197 file = (char *) malloc(strlen(name) + strlen(suffix) + 2);
199 strcat(file, suffix);
212 get_byte_resource (Display *dpy, char *name, char *class)
214 char *s = get_string_resource (dpy, name, class);
219 while (isspace(*s2)) s2++;
220 while (*s2 >= '0' && *s2 <= '9')
222 n = (n * 10) + (*s2 - '0');
225 while (isspace(*s2)) s2++;
226 if (*s2 == 'k' || *s2 == 'K') n <<= 10;
227 else if (*s2 == 'm' || *s2 == 'M') n <<= 20;
228 else if (*s2 == 'g' || *s2 == 'G') n <<= 30;
232 fprintf (stderr, "%s: %s must be a number of bytes, not \"%s\".\n",
238 if (*s2 == 'b' || *s2 == 'B') s2++;
239 while (isspace(*s2)) s2++;
247 static const char * const prefs[] = {
251 "lockVTs", /* not saved */
264 "helpURL", /* not saved */
265 "loadURL", /* not saved */
266 "newLoginCommand", /* not saved */
274 "captureStdout", /* not saved -- obsolete */
275 "logFile", /* not saved */
276 "ignoreUninstalledPrograms",
285 "chooseRandomImages",
299 "windowCreationTimeout",
301 "sgiSaverExtension", /* not saved -- obsolete */
302 "mitSaverExtension", /* not saved -- obsolete */
303 "xidleExtension", /* not saved -- obsolete */
304 "GetViewPortIsFullOfLies",
306 "xinputExtensionDev",
308 "overlayTextBackground", /* not saved -- X resources only */
309 "overlayTextForeground", /* not saved -- X resources only */
310 "bourneShell", /* not saved -- X resources only */
318 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
320 for (s2 = s; *s2; s2++)
322 for (s2--; s2 >= s; s2--)
323 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
335 handle_entry (XrmDatabase *db, const char *key, const char *value,
336 const char *filename, int line)
339 for (i = 0; prefs[i]; i++)
340 if (*prefs[i] && !strcasecmp(key, prefs[i]))
342 char *val = strdup(value);
343 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
344 strcpy(spec, progclass);
346 strcat(spec, prefs[i]);
348 XrmPutStringResource (db, spec, val);
355 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
356 blurb(), filename, line, key);
362 parse_init_file (saver_preferences *p)
364 time_t write_date = 0;
365 const char *name = init_file_name();
374 if (stat(name, &st) != 0)
376 p->init_file_date = 0;
380 in = fopen(name, "r");
383 char *buf = (char *) malloc(1024 + strlen(name));
384 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
390 if (fstat (fileno(in), &st) == 0)
392 write_date = st.st_mtime;
396 char *buf = (char *) malloc(1024 + strlen(name));
397 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
403 buf = (char *) malloc(buf_size);
405 while (fgets (buf, buf_size-1, in))
412 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
413 buf[L-2] == '\\')) /* or line ended with backslash */
415 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
421 buf = (char *) realloc(buf, buf_size);
425 if (!fgets (buf + L, buf_size-L-1, in))
430 /* Now handle other backslash escapes. */
433 for (i = 0; buf[i]; i++)
438 case 'n': buf[i] = '\n'; break;
439 case 'r': buf[i] = '\r'; break;
440 case 't': buf[i] = '\t'; break;
441 default: buf[i] = buf[i+1]; break;
443 for (j = i+2; buf[j]; j++)
451 if (*key == '#' || *key == '!' || *key == ';' ||
452 *key == '\n' || *key == 0)
455 value = strchr (key, ':');
458 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
465 value = strip(value);
469 handle_entry (&p->db, key, value, name, line);
474 p->init_file_date = write_date;
480 init_file_changed_p (saver_preferences *p)
482 const char *name = init_file_name();
485 if (!name) return False;
487 if (stat(name, &st) != 0)
490 if (p->init_file_date == st.st_mtime)
501 tab_to (FILE *out, int from, int to)
504 int to_mod = (to / tab_width) * tab_width;
505 while (from < to_mod)
508 from = (((from / tab_width) + 1) * tab_width);
519 stab_to (char *out, int from, int to)
522 int to_mod = (to / tab_width) * tab_width;
523 while (from < to_mod)
526 from = (((from / tab_width) + 1) * tab_width);
537 string_columns (const char *string, int length, int start)
541 const char *end = string + length;
546 else if (*string == '\t')
547 col = (((col / tab_width) + 1) * tab_width);
557 write_entry (FILE *out, const char *key, const char *value)
559 char *v = strdup(value ? value : "");
563 Bool programs_p = (!strcmp(key, "programs"));
564 int tab = (programs_p ? 32 : 16);
567 fprintf(out, "%s:", key);
568 col = strlen(key) + 1;
570 if (strlen(key) > 14)
571 col = tab_to (out, col, 20);
577 nl = strchr(v2, '\n');
581 if (first && programs_p)
583 col = tab_to (out, col, 77);
584 fprintf (out, " \\\n");
592 col = tab_to (out, col, 75);
593 fprintf (out, " \\n\\\n");
598 col = tab_to (out, col, tab);
601 string_columns(v2, strlen (v2), col) + col > 75)
608 while (v2[end] == ' ' || v2[end] == '\t')
610 while (v2[end] != ' ' && v2[end] != '\t' &&
611 v2[end] != '\n' && v2[end] != 0)
613 if (string_columns (v2 + start, (end - start), col) >= 74)
615 col = tab_to (out, col, 75);
616 fprintf(out, " \\\n");
617 col = tab_to (out, 0, tab + 2);
618 while (v2[start] == ' ' || v2[start] == '\t')
622 col = string_columns (v2 + start, (end - start), col);
624 fputc(v2[start++], out);
629 fprintf (out, "%s", v2);
630 col += string_columns(v2, strlen (v2), col);
644 write_init_file (Display *dpy,
645 saver_preferences *p, const char *version_string,
649 const char *name = init_file_name();
650 const char *tmp_name = init_file_tmp_name();
651 char *n2 = chase_symlinks (name);
655 /* Kludge, since these aren't in the saver_preferences struct as strings...
659 Bool overlay_stderr_p;
667 /* Throttle the various timeouts to reasonable values before writing
669 stop_the_insanity (p);
673 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
676 out = fopen(tmp_name, "w");
679 char *buf = (char *) malloc(1024 + strlen(name));
680 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
686 /* Give the new .xscreensaver file the same permissions as the old one;
687 except ensure that it is readable and writable by owner, and not
688 executable. Extra hack: if we're running as root, make the file
689 be world-readable (so that the daemon, running as "nobody", will
690 still be able to read it.)
692 if (stat(name, &st) == 0)
694 mode_t mode = st.st_mode;
695 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
696 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
698 if (getuid() == (uid_t) 0) /* read by group/other */
699 mode |= S_IRGRP | S_IROTH;
701 if (fchmod (fileno(out), mode) != 0)
703 char *buf = (char *) malloc(1024 + strlen(name));
704 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
705 tmp_name, (unsigned int) mode);
712 /* Kludge, since these aren't in the saver_preferences struct... */
713 visual_name = get_string_resource (dpy, "visualID", "VisualID");
715 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
716 stderr_font = get_string_resource (dpy, "font", "Font");
721 char **hack_strings = (char **)
722 calloc (p->screenhacks_count, sizeof(char *));
724 for (j = 0; j < p->screenhacks_count; j++)
726 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
727 i += strlen (hack_strings[j]);
731 ss = programs = (char *) malloc(i + 10);
733 for (j = 0; j < p->screenhacks_count; j++)
735 strcat (ss, hack_strings[j]);
736 free (hack_strings[j]);
745 struct passwd *pw = getpwuid (getuid ());
746 char *whoami = (pw && pw->pw_name && *pw->pw_name
749 time_t now = time ((time_t *) 0);
750 char *timestr = (char *) ctime (&now);
751 char *nl = (char *) strchr (timestr, '\n');
754 "# %s Preferences File\n"
755 "# Written by %s %s for %s on %s.\n"
756 "# http://www.jwz.org/xscreensaver/\n"
758 progclass, progname, version_string, whoami, timestr);
761 for (j = 0; prefs[j]; j++)
764 const char *pr = prefs[j];
765 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
779 # define CHECK(X) else if (!strcmp(pr, X))
781 CHECK("timeout") type = pref_time, t = p->timeout;
782 CHECK("cycle") type = pref_time, t = p->cycle;
783 CHECK("lock") type = pref_bool, b = p->lock_p;
784 CHECK("lockVTs") continue; /* don't save, unused */
785 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
786 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
787 CHECK("visualID") type = pref_str, s = visual_name;
788 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
789 CHECK("verbose") type = pref_bool, b = p->verbose_p;
790 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
791 CHECK("splash") type = pref_bool, b = p->splash_p;
792 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
794 CHECK("quad") type = pref_bool, b = p->quad_p;
795 # else /* !QUAD_MODE */
796 CHECK("quad") continue; /* don't save */
797 # endif /* !QUAD_MODE */
798 CHECK("demoCommand") type = pref_str, s = p->demo_command;
799 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
800 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
801 CHECK("helpURL") continue; /* don't save */
802 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
803 CHECK("loadURL") continue; /* don't save */
804 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
805 CHECK("newLoginCommand") continue; /* don't save */
806 CHECK("nice") type = pref_int, i = p->nice_inferior;
807 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
808 CHECK("fade") type = pref_bool, b = p->fade_p;
809 CHECK("unfade") type = pref_bool, b = p->unfade_p;
810 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
811 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
812 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
813 CHECK("captureStdout") continue; /* don't save */
814 CHECK("logFile") continue; /* don't save */
815 CHECK("ignoreUninstalledPrograms")
816 type = pref_bool, b = p->ignore_uninstalled_p;
818 CHECK("font") type = pref_str, s = stderr_font;
820 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
821 CHECK("dpmsQuickOff") type = pref_bool, b = p->dpms_quickoff_p;
822 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
823 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
824 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
826 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
827 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
828 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
829 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
831 CHECK("mode") type = pref_str,
832 s = (p->mode == ONE_HACK ? "one" :
833 p->mode == BLANK_ONLY ? "blank" :
834 p->mode == DONT_BLANK ? "off" :
835 p->mode == RANDOM_HACKS_SAME
838 CHECK("selected") type = pref_int, i = p->selected_hack;
840 CHECK("textMode") type = pref_str,
841 s = (p->tmode == TEXT_URL ? "url" :
842 p->tmode == TEXT_LITERAL ? "literal" :
843 p->tmode == TEXT_FILE ? "file" :
844 p->tmode == TEXT_PROGRAM ? "program" :
846 CHECK("textLiteral") type = pref_str, s = p->text_literal;
847 CHECK("textFile") type = pref_str, s = p->text_file;
848 CHECK("textProgram") type = pref_str, s = p->text_program;
849 CHECK("textURL") type = pref_str, s = p->text_url;
851 CHECK("programs") type = pref_str, s = programs;
852 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
853 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
854 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
855 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
856 CHECK("sgiSaverExtension") continue; /* don't save */
857 CHECK("mitSaverExtension") continue; /* don't save */
858 CHECK("xidleExtension") continue; /* don't save */
859 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
860 CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension;
861 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
862 b = p->getviewport_full_of_lies_p;
863 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
864 CHECK("overlayTextBackground") continue; /* don't save */
865 CHECK("overlayTextForeground") continue; /* don't save */
866 CHECK("bourneShell") continue; /* don't save */
875 sprintf(buf, "%d", i);
879 s = b ? "True" : "False";
883 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
894 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
900 if (i >= (1<<30) && i == ((i >> 30) << 30))
901 sprintf(buf, "%dG", i >> 30);
902 else if (i >= (1<<20) && i == ((i >> 20) << 20))
903 sprintf(buf, "%dM", i >> 20);
904 else if (i >= (1<<10) && i == ((i >> 10) << 10))
905 sprintf(buf, "%dK", i >> 10);
907 sprintf(buf, "%d", i);
916 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
919 write_entry (out, pr, s);
924 if (visual_name) free(visual_name);
925 if (stderr_font) free(stderr_font);
926 if (programs) free(programs);
928 if (fclose(out) == 0)
930 time_t write_date = 0;
932 if (stat(tmp_name, &st) == 0)
934 write_date = st.st_mtime;
938 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
939 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
946 if (rename (tmp_name, name) != 0)
948 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
949 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
950 blurb(), tmp_name, name);
958 p->init_file_date = write_date;
960 /* Since the .xscreensaver file is used for IPC, let's try and make
961 sure that the bits actually land on the disk right away. */
964 status = 0; /* wrote and renamed successfully! */
969 char *buf = (char *) malloc(1024 + strlen(name));
970 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
983 /* Parsing the resource database
987 free_screenhack (screenhack *hack)
989 if (hack->visual) free (hack->visual);
990 if (hack->name) free (hack->name);
991 free (hack->command);
992 memset (hack, 0, sizeof(*hack));
997 free_screenhack_list (screenhack **list, int count)
1001 for (i = 0; i < count; i++)
1003 free_screenhack (list[i]);
1009 /* Populate `saver_preferences' with the contents of the resource database.
1010 Note that this may be called multiple times -- it is re-run each time
1011 the ~/.xscreensaver file is reloaded.
1013 This function can be very noisy, since it issues resource syntax errors
1017 load_init_file (Display *dpy, saver_preferences *p)
1019 static Bool first_time = True;
1021 screenhack **system_default_screenhacks = 0;
1022 int system_default_screenhack_count = 0;
1026 /* Get the programs resource before the .xscreensaver file has been
1027 parsed and merged into the resource database for the first time:
1028 this is the value of *programs from the app-defaults file.
1029 Then clear it out so that it will be parsed again later, after
1030 the init file has been read.
1032 get_screenhacks (dpy, p);
1033 system_default_screenhacks = p->screenhacks;
1034 system_default_screenhack_count = p->screenhacks_count;
1036 p->screenhacks_count = 0;
1039 if (parse_init_file (p) != 0) /* file might have gone away */
1040 if (!first_time) return;
1044 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1045 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1046 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1047 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1048 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1049 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1050 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1051 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1052 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1053 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1054 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1056 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1058 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1060 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1061 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1062 "ignoreUninstalledPrograms",
1065 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1066 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1067 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1068 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1069 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1070 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1071 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1072 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1073 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1074 "windowCreationTimeout",
1077 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1078 p->dpms_quickoff_p = get_boolean_resource (dpy, "dpmsQuickOff", "Boolean");
1079 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1080 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1081 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1083 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1084 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1085 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1086 p->image_directory = get_string_resource (dpy,
1090 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1091 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1092 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1093 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1095 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1097 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1098 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1099 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1100 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1101 p->new_login_command = get_string_resource(dpy,
1105 /* If "*splash" is unset, default to true. */
1107 char *s = get_string_resource (dpy, "splash", "Boolean");
1114 /* If "*grabDesktopImages" is unset, default to true. */
1116 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1120 p->grab_desktop_p = True;
1123 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1124 #if 0 /* obsolete. */
1125 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1126 "sgiSaverExtension",
1130 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1133 #if 0 /* broken and evil. */
1134 p->use_mit_saver_extension = get_boolean_resource (dpy,
1135 "mitSaverExtension",
1139 p->use_proc_interrupts = get_boolean_resource (dpy,
1140 "procInterrupts", "Boolean");
1142 p->getviewport_full_of_lies_p =
1143 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1145 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1148 char *s = get_string_resource (dpy, "selected", "Integer");
1150 p->selected_hack = -1;
1152 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1154 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1155 p->selected_hack = -1;
1159 char *s = get_string_resource (dpy, "mode", "Mode");
1160 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1161 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1162 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1163 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1164 else p->mode = RANDOM_HACKS;
1169 char *s = get_string_resource (dpy, "textMode", "TextMode");
1170 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1171 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1172 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1173 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1174 else p->tmode = TEXT_DATE;
1178 if (system_default_screenhack_count) /* note: first_time is also true */
1180 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1181 system_default_screenhack_count);
1182 free_screenhack_list (system_default_screenhacks,
1183 system_default_screenhack_count);
1184 system_default_screenhacks = 0;
1185 system_default_screenhack_count = 0;
1191 p->verbose_p = True;
1192 p->timestamp_p = True;
1193 p->initial_delay = 0;
1196 /* Throttle the various timeouts to reasonable values after reading the
1198 stop_the_insanity (p);
1202 /* If there are any hacks in the system-wide defaults that are not in
1203 the ~/.xscreensaver file, add the new ones to the end of the list.
1204 This does *not* actually save the file.
1207 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1208 screenhack **system_list, int system_count)
1210 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1215 for (i = 0; i < system_count; i++)
1218 Bool matched_p = False;
1220 for (j = 0; j < p->screenhacks_count; j++)
1223 if (!system_list[i]->name)
1224 system_list[i]->name = make_hack_name (dpy,
1225 system_list[i]->command);
1227 name = p->screenhacks[j]->name;
1229 name = make_hack_name (dpy, p->screenhacks[j]->command);
1231 matched_p = !strcasecmp (name, system_list[i]->name);
1233 if (name != p->screenhacks[j]->name)
1242 /* We have an entry in the system-wide list that is not in the
1243 user's .xscreensaver file. Add it to the end.
1244 Note that p->screenhacks is a single malloc block, not a
1245 linked list, so we have to realloc it.
1247 screenhack *oh = system_list[i];
1248 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1250 if (made_space == 0)
1253 p->screenhacks = (screenhack **)
1254 realloc (p->screenhacks,
1255 (p->screenhacks_count + made_space + 1)
1256 * sizeof(screenhack));
1257 if (!p->screenhacks) abort();
1260 nh->enabled_p = oh->enabled_p;
1261 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1262 nh->name = oh->name ? strdup(oh->name) : 0;
1263 nh->command = oh->command ? strdup(oh->command) : 0;
1265 p->screenhacks[p->screenhacks_count++] = nh;
1266 p->screenhacks[p->screenhacks_count] = 0;
1270 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1271 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1279 /* Parsing the programs resource.
1283 parse_screenhack (const char *line)
1285 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1288 h->enabled_p = True;
1290 while (isspace(*line)) line++; /* skip whitespace */
1291 if (*line == '-') /* handle "-" */
1293 h->enabled_p = False;
1295 while (isspace(*line)) line++; /* skip whitespace */
1298 s = line; /* handle "visual:" */
1299 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1305 h->visual = (char *) malloc (line-s+1);
1306 strncpy (h->visual, s, line-s);
1307 h->visual[line-s] = 0;
1308 if (*line == ':') line++; /* skip ":" */
1309 while (isspace(*line)) line++; /* skip whitespace */
1312 if (*line == '"') /* handle "name" */
1316 while (*line && *line != '"')
1318 h->name = (char *) malloc (line-s+1);
1319 strncpy (h->name, s, line-s);
1320 h->name[line-s] = 0;
1321 if (*line == '"') line++; /* skip "\"" */
1322 while (isspace(*line)) line++; /* skip whitespace */
1325 h->command = format_command (line, False); /* handle command */
1331 format_command (const char *cmd, Bool wrap_p)
1335 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1336 const char *in = cmd;
1340 /* shrink all whitespace to one space, for the benefit of the "demo"
1341 mode display. We only do this when we can easily tell that the
1342 whitespace is not significant (no shell metachars).
1346 case '\'': case '"': case '`': case '\\':
1347 /* Metachars are scary. Copy the rest of the line unchanged. */
1349 *out++ = *in++, col++;
1352 case ' ': case '\t':
1353 /* Squeeze all other whitespace down to one space. */
1354 while (*in == ' ' || *in == '\t')
1356 *out++ = ' ', col++;
1360 /* Copy other chars unchanged. */
1361 *out++ = *in++, col++;
1368 /* Strip trailing whitespace */
1369 while (out > cmd2 && isspace (out[-1]))
1376 /* Returns a new string describing the shell command.
1377 This may be just the name of the program, capitalized.
1378 It also may be something from the resource database (gotten
1379 by looking for "hacks.XYZ.name", where XYZ is the program.)
1382 make_hack_name (Display *dpy, const char *shell_command)
1384 char *s = strdup (shell_command);
1388 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1395 s2 = strrchr (s, '/'); /* if pathname, take last component */
1403 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1406 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1407 s2 = get_string_resource (dpy, res_name, res_name);
1414 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1415 if (*s2 >= 'A' && *s2 <= 'Z')
1418 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1420 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1422 if (s[0] == 'G' && s[1] == 'l' &&
1423 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1431 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1438 char *def_name = make_hack_name (dpy, hack->command);
1440 /* Don't ever write out a name for a hack if it's the same as the default.
1442 if (hack->name && !strcmp (hack->name, def_name))
1449 size = (2 * (strlen(hack->command) +
1450 (hack->visual ? strlen(hack->visual) : 0) +
1451 (hack->name ? strlen(hack->name) : 0) +
1453 h2 = (char *) malloc (size);
1456 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1458 if (hack->visual && *hack->visual) /* write visual name */
1460 if (hack->enabled_p) *out++ = ' ';
1462 strcpy (out, hack->visual);
1463 out += strlen (hack->visual);
1469 col = string_columns (h2, strlen (h2), 0);
1471 if (hack->name && *hack->name) /* write pretty name */
1473 int L = (strlen (hack->name) + 2);
1475 out = stab_to (out, col, tab - L - 2);
1479 strcpy (out, hack->name);
1480 out += strlen (hack->name);
1484 col = string_columns (h2, strlen (h2), 0);
1485 if (wrap_p && col >= tab)
1486 out = stab_to (out, col, 77);
1490 if (out >= h2+size) abort();
1494 col = string_columns (h2, strlen (h2), 0);
1495 out = stab_to (out, col, tab); /* indent */
1497 if (out >= h2+size) abort();
1498 s = format_command (hack->command, wrap_p);
1509 get_screenhacks (Display *dpy, saver_preferences *p)
1517 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1518 if (d && !*d) { free(d); d = 0; }
1520 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1521 if (d && !*d) { free(d); d = 0; }
1526 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1527 see the manual for details.\n", blurb());
1531 d = get_string_resource (dpy, "programs", "Programs");
1533 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1535 p->screenhacks_count = 0;
1543 /* Count up the number of newlines (which will be equal to or larger than
1544 one less than the number of hacks.)
1546 for (i = j = 0; d[i]; i++)
1551 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1553 /* Iterate over the lines in `d' (the string with newlines)
1554 and make new strings to stuff into the `screenhacks' array.
1556 p->screenhacks_count = 0;
1557 while (start < size)
1559 /* skip forward over whitespace. */
1560 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1563 /* skip forward to newline or end of string. */
1565 while (d[end] != 0 && d[end] != '\n')
1568 /* null terminate. */
1571 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1572 if (p->screenhacks_count >= i)
1580 if (p->screenhacks_count == 0)
1582 free (p->screenhacks);
1588 /* Make sure all the values in the preferences struct are sane.
1591 stop_the_insanity (saver_preferences *p)
1593 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1594 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1595 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1596 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1597 if (p->notice_events_timeout <= 0)
1598 p->notice_events_timeout = 10000; /* 10 secs */
1599 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1601 if (! p->fade_p) p->unfade_p = False;
1603 /* The DPMS settings may have the value 0.
1604 But if they are negative, or are a range less than 10 seconds,
1605 reset them to sensible defaults. (Since that must be a mistake.)
1607 if (p->dpms_standby != 0 &&
1608 p->dpms_standby < 10 * 1000)
1609 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1610 if (p->dpms_suspend != 0 &&
1611 p->dpms_suspend < 10 * 1000)
1612 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1613 if (p->dpms_off != 0 &&
1614 p->dpms_off < 10 * 1000)
1615 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1617 /* suspend may not be greater than off, unless off is 0.
1618 standby may not be greater than suspend, unless suspend is 0.
1620 if (p->dpms_off != 0 &&
1621 p->dpms_suspend > p->dpms_off)
1622 p->dpms_suspend = p->dpms_off;
1623 if (p->dpms_suspend != 0 &&
1624 p->dpms_standby > p->dpms_suspend)
1625 p->dpms_standby = p->dpms_suspend;
1627 /* These fixes above ignores the case
1628 suspend = 0 and standby > off ...
1630 if (p->dpms_off != 0 &&
1631 p->dpms_standby > p->dpms_off)
1632 p->dpms_standby = p->dpms_off;
1635 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1636 p->dpms_suspend == 0 &&
1638 p->dpms_enabled_p = False;
1641 /* Set watchdog timeout to about half of the cycle timeout, but
1642 don't let it be faster than 1/2 minute or slower than 1 minute.
1644 p->watchdog_timeout = p->cycle * 0.6;
1645 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1646 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1648 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1649 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;