1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2008 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",
301 "mitSaverExtension", /* 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")type = pref_bool, b=p->use_sgi_saver_extension;
855 CHECK("mitSaverExtension") continue; /* don't save */
856 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
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;
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 /* ignore this, it is evil. */
1122 p->use_mit_saver_extension = get_boolean_resource (dpy,
1123 "mitSaverExtension",
1126 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1127 "sgiSaverExtension",
1130 p->use_xinput_extension = get_boolean_resource (dpy, "xinputExtensionDev",
1133 p->use_proc_interrupts = get_boolean_resource (dpy,
1134 "procInterrupts", "Boolean");
1136 p->getviewport_full_of_lies_p =
1137 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1139 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1142 char *s = get_string_resource (dpy, "selected", "Integer");
1144 p->selected_hack = -1;
1146 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1148 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1149 p->selected_hack = -1;
1153 char *s = get_string_resource (dpy, "mode", "Mode");
1154 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1155 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1156 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1157 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1158 else p->mode = RANDOM_HACKS;
1163 char *s = get_string_resource (dpy, "textMode", "TextMode");
1164 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1165 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1166 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1167 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1168 else p->tmode = TEXT_DATE;
1172 if (system_default_screenhack_count) /* note: first_time is also true */
1174 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1175 system_default_screenhack_count);
1176 free_screenhack_list (system_default_screenhacks,
1177 system_default_screenhack_count);
1178 system_default_screenhacks = 0;
1179 system_default_screenhack_count = 0;
1185 p->verbose_p = True;
1186 p->timestamp_p = True;
1187 p->initial_delay = 0;
1190 /* Throttle the various timeouts to reasonable values after reading the
1192 stop_the_insanity (p);
1196 /* If there are any hacks in the system-wide defaults that are not in
1197 the ~/.xscreensaver file, add the new ones to the end of the list.
1198 This does *not* actually save the file.
1201 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1202 screenhack **system_list, int system_count)
1204 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1209 for (i = 0; i < system_count; i++)
1212 Bool matched_p = False;
1214 for (j = 0; j < p->screenhacks_count; j++)
1217 if (!system_list[i]->name)
1218 system_list[i]->name = make_hack_name (dpy,
1219 system_list[i]->command);
1221 name = p->screenhacks[j]->name;
1223 name = make_hack_name (dpy, p->screenhacks[j]->command);
1225 matched_p = !strcasecmp (name, system_list[i]->name);
1227 if (name != p->screenhacks[j]->name)
1236 /* We have an entry in the system-wide list that is not in the
1237 user's .xscreensaver file. Add it to the end.
1238 Note that p->screenhacks is a single malloc block, not a
1239 linked list, so we have to realloc it.
1241 screenhack *oh = system_list[i];
1242 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1244 if (made_space == 0)
1247 p->screenhacks = (screenhack **)
1248 realloc (p->screenhacks,
1249 (p->screenhacks_count + made_space + 1)
1250 * sizeof(screenhack));
1251 if (!p->screenhacks) abort();
1254 nh->enabled_p = oh->enabled_p;
1255 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1256 nh->name = oh->name ? strdup(oh->name) : 0;
1257 nh->command = oh->command ? strdup(oh->command) : 0;
1259 p->screenhacks[p->screenhacks_count++] = nh;
1260 p->screenhacks[p->screenhacks_count] = 0;
1264 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1265 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1273 /* Parsing the programs resource.
1277 parse_screenhack (const char *line)
1279 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1282 h->enabled_p = True;
1284 while (isspace(*line)) line++; /* skip whitespace */
1285 if (*line == '-') /* handle "-" */
1287 h->enabled_p = False;
1289 while (isspace(*line)) line++; /* skip whitespace */
1292 s = line; /* handle "visual:" */
1293 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1299 h->visual = (char *) malloc (line-s+1);
1300 strncpy (h->visual, s, line-s);
1301 h->visual[line-s] = 0;
1302 if (*line == ':') line++; /* skip ":" */
1303 while (isspace(*line)) line++; /* skip whitespace */
1306 if (*line == '"') /* handle "name" */
1310 while (*line && *line != '"')
1312 h->name = (char *) malloc (line-s+1);
1313 strncpy (h->name, s, line-s);
1314 h->name[line-s] = 0;
1315 if (*line == '"') line++; /* skip "\"" */
1316 while (isspace(*line)) line++; /* skip whitespace */
1319 h->command = format_command (line, False); /* handle command */
1325 format_command (const char *cmd, Bool wrap_p)
1329 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1330 const char *in = cmd;
1334 /* shrink all whitespace to one space, for the benefit of the "demo"
1335 mode display. We only do this when we can easily tell that the
1336 whitespace is not significant (no shell metachars).
1340 case '\'': case '"': case '`': case '\\':
1341 /* Metachars are scary. Copy the rest of the line unchanged. */
1343 *out++ = *in++, col++;
1346 case ' ': case '\t':
1347 /* Squeeze all other whitespace down to one space. */
1348 while (*in == ' ' || *in == '\t')
1350 *out++ = ' ', col++;
1354 /* Copy other chars unchanged. */
1355 *out++ = *in++, col++;
1362 /* Strip trailing whitespace */
1363 while (out > cmd2 && isspace (out[-1]))
1370 /* Returns a new string describing the shell command.
1371 This may be just the name of the program, capitalized.
1372 It also may be something from the resource database (gotten
1373 by looking for "hacks.XYZ.name", where XYZ is the program.)
1376 make_hack_name (Display *dpy, const char *shell_command)
1378 char *s = strdup (shell_command);
1382 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1389 s2 = strrchr (s, '/'); /* if pathname, take last component */
1397 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1400 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1401 s2 = get_string_resource (dpy, res_name, res_name);
1408 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1409 if (*s2 >= 'A' && *s2 <= 'Z')
1412 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1414 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1416 if (s[0] == 'G' && s[1] == 'l' &&
1417 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1425 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1432 char *def_name = make_hack_name (dpy, hack->command);
1434 /* Don't ever write out a name for a hack if it's the same as the default.
1436 if (hack->name && !strcmp (hack->name, def_name))
1443 size = (2 * (strlen(hack->command) +
1444 (hack->visual ? strlen(hack->visual) : 0) +
1445 (hack->name ? strlen(hack->name) : 0) +
1447 h2 = (char *) malloc (size);
1450 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1452 if (hack->visual && *hack->visual) /* write visual name */
1454 if (hack->enabled_p) *out++ = ' ';
1456 strcpy (out, hack->visual);
1457 out += strlen (hack->visual);
1463 col = string_columns (h2, strlen (h2), 0);
1465 if (hack->name && *hack->name) /* write pretty name */
1467 int L = (strlen (hack->name) + 2);
1469 out = stab_to (out, col, tab - L - 2);
1473 strcpy (out, hack->name);
1474 out += strlen (hack->name);
1478 col = string_columns (h2, strlen (h2), 0);
1479 if (wrap_p && col >= tab)
1480 out = stab_to (out, col, 77);
1484 if (out >= h2+size) abort();
1488 col = string_columns (h2, strlen (h2), 0);
1489 out = stab_to (out, col, tab); /* indent */
1491 if (out >= h2+size) abort();
1492 s = format_command (hack->command, wrap_p);
1503 get_screenhacks (Display *dpy, saver_preferences *p)
1511 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1512 if (d && !*d) { free(d); d = 0; }
1514 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1515 if (d && !*d) { free(d); d = 0; }
1520 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1521 see the manual for details.\n", blurb());
1525 d = get_string_resource (dpy, "programs", "Programs");
1527 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1529 p->screenhacks_count = 0;
1537 /* Count up the number of newlines (which will be equal to or larger than
1538 one less than the number of hacks.)
1540 for (i = j = 0; d[i]; i++)
1545 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1547 /* Iterate over the lines in `d' (the string with newlines)
1548 and make new strings to stuff into the `screenhacks' array.
1550 p->screenhacks_count = 0;
1551 while (start < size)
1553 /* skip forward over whitespace. */
1554 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1557 /* skip forward to newline or end of string. */
1559 while (d[end] != 0 && d[end] != '\n')
1562 /* null terminate. */
1565 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1566 if (p->screenhacks_count >= i)
1574 if (p->screenhacks_count == 0)
1576 free (p->screenhacks);
1582 /* Make sure all the values in the preferences struct are sane.
1585 stop_the_insanity (saver_preferences *p)
1587 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1588 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1589 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1590 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1591 if (p->notice_events_timeout <= 0)
1592 p->notice_events_timeout = 10000; /* 10 secs */
1593 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1595 if (! p->fade_p) p->unfade_p = False;
1597 /* The DPMS settings may have the value 0.
1598 But if they are negative, or are a range less than 10 seconds,
1599 reset them to sensible defaults. (Since that must be a mistake.)
1601 if (p->dpms_standby != 0 &&
1602 p->dpms_standby < 10 * 1000)
1603 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1604 if (p->dpms_suspend != 0 &&
1605 p->dpms_suspend < 10 * 1000)
1606 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1607 if (p->dpms_off != 0 &&
1608 p->dpms_off < 10 * 1000)
1609 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1611 /* suspend may not be greater than off, unless off is 0.
1612 standby may not be greater than suspend, unless suspend is 0.
1614 if (p->dpms_off != 0 &&
1615 p->dpms_suspend > p->dpms_off)
1616 p->dpms_suspend = p->dpms_off;
1617 if (p->dpms_suspend != 0 &&
1618 p->dpms_standby > p->dpms_suspend)
1619 p->dpms_standby = p->dpms_suspend;
1621 /* These fixes above ignores the case
1622 suspend = 0 and standby > off ...
1624 if (p->dpms_off != 0 &&
1625 p->dpms_standby > p->dpms_off)
1626 p->dpms_standby = p->dpms_off;
1629 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1630 p->dpms_suspend == 0 &&
1632 p->dpms_enabled_p = False;
1635 /* Set watchdog timeout to about half of the cycle timeout, but
1636 don't let it be faster than 1/2 minute or slower than 1 minute.
1638 p->watchdog_timeout = p->cycle * 0.6;
1639 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1640 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1642 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1643 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;