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",
306 "overlayTextBackground", /* not saved -- X resources only */
307 "overlayTextForeground", /* not saved -- X resources only */
308 "bourneShell", /* not saved -- X resources only */
316 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
318 for (s2 = s; *s2; s2++)
320 for (s2--; s2 >= s; s2--)
321 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
333 handle_entry (XrmDatabase *db, const char *key, const char *value,
334 const char *filename, int line)
337 for (i = 0; prefs[i]; i++)
338 if (*prefs[i] && !strcasecmp(key, prefs[i]))
340 char *val = strdup(value);
341 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
342 strcpy(spec, progclass);
344 strcat(spec, prefs[i]);
346 XrmPutStringResource (db, spec, val);
353 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
354 blurb(), filename, line, key);
360 parse_init_file (saver_preferences *p)
362 time_t write_date = 0;
363 const char *name = init_file_name();
372 if (stat(name, &st) != 0)
374 p->init_file_date = 0;
378 in = fopen(name, "r");
381 char *buf = (char *) malloc(1024 + strlen(name));
382 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
388 if (fstat (fileno(in), &st) == 0)
390 write_date = st.st_mtime;
394 char *buf = (char *) malloc(1024 + strlen(name));
395 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
401 buf = (char *) malloc(buf_size);
403 while (fgets (buf, buf_size-1, in))
410 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
411 buf[L-2] == '\\')) /* or line ended with backslash */
413 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
419 buf = (char *) realloc(buf, buf_size);
423 if (!fgets (buf + L, buf_size-L-1, in))
428 /* Now handle other backslash escapes. */
431 for (i = 0; buf[i]; i++)
436 case 'n': buf[i] = '\n'; break;
437 case 'r': buf[i] = '\r'; break;
438 case 't': buf[i] = '\t'; break;
439 default: buf[i] = buf[i+1]; break;
441 for (j = i+2; buf[j]; j++)
449 if (*key == '#' || *key == '!' || *key == ';' ||
450 *key == '\n' || *key == 0)
453 value = strchr (key, ':');
456 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
463 value = strip(value);
467 handle_entry (&p->db, key, value, name, line);
472 p->init_file_date = write_date;
478 init_file_changed_p (saver_preferences *p)
480 const char *name = init_file_name();
483 if (!name) return False;
485 if (stat(name, &st) != 0)
488 if (p->init_file_date == st.st_mtime)
499 tab_to (FILE *out, int from, int to)
502 int to_mod = (to / tab_width) * tab_width;
503 while (from < to_mod)
506 from = (((from / tab_width) + 1) * tab_width);
517 stab_to (char *out, int from, int to)
520 int to_mod = (to / tab_width) * tab_width;
521 while (from < to_mod)
524 from = (((from / tab_width) + 1) * tab_width);
535 string_columns (const char *string, int length, int start)
539 const char *end = string + length;
544 else if (*string == '\t')
545 col = (((col / tab_width) + 1) * tab_width);
555 write_entry (FILE *out, const char *key, const char *value)
557 char *v = strdup(value ? value : "");
561 Bool programs_p = (!strcmp(key, "programs"));
562 int tab = (programs_p ? 32 : 16);
565 fprintf(out, "%s:", key);
566 col = strlen(key) + 1;
568 if (strlen(key) > 14)
569 col = tab_to (out, col, 20);
575 nl = strchr(v2, '\n');
579 if (first && programs_p)
581 col = tab_to (out, col, 77);
582 fprintf (out, " \\\n");
590 col = tab_to (out, col, 75);
591 fprintf (out, " \\n\\\n");
596 col = tab_to (out, col, tab);
599 string_columns(v2, strlen (v2), col) + col > 75)
606 while (v2[end] == ' ' || v2[end] == '\t')
608 while (v2[end] != ' ' && v2[end] != '\t' &&
609 v2[end] != '\n' && v2[end] != 0)
611 if (string_columns (v2 + start, (end - start), col) >= 74)
613 col = tab_to (out, col, 75);
614 fprintf(out, " \\\n");
615 col = tab_to (out, 0, tab + 2);
616 while (v2[start] == ' ' || v2[start] == '\t')
620 col = string_columns (v2 + start, (end - start), col);
622 fputc(v2[start++], out);
627 fprintf (out, "%s", v2);
628 col += string_columns(v2, strlen (v2), col);
642 write_init_file (Display *dpy,
643 saver_preferences *p, const char *version_string,
647 const char *name = init_file_name();
648 const char *tmp_name = init_file_tmp_name();
649 char *n2 = chase_symlinks (name);
653 /* Kludge, since these aren't in the saver_preferences struct as strings...
657 Bool overlay_stderr_p;
665 /* Throttle the various timeouts to reasonable values before writing
667 stop_the_insanity (p);
671 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
674 out = fopen(tmp_name, "w");
677 char *buf = (char *) malloc(1024 + strlen(name));
678 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
684 /* Give the new .xscreensaver file the same permissions as the old one;
685 except ensure that it is readable and writable by owner, and not
686 executable. Extra hack: if we're running as root, make the file
687 be world-readable (so that the daemon, running as "nobody", will
688 still be able to read it.)
690 if (stat(name, &st) == 0)
692 mode_t mode = st.st_mode;
693 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
694 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
696 if (getuid() == (uid_t) 0) /* read by group/other */
697 mode |= S_IRGRP | S_IROTH;
699 if (fchmod (fileno(out), mode) != 0)
701 char *buf = (char *) malloc(1024 + strlen(name));
702 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
703 tmp_name, (unsigned int) mode);
710 /* Kludge, since these aren't in the saver_preferences struct... */
711 visual_name = get_string_resource (dpy, "visualID", "VisualID");
713 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
714 stderr_font = get_string_resource (dpy, "font", "Font");
719 char **hack_strings = (char **)
720 calloc (p->screenhacks_count, sizeof(char *));
722 for (j = 0; j < p->screenhacks_count; j++)
724 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
725 i += strlen (hack_strings[j]);
729 ss = programs = (char *) malloc(i + 10);
731 for (j = 0; j < p->screenhacks_count; j++)
733 strcat (ss, hack_strings[j]);
734 free (hack_strings[j]);
743 struct passwd *pw = getpwuid (getuid ());
744 char *whoami = (pw && pw->pw_name && *pw->pw_name
747 time_t now = time ((time_t *) 0);
748 char *timestr = (char *) ctime (&now);
749 char *nl = (char *) strchr (timestr, '\n');
752 "# %s Preferences File\n"
753 "# Written by %s %s for %s on %s.\n"
754 "# http://www.jwz.org/xscreensaver/\n"
756 progclass, progname, version_string, whoami, timestr);
759 for (j = 0; prefs[j]; j++)
762 const char *pr = prefs[j];
763 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
777 # define CHECK(X) else if (!strcmp(pr, X))
779 CHECK("timeout") type = pref_time, t = p->timeout;
780 CHECK("cycle") type = pref_time, t = p->cycle;
781 CHECK("lock") type = pref_bool, b = p->lock_p;
782 CHECK("lockVTs") continue; /* don't save, unused */
783 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
784 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
785 CHECK("visualID") type = pref_str, s = visual_name;
786 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
787 CHECK("verbose") type = pref_bool, b = p->verbose_p;
788 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
789 CHECK("splash") type = pref_bool, b = p->splash_p;
790 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
792 CHECK("quad") type = pref_bool, b = p->quad_p;
793 # else /* !QUAD_MODE */
794 CHECK("quad") continue; /* don't save */
795 # endif /* !QUAD_MODE */
796 CHECK("demoCommand") type = pref_str, s = p->demo_command;
797 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
798 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
799 CHECK("helpURL") continue; /* don't save */
800 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
801 CHECK("loadURL") continue; /* don't save */
802 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
803 CHECK("newLoginCommand") continue; /* don't save */
804 CHECK("nice") type = pref_int, i = p->nice_inferior;
805 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
806 CHECK("fade") type = pref_bool, b = p->fade_p;
807 CHECK("unfade") type = pref_bool, b = p->unfade_p;
808 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
809 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
810 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
811 CHECK("captureStdout") continue; /* don't save */
812 CHECK("logFile") continue; /* don't save */
813 CHECK("ignoreUninstalledPrograms")
814 type = pref_bool, b = p->ignore_uninstalled_p;
816 CHECK("font") type = pref_str, s = stderr_font;
818 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
819 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
820 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
821 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
823 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
824 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
825 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
826 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
828 CHECK("mode") type = pref_str,
829 s = (p->mode == ONE_HACK ? "one" :
830 p->mode == BLANK_ONLY ? "blank" :
831 p->mode == DONT_BLANK ? "off" :
832 p->mode == RANDOM_HACKS_SAME
835 CHECK("selected") type = pref_int, i = p->selected_hack;
837 CHECK("textMode") type = pref_str,
838 s = (p->tmode == TEXT_URL ? "url" :
839 p->tmode == TEXT_LITERAL ? "literal" :
840 p->tmode == TEXT_FILE ? "file" :
841 p->tmode == TEXT_PROGRAM ? "program" :
843 CHECK("textLiteral") type = pref_str, s = p->text_literal;
844 CHECK("textFile") type = pref_str, s = p->text_file;
845 CHECK("textProgram") type = pref_str, s = p->text_program;
846 CHECK("textURL") type = pref_str, s = p->text_url;
848 CHECK("programs") type = pref_str, s = programs;
849 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
850 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
851 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
852 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
853 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
854 CHECK("mitSaverExtension") continue; /* don't save */
855 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
856 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
857 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
858 b = p->getviewport_full_of_lies_p;
859 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
860 CHECK("overlayTextBackground") continue; /* don't save */
861 CHECK("overlayTextForeground") continue; /* don't save */
862 CHECK("bourneShell") continue;
871 sprintf(buf, "%d", i);
875 s = b ? "True" : "False";
879 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
890 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
896 if (i >= (1<<30) && i == ((i >> 30) << 30))
897 sprintf(buf, "%dG", i >> 30);
898 else if (i >= (1<<20) && i == ((i >> 20) << 20))
899 sprintf(buf, "%dM", i >> 20);
900 else if (i >= (1<<10) && i == ((i >> 10) << 10))
901 sprintf(buf, "%dK", i >> 10);
903 sprintf(buf, "%d", i);
912 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
915 write_entry (out, pr, s);
920 if (visual_name) free(visual_name);
921 if (stderr_font) free(stderr_font);
922 if (programs) free(programs);
924 if (fclose(out) == 0)
926 time_t write_date = 0;
928 if (stat(tmp_name, &st) == 0)
930 write_date = st.st_mtime;
934 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
935 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
942 if (rename (tmp_name, name) != 0)
944 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
945 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
946 blurb(), tmp_name, name);
954 p->init_file_date = write_date;
956 /* Since the .xscreensaver file is used for IPC, let's try and make
957 sure that the bits actually land on the disk right away. */
960 status = 0; /* wrote and renamed successfully! */
965 char *buf = (char *) malloc(1024 + strlen(name));
966 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
979 /* Parsing the resource database
983 free_screenhack (screenhack *hack)
985 if (hack->visual) free (hack->visual);
986 if (hack->name) free (hack->name);
987 free (hack->command);
988 memset (hack, 0, sizeof(*hack));
993 free_screenhack_list (screenhack **list, int count)
997 for (i = 0; i < count; i++)
999 free_screenhack (list[i]);
1005 /* Populate `saver_preferences' with the contents of the resource database.
1006 Note that this may be called multiple times -- it is re-run each time
1007 the ~/.xscreensaver file is reloaded.
1009 This function can be very noisy, since it issues resource syntax errors
1013 load_init_file (Display *dpy, saver_preferences *p)
1015 static Bool first_time = True;
1017 screenhack **system_default_screenhacks = 0;
1018 int system_default_screenhack_count = 0;
1022 /* Get the programs resource before the .xscreensaver file has been
1023 parsed and merged into the resource database for the first time:
1024 this is the value of *programs from the app-defaults file.
1025 Then clear it out so that it will be parsed again later, after
1026 the init file has been read.
1028 get_screenhacks (dpy, p);
1029 system_default_screenhacks = p->screenhacks;
1030 system_default_screenhack_count = p->screenhacks_count;
1032 p->screenhacks_count = 0;
1035 if (parse_init_file (p) != 0) /* file might have gone away */
1036 if (!first_time) return;
1040 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1041 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1042 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1043 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1044 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1045 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1046 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1047 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1048 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1049 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1050 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1052 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1054 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1056 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1057 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1058 "ignoreUninstalledPrograms",
1061 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1062 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1063 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1064 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1065 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1066 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1067 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1068 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1069 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1070 "windowCreationTimeout",
1073 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1074 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1075 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1076 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1078 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1079 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1080 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1081 p->image_directory = get_string_resource (dpy,
1085 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1086 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1087 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1088 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1090 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1092 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1093 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1094 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1095 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1096 p->new_login_command = get_string_resource(dpy,
1100 /* If "*splash" is unset, default to true. */
1102 char *s = get_string_resource (dpy, "splash", "Boolean");
1109 /* If "*grabDesktopImages" is unset, default to true. */
1111 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1115 p->grab_desktop_p = True;
1118 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1119 #if 0 /* ignore this, it is evil. */
1120 p->use_mit_saver_extension = get_boolean_resource (dpy,
1121 "mitSaverExtension",
1124 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1125 "sgiSaverExtension",
1127 p->use_proc_interrupts = get_boolean_resource (dpy,
1128 "procInterrupts", "Boolean");
1130 p->getviewport_full_of_lies_p =
1131 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1133 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1136 char *s = get_string_resource (dpy, "selected", "Integer");
1138 p->selected_hack = -1;
1140 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1142 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1143 p->selected_hack = -1;
1147 char *s = get_string_resource (dpy, "mode", "Mode");
1148 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1149 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1150 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1151 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1152 else p->mode = RANDOM_HACKS;
1157 char *s = get_string_resource (dpy, "textMode", "TextMode");
1158 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1159 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1160 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1161 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1162 else p->tmode = TEXT_DATE;
1166 if (system_default_screenhack_count) /* note: first_time is also true */
1168 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1169 system_default_screenhack_count);
1170 free_screenhack_list (system_default_screenhacks,
1171 system_default_screenhack_count);
1172 system_default_screenhacks = 0;
1173 system_default_screenhack_count = 0;
1179 p->verbose_p = True;
1180 p->timestamp_p = True;
1181 p->initial_delay = 0;
1184 /* Throttle the various timeouts to reasonable values after reading the
1186 stop_the_insanity (p);
1190 /* If there are any hacks in the system-wide defaults that are not in
1191 the ~/.xscreensaver file, add the new ones to the end of the list.
1192 This does *not* actually save the file.
1195 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1196 screenhack **system_list, int system_count)
1198 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1203 for (i = 0; i < system_count; i++)
1206 Bool matched_p = False;
1208 for (j = 0; j < p->screenhacks_count; j++)
1211 if (!system_list[i]->name)
1212 system_list[i]->name = make_hack_name (dpy,
1213 system_list[i]->command);
1215 name = p->screenhacks[j]->name;
1217 name = make_hack_name (dpy, p->screenhacks[j]->command);
1219 matched_p = !strcasecmp (name, system_list[i]->name);
1221 if (name != p->screenhacks[j]->name)
1230 /* We have an entry in the system-wide list that is not in the
1231 user's .xscreensaver file. Add it to the end.
1232 Note that p->screenhacks is a single malloc block, not a
1233 linked list, so we have to realloc it.
1235 screenhack *oh = system_list[i];
1236 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1238 if (made_space == 0)
1241 p->screenhacks = (screenhack **)
1242 realloc (p->screenhacks,
1243 (p->screenhacks_count + made_space + 1)
1244 * sizeof(screenhack));
1245 if (!p->screenhacks) abort();
1248 nh->enabled_p = oh->enabled_p;
1249 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1250 nh->name = oh->name ? strdup(oh->name) : 0;
1251 nh->command = oh->command ? strdup(oh->command) : 0;
1253 p->screenhacks[p->screenhacks_count++] = nh;
1254 p->screenhacks[p->screenhacks_count] = 0;
1258 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1259 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1267 /* Parsing the programs resource.
1271 parse_screenhack (const char *line)
1273 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1276 h->enabled_p = True;
1278 while (isspace(*line)) line++; /* skip whitespace */
1279 if (*line == '-') /* handle "-" */
1281 h->enabled_p = False;
1283 while (isspace(*line)) line++; /* skip whitespace */
1286 s = line; /* handle "visual:" */
1287 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1293 h->visual = (char *) malloc (line-s+1);
1294 strncpy (h->visual, s, line-s);
1295 h->visual[line-s] = 0;
1296 if (*line == ':') line++; /* skip ":" */
1297 while (isspace(*line)) line++; /* skip whitespace */
1300 if (*line == '"') /* handle "name" */
1304 while (*line && *line != '"')
1306 h->name = (char *) malloc (line-s+1);
1307 strncpy (h->name, s, line-s);
1308 h->name[line-s] = 0;
1309 if (*line == '"') line++; /* skip "\"" */
1310 while (isspace(*line)) line++; /* skip whitespace */
1313 h->command = format_command (line, False); /* handle command */
1319 format_command (const char *cmd, Bool wrap_p)
1323 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1324 const char *in = cmd;
1328 /* shrink all whitespace to one space, for the benefit of the "demo"
1329 mode display. We only do this when we can easily tell that the
1330 whitespace is not significant (no shell metachars).
1334 case '\'': case '"': case '`': case '\\':
1335 /* Metachars are scary. Copy the rest of the line unchanged. */
1337 *out++ = *in++, col++;
1340 case ' ': case '\t':
1341 /* Squeeze all other whitespace down to one space. */
1342 while (*in == ' ' || *in == '\t')
1344 *out++ = ' ', col++;
1348 /* Copy other chars unchanged. */
1349 *out++ = *in++, col++;
1356 /* Strip trailing whitespace */
1357 while (out > cmd2 && isspace (out[-1]))
1364 /* Returns a new string describing the shell command.
1365 This may be just the name of the program, capitalized.
1366 It also may be something from the resource database (gotten
1367 by looking for "hacks.XYZ.name", where XYZ is the program.)
1370 make_hack_name (Display *dpy, const char *shell_command)
1372 char *s = strdup (shell_command);
1376 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1383 s2 = strrchr (s, '/'); /* if pathname, take last component */
1391 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1394 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1395 s2 = get_string_resource (dpy, res_name, res_name);
1402 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1403 if (*s2 >= 'A' && *s2 <= 'Z')
1406 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1408 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1410 if (s[0] == 'G' && s[1] == 'l' &&
1411 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1419 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1426 char *def_name = make_hack_name (dpy, hack->command);
1428 /* Don't ever write out a name for a hack if it's the same as the default.
1430 if (hack->name && !strcmp (hack->name, def_name))
1437 size = (2 * (strlen(hack->command) +
1438 (hack->visual ? strlen(hack->visual) : 0) +
1439 (hack->name ? strlen(hack->name) : 0) +
1441 h2 = (char *) malloc (size);
1444 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1446 if (hack->visual && *hack->visual) /* write visual name */
1448 if (hack->enabled_p) *out++ = ' ';
1450 strcpy (out, hack->visual);
1451 out += strlen (hack->visual);
1457 col = string_columns (h2, strlen (h2), 0);
1459 if (hack->name && *hack->name) /* write pretty name */
1461 int L = (strlen (hack->name) + 2);
1463 out = stab_to (out, col, tab - L - 2);
1467 strcpy (out, hack->name);
1468 out += strlen (hack->name);
1472 col = string_columns (h2, strlen (h2), 0);
1473 if (wrap_p && col >= tab)
1474 out = stab_to (out, col, 77);
1478 if (out >= h2+size) abort();
1482 col = string_columns (h2, strlen (h2), 0);
1483 out = stab_to (out, col, tab); /* indent */
1485 if (out >= h2+size) abort();
1486 s = format_command (hack->command, wrap_p);
1497 get_screenhacks (Display *dpy, saver_preferences *p)
1505 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1506 if (d && !*d) { free(d); d = 0; }
1508 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1509 if (d && !*d) { free(d); d = 0; }
1514 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1515 see the manual for details.\n", blurb());
1519 d = get_string_resource (dpy, "programs", "Programs");
1521 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1523 p->screenhacks_count = 0;
1531 /* Count up the number of newlines (which will be equal to or larger than
1532 one less than the number of hacks.)
1534 for (i = j = 0; d[i]; i++)
1539 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1541 /* Iterate over the lines in `d' (the string with newlines)
1542 and make new strings to stuff into the `screenhacks' array.
1544 p->screenhacks_count = 0;
1545 while (start < size)
1547 /* skip forward over whitespace. */
1548 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1551 /* skip forward to newline or end of string. */
1553 while (d[end] != 0 && d[end] != '\n')
1556 /* null terminate. */
1559 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1560 if (p->screenhacks_count >= i)
1568 if (p->screenhacks_count == 0)
1570 free (p->screenhacks);
1576 /* Make sure all the values in the preferences struct are sane.
1579 stop_the_insanity (saver_preferences *p)
1581 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1582 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1583 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1584 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1585 if (p->notice_events_timeout <= 0)
1586 p->notice_events_timeout = 10000; /* 10 secs */
1587 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1589 if (! p->fade_p) p->unfade_p = False;
1591 /* The DPMS settings may have the value 0.
1592 But if they are negative, or are a range less than 10 seconds,
1593 reset them to sensible defaults. (Since that must be a mistake.)
1595 if (p->dpms_standby != 0 &&
1596 p->dpms_standby < 10 * 1000)
1597 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1598 if (p->dpms_suspend != 0 &&
1599 p->dpms_suspend < 10 * 1000)
1600 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1601 if (p->dpms_off != 0 &&
1602 p->dpms_off < 10 * 1000)
1603 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1605 /* suspend may not be greater than off, unless off is 0.
1606 standby may not be greater than suspend, unless suspend is 0.
1608 if (p->dpms_off != 0 &&
1609 p->dpms_suspend > p->dpms_off)
1610 p->dpms_suspend = p->dpms_off;
1611 if (p->dpms_suspend != 0 &&
1612 p->dpms_standby > p->dpms_suspend)
1613 p->dpms_standby = p->dpms_suspend;
1615 /* These fixes above ignores the case
1616 suspend = 0 and standby > off ...
1618 if (p->dpms_off != 0 &&
1619 p->dpms_standby > p->dpms_off)
1620 p->dpms_standby = p->dpms_off;
1623 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1624 p->dpms_suspend == 0 &&
1626 p->dpms_enabled_p = False;
1629 /* Set watchdog timeout to about half of the cycle timeout, but
1630 don't let it be faster than 1/2 minute or slower than 1 minute.
1632 p->watchdog_timeout = p->cycle * 0.6;
1633 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1634 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1636 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1637 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;