1 /* dotfile.c --- management of the ~/.xscreensaver file.
2 * xscreensaver, Copyright (c) 1998-2006 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 "ignoreUninstalledPrograms",
283 "chooseRandomImages",
297 "windowCreationTimeout",
300 "mitSaverExtension", /* not saved -- obsolete */
302 "GetViewPortIsFullOfLies",
305 "overlayTextBackground", /* not saved -- X resources only */
306 "overlayTextForeground", /* not saved -- X resources only */
307 "bourneShell", /* not saved -- X resources only */
315 while (*s == '\t' || *s == ' ' || *s == '\r' || *s == '\n')
317 for (s2 = s; *s2; s2++)
319 for (s2--; s2 >= s; s2--)
320 if (*s2 == '\t' || *s2 == ' ' || *s2 == '\r' || *s2 =='\n')
332 handle_entry (XrmDatabase *db, const char *key, const char *value,
333 const char *filename, int line)
336 for (i = 0; prefs[i]; i++)
337 if (*prefs[i] && !strcasecmp(key, prefs[i]))
339 char *val = strdup(value);
340 char *spec = (char *) malloc(strlen(progclass) + strlen(prefs[i]) +10);
341 strcpy(spec, progclass);
343 strcat(spec, prefs[i]);
345 XrmPutStringResource (db, spec, val);
352 fprintf(stderr, "%s: %s:%d: unknown option \"%s\"\n",
353 blurb(), filename, line, key);
359 parse_init_file (saver_preferences *p)
361 time_t write_date = 0;
362 const char *name = init_file_name();
371 if (stat(name, &st) != 0)
373 p->init_file_date = 0;
377 in = fopen(name, "r");
380 char *buf = (char *) malloc(1024 + strlen(name));
381 sprintf(buf, "%s: error reading \"%s\"", blurb(), name);
387 if (fstat (fileno(in), &st) == 0)
389 write_date = st.st_mtime;
393 char *buf = (char *) malloc(1024 + strlen(name));
394 sprintf(buf, "%s: couldn't re-stat \"%s\"", blurb(), name);
400 buf = (char *) malloc(buf_size);
402 while (fgets (buf, buf_size-1, in))
409 (buf[L-1] != '\n' || /* whole line didn't fit in buffer */
410 buf[L-2] == '\\')) /* or line ended with backslash */
412 if (buf[L-2] == '\\') /* backslash-newline gets swallowed */
418 buf = (char *) realloc(buf, buf_size);
422 if (!fgets (buf + L, buf_size-L-1, in))
427 /* Now handle other backslash escapes. */
430 for (i = 0; buf[i]; i++)
435 case 'n': buf[i] = '\n'; break;
436 case 'r': buf[i] = '\r'; break;
437 case 't': buf[i] = '\t'; break;
438 default: buf[i] = buf[i+1]; break;
440 for (j = i+2; buf[j]; j++)
448 if (*key == '#' || *key == '!' || *key == ';' ||
449 *key == '\n' || *key == 0)
452 value = strchr (key, ':');
455 fprintf(stderr, "%s: %s:%d: unparsable line: %s\n", blurb(),
462 value = strip(value);
466 handle_entry (&p->db, key, value, name, line);
471 p->init_file_date = write_date;
477 init_file_changed_p (saver_preferences *p)
479 const char *name = init_file_name();
482 if (!name) return False;
484 if (stat(name, &st) != 0)
487 if (p->init_file_date == st.st_mtime)
498 tab_to (FILE *out, int from, int to)
501 int to_mod = (to / tab_width) * tab_width;
502 while (from < to_mod)
505 from = (((from / tab_width) + 1) * tab_width);
516 stab_to (char *out, int from, int to)
519 int to_mod = (to / tab_width) * tab_width;
520 while (from < to_mod)
523 from = (((from / tab_width) + 1) * tab_width);
534 string_columns (const char *string, int length, int start)
538 const char *end = string + length;
543 else if (*string == '\t')
544 col = (((col / tab_width) + 1) * tab_width);
554 write_entry (FILE *out, const char *key, const char *value)
556 char *v = strdup(value ? value : "");
560 Bool programs_p = (!strcmp(key, "programs"));
561 int tab = (programs_p ? 32 : 16);
564 fprintf(out, "%s:", key);
565 col = strlen(key) + 1;
567 if (strlen(key) > 14)
568 col = tab_to (out, col, 20);
574 nl = strchr(v2, '\n');
578 if (first && programs_p)
580 col = tab_to (out, col, 77);
581 fprintf (out, " \\\n");
589 col = tab_to (out, col, 75);
590 fprintf (out, " \\n\\\n");
595 col = tab_to (out, col, tab);
598 string_columns(v2, strlen (v2), col) + col > 75)
605 while (v2[end] == ' ' || v2[end] == '\t')
607 while (v2[end] != ' ' && v2[end] != '\t' &&
608 v2[end] != '\n' && v2[end] != 0)
610 if (string_columns (v2 + start, (end - start), col) >= 74)
612 col = tab_to (out, col, 75);
613 fprintf(out, " \\\n");
614 col = tab_to (out, 0, tab + 2);
615 while (v2[start] == ' ' || v2[start] == '\t')
619 col = string_columns (v2 + start, (end - start), col);
621 fputc(v2[start++], out);
626 fprintf (out, "%s", v2);
627 col += string_columns(v2, strlen (v2), col);
641 write_init_file (Display *dpy,
642 saver_preferences *p, const char *version_string,
646 const char *name = init_file_name();
647 const char *tmp_name = init_file_tmp_name();
648 char *n2 = chase_symlinks (name);
652 /* Kludge, since these aren't in the saver_preferences struct as strings...
656 Bool overlay_stderr_p;
664 /* Throttle the various timeouts to reasonable values before writing
666 stop_the_insanity (p);
670 fprintf (stderr, "%s: writing \"%s\".\n", blurb(), name);
673 out = fopen(tmp_name, "w");
676 char *buf = (char *) malloc(1024 + strlen(name));
677 sprintf(buf, "%s: error writing \"%s\"", blurb(), name);
683 /* Give the new .xscreensaver file the same permissions as the old one;
684 except ensure that it is readable and writable by owner, and not
685 executable. Extra hack: if we're running as root, make the file
686 be world-readable (so that the daemon, running as "nobody", will
687 still be able to read it.)
689 if (stat(name, &st) == 0)
691 mode_t mode = st.st_mode;
692 mode |= S_IRUSR | S_IWUSR; /* read/write by user */
693 mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* executable by none */
695 if (getuid() == (uid_t) 0) /* read by group/other */
696 mode |= S_IRGRP | S_IROTH;
698 if (fchmod (fileno(out), mode) != 0)
700 char *buf = (char *) malloc(1024 + strlen(name));
701 sprintf (buf, "%s: error fchmodding \"%s\" to 0%o", blurb(),
702 tmp_name, (unsigned int) mode);
709 /* Kludge, since these aren't in the saver_preferences struct... */
710 visual_name = get_string_resource (dpy, "visualID", "VisualID");
712 overlay_stderr_p = get_boolean_resource (dpy, "overlayStderr", "Boolean");
713 stderr_font = get_string_resource (dpy, "font", "Font");
718 char **hack_strings = (char **)
719 calloc (p->screenhacks_count, sizeof(char *));
721 for (j = 0; j < p->screenhacks_count; j++)
723 hack_strings[j] = format_hack (dpy, p->screenhacks[j], True);
724 i += strlen (hack_strings[j]);
728 ss = programs = (char *) malloc(i + 10);
730 for (j = 0; j < p->screenhacks_count; j++)
732 strcat (ss, hack_strings[j]);
733 free (hack_strings[j]);
742 struct passwd *pw = getpwuid (getuid ());
743 char *whoami = (pw && pw->pw_name && *pw->pw_name
746 time_t now = time ((time_t *) 0);
747 char *timestr = (char *) ctime (&now);
748 char *nl = (char *) strchr (timestr, '\n');
751 "# %s Preferences File\n"
752 "# Written by %s %s for %s on %s.\n"
753 "# http://www.jwz.org/xscreensaver/\n"
755 progclass, progname, version_string, whoami, timestr);
758 for (j = 0; prefs[j]; j++)
761 const char *pr = prefs[j];
762 enum pref_type { pref_str, pref_int, pref_bool, pref_byte, pref_time
776 # define CHECK(X) else if (!strcmp(pr, X))
778 CHECK("timeout") type = pref_time, t = p->timeout;
779 CHECK("cycle") type = pref_time, t = p->cycle;
780 CHECK("lock") type = pref_bool, b = p->lock_p;
781 CHECK("lockVTs") continue; /* don't save, unused */
782 CHECK("lockTimeout") type = pref_time, t = p->lock_timeout;
783 CHECK("passwdTimeout") type = pref_time, t = p->passwd_timeout;
784 CHECK("visualID") type = pref_str, s = visual_name;
785 CHECK("installColormap") type = pref_bool, b = p->install_cmap_p;
786 CHECK("verbose") type = pref_bool, b = p->verbose_p;
787 CHECK("timestamp") type = pref_bool, b = p->timestamp_p;
788 CHECK("splash") type = pref_bool, b = p->splash_p;
789 CHECK("splashDuration") type = pref_time, t = p->splash_duration;
791 CHECK("quad") type = pref_bool, b = p->quad_p;
792 # else /* !QUAD_MODE */
793 CHECK("quad") continue; /* don't save */
794 # endif /* !QUAD_MODE */
795 CHECK("demoCommand") type = pref_str, s = p->demo_command;
796 CHECK("prefsCommand") type = pref_str, s = p->prefs_command;
797 /* CHECK("helpURL") type = pref_str, s = p->help_url; */
798 CHECK("helpURL") continue; /* don't save */
799 /* CHECK("loadURL") type = pref_str, s = p->load_url_command; */
800 CHECK("loadURL") continue; /* don't save */
801 /* CHECK("newLoginCommand") type = pref_str, s = p->new_login_command; */
802 CHECK("newLoginCommand") continue; /* don't save */
803 CHECK("nice") type = pref_int, i = p->nice_inferior;
804 CHECK("memoryLimit") type = pref_byte, i = p->inferior_memory_limit;
805 CHECK("fade") type = pref_bool, b = p->fade_p;
806 CHECK("unfade") type = pref_bool, b = p->unfade_p;
807 CHECK("fadeSeconds") type = pref_time, t = p->fade_seconds;
808 CHECK("fadeTicks") type = pref_int, i = p->fade_ticks;
809 CHECK("captureStderr") type = pref_bool, b = p->capture_stderr_p;
810 CHECK("captureStdout") continue; /* don't save */
811 CHECK("ignoreUninstalledPrograms")
812 type = pref_bool, b = p->ignore_uninstalled_p;
814 CHECK("font") type = pref_str, s = stderr_font;
816 CHECK("dpmsEnabled") type = pref_bool, b = p->dpms_enabled_p;
817 CHECK("dpmsStandby") type = pref_time, t = p->dpms_standby;
818 CHECK("dpmsSuspend") type = pref_time, t = p->dpms_suspend;
819 CHECK("dpmsOff") type = pref_time, t = p->dpms_off;
821 CHECK("grabDesktopImages") type =pref_bool, b = p->grab_desktop_p;
822 CHECK("grabVideoFrames") type =pref_bool, b = p->grab_video_p;
823 CHECK("chooseRandomImages")type =pref_bool, b = p->random_image_p;
824 CHECK("imageDirectory") type =pref_str, s = p->image_directory;
826 CHECK("mode") type = pref_str,
827 s = (p->mode == ONE_HACK ? "one" :
828 p->mode == BLANK_ONLY ? "blank" :
829 p->mode == DONT_BLANK ? "off" :
830 p->mode == RANDOM_HACKS_SAME
833 CHECK("selected") type = pref_int, i = p->selected_hack;
835 CHECK("textMode") type = pref_str,
836 s = (p->tmode == TEXT_URL ? "url" :
837 p->tmode == TEXT_LITERAL ? "literal" :
838 p->tmode == TEXT_FILE ? "file" :
839 p->tmode == TEXT_PROGRAM ? "program" :
841 CHECK("textLiteral") type = pref_str, s = p->text_literal;
842 CHECK("textFile") type = pref_str, s = p->text_file;
843 CHECK("textProgram") type = pref_str, s = p->text_program;
844 CHECK("textURL") type = pref_str, s = p->text_url;
846 CHECK("programs") type = pref_str, s = programs;
847 CHECK("pointerPollTime") type = pref_time, t = p->pointer_timeout;
848 CHECK("pointerHysteresis")type = pref_int, i = p->pointer_hysteresis;
849 CHECK("windowCreationTimeout")type=pref_time,t= p->notice_events_timeout;
850 CHECK("initialDelay") type = pref_time, t = p->initial_delay;
851 CHECK("sgiSaverExtension")type = pref_bool, b=p->use_sgi_saver_extension;
852 CHECK("mitSaverExtension") continue; /* don't save */
853 CHECK("xidleExtension") type = pref_bool, b = p->use_xidle_extension;
854 CHECK("procInterrupts") type = pref_bool, b = p->use_proc_interrupts;
855 CHECK("GetViewPortIsFullOfLies") type = pref_bool,
856 b = p->getviewport_full_of_lies_p;
857 CHECK("overlayStderr") type = pref_bool, b = overlay_stderr_p;
858 CHECK("overlayTextBackground") continue; /* don't save */
859 CHECK("overlayTextForeground") continue; /* don't save */
860 CHECK("bourneShell") continue;
869 sprintf(buf, "%d", i);
873 s = b ? "True" : "False";
877 unsigned int hour = 0, min = 0, sec = (unsigned int) (t/1000);
888 sprintf (buf, "%u:%02u:%02u", hour, min, sec);
894 if (i >= (1<<30) && i == ((i >> 30) << 30))
895 sprintf(buf, "%dG", i >> 30);
896 else if (i >= (1<<20) && i == ((i >> 20) << 20))
897 sprintf(buf, "%dM", i >> 20);
898 else if (i >= (1<<10) && i == ((i >> 10) << 10))
899 sprintf(buf, "%dK", i >> 10);
901 sprintf(buf, "%d", i);
910 if (pr && (!strcmp(pr, "mode") || !strcmp(pr, "textMode")))
913 write_entry (out, pr, s);
918 if (visual_name) free(visual_name);
919 if (stderr_font) free(stderr_font);
920 if (programs) free(programs);
922 if (fclose(out) == 0)
924 time_t write_date = 0;
926 if (stat(tmp_name, &st) == 0)
928 write_date = st.st_mtime;
932 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
933 sprintf(buf, "%s: couldn't stat \"%s\"", blurb(), tmp_name);
940 if (rename (tmp_name, name) != 0)
942 char *buf = (char *) malloc(1024 + strlen(tmp_name) + strlen(name));
943 sprintf(buf, "%s: error renaming \"%s\" to \"%s\"",
944 blurb(), tmp_name, name);
952 p->init_file_date = write_date;
954 /* Since the .xscreensaver file is used for IPC, let's try and make
955 sure that the bits actually land on the disk right away. */
958 status = 0; /* wrote and renamed successfully! */
963 char *buf = (char *) malloc(1024 + strlen(name));
964 sprintf(buf, "%s: error closing \"%s\"", blurb(), name);
977 /* Parsing the resource database
981 free_screenhack (screenhack *hack)
983 if (hack->visual) free (hack->visual);
984 if (hack->name) free (hack->name);
985 free (hack->command);
986 memset (hack, 0, sizeof(*hack));
991 free_screenhack_list (screenhack **list, int count)
995 for (i = 0; i < count; i++)
997 free_screenhack (list[i]);
1003 /* Populate `saver_preferences' with the contents of the resource database.
1004 Note that this may be called multiple times -- it is re-run each time
1005 the ~/.xscreensaver file is reloaded.
1007 This function can be very noisy, since it issues resource syntax errors
1011 load_init_file (Display *dpy, saver_preferences *p)
1013 static Bool first_time = True;
1015 screenhack **system_default_screenhacks = 0;
1016 int system_default_screenhack_count = 0;
1020 /* Get the programs resource before the .xscreensaver file has been
1021 parsed and merged into the resource database for the first time:
1022 this is the value of *programs from the app-defaults file.
1023 Then clear it out so that it will be parsed again later, after
1024 the init file has been read.
1026 get_screenhacks (dpy, p);
1027 system_default_screenhacks = p->screenhacks;
1028 system_default_screenhack_count = p->screenhacks_count;
1030 p->screenhacks_count = 0;
1033 if (parse_init_file (p) != 0) /* file might have gone away */
1034 if (!first_time) return;
1038 p->xsync_p = get_boolean_resource (dpy, "synchronous", "Synchronous");
1039 p->verbose_p = get_boolean_resource (dpy, "verbose", "Boolean");
1040 p->timestamp_p = get_boolean_resource (dpy, "timestamp", "Boolean");
1041 p->lock_p = get_boolean_resource (dpy, "lock", "Boolean");
1042 p->fade_p = get_boolean_resource (dpy, "fade", "Boolean");
1043 p->unfade_p = get_boolean_resource (dpy, "unfade", "Boolean");
1044 p->fade_seconds = 1000 * get_seconds_resource (dpy, "fadeSeconds", "Time");
1045 p->fade_ticks = get_integer_resource (dpy, "fadeTicks", "Integer");
1046 p->install_cmap_p = get_boolean_resource (dpy, "installColormap", "Boolean");
1047 p->nice_inferior = get_integer_resource (dpy, "nice", "Nice");
1048 p->inferior_memory_limit = get_byte_resource (dpy, "memoryLimit",
1050 p->splash_p = get_boolean_resource (dpy, "splash", "Boolean");
1052 p->quad_p = get_boolean_resource (dpy, "quad", "Boolean");
1054 p->capture_stderr_p = get_boolean_resource (dpy, "captureStderr", "Boolean");
1055 p->ignore_uninstalled_p = get_boolean_resource (dpy,
1056 "ignoreUninstalledPrograms",
1059 p->initial_delay = 1000 * get_seconds_resource (dpy, "initialDelay", "Time");
1060 p->splash_duration = 1000 * get_seconds_resource (dpy, "splashDuration", "Time");
1061 p->timeout = 1000 * get_minutes_resource (dpy, "timeout", "Time");
1062 p->lock_timeout = 1000 * get_minutes_resource (dpy, "lockTimeout", "Time");
1063 p->cycle = 1000 * get_minutes_resource (dpy, "cycle", "Time");
1064 p->passwd_timeout = 1000 * get_seconds_resource (dpy, "passwdTimeout", "Time");
1065 p->pointer_timeout = 1000 * get_seconds_resource (dpy, "pointerPollTime", "Time");
1066 p->pointer_hysteresis = get_integer_resource (dpy, "pointerHysteresis","Integer");
1067 p->notice_events_timeout = 1000*get_seconds_resource(dpy,
1068 "windowCreationTimeout",
1071 p->dpms_enabled_p = get_boolean_resource (dpy, "dpmsEnabled", "Boolean");
1072 p->dpms_standby = 1000 * get_minutes_resource (dpy, "dpmsStandby", "Time");
1073 p->dpms_suspend = 1000 * get_minutes_resource (dpy, "dpmsSuspend", "Time");
1074 p->dpms_off = 1000 * get_minutes_resource (dpy, "dpmsOff", "Time");
1076 p->grab_desktop_p = get_boolean_resource (dpy, "grabDesktopImages", "Boolean");
1077 p->grab_video_p = get_boolean_resource (dpy, "grabVideoFrames", "Boolean");
1078 p->random_image_p = get_boolean_resource (dpy, "chooseRandomImages", "Boolean");
1079 p->image_directory = get_string_resource (dpy,
1083 p->text_literal = get_string_resource (dpy, "textLiteral", "TextLiteral");
1084 p->text_file = get_string_resource (dpy, "textFile", "TextFile");
1085 p->text_program = get_string_resource (dpy, "textProgram", "TextProgram");
1086 p->text_url = get_string_resource (dpy, "textURL", "TextURL");
1088 p->shell = get_string_resource (dpy, "bourneShell", "BourneShell");
1090 p->demo_command = get_string_resource(dpy, "demoCommand", "URL");
1091 p->prefs_command = get_string_resource(dpy, "prefsCommand", "URL");
1092 p->help_url = get_string_resource(dpy, "helpURL", "URL");
1093 p->load_url_command = get_string_resource(dpy, "loadURL", "LoadURL");
1094 p->new_login_command = get_string_resource(dpy,
1098 /* If "*splash" is unset, default to true. */
1100 char *s = get_string_resource (dpy, "splash", "Boolean");
1107 /* If "*grabDesktopImages" is unset, default to true. */
1109 char *s = get_string_resource (dpy, "grabDesktopImages", "Boolean");
1113 p->grab_desktop_p = True;
1116 p->use_xidle_extension = get_boolean_resource (dpy, "xidleExtension","Boolean");
1117 #if 0 /* ignore this, it is evil. */
1118 p->use_mit_saver_extension = get_boolean_resource (dpy,
1119 "mitSaverExtension",
1122 p->use_sgi_saver_extension = get_boolean_resource (dpy,
1123 "sgiSaverExtension",
1125 p->use_proc_interrupts = get_boolean_resource (dpy,
1126 "procInterrupts", "Boolean");
1128 p->getviewport_full_of_lies_p =
1129 get_boolean_resource (dpy, "GetViewPortIsFullOfLies", "Boolean");
1131 get_screenhacks (dpy, p); /* Parse the "programs" resource. */
1134 char *s = get_string_resource (dpy, "selected", "Integer");
1136 p->selected_hack = -1;
1138 p->selected_hack = get_integer_resource (dpy, "selected", "Integer");
1140 if (p->selected_hack < 0 || p->selected_hack >= p->screenhacks_count)
1141 p->selected_hack = -1;
1145 char *s = get_string_resource (dpy, "mode", "Mode");
1146 if (s && !strcasecmp (s, "one")) p->mode = ONE_HACK;
1147 else if (s && !strcasecmp (s, "blank")) p->mode = BLANK_ONLY;
1148 else if (s && !strcasecmp (s, "off")) p->mode = DONT_BLANK;
1149 else if (s && !strcasecmp (s, "random-same")) p->mode = RANDOM_HACKS_SAME;
1150 else p->mode = RANDOM_HACKS;
1155 char *s = get_string_resource (dpy, "textMode", "TextMode");
1156 if (s && !strcasecmp (s, "url")) p->tmode = TEXT_URL;
1157 else if (s && !strcasecmp (s, "literal")) p->tmode = TEXT_LITERAL;
1158 else if (s && !strcasecmp (s, "file")) p->tmode = TEXT_FILE;
1159 else if (s && !strcasecmp (s, "program")) p->tmode = TEXT_PROGRAM;
1160 else p->tmode = TEXT_DATE;
1164 if (system_default_screenhack_count) /* note: first_time is also true */
1166 merge_system_screenhacks (dpy, p, system_default_screenhacks,
1167 system_default_screenhack_count);
1168 free_screenhack_list (system_default_screenhacks,
1169 system_default_screenhack_count);
1170 system_default_screenhacks = 0;
1171 system_default_screenhack_count = 0;
1177 p->verbose_p = True;
1178 p->timestamp_p = True;
1179 p->initial_delay = 0;
1182 /* Throttle the various timeouts to reasonable values after reading the
1184 stop_the_insanity (p);
1188 /* If there are any hacks in the system-wide defaults that are not in
1189 the ~/.xscreensaver file, add the new ones to the end of the list.
1190 This does *not* actually save the file.
1193 merge_system_screenhacks (Display *dpy, saver_preferences *p,
1194 screenhack **system_list, int system_count)
1196 /* Yeah yeah, this is an N^2 operation, but I don't have hashtables handy,
1201 for (i = 0; i < system_count; i++)
1204 Bool matched_p = False;
1206 for (j = 0; j < p->screenhacks_count; j++)
1209 if (!system_list[i]->name)
1210 system_list[i]->name = make_hack_name (dpy,
1211 system_list[i]->command);
1213 name = p->screenhacks[j]->name;
1215 name = make_hack_name (dpy, p->screenhacks[j]->command);
1217 matched_p = !strcasecmp (name, system_list[i]->name);
1219 if (name != p->screenhacks[j]->name)
1228 /* We have an entry in the system-wide list that is not in the
1229 user's .xscreensaver file. Add it to the end.
1230 Note that p->screenhacks is a single malloc block, not a
1231 linked list, so we have to realloc it.
1233 screenhack *oh = system_list[i];
1234 screenhack *nh = (screenhack *) malloc (sizeof(screenhack));
1236 if (made_space == 0)
1239 p->screenhacks = (screenhack **)
1240 realloc (p->screenhacks,
1241 (p->screenhacks_count + made_space + 1)
1242 * sizeof(screenhack));
1243 if (!p->screenhacks) abort();
1246 nh->enabled_p = oh->enabled_p;
1247 nh->visual = oh->visual ? strdup(oh->visual) : 0;
1248 nh->name = oh->name ? strdup(oh->name) : 0;
1249 nh->command = oh->command ? strdup(oh->command) : 0;
1251 p->screenhacks[p->screenhacks_count++] = nh;
1252 p->screenhacks[p->screenhacks_count] = 0;
1256 fprintf (stderr, "%s: noticed new hack: %s\n", blurb(),
1257 (nh->name ? nh->name : make_hack_name (dpy, nh->command)));
1265 /* Parsing the programs resource.
1269 parse_screenhack (const char *line)
1271 screenhack *h = (screenhack *) calloc (1, sizeof(*h));
1274 h->enabled_p = True;
1276 while (isspace(*line)) line++; /* skip whitespace */
1277 if (*line == '-') /* handle "-" */
1279 h->enabled_p = False;
1281 while (isspace(*line)) line++; /* skip whitespace */
1284 s = line; /* handle "visual:" */
1285 while (*line && *line != ':' && *line != '"' && !isspace(*line))
1291 h->visual = (char *) malloc (line-s+1);
1292 strncpy (h->visual, s, line-s);
1293 h->visual[line-s] = 0;
1294 if (*line == ':') line++; /* skip ":" */
1295 while (isspace(*line)) line++; /* skip whitespace */
1298 if (*line == '"') /* handle "name" */
1302 while (*line && *line != '"')
1304 h->name = (char *) malloc (line-s+1);
1305 strncpy (h->name, s, line-s);
1306 h->name[line-s] = 0;
1307 if (*line == '"') line++; /* skip "\"" */
1308 while (isspace(*line)) line++; /* skip whitespace */
1311 h->command = format_command (line, False); /* handle command */
1317 format_command (const char *cmd, Bool wrap_p)
1321 char *cmd2 = (char *) calloc (1, 2 * (strlen (cmd) + 1));
1322 const char *in = cmd;
1326 /* shrink all whitespace to one space, for the benefit of the "demo"
1327 mode display. We only do this when we can easily tell that the
1328 whitespace is not significant (no shell metachars).
1332 case '\'': case '"': case '`': case '\\':
1333 /* Metachars are scary. Copy the rest of the line unchanged. */
1335 *out++ = *in++, col++;
1338 case ' ': case '\t':
1339 /* Squeeze all other whitespace down to one space. */
1340 while (*in == ' ' || *in == '\t')
1342 *out++ = ' ', col++;
1346 /* Copy other chars unchanged. */
1347 *out++ = *in++, col++;
1354 /* Strip trailing whitespace */
1355 while (out > cmd2 && isspace (out[-1]))
1362 /* Returns a new string describing the shell command.
1363 This may be just the name of the program, capitalized.
1364 It also may be something from the resource database (gotten
1365 by looking for "hacks.XYZ.name", where XYZ is the program.)
1368 make_hack_name (Display *dpy, const char *shell_command)
1370 char *s = strdup (shell_command);
1374 for (s2 = s; *s2; s2++) /* truncate at first whitespace */
1381 s2 = strrchr (s, '/'); /* if pathname, take last component */
1389 if (strlen (s) > 50) /* 51 is hereby defined as "unreasonable" */
1392 sprintf (res_name, "hacks.%s.name", s); /* resource? */
1393 s2 = get_string_resource (dpy, res_name, res_name);
1400 for (s2 = s; *s2; s2++) /* if it has any capitals, return it */
1401 if (*s2 >= 'A' && *s2 <= 'Z')
1404 if (s[0] >= 'a' && s[0] <= 'z') /* else cap it */
1406 if (s[0] == 'X' && s[1] >= 'a' && s[1] <= 'z') /* (magic leading X) */
1408 if (s[0] == 'G' && s[1] == 'l' &&
1409 s[2] >= 'a' && s[2] <= 'z') /* (magic leading GL) */
1417 format_hack (Display *dpy, screenhack *hack, Bool wrap_p)
1424 char *def_name = make_hack_name (dpy, hack->command);
1426 /* Don't ever write out a name for a hack if it's the same as the default.
1428 if (hack->name && !strcmp (hack->name, def_name))
1435 size = (2 * (strlen(hack->command) +
1436 (hack->visual ? strlen(hack->visual) : 0) +
1437 (hack->name ? strlen(hack->name) : 0) +
1439 h2 = (char *) malloc (size);
1442 if (!hack->enabled_p) *out++ = '-'; /* write disabled flag */
1444 if (hack->visual && *hack->visual) /* write visual name */
1446 if (hack->enabled_p) *out++ = ' ';
1448 strcpy (out, hack->visual);
1449 out += strlen (hack->visual);
1455 col = string_columns (h2, strlen (h2), 0);
1457 if (hack->name && *hack->name) /* write pretty name */
1459 int L = (strlen (hack->name) + 2);
1461 out = stab_to (out, col, tab - L - 2);
1465 strcpy (out, hack->name);
1466 out += strlen (hack->name);
1470 col = string_columns (h2, strlen (h2), 0);
1471 if (wrap_p && col >= tab)
1472 out = stab_to (out, col, 77);
1476 if (out >= h2+size) abort();
1480 col = string_columns (h2, strlen (h2), 0);
1481 out = stab_to (out, col, tab); /* indent */
1483 if (out >= h2+size) abort();
1484 s = format_command (hack->command, wrap_p);
1495 get_screenhacks (Display *dpy, saver_preferences *p)
1503 d = get_string_resource (dpy, "monoPrograms", "MonoPrograms");
1504 if (d && !*d) { free(d); d = 0; }
1506 d = get_string_resource (dpy, "colorPrograms", "ColorPrograms");
1507 if (d && !*d) { free(d); d = 0; }
1512 "%s: the `monoPrograms' and `colorPrograms' resources are obsolete;\n\
1513 see the manual for details.\n", blurb());
1517 d = get_string_resource (dpy, "programs", "Programs");
1519 free_screenhack_list (p->screenhacks, p->screenhacks_count);
1521 p->screenhacks_count = 0;
1529 /* Count up the number of newlines (which will be equal to or larger than
1530 one less than the number of hacks.)
1532 for (i = j = 0; d[i]; i++)
1537 p->screenhacks = (screenhack **) calloc (j + 1, sizeof (screenhack *));
1539 /* Iterate over the lines in `d' (the string with newlines)
1540 and make new strings to stuff into the `screenhacks' array.
1542 p->screenhacks_count = 0;
1543 while (start < size)
1545 /* skip forward over whitespace. */
1546 while (d[start] == ' ' || d[start] == '\t' || d[start] == '\n')
1549 /* skip forward to newline or end of string. */
1551 while (d[end] != 0 && d[end] != '\n')
1554 /* null terminate. */
1557 p->screenhacks[p->screenhacks_count++] = parse_screenhack (d + start);
1558 if (p->screenhacks_count >= i)
1566 if (p->screenhacks_count == 0)
1568 free (p->screenhacks);
1574 /* Make sure all the values in the preferences struct are sane.
1577 stop_the_insanity (saver_preferences *p)
1579 if (p->passwd_timeout <= 0) p->passwd_timeout = 30000; /* 30 secs */
1580 if (p->timeout < 15000) p->timeout = 15000; /* 15 secs */
1581 if (p->cycle != 0 && p->cycle < 2000) p->cycle = 2000; /* 2 secs */
1582 if (p->pointer_timeout <= 0) p->pointer_timeout = 5000; /* 5 secs */
1583 if (p->notice_events_timeout <= 0)
1584 p->notice_events_timeout = 10000; /* 10 secs */
1585 if (p->fade_seconds <= 0 || p->fade_ticks <= 0)
1587 if (! p->fade_p) p->unfade_p = False;
1589 /* The DPMS settings may have the value 0.
1590 But if they are negative, or are a range less than 10 seconds,
1591 reset them to sensible defaults. (Since that must be a mistake.)
1593 if (p->dpms_standby != 0 &&
1594 p->dpms_standby < 10 * 1000)
1595 p->dpms_standby = 2 * 60 * 60 * 1000; /* 2 hours */
1596 if (p->dpms_suspend != 0 &&
1597 p->dpms_suspend < 10 * 1000)
1598 p->dpms_suspend = 2 * 60 * 60 * 1000; /* 2 hours */
1599 if (p->dpms_off != 0 &&
1600 p->dpms_off < 10 * 1000)
1601 p->dpms_off = 4 * 60 * 60 * 1000; /* 4 hours */
1603 /* suspend may not be greater than off, unless off is 0.
1604 standby may not be greater than suspend, unless suspend is 0.
1606 if (p->dpms_off != 0 &&
1607 p->dpms_suspend > p->dpms_off)
1608 p->dpms_suspend = p->dpms_off;
1609 if (p->dpms_suspend != 0 &&
1610 p->dpms_standby > p->dpms_suspend)
1611 p->dpms_standby = p->dpms_suspend;
1613 /* These fixes above ignores the case
1614 suspend = 0 and standby > off ...
1616 if (p->dpms_off != 0 &&
1617 p->dpms_standby > p->dpms_off)
1618 p->dpms_standby = p->dpms_off;
1621 if (p->dpms_standby == 0 && /* if *all* are 0, then DPMS is disabled */
1622 p->dpms_suspend == 0 &&
1624 p->dpms_enabled_p = False;
1627 /* Set watchdog timeout to about half of the cycle timeout, but
1628 don't let it be faster than 1/2 minute or slower than 1 minute.
1630 p->watchdog_timeout = p->cycle * 0.6;
1631 if (p->watchdog_timeout < 27000) p->watchdog_timeout = 27000; /* 27 secs */
1632 if (p->watchdog_timeout > 57000) p->watchdog_timeout = 57000; /* 57 secs */
1634 if (p->pointer_hysteresis < 0) p->pointer_hysteresis = 0;
1635 if (p->pointer_hysteresis > 100) p->pointer_hysteresis = 100;