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",
284 "chooseRandomImages",
298 "windowCreationTimeout",
300 "sgiSaverExtension", /* not saved -- obsolete */
301 "mitSaverExtension", /* not saved -- obsolete */
302 "xidleExtension", /* not saved -- obsolete */
303 "GetViewPortIsFullOfLies",
305 "xinputExtensionDev",
307 "overlayTextBackground", /* not saved -- X resources only */
308 "overlayTextForeground", /* not saved -- X resources only */
309 "bourneShell", /* not saved -- X resources only */
317 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
319 for (s2 = s; *s2; s2++)
321 for (s2--; s2 >= s; s2--)
322 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
334 handle_entry (XrmDatabase *db, const char *key, const char *value,
335 const char *filename, int line)
338 for (i = 0; prefs[i]; i++)
339 if (*prefs[i] && !strcasecmp(key, prefs[i]))
341 char *val = strdup(value);
342 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
343 strcpy(spec, progclass);
345 strcat(spec, prefs[i]);
347 XrmPutStringResource (db, spec, val);
354 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
355 blurb(), filename, line, key);
361 parse_init_file (saver_preferences *p)
363 time_t write_date = 0;
364 const char *name = init_file_name();
373 if (stat(name, &st) != 0)
375 p->init_file_date = 0;
379 in = fopen(name, "r");
382 char *buf = (char *) malloc(1024 + strlen(name));
383 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
389 if (fstat (fileno(in), &st) == 0)
391 write_date = st.st_mtime;
395 char *buf = (char *) malloc(1024 + strlen(name));
396 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
402 buf = (char *) malloc(buf_size);
404 while (fgets (buf, buf_size-1, in))
411 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
412 buf[L-2] == '\\')) /* or line ended with backslash */
414 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
420 buf = (char *) realloc(buf, buf_size);
424 if (!fgets (buf + L, buf_size-L-1, in))
429 /* Now handle other backslash escapes. */
432 for (i = 0; buf[i]; i++)
437 case 'n': buf[i] = '\n'; break;
438 case 'r': buf[i] = '\r'; break;
439 case 't': buf[i] = '\t'; break;
440 default: buf[i] = buf[i+1]; break;
442 for (j = i+2; buf[j]; j++)
450 if (*key == '#' || *key == '!' || *key == ';' ||
451 *key == '\n' || *key == 0)
454 value = strchr (key, ':');
457 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
464 value = strip(value);
468 handle_entry (&p->db, key, value, name, line);
473 p->init_file_date = write_date;
479 init_file_changed_p (saver_preferences *p)
481 const char *name = init_file_name();
484 if (!name) return False;
486 if (stat(name, &st) != 0)
489 if (p->init_file_date == st.st_mtime)
500 tab_to (FILE *out, int from, int to)
503 int to_mod = (to / tab_width) * tab_width;
504 while (from < to_mod)
507 from = (((from / tab_width) + 1) * tab_width);
518 stab_to (char *out, int from, int to)
521 int to_mod = (to / tab_width) * tab_width;
522 while (from < to_mod)
525 from = (((from / tab_width) + 1) * tab_width);
536 string_columns (const char *string, int length, int start)
540 const char *end = string + length;
545 else if (*string == '\t')
546 col = (((col / tab_width) + 1) * tab_width);
556 write_entry (FILE *out, const char *key, const char *value)
558 char *v = strdup(value ? value : "");
562 Bool programs_p = (!strcmp(key, "programs"));
563 int tab = (programs_p ? 32 : 16);
566 fprintf(out, "%s:", key);
567 col = strlen(key) + 1;
569 if (strlen(key) > 14)
570 col = tab_to (out, col, 20);
576 nl = strchr(v2, '\n');
580 if (first && programs_p)
582 col = tab_to (out, col, 77);
583 fprintf (out, " \\\n");
591 col = tab_to (out, col, 75);
592 fprintf (out, " \\n\\\n");
597 col = tab_to (out, col, tab);
600 string_columns(v2, strlen (v2), col) + col > 75)
607 while (v2[end] == ' ' || v2[end] == '\t')
609 while (v2[end] != ' ' && v2[end] != '\t' &&
610 v2[end] != '\n' && v2[end] != 0)
612 if (string_columns (v2 + start, (end - start), col) >= 74)
614 col = tab_to (out, col, 75);
615 fprintf(out, " \\\n");
616 col = tab_to (out, 0, tab + 2);
617 while (v2[start] == ' ' || v2[start] == '\t')
621 col = string_columns (v2 + start, (end - start), col);
623 fputc(v2[start++], out);
628 fprintf (out, "%s", v2);
629 col += string_columns(v2, strlen (v2), col);
643 write_init_file (Display *dpy,
644 saver_preferences *p, const char *version_string,
648 const char *name = init_file_name();
649 const char *tmp_name = init_file_tmp_name();
650 char *n2 = chase_symlinks (name);
654 /* Kludge, since these aren't in the saver_preferences struct as strings...
658 Bool overlay_stderr_p;
666 /* Throttle the various timeouts to reasonable values before writing
668 stop_the_insanity (p);
672 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
675 out = fopen(tmp_name, "w");
678 char *buf = (char *) malloc(1024 + strlen(name));
679 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
685 /* Give the new .xscreensaver file the same permissions as the old one;
686 except ensure that it is readable and writable by owner, and not
687 executable. Extra hack: if we're running as root, make the file
688 be world-readable (so that the daemon, running as "nobody", will
689 still be able to read it.)
691 if (stat(name, &st) == 0)
693 mode_t mode = st.st_mode;
694 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
695 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
697 if (getuid() == (uid_t) 0) /* read by group/other */
698 mode |= S_IRGRP | S_IROTH;
700 if (fchmod (fileno(out), mode) != 0)
702 char *buf = (char *) malloc(1024 + strlen(name));
703 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
704 tmp_name, (unsigned int) mode);
711 /* Kludge, since these aren't in the saver_preferences struct... */
712 visual_name = get_string_resource (dpy, "visualID", "VisualID");
714 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
715 stderr_font = get_string_resource (dpy, "font", "Font");
720 char **hack_strings = (char **)
721 calloc (p->screenhacks_count, sizeof(char *));
723 for (j = 0; j < p->screenhacks_count; j++)
725 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
726 i += strlen (hack_strings[j]);
730 ss = programs = (char *) malloc(i + 10);
732 for (j = 0; j < p->screenhacks_count; j++)
734 strcat (ss, hack_strings[j]);
735 free (hack_strings[j]);
744 struct passwd *pw = getpwuid (getuid ());
745 char *whoami = (pw && pw->pw_name && *pw->pw_name
748 time_t now = time ((time_t *) 0);
749 char *timestr = (char *) ctime (&now);
750 char *nl = (char *) strchr (timestr, '\n');
753 "# %s Preferences File\n"
754 "# Written by %s %s for %s on %s.\n"
755 "# http://www.jwz.org/xscreensaver/\n"
757 progclass, progname, version_string, whoami, timestr);
760 for (j = 0; prefs[j]; j++)
763 const char *pr = prefs[j];
764 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
778 # define CHECK(X) else if (!strcmp(pr, X))
780 CHECK("timeout") type = pref_time, t = p->timeout;
781 CHECK("cycle") type = pref_time, t = p->cycle;
782 CHECK("lock") type = pref_bool, b = p->lock_p;
783 CHECK("lockVTs") continue; /* don't save, unused */
784 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
785 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
786 CHECK("visualID") type = pref_str, s = visual_name;
787 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
788 CHECK("verbose") type = pref_bool, b = p->verbose_p;
789 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
790 CHECK("splash") type = pref_bool, b = p->splash_p;
791 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
793 CHECK("quad") type = pref_bool, b = p->quad_p;
794 # else /* !QUAD_MODE */
795 CHECK("quad") continue; /* don't save */
796 # endif /* !QUAD_MODE */
797 CHECK("demoCommand") type = pref_str, s = p->demo_command;
798 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
799 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
800 CHECK("helpURL") continue; /* don't save */
801 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
802 CHECK("loadURL") continue; /* don't save */
803 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
804 CHECK("newLoginCommand") continue; /* don't save */
805 CHECK("nice") type = pref_int, i = p->nice_inferior;
806 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
807 CHECK("fade") type = pref_bool, b = p->fade_p;
808 CHECK("unfade") type = pref_bool, b = p->unfade_p;
809 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
810 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
811 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
812 CHECK("captureStdout") continue; /* don't save */
813 CHECK("logFile") continue; /* don't save */
814 CHECK("ignoreUninstalledPrograms")
815 type = pref_bool, b = p->ignore_uninstalled_p;
817 CHECK("font") type = pref_str, s = stderr_font;
819 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
820 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
821 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
822 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
824 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
825 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
826 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
827 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
829 CHECK("mode") type = pref_str,
830 s = (p->mode == ONE_HACK ? "one" :
831 p->mode == BLANK_ONLY ? "blank" :
832 p->mode == DONT_BLANK ? "off" :
833 p->mode == RANDOM_HACKS_SAME
836 CHECK("selected") type = pref_int, i = p->selected_hack;
838 CHECK("textMode") type = pref_str,
839 s = (p->tmode == TEXT_URL ? "url" :
840 p->tmode == TEXT_LITERAL ? "literal" :
841 p->tmode == TEXT_FILE ? "file" :
842 p->tmode == TEXT_PROGRAM ? "program" :
844 CHECK("textLiteral") type = pref_str, s = p->text_literal;
845 CHECK("textFile") type = pref_str, s = p->text_file;
846 CHECK("textProgram") type = pref_str, s = p->text_program;
847 CHECK("textURL") type = pref_str, s = p->text_url;
849 CHECK("programs") type = pref_str, s = programs;
850 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
851 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
852 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
853 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
854 CHECK("sgiSaverExtension") continue; /* don't save */
855 CHECK("mitSaverExtension") continue; /* don't save */
856 CHECK("xidleExtension") continue; /* don't save */
857 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
858 CHECK("xinputExtensionDev") type = pref_bool, b = p->use_xinput_extension;
859 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
860 b = p->getviewport_full_of_lies_p;
861 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
862 CHECK("overlayTextBackground") continue; /* don't save */
863 CHECK("overlayTextForeground") continue; /* don't save */
864 CHECK("bourneShell") continue; /* don't save */
873 sprintf(buf, "%d", i);
877 s = b ? "True" : "False";
881 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
892 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
898 if (i >= (1<<30) && i == ((i >> 30) << 30))
899 sprintf(buf, "%dG", i >> 30);
900 else if (i >= (1<<20) && i == ((i >> 20) << 20))
901 sprintf(buf, "%dM", i >> 20);
902 else if (i >= (1<<10) && i == ((i >> 10) << 10))
903 sprintf(buf, "%dK", i >> 10);
905 sprintf(buf, "%d", i);
914 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
917 write_entry (out, pr, s);
922 if (visual_name) free(visual_name);
923 if (stderr_font) free(stderr_font);
924 if (programs) free(programs);
926 if (fclose(out) == 0)
928 time_t write_date = 0;
930 if (stat(tmp_name, &st) == 0)
932 write_date = st.st_mtime;
936 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
937 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
944 if (rename (tmp_name, name) != 0)
946 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
947 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
948 blurb(), tmp_name, name);
956 p->init_file_date = write_date;
958 /* Since the .xscreensaver file is used for IPC, let's try and make
959 sure that the bits actually land on the disk right away. */
962 status = 0; /* wrote and renamed successfully! */
967 char *buf = (char *) malloc(1024 + strlen(name));
968 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
981 /* Parsing the resource database
985 free_screenhack (screenhack *hack)
987 if (hack->visual) free (hack->visual);
988 if (hack->name) free (hack->name);
989 free (hack->command);
990 memset (hack, 0, sizeof(*hack));
995 free_screenhack_list (screenhack **list, int count)
999 for (i = 0; i < count; i++)
1001 free_screenhack (list[i]);
1007 /* Populate `saver_preferences' with the contents of the resource database.
1008 Note that this may be called multiple times -- it is re-run each time
1009 the ~/.xscreensaver file is reloaded.
1011 This function can be very noisy, since it issues resource syntax errors
1015 load_init_file (Display *dpy, saver_preferences *p)
1017 static Bool first_time = True;
1019 screenhack **system_default_screenhacks = 0;
1020 int system_default_screenhack_count = 0;
1024 /* Get the programs resource before the .xscreensaver file has been
1025 parsed and merged into the resource database for the first time:
1026 this is the value of *programs from the app-defaults file.
1027 Then clear it out so that it will be parsed again later, after
1028 the init file has been read.
1030 get_screenhacks (dpy, p);
1031 system_default_screenhacks = p->screenhacks;
1032 system_default_screenhack_count = p->screenhacks_count;
1034 p->screenhacks_count = 0;
1037 if (parse_init_file (p) != 0) /* file might have gone away */
1038 if (!first_time) return;
1042 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1043 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1044 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1045 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1046 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1047 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1048 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1049 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1050 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1051 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1052 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1054 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1056 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1058 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1059 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1060 "ignoreUninstalledPrograms",
1063 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1064 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1065 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1066 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1067 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1068 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1069 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1070 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1071 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1072 "windowCreationTimeout",
1075 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1076 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1077 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1078 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1080 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1081 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1082 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1083 p->image_directory = get_string_resource (dpy,
1087 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1088 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1089 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1090 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1092 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1094 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1095 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1096 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1097 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1098 p->new_login_command = get_string_resource(dpy,
1102 /* If "*splash" is unset, default to true. */
1104 char *s = get_string_resource (dpy, "splash", "Boolean");
1111 /* If "*grabDesktopImages" is unset, default to true. */
1113 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1117 p->grab_desktop_p = True;
1120 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1121 #if 0 /* obsolete. */
1122 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1123 "sgiSaverExtension",
1126 #if 0 /* obsolete. */
1127 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1130 #if 0 /* broken and evil. */
1131 p->use_mit_saver_extension = get_boolean_resource (dpy,
1132 "mitSaverExtension",
1136 p->use_proc_interrupts = get_boolean_resource (dpy,
1137 "procInterrupts", "Boolean");
1139 p->getviewport_full_of_lies_p =
1140 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1142 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1145 char *s = get_string_resource (dpy, "selected", "Integer");
1147 p->selected_hack = -1;
1149 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1151 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1152 p->selected_hack = -1;
1156 char *s = get_string_resource (dpy, "mode", "Mode");
1157 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1158 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1159 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1160 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1161 else p->mode = RANDOM_HACKS;
1166 char *s = get_string_resource (dpy, "textMode", "TextMode");
1167 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1168 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1169 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1170 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1171 else p->tmode = TEXT_DATE;
1175 if (system_default_screenhack_count) /* note: first_time is also true */
1177 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1178 system_default_screenhack_count);
1179 free_screenhack_list (system_default_screenhacks,
1180 system_default_screenhack_count);
1181 system_default_screenhacks = 0;
1182 system_default_screenhack_count = 0;
1188 p->verbose_p = True;
1189 p->timestamp_p = True;
1190 p->initial_delay = 0;
1193 /* Throttle the various timeouts to reasonable values after reading the
1195 stop_the_insanity (p);
1199 /* If there are any hacks in the system-wide defaults that are not in
1200 the ~/.xscreensaver file, add the new ones to the end of the list.
1201 This does *not* actually save the file.
1204 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1205 screenhack **system_list, int system_count)
1207 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1212 for (i = 0; i < system_count; i++)
1215 Bool matched_p = False;
1217 for (j = 0; j < p->screenhacks_count; j++)
1220 if (!system_list[i]->name)
1221 system_list[i]->name = make_hack_name (dpy,
1222 system_list[i]->command);
1224 name = p->screenhacks[j]->name;
1226 name = make_hack_name (dpy, p->screenhacks[j]->command);
1228 matched_p = !strcasecmp (name, system_list[i]->name);
1230 if (name != p->screenhacks[j]->name)
1239 /* We have an entry in the system-wide list that is not in the
1240 user's .xscreensaver file. Add it to the end.
1241 Note that p->screenhacks is a single malloc block, not a
1242 linked list, so we have to realloc it.
1244 screenhack *oh = system_list[i];
1245 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1247 if (made_space == 0)
1250 p->screenhacks = (screenhack **)
1251 realloc (p->screenhacks,
1252 (p->screenhacks_count + made_space + 1)
1253 * sizeof(screenhack));
1254 if (!p->screenhacks) abort();
1257 nh->enabled_p = oh->enabled_p;
1258 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1259 nh->name = oh->name ? strdup(oh->name) : 0;
1260 nh->command = oh->command ? strdup(oh->command) : 0;
1262 p->screenhacks[p->screenhacks_count++] = nh;
1263 p->screenhacks[p->screenhacks_count] = 0;
1267 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1268 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1276 /* Parsing the programs resource.
1280 parse_screenhack (const char *line)
1282 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1285 h->enabled_p = True;
1287 while (isspace(*line)) line++; /* skip whitespace */
1288 if (*line == '-') /* handle "-" */
1290 h->enabled_p = False;
1292 while (isspace(*line)) line++; /* skip whitespace */
1295 s = line; /* handle "visual:" */
1296 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1302 h->visual = (char *) malloc (line-s+1);
1303 strncpy (h->visual, s, line-s);
1304 h->visual[line-s] = 0;
1305 if (*line == ':') line++; /* skip ":" */
1306 while (isspace(*line)) line++; /* skip whitespace */
1309 if (*line == '"') /* handle "name" */
1313 while (*line && *line != '"')
1315 h->name = (char *) malloc (line-s+1);
1316 strncpy (h->name, s, line-s);
1317 h->name[line-s] = 0;
1318 if (*line == '"') line++; /* skip "\"" */
1319 while (isspace(*line)) line++; /* skip whitespace */
1322 h->command = format_command (line, False); /* handle command */
1328 format_command (const char *cmd, Bool wrap_p)
1332 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1333 const char *in = cmd;
1337 /* shrink all whitespace to one space, for the benefit of the "demo"
1338 mode display. We only do this when we can easily tell that the
1339 whitespace is not significant (no shell metachars).
1343 case '\'': case '"': case '`': case '\\':
1344 /* Metachars are scary. Copy the rest of the line unchanged. */
1346 *out++ = *in++, col++;
1349 case ' ': case '\t':
1350 /* Squeeze all other whitespace down to one space. */
1351 while (*in == ' ' || *in == '\t')
1353 *out++ = ' ', col++;
1357 /* Copy other chars unchanged. */
1358 *out++ = *in++, col++;
1365 /* Strip trailing whitespace */
1366 while (out > cmd2 && isspace (out[-1]))
1373 /* Returns a new string describing the shell command.
1374 This may be just the name of the program, capitalized.
1375 It also may be something from the resource database (gotten
1376 by looking for "hacks.XYZ.name", where XYZ is the program.)
1379 make_hack_name (Display *dpy, const char *shell_command)
1381 char *s = strdup (shell_command);
1385 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1392 s2 = strrchr (s, '/'); /* if pathname, take last component */
1400 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1403 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1404 s2 = get_string_resource (dpy, res_name, res_name);
1411 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1412 if (*s2 >= 'A' && *s2 <= 'Z')
1415 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1417 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1419 if (s[0] == 'G' && s[1] == 'l' &&
1420 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1428 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1435 char *def_name = make_hack_name (dpy, hack->command);
1437 /* Don't ever write out a name for a hack if it's the same as the default.
1439 if (hack->name && !strcmp (hack->name, def_name))
1446 size = (2 * (strlen(hack->command) +
1447 (hack->visual ? strlen(hack->visual) : 0) +
1448 (hack->name ? strlen(hack->name) : 0) +
1450 h2 = (char *) malloc (size);
1453 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1455 if (hack->visual && *hack->visual) /* write visual name */
1457 if (hack->enabled_p) *out++ = ' ';
1459 strcpy (out, hack->visual);
1460 out += strlen (hack->visual);
1466 col = string_columns (h2, strlen (h2), 0);
1468 if (hack->name && *hack->name) /* write pretty name */
1470 int L = (strlen (hack->name) + 2);
1472 out = stab_to (out, col, tab - L - 2);
1476 strcpy (out, hack->name);
1477 out += strlen (hack->name);
1481 col = string_columns (h2, strlen (h2), 0);
1482 if (wrap_p && col >= tab)
1483 out = stab_to (out, col, 77);
1487 if (out >= h2+size) abort();
1491 col = string_columns (h2, strlen (h2), 0);
1492 out = stab_to (out, col, tab); /* indent */
1494 if (out >= h2+size) abort();
1495 s = format_command (hack->command, wrap_p);
1506 get_screenhacks (Display *dpy, saver_preferences *p)
1514 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1515 if (d && !*d) { free(d); d = 0; }
1517 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1518 if (d && !*d) { free(d); d = 0; }
1523 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1524 see the manual for details.\n", blurb());
1528 d = get_string_resource (dpy, "programs", "Programs");
1530 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1532 p->screenhacks_count = 0;
1540 /* Count up the number of newlines (which will be equal to or larger than
1541 one less than the number of hacks.)
1543 for (i = j = 0; d[i]; i++)
1548 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1550 /* Iterate over the lines in `d' (the string with newlines)
1551 and make new strings to stuff into the `screenhacks' array.
1553 p->screenhacks_count = 0;
1554 while (start < size)
1556 /* skip forward over whitespace. */
1557 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1560 /* skip forward to newline or end of string. */
1562 while (d[end] != 0 && d[end] != '\n')
1565 /* null terminate. */
1568 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1569 if (p->screenhacks_count >= i)
1577 if (p->screenhacks_count == 0)
1579 free (p->screenhacks);
1585 /* Make sure all the values in the preferences struct are sane.
1588 stop_the_insanity (saver_preferences *p)
1590 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1591 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1592 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1593 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1594 if (p->notice_events_timeout <= 0)
1595 p->notice_events_timeout = 10000; /* 10 secs */
1596 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1598 if (! p->fade_p) p->unfade_p = False;
1600 /* The DPMS settings may have the value 0.
1601 But if they are negative, or are a range less than 10 seconds,
1602 reset them to sensible defaults. (Since that must be a mistake.)
1604 if (p->dpms_standby != 0 &&
1605 p->dpms_standby < 10 * 1000)
1606 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1607 if (p->dpms_suspend != 0 &&
1608 p->dpms_suspend < 10 * 1000)
1609 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1610 if (p->dpms_off != 0 &&
1611 p->dpms_off < 10 * 1000)
1612 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1614 /* suspend may not be greater than off, unless off is 0.
1615 standby may not be greater than suspend, unless suspend is 0.
1617 if (p->dpms_off != 0 &&
1618 p->dpms_suspend > p->dpms_off)
1619 p->dpms_suspend = p->dpms_off;
1620 if (p->dpms_suspend != 0 &&
1621 p->dpms_standby > p->dpms_suspend)
1622 p->dpms_standby = p->dpms_suspend;
1624 /* These fixes above ignores the case
1625 suspend = 0 and standby > off ...
1627 if (p->dpms_off != 0 &&
1628 p->dpms_standby > p->dpms_off)
1629 p->dpms_standby = p->dpms_off;
1632 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1633 p->dpms_suspend == 0 &&
1635 p->dpms_enabled_p = False;
1638 /* Set watchdog timeout to about half of the cycle timeout, but
1639 don't let it be faster than 1/2 minute or slower than 1 minute.
1641 p->watchdog_timeout = p->cycle * 0.6;
1642 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1643 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1645 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1646 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;